device: Refactor device structures

When the device driver model got introduced, there were no concept of
SYS_INIT() which can be seen as software service. These were introduced
afterwards and reusing the device infrastructure for simplicity.
However, it meant to allocate a bit too much for something that only
required an initialization function to be called at right time.

Thus refactoring the devices structures relevantly:
- introducing struct init_entry which is a generic init end-point
- struct deviceconfig is removed and struct device owns everything now.
- SYS_INIT() generates only a struct init_entry via calling
  INIT_ENTRY_DEFINE()
- DEVICE_AND_API_INIT() generates a struct device and calls
  INIT_ENTRY_DEFINE()
- init objects sections are in ROM
- device objects sections are in RAM (but will end up in ROM once they
  will be 'constified')

It also generate a tiny memory gain on both ROM and RAM, which is nice.

Perhaps kernel/device.c could be renamed to something more relevant.

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2020-03-09 11:02:20 +01:00 committed by Carles Cufí
commit 8d7bb8ffd8
11 changed files with 304 additions and 250 deletions

View file

@ -17,8 +17,8 @@
#define BUF_ARRAY_CNT 16 #define BUF_ARRAY_CNT 16
#define TEST_ARR_SIZE 0x1000 #define TEST_ARR_SIZE 0x1000
extern struct device __device_init_start[]; extern struct device __device_start[];
extern struct device __device_init_end[]; extern struct device __device_end[];
static u8_t test_arr[TEST_ARR_SIZE]; static u8_t test_arr[TEST_ARR_SIZE];
@ -229,7 +229,7 @@ static void device_name_get(size_t idx, struct shell_static_entry *entry)
entry->help = NULL; entry->help = NULL;
entry->subcmd = &dsub_device_name; entry->subcmd = &dsub_device_name;
for (dev = __device_init_start; dev != __device_init_end; dev++) { for (dev = __device_start; dev != __device_end; dev++) {
if ((dev->driver_api != NULL) && if ((dev->driver_api != NULL) &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) { strcmp(dev->config->name, "") && (dev->config->name != NULL)) {
if (idx == device_idx) { if (idx == device_idx) {

View file

@ -15,8 +15,8 @@ LOG_MODULE_REGISTER(i2c_shell, CONFIG_LOG_DEFAULT_LEVEL);
#define I2C_DEVICE_PREFIX "I2C_" #define I2C_DEVICE_PREFIX "I2C_"
extern struct device __device_init_start[]; extern struct device __device_start[];
extern struct device __device_init_end[]; extern struct device __device_end[];
static int cmd_i2c_scan(const struct shell *shell, static int cmd_i2c_scan(const struct shell *shell,
size_t argc, char **argv) size_t argc, char **argv)
@ -80,7 +80,7 @@ static void device_name_get(size_t idx, struct shell_static_entry *entry)
entry->help = NULL; entry->help = NULL;
entry->subcmd = &dsub_device_name; entry->subcmd = &dsub_device_name;
for (dev = __device_init_start; dev != __device_init_end; dev++) { for (dev = __device_start; dev != __device_end; dev++) {
if ((dev->driver_api != NULL) && if ((dev->driver_api != NULL) &&
strstr(dev->config->name, I2C_DEVICE_PREFIX) != NULL && strstr(dev->config->name, I2C_DEVICE_PREFIX) != NULL &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) { strcmp(dev->config->name, "") && (dev->config->name != NULL)) {

View file

@ -16,8 +16,8 @@
"when no channels are provided. Syntax:\n" \ "when no channels are provided. Syntax:\n" \
"<device_name> <channel name 0> .. <channel name N>" "<device_name> <channel name 0> .. <channel name N>"
extern struct device __device_init_start[]; extern struct device __device_start[];
extern struct device __device_init_end[]; extern struct device __device_end[];
const char *sensor_channel_name[SENSOR_CHAN_ALL] = { const char *sensor_channel_name[SENSOR_CHAN_ALL] = {
[SENSOR_CHAN_ACCEL_X] = "accel_x", [SENSOR_CHAN_ACCEL_X] = "accel_x",
@ -170,7 +170,7 @@ static void device_name_get(size_t idx, struct shell_static_entry *entry)
entry->help = NULL; entry->help = NULL;
entry->subcmd = &dsub_channel_name; entry->subcmd = &dsub_channel_name;
for (dev = __device_init_start; dev != __device_init_end; dev++) { for (dev = __device_start; dev != __device_end; dev++) {
if ((dev->driver_api != NULL) && if ((dev->driver_api != NULL) &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) { strcmp(dev->config->name, "") && (dev->config->name != NULL)) {
if (idx == device_idx) { if (idx == device_idx) {

View file

@ -7,8 +7,6 @@
#ifndef ZEPHYR_INCLUDE_DEVICE_H_ #ifndef ZEPHYR_INCLUDE_DEVICE_H_
#define ZEPHYR_INCLUDE_DEVICE_H_ #define ZEPHYR_INCLUDE_DEVICE_H_
#include <kernel.h>
/** /**
* @brief Device Driver APIs * @brief Device Driver APIs
* @defgroup io_interfaces Device Driver APIs * @defgroup io_interfaces Device Driver APIs
@ -21,7 +19,7 @@
* @{ * @{
*/ */
#include <zephyr/types.h> #include <init.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -29,6 +27,27 @@ extern "C" {
#define Z_DEVICE_MAX_NAME_LEN 48 #define Z_DEVICE_MAX_NAME_LEN 48
/**
* @def SYS_DEVICE_DEFINE
*
* @brief Run an initialization function at boot at specified priority,
* and define device PM control function.
*
* @details This macro lets you run a function at system boot.
*
* @param drv_name Name of this system device
* @param init_fn Pointer to the boot function to run
* @param pm_control_fn Pointer to device_pm_control function.
* Can be empty function (device_pm_control_nop) if not implemented.
* @param level The initialization level, See Z_INIT_ENTRY_DEFINE for details.
* @param prio Priority within the selected initialization level. See
* Z_INIT_ENTRY_DEFINE for details.
*/
#define SYS_DEVICE_DEFINE(drv_name, init_fn, pm_control_fn, level, prio) \
DEVICE_DEFINE(Z_SYS_NAME(init_fn), drv_name, init_fn, pm_control_fn, \
NULL, NULL, level, prio, NULL)
/** /**
* @def DEVICE_INIT * @def DEVICE_INIT
* *
@ -53,36 +72,10 @@ extern "C" {
* @param cfg_info The address to the structure containing the * @param cfg_info The address to the structure containing the
* configuration information for this instance of the driver. * configuration information for this instance of the driver.
* *
* @param level The initialization level at which configuration occurs. * @param level The initialization level, See Z_INIT_ENTRY_DEFINE for details.
* Must be one of the following symbols, which are listed in the order
* they are performed by the kernel:
* \n
* \li PRE_KERNEL_1: Used for devices that have no dependencies, such as those
* that rely solely on hardware present in the processor/SOC. These devices
* cannot use any kernel services during configuration, since they are not
* yet available.
* \n
* \li PRE_KERNEL_2: Used for devices that rely on the initialization of devices
* initialized as part of the PRE_KERNEL_1 level. These devices cannot use any
* kernel services during configuration, since they are not yet available.
* \n
* \li POST_KERNEL: Used for devices that require kernel services during
* configuration.
* \n
* \li POST_KERNEL_SMP: Used for devices that require kernel services during
* configuration after SMP initialization.
* \n
* \li APPLICATION: Used for application components (i.e. non-kernel components)
* that need automatic configuration. These devices can use all services
* provided by the kernel during configuration.
* *
* @param prio The initialization priority of the device, relative to * @param prio Priority within the selected initialization level. See
* other devices of the same initialization level. Specified as an integer * Z_INIT_ENTRY_DEFINE for details.
* value in the range 0 to 99; lower values indicate earlier initialization.
* Must be a decimal integer literal without leading zeroes or sign (e.g. 32),
* or an equivalent symbolic name (e.g. \#define MY_INIT_PRIO 32); symbolic
* expressions are *not* permitted
* (e.g. CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5).
*/ */
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \ #define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, \ DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, \
@ -102,19 +95,18 @@ extern "C" {
* during initialization. * during initialization.
*/ */
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT #ifndef CONFIG_DEVICE_POWER_MANAGEMENT
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \ #define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
level, prio, api) \ level, prio, api) \
static const struct device_config _CONCAT(__config_, dev_name) __used \ static Z_DECL_ALIGN(struct device) \
__attribute__((__section__(".devconfig.init"))) = { \ _CONCAT(__device_, dev_name) __used \
.name = drv_name, .init = (init_fn), \ __attribute__((__section__(".device_" #level STRINGIFY(prio)))) = { \
.config_info = (cfg_info) \ .name = drv_name, \
}; \ .config_info = (cfg_info), \
static Z_DECL_ALIGN(struct device) _CONCAT(__device_, dev_name) __used \ .driver_api = (api), \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \ .driver_data = (data), \
.config = &_CONCAT(__config_, dev_name), \ }; \
.driver_api = api, \ Z_INIT_ENTRY_DEFINE(_CONCAT(__device_, dev_name), init_fn, \
.driver_data = data \ (&_CONCAT(__device_, dev_name)), level, prio)
}
#else #else
/* /*
* Use the default device_pm_control for devices that do not call the * Use the default device_pm_control for devices that do not call the
@ -146,32 +138,32 @@ extern "C" {
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \ DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
level, prio, api) level, prio, api)
#else #else
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \ #define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
data, cfg_info, level, prio, api) \ data, cfg_info, level, prio, api) \
static struct device_pm _CONCAT(__pm_, dev_name) __used \ static struct device_pm _CONCAT(__pm_, dev_name) __used = { \
= { \ .usage = ATOMIC_INIT(0), \
.usage = ATOMIC_INIT(0), \ .lock = Z_SEM_INITIALIZER( \
.lock = Z_SEM_INITIALIZER( \ _CONCAT(__pm_, dev_name).lock, 1, 1), \
_CONCAT(__pm_, dev_name).lock, 1, 1), \ .signal = K_POLL_SIGNAL_INITIALIZER( \
.signal = K_POLL_SIGNAL_INITIALIZER( \ _CONCAT(__pm_, dev_name).signal), \
_CONCAT(__pm_, dev_name).signal), \ .event = K_POLL_EVENT_INITIALIZER( \
.event = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, \ K_POLL_TYPE_SIGNAL, \
K_POLL_MODE_NOTIFY_ONLY, \ K_POLL_MODE_NOTIFY_ONLY, \
&_CONCAT(__pm_, dev_name).signal), \ &_CONCAT(__pm_, dev_name).signal), \
}; \ }; \
static struct device_config _CONCAT(__config_, dev_name) __used \ static Z_DECL_ALIGN(struct device) \
__attribute__((__section__(".devconfig.init"))) = { \ _CONCAT(__device_, dev_name) __used \
.name = drv_name, .init = (init_fn), \ __attribute__((__section__(".device_" #level STRINGIFY(prio)))) = { \
.device_pm_control = (pm_control_fn), \ .name = drv_name, \
.pm = &_CONCAT(__pm_, dev_name), \ .config_info = (cfg_info), \
.config_info = (cfg_info) \ .driver_api = (api), \
}; \ .driver_data = (data), \
static Z_DECL_ALIGN(struct device) _CONCAT(__device_, dev_name) __used \ .device_pm_control = (pm_control_fn), \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \ .pm = &_CONCAT(__pm_, dev_name), \
.config = &_CONCAT(__config_, dev_name), \ }; \
.driver_api = api, \ Z_INIT_ENTRY_DEFINE(_CONCAT(__device_, dev_name), init_fn, \
.driver_data = data, \ (&_CONCAT(__device_, dev_name)), level, prio)
}
#endif #endif
/** /**
@ -222,8 +214,6 @@ extern "C" {
*/ */
#define DEVICE_DECLARE(name) static struct device DEVICE_NAME_GET(name) #define DEVICE_DECLARE(name) static struct device DEVICE_NAME_GET(name)
struct device;
typedef void (*device_pm_cb)(struct device *dev, typedef void (*device_pm_cb)(struct device *dev,
int status, void *context, void *arg); int status, void *context, void *arg);
@ -250,38 +240,28 @@ struct device_pm {
}; };
/** /**
* @brief Static device information (In ROM) Per driver instance * @brief Runtime device structure (in memory) per driver instance
* *
* @param name name of the device * @param name name of the device
* @param init init function for the driver * @param init init function for the driver
* @param config_info address of driver instance config information * @param config_info address of driver instance config information
*/
struct device_config {
const char *name;
int (*init)(struct device *device);
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
int (*device_pm_control)(struct device *device, u32_t command,
void *context, device_pm_cb cb, void *arg);
struct device_pm *pm;
#endif
const void *config_info;
};
/**
* @brief Runtime device structure (In memory) Per driver instance
* @param device_config Build time config information * @param device_config Build time config information
* @param driver_api pointer to structure containing the API functions for * @param driver_api pointer to structure containing the API functions for
* the device type. This pointer is filled in by the driver at init time. * the device type.
* @param driver_data driver instance data. For driver use only * @param driver_data driver instance data. For driver use only
*/ */
struct device { struct device {
const struct device_config *config; const char *name;
const void *config_info;
const void *driver_api; const void *driver_api;
void *driver_data; void * const driver_data;
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
int (*device_pm_control)(struct device *device, u32_t command,
void *context, device_pm_cb cb, void *arg);
struct device_pm * const pm;
#endif
}; };
void z_sys_device_do_config_level(s32_t level);
/** /**
* @brief Retrieve the device structure for a driver by name * @brief Retrieve the device structure for a driver by name
* *
@ -432,9 +412,9 @@ static inline int device_set_power_state(struct device *device,
u32_t device_power_state, u32_t device_power_state,
device_pm_cb cb, void *arg) device_pm_cb cb, void *arg)
{ {
return device->config->device_pm_control(device, return device->device_pm_control(device,
DEVICE_PM_SET_POWER_STATE, DEVICE_PM_SET_POWER_STATE,
&device_power_state, cb, arg); &device_power_state, cb, arg);
} }
/** /**
@ -453,10 +433,10 @@ static inline int device_set_power_state(struct device *device,
static inline int device_get_power_state(struct device *device, static inline int device_get_power_state(struct device *device,
u32_t *device_power_state) u32_t *device_power_state)
{ {
return device->config->device_pm_control(device, return device->device_pm_control(device,
DEVICE_PM_GET_POWER_STATE, DEVICE_PM_GET_POWER_STATE,
device_power_state, device_power_state,
NULL, NULL); NULL, NULL);
} }
/** /**

View file

@ -7,8 +7,9 @@
#ifndef ZEPHYR_INCLUDE_INIT_H_ #ifndef ZEPHYR_INCLUDE_INIT_H_
#define ZEPHYR_INCLUDE_INIT_H_ #define ZEPHYR_INCLUDE_INIT_H_
#include <device.h>
#include <toolchain.h> #include <toolchain.h>
#include <kernel.h>
#include <zephyr/types.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -29,11 +30,85 @@ extern "C" {
#define _SYS_INIT_LEVEL_SMP 4 #define _SYS_INIT_LEVEL_SMP 4
#endif #endif
struct device;
/**
* @brief Static init entry structure for each device driver or services
*
* @param init init function for the init entry which will take the dev
* attribute as parameter. See below.
* @param dev pointer to a device driver instance structure. Can be NULL
* if the init entry is not used for a device driver but a service.
*/
struct init_entry {
int (*init)(struct device *dev);
struct device *dev;
};
void z_sys_init_run_level(s32_t level);
/* A counter is used to avoid issues when two or more system devices /* A counter is used to avoid issues when two or more system devices
* are declared in the same C file with the same init function. * are declared in the same C file with the same init function.
*/ */
#define Z_SYS_NAME(init_fn) _CONCAT(_CONCAT(sys_init_, init_fn), __COUNTER__) #define Z_SYS_NAME(init_fn) _CONCAT(_CONCAT(sys_init_, init_fn), __COUNTER__)
/**
* @def Z_INIT_ENTRY_DEFINE
*
* @brief Create an init entry object and set it up for boot time initialization
*
* @details This macro defines an init entry object that will be automatically
* configured by the kernel during system initialization. Note that
* init entries will not be accessible from user mode. Also this macro should
* not be used directly, use relevant macro such as SYS_INIT() or
* DEVICE_AND_API_INIT() instead.
*
* @param entry_name Init entry name. It is the name this instance exposes to
* the system.
*
* @param init_fn Address to the init function of the entry.
*
* @param device A device driver instance pointer or NULL
*
* @param level The initialization level at which configuration occurs.
* Must be one of the following symbols, which are listed in the order
* they are performed by the kernel:
* \n
* \li PRE_KERNEL_1: Used for initialization objects that have no dependencies,
* such as those that rely solely on hardware present in the processor/SOC.
* These objects cannot use any kernel services during configuration, since
* they are not yet available.
* \n
* \li PRE_KERNEL_2: Used for initialization objects that rely on objects
* initialized as part of the PRE_KERNEL_1 level. These objects cannot use any
* kernel services during configuration, since they are not yet available.
* \n
* \li POST_KERNEL: Used for initialization objects that require kernel services
* during configuration.
* \n
* \li POST_KERNEL_SMP: Used for initialization objects that require kernel
* services during configuration after SMP initialization.
* \n
* \li APPLICATION: Used for application components (i.e. non-kernel components)
* that need automatic configuration. These objects can use all services
* provided by the kernel during configuration.
*
* @param prio The initialization priority of the object, relative to
* other objects of the same initialization level. Specified as an integer
* value in the range 0 to 99; lower values indicate earlier initialization.
* Must be a decimal integer literal without leading zeroes or sign (e.g. 32),
* or an equivalent symbolic name (e.g. \#define MY_INIT_PRIO 32); symbolic
* expressions are *not* permitted
* (e.g. CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5).
*/
#define Z_INIT_ENTRY_DEFINE(entry_name, init_fn, device, level, prio) \
static const Z_DECL_ALIGN(struct init_entry) \
_CONCAT(__init_, entry_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.init = (init_fn), \
.dev = (device), \
}
/** /**
* @def SYS_INIT * @def SYS_INIT
* *
@ -43,35 +118,13 @@ extern "C" {
* *
* @param init_fn Pointer to the boot function to run * @param init_fn Pointer to the boot function to run
* *
* @param level The initialization level, See DEVICE_AND_API_INIT for details. * @param level The initialization level, See Z_INIT_ENTRY_DEFINE for details.
* *
* @param prio Priority within the selected initialization level. See * @param prio Priority within the selected initialization level. See
* DEVICE_AND_API_INIT for details. * Z_INIT_ENTRY_DEFINE for details.
*/ */
#define SYS_INIT(init_fn, level, prio) \ #define SYS_INIT(init_fn, level, prio) \
DEVICE_AND_API_INIT(Z_SYS_NAME(init_fn), "", init_fn, \ Z_INIT_ENTRY_DEFINE(Z_SYS_NAME(init_fn), init_fn, NULL, level, prio)
NULL, NULL, level, prio, NULL)
/**
* @def SYS_DEVICE_DEFINE
*
* @brief Run an initialization function at boot at specified priority,
* and define device PM control function.
*
* @details This macro lets you run a function at system boot.
*
* @param drv_name Name of this system device
* @param init_fn Pointer to the boot function to run
* @param pm_control_fn Pointer to device_pm_control function.
* Can be empty function (device_pm_control_nop) if not
* implemented.
* @param level The initialization level, See DEVICE_INIT for details.
* @param prio Priority within the selected initialization level. See
* DEVICE_INIT for details.
*/
#define SYS_DEVICE_DEFINE(drv_name, init_fn, pm_control_fn, level, prio) \
DEVICE_DEFINE(Z_SYS_NAME(init_fn), drv_name, init_fn, pm_control_fn, \
NULL, NULL, level, prio, NULL)
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1,10 +1,5 @@
/* SPDX-License-Identifier: Apache-2.0 */ /* SPDX-License-Identifier: Apache-2.0 */
SECTION_DATA_PROLOGUE(initlevel,,)
{
DEVICE_INIT_SECTIONS()
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS) #if defined(CONFIG_GEN_SW_ISR_TABLE) && defined(CONFIG_DYNAMIC_INTERRUPTS)
SECTION_DATA_PROLOGUE(sw_isr_table,,) SECTION_DATA_PROLOGUE(sw_isr_table,,)
{ {
@ -18,12 +13,10 @@
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION) } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#endif #endif
/* verify we don't have rogue .init_<something> initlevel sections */ SECTION_DATA_PROLOGUE(devices,,)
SECTION_DATA_PROLOGUE(initlevel_error,,)
{ {
DEVICE_INIT_UNDEFINED_SECTION() DEVICE_SECTIONS()
} } GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
ASSERT(SIZEOF(initlevel_error) == 0, "Undefined initialization levels used.")
SECTION_DATA_PROLOGUE(initshell,,) SECTION_DATA_PROLOGUE(initshell,,)
{ {

View file

@ -1,5 +1,10 @@
/* SPDX-License-Identifier: Apache-2.0 */ /* SPDX-License-Identifier: Apache-2.0 */
SECTION_PROLOGUE(initlevel,,)
{
INIT_SECTIONS()
} GROUP_LINK_IN(ROMABLE_REGION)
#if defined(CONFIG_GEN_SW_ISR_TABLE) && !defined(CONFIG_DYNAMIC_INTERRUPTS) #if defined(CONFIG_GEN_SW_ISR_TABLE) && !defined(CONFIG_DYNAMIC_INTERRUPTS)
SECTION_PROLOGUE(sw_isr_table,,) SECTION_PROLOGUE(sw_isr_table,,)
{ {
@ -12,6 +17,14 @@
*(_SW_ISR_TABLE_SECTION_NAME) *(_SW_ISR_TABLE_SECTION_NAME)
} GROUP_LINK_IN(ROMABLE_REGION) } GROUP_LINK_IN(ROMABLE_REGION)
#endif #endif
/* verify we don't have rogue .init_<something> initlevel sections */
SECTION_PROLOGUE(initlevel_error,,)
{
INIT_UNDEFINED_SECTION()
}
ASSERT(SIZEOF(initlevel_error) == 0, "Undefined initialization levels used.")
#ifdef CONFIG_CPLUSPLUS #ifdef CONFIG_CPLUSPLUS
SECTION_PROLOGUE(_CTOR_SECTION_NAME,,) SECTION_PROLOGUE(_CTOR_SECTION_NAME,,)
{ {
@ -66,14 +79,6 @@
__app_shmem_regions_end = .; __app_shmem_regions_end = .;
} GROUP_LINK_IN(ROMABLE_REGION) } GROUP_LINK_IN(ROMABLE_REGION)
SECTION_PROLOGUE (devconfig,,)
{
__devconfig_start = .;
*(".devconfig.*")
KEEP(*(SORT_BY_NAME(".devconfig*")))
__devconfig_end = .;
} GROUP_LINK_IN(ROMABLE_REGION)
SECTION_PROLOGUE(net_l2,,) SECTION_PROLOGUE(net_l2,,)
{ {
__net_l2_start = .; __net_l2_start = .;

View file

@ -45,7 +45,7 @@
*/ */
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT #ifdef CONFIG_DEVICE_POWER_MANAGEMENT
#define DEVICE_COUNT \ #define DEVICE_COUNT \
((__device_init_end - __device_init_start) / _DEVICE_STRUCT_SIZEOF) ((__device_end - __device_start) / _DEVICE_STRUCT_SIZEOF)
#define DEV_BUSY_SZ (((DEVICE_COUNT + 31) / 32) * 4) #define DEV_BUSY_SZ (((DEVICE_COUNT + 31) / 32) * 4)
#define DEVICE_BUSY_BITFIELD() \ #define DEVICE_BUSY_BITFIELD() \
FILL(0x00) ; \ FILL(0x00) ; \
@ -57,37 +57,53 @@
#endif #endif
/* /*
* generate a symbol to mark the start of the device initialization objects for * generate a symbol to mark the start of the objects array for
* the specified level, then link all of those objects (sorted by priority); * the specified object and level, then link all of those objects
* ensure the objects aren't discarded if there is no direct reference to them * (sorted by priority). Ensure the objects aren't discarded if there is
* no direct reference to them
*/ */
#define CREATE_OBJ_LEVEL(object, level) \
#define DEVICE_INIT_LEVEL(level) \ __##object##_##level##_start = .; \
__device_##level##_start = .; \ KEEP(*(SORT(.##object##_##level[0-9]))); \
KEEP(*(SORT(.init_##level[0-9]))); \ KEEP(*(SORT(.##object##_##level[1-9][0-9]))); \
KEEP(*(SORT(.init_##level[1-9][0-9]))); \
/* /*
* link in device initialization objects for all devices that are automatically * link in initialization objects for all objects that are automatically
* initialized by the kernel; the objects are sorted in the order they will be * initialized by the kernel; the objects are sorted in the order they will be
* initialized (i.e. ordered by level, sorted by priority within a level) * initialized (i.e. ordered by level, sorted by priority within a level)
*/ */
#define DEVICE_INIT_SECTIONS() \ #define INIT_SECTIONS() \
__device_init_start = .; \ __init_start = .; \
DEVICE_INIT_LEVEL(PRE_KERNEL_1) \ CREATE_OBJ_LEVEL(init, PRE_KERNEL_1) \
DEVICE_INIT_LEVEL(PRE_KERNEL_2) \ CREATE_OBJ_LEVEL(init, PRE_KERNEL_2) \
DEVICE_INIT_LEVEL(POST_KERNEL) \ CREATE_OBJ_LEVEL(init, POST_KERNEL) \
DEVICE_INIT_LEVEL(APPLICATION) \ CREATE_OBJ_LEVEL(init, APPLICATION) \
DEVICE_INIT_LEVEL(SMP) \ CREATE_OBJ_LEVEL(init, SMP) \
__device_init_end = .; \ __init_end = .; \
DEVICE_BUSY_BITFIELD() \
/* define a section for undefined device initialization levels */ /* define a section for undefined device initialization levels */
#define DEVICE_INIT_UNDEFINED_SECTION() \ #define INIT_UNDEFINED_SECTION() \
KEEP(*(SORT(.init_[_A-Z0-9]*))) \ KEEP(*(SORT(.init_[_A-Z0-9]*))) \
/*
* link in devices objects, which are tied to the init ones;
* the objects are thus sorted the same way as their init object parent
* see include/device.h
*/
#define DEVICE_SECTIONS() \
__device_start = .; \
CREATE_OBJ_LEVEL(device, PRE_KERNEL_1) \
CREATE_OBJ_LEVEL(device, PRE_KERNEL_2) \
CREATE_OBJ_LEVEL(device, POST_KERNEL) \
CREATE_OBJ_LEVEL(device, APPLICATION) \
CREATE_OBJ_LEVEL(device, SMP) \
__device_end = .; \
DEVICE_BUSY_BITFIELD() \
/* /*
* link in shell initialization objects for all modules that use shell and * link in shell initialization objects for all modules that use shell and
* their shell commands are automatically initialized by the kernel. * their shell commands are automatically initialized by the kernel.

View file

@ -9,17 +9,20 @@
#include <sys/atomic.h> #include <sys/atomic.h>
#include <syscall_handler.h> #include <syscall_handler.h>
extern struct device __device_init_start[]; extern const struct init_entry __init_start[];
extern struct device __device_PRE_KERNEL_1_start[]; extern const struct init_entry __init_PRE_KERNEL_1_start[];
extern struct device __device_PRE_KERNEL_2_start[]; extern const struct init_entry __init_PRE_KERNEL_2_start[];
extern struct device __device_POST_KERNEL_start[]; extern const struct init_entry __init_POST_KERNEL_start[];
extern struct device __device_APPLICATION_start[]; extern const struct init_entry __init_APPLICATION_start[];
extern struct device __device_init_end[]; extern const struct init_entry __init_end[];
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern struct device __device_SMP_start[]; extern const struct init_entry __init_SMP_start[];
#endif #endif
extern struct device __device_start[];
extern struct device __device_end[];
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT #ifdef CONFIG_DEVICE_POWER_MANAGEMENT
extern u32_t __device_busy_start[]; extern u32_t __device_busy_start[];
extern u32_t __device_busy_end[]; extern u32_t __device_busy_end[];
@ -27,71 +30,72 @@ extern u32_t __device_busy_end[];
#endif #endif
/** /**
* @brief Execute all the device initialization functions at a given level * @brief Execute all the init entry initialization functions at a given level
* *
* @details Invokes the initialization routine for each device object * @details Invokes the initialization routine for each init entry object
* created by the DEVICE_INIT() macro using the specified level. * created by the INIT_ENTRY_DEFINE() macro using the specified level.
* The linker script places the device objects in memory in the order * The linker script places the init entry objects in memory in the order
* they need to be invoked, with symbols indicating where one level leaves * they need to be invoked, with symbols indicating where one level leaves
* off and the next one begins. * off and the next one begins.
* *
* @param level init level to run. * @param level init level to run.
*/ */
void z_sys_device_do_config_level(s32_t level) void z_sys_init_run_level(s32_t level)
{ {
struct device *info; static const struct init_entry *levels[] = {
static struct device *config_levels[] = { __init_PRE_KERNEL_1_start,
__device_PRE_KERNEL_1_start, __init_PRE_KERNEL_2_start,
__device_PRE_KERNEL_2_start, __init_POST_KERNEL_start,
__device_POST_KERNEL_start, __init_APPLICATION_start,
__device_APPLICATION_start,
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
__device_SMP_start, __init_SMP_start,
#endif #endif
/* End marker */ /* End marker */
__device_init_end, __init_end,
}; };
const struct init_entry *entry;
for (info = config_levels[level]; info < config_levels[level+1]; for (entry = levels[level]; entry < levels[level+1]; entry++) {
info++) { struct device *dev = entry->dev;
int retval; int retval;
const struct device_config *device_conf = info->config;
retval = device_conf->init(info); if (dev != NULL) {
z_object_init(dev);
}
retval = entry->init(dev);
if (retval != 0) { if (retval != 0) {
/* Initialization failed. Clear the API struct so that if (dev) {
* device_get_binding() will not succeed for it. /* Initialization failed. Clear the API struct
*/ * so that device_get_binding() will not succeed
info->driver_api = NULL; * for it.
} else { */
z_object_init(info); dev->driver_api = NULL;
}
} }
} }
} }
struct device *z_impl_device_get_binding(const char *name) struct device *z_impl_device_get_binding(const char *name)
{ {
struct device *info; struct device *dev;
/* Split the search into two loops: in the common scenario, where /* Split the search into two loops: in the common scenario, where
* device names are stored in ROM (and are referenced by the user * device names are stored in ROM (and are referenced by the user
* with CONFIG_* macros), only cheap pointer comparisons will be * with CONFIG_* macros), only cheap pointer comparisons will be
* performed. Reserve string comparisons for a fallback. * performed. Reserve string comparisons for a fallback.
*/ */
for (info = __device_init_start; info != __device_init_end; info++) { for (dev = __device_start; dev != __device_end; dev++) {
if ((info->driver_api != NULL) && if ((dev->driver_api != NULL) &&
(info->config->name == name)) { (dev->name == name)) {
return info; return dev;
} }
} }
for (info = __device_init_start; info != __device_init_end; info++) { for (dev = __device_start; dev != __device_end; dev++) {
if (info->driver_api == NULL) { if ((dev->driver_api != NULL) &&
continue; (strcmp(name, dev->name) == 0)) {
} return dev;
if (strcmp(name, info->config->name) == 0) {
return info;
} }
} }
@ -126,8 +130,8 @@ int device_pm_control_nop(struct device *unused_device,
void device_list_get(struct device **device_list, int *device_count) void device_list_get(struct device **device_list, int *device_count)
{ {
*device_list = __device_init_start; *device_list = __device_start;
*device_count = __device_init_end - __device_init_start; *device_count = __device_end - __device_start;
} }
@ -146,7 +150,7 @@ int device_any_busy_check(void)
int device_busy_check(struct device *chk_dev) int device_busy_check(struct device *chk_dev)
{ {
if (atomic_test_bit((const atomic_t *)__device_busy_start, if (atomic_test_bit((const atomic_t *)__device_busy_start,
(chk_dev - __device_init_start))) { (chk_dev - __device_start))) {
return -EBUSY; return -EBUSY;
} }
return 0; return 0;
@ -158,7 +162,7 @@ void device_busy_set(struct device *busy_dev)
{ {
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT #ifdef CONFIG_DEVICE_POWER_MANAGEMENT
atomic_set_bit((atomic_t *) __device_busy_start, atomic_set_bit((atomic_t *) __device_busy_start,
(busy_dev - __device_init_start)); (busy_dev - __device_start));
#else #else
ARG_UNUSED(busy_dev); ARG_UNUSED(busy_dev);
#endif #endif
@ -168,7 +172,7 @@ void device_busy_clear(struct device *busy_dev)
{ {
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT #ifdef CONFIG_DEVICE_POWER_MANAGEMENT
atomic_clear_bit((atomic_t *) __device_busy_start, atomic_clear_bit((atomic_t *) __device_busy_start,
(busy_dev - __device_init_start)); (busy_dev - __device_start));
#else #else
ARG_UNUSED(busy_dev); ARG_UNUSED(busy_dev);
#endif #endif

View file

@ -213,7 +213,7 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
z_sys_post_kernel = true; z_sys_post_kernel = true;
z_sys_device_do_config_level(_SYS_INIT_LEVEL_POST_KERNEL); z_sys_init_run_level(_SYS_INIT_LEVEL_POST_KERNEL);
#if CONFIG_STACK_POINTER_RANDOM #if CONFIG_STACK_POINTER_RANDOM
z_stack_adjust_initialized = 1; z_stack_adjust_initialized = 1;
#endif #endif
@ -234,7 +234,7 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
#endif #endif
/* Final init level before app starts */ /* Final init level before app starts */
z_sys_device_do_config_level(_SYS_INIT_LEVEL_APPLICATION); z_sys_init_run_level(_SYS_INIT_LEVEL_APPLICATION);
#ifdef CONFIG_CPLUSPLUS #ifdef CONFIG_CPLUSPLUS
/* Process the .ctors and .init_array sections */ /* Process the .ctors and .init_array sections */
@ -248,7 +248,7 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
z_smp_init(); z_smp_init();
z_sys_device_do_config_level(_SYS_INIT_LEVEL_SMP); z_sys_init_run_level(_SYS_INIT_LEVEL_SMP);
#endif #endif
#ifdef CONFIG_BOOT_TIME_MEASUREMENT #ifdef CONFIG_BOOT_TIME_MEASUREMENT
@ -488,8 +488,8 @@ FUNC_NORETURN void z_cstart(void)
#endif #endif
/* perform basic hardware initialization */ /* perform basic hardware initialization */
z_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_1); z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);
z_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_2); z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);
#ifdef CONFIG_STACK_CANARIES #ifdef CONFIG_STACK_CANARIES
z_early_boot_rand_get((u8_t *)&stack_guard, sizeof(stack_guard)); z_early_boot_rand_get((u8_t *)&stack_guard, sizeof(stack_guard));

View file

@ -10,18 +10,18 @@
#include <string.h> #include <string.h>
#include <device.h> #include <device.h>
extern struct device __device_init_start[]; extern struct device __device_start[];
extern struct device __device_PRE_KERNEL_1_start[]; extern struct device __device_PRE_KERNEL_1_start[];
extern struct device __device_PRE_KERNEL_2_start[]; extern struct device __device_PRE_KERNEL_2_start[];
extern struct device __device_POST_KERNEL_start[]; extern struct device __device_POST_KERNEL_start[];
extern struct device __device_APPLICATION_start[]; extern struct device __device_APPLICATION_start[];
extern struct device __device_init_end[]; extern struct device __device_end[];
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern struct device __device_SMP_start[]; extern struct device __device_SMP_start[];
#endif #endif
static struct device *config_levels[] = { static struct device *levels[] = {
__device_PRE_KERNEL_1_start, __device_PRE_KERNEL_1_start,
__device_PRE_KERNEL_2_start, __device_PRE_KERNEL_2_start,
__device_POST_KERNEL_start, __device_POST_KERNEL_start,
@ -30,20 +30,20 @@ static struct device *config_levels[] = {
__device_SMP_start, __device_SMP_start,
#endif #endif
/* End marker */ /* End marker */
__device_init_end, __device_end,
}; };
static bool device_get_config_level(const struct shell *shell, int level) static bool device_get_config_level(const struct shell *shell, int level)
{ {
struct device *info; struct device *dev;
bool devices = false; bool devices = false;
for (info = config_levels[level]; info < config_levels[level+1]; for (dev = levels[level]; dev < levels[level+1]; dev++) {
info++) { if (dev->driver_api != NULL) {
if (info->driver_api != NULL) {
devices = true; devices = true;
shell_fprintf(shell, SHELL_NORMAL, "- %s\n", shell_fprintf(shell, SHELL_NORMAL, "- %s\n",
info->config->name); dev->config->name);
} }
} }
return devices; return devices;
@ -86,27 +86,30 @@ static int cmd_device_levels(const struct shell *shell,
static int cmd_device_list(const struct shell *shell, static int cmd_device_list(const struct shell *shell,
size_t argc, char **argv) size_t argc, char **argv)
{ {
struct device *info; struct device *dev;
ARG_UNUSED(argc); ARG_UNUSED(argc);
ARG_UNUSED(argv); ARG_UNUSED(argv);
shell_fprintf(shell, SHELL_NORMAL, "devices:\n"); shell_fprintf(shell, SHELL_NORMAL, "devices:\n");
for (info = __device_init_start; info != __device_init_end; info++) {
if (info->driver_api != NULL) {
shell_fprintf(shell, SHELL_NORMAL, "- %s",
info->config->name);
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
u32_t state = DEVICE_PM_ACTIVE_STATE;
int err;
err = device_get_power_state(info, &state); for (dev = __device_start; dev != __device_end; dev++) {
if (!err) { if (dev->driver_api == NULL) {
shell_fprintf(shell, SHELL_NORMAL, " (%s)", continue;
device_pm_state_str(state));
}
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
shell_fprintf(shell, SHELL_NORMAL, "\n");
} }
shell_fprintf(shell, SHELL_NORMAL, "- %s", dev->config->name);
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
u32_t state = DEVICE_PM_ACTIVE_STATE;
int err;
err = device_get_power_state(dev, &state);
if (!err) {
shell_fprintf(shell, SHELL_NORMAL, " (%s)",
device_pm_state_str(state));
}
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
shell_fprintf(shell, SHELL_NORMAL, "\n");
} }
return 0; return 0;