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 TEST_ARR_SIZE 0x1000
extern struct device __device_init_start[];
extern struct device __device_init_end[];
extern struct device __device_start[];
extern struct device __device_end[];
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->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) &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) {
if (idx == device_idx) {

View file

@ -15,8 +15,8 @@ LOG_MODULE_REGISTER(i2c_shell, CONFIG_LOG_DEFAULT_LEVEL);
#define I2C_DEVICE_PREFIX "I2C_"
extern struct device __device_init_start[];
extern struct device __device_init_end[];
extern struct device __device_start[];
extern struct device __device_end[];
static int cmd_i2c_scan(const struct shell *shell,
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->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) &&
strstr(dev->config->name, I2C_DEVICE_PREFIX) != NULL &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) {

View file

@ -16,8 +16,8 @@
"when no channels are provided. Syntax:\n" \
"<device_name> <channel name 0> .. <channel name N>"
extern struct device __device_init_start[];
extern struct device __device_init_end[];
extern struct device __device_start[];
extern struct device __device_end[];
const char *sensor_channel_name[SENSOR_CHAN_ALL] = {
[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->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) &&
strcmp(dev->config->name, "") && (dev->config->name != NULL)) {
if (idx == device_idx) {

View file

@ -7,8 +7,6 @@
#ifndef ZEPHYR_INCLUDE_DEVICE_H_
#define ZEPHYR_INCLUDE_DEVICE_H_
#include <kernel.h>
/**
* @brief Device Driver APIs
* @defgroup io_interfaces Device Driver APIs
@ -21,7 +19,7 @@
* @{
*/
#include <zephyr/types.h>
#include <init.h>
#ifdef __cplusplus
extern "C" {
@ -29,6 +27,27 @@ extern "C" {
#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
*
@ -53,36 +72,10 @@ extern "C" {
* @param cfg_info The address to the structure containing the
* configuration information for this instance of the driver.
*
* @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 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 level The initialization level, See Z_INIT_ENTRY_DEFINE for details.
*
* @param prio The initialization priority of the device, relative to
* other devices 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).
* @param prio Priority within the selected initialization level. See
* Z_INIT_ENTRY_DEFINE for details.
*/
#define DEVICE_INIT(dev_name, drv_name, init_fn, data, cfg_info, level, prio) \
DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, \
@ -102,19 +95,18 @@ extern "C" {
* during initialization.
*/
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
level, prio, api) \
static const struct device_config _CONCAT(__config_, dev_name) __used \
__attribute__((__section__(".devconfig.init"))) = { \
.name = drv_name, .init = (init_fn), \
.config_info = (cfg_info) \
}; \
static Z_DECL_ALIGN(struct device) _CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.config = &_CONCAT(__config_, dev_name), \
.driver_api = api, \
.driver_data = data \
}
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
level, prio, api) \
static Z_DECL_ALIGN(struct device) \
_CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".device_" #level STRINGIFY(prio)))) = { \
.name = drv_name, \
.config_info = (cfg_info), \
.driver_api = (api), \
.driver_data = (data), \
}; \
Z_INIT_ENTRY_DEFINE(_CONCAT(__device_, dev_name), init_fn, \
(&_CONCAT(__device_, dev_name)), level, prio)
#else
/*
* 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, \
level, prio, api)
#else
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
data, cfg_info, level, prio, api) \
static struct device_pm _CONCAT(__pm_, dev_name) __used \
= { \
.usage = ATOMIC_INIT(0), \
.lock = Z_SEM_INITIALIZER( \
_CONCAT(__pm_, dev_name).lock, 1, 1), \
.signal = K_POLL_SIGNAL_INITIALIZER( \
_CONCAT(__pm_, dev_name).signal), \
.event = K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SIGNAL, \
K_POLL_MODE_NOTIFY_ONLY, \
&_CONCAT(__pm_, dev_name).signal), \
}; \
static struct device_config _CONCAT(__config_, dev_name) __used \
__attribute__((__section__(".devconfig.init"))) = { \
.name = drv_name, .init = (init_fn), \
.device_pm_control = (pm_control_fn), \
.pm = &_CONCAT(__pm_, dev_name), \
.config_info = (cfg_info) \
}; \
static Z_DECL_ALIGN(struct device) _CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
.config = &_CONCAT(__config_, dev_name), \
.driver_api = api, \
.driver_data = data, \
}
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
data, cfg_info, level, prio, api) \
static struct device_pm _CONCAT(__pm_, dev_name) __used = { \
.usage = ATOMIC_INIT(0), \
.lock = Z_SEM_INITIALIZER( \
_CONCAT(__pm_, dev_name).lock, 1, 1), \
.signal = K_POLL_SIGNAL_INITIALIZER( \
_CONCAT(__pm_, dev_name).signal), \
.event = K_POLL_EVENT_INITIALIZER( \
K_POLL_TYPE_SIGNAL, \
K_POLL_MODE_NOTIFY_ONLY, \
&_CONCAT(__pm_, dev_name).signal), \
}; \
static Z_DECL_ALIGN(struct device) \
_CONCAT(__device_, dev_name) __used \
__attribute__((__section__(".device_" #level STRINGIFY(prio)))) = { \
.name = drv_name, \
.config_info = (cfg_info), \
.driver_api = (api), \
.driver_data = (data), \
.device_pm_control = (pm_control_fn), \
.pm = &_CONCAT(__pm_, dev_name), \
}; \
Z_INIT_ENTRY_DEFINE(_CONCAT(__device_, dev_name), init_fn, \
(&_CONCAT(__device_, dev_name)), level, prio)
#endif
/**
@ -222,8 +214,6 @@ extern "C" {
*/
#define DEVICE_DECLARE(name) static struct device DEVICE_NAME_GET(name)
struct device;
typedef void (*device_pm_cb)(struct device *dev,
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 init init function for the driver
* @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 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
*/
struct device {
const struct device_config *config;
const char *name;
const void *config_info;
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
*
@ -432,9 +412,9 @@ static inline int device_set_power_state(struct device *device,
u32_t device_power_state,
device_pm_cb cb, void *arg)
{
return device->config->device_pm_control(device,
DEVICE_PM_SET_POWER_STATE,
&device_power_state, cb, arg);
return device->device_pm_control(device,
DEVICE_PM_SET_POWER_STATE,
&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,
u32_t *device_power_state)
{
return device->config->device_pm_control(device,
DEVICE_PM_GET_POWER_STATE,
device_power_state,
NULL, NULL);
return device->device_pm_control(device,
DEVICE_PM_GET_POWER_STATE,
device_power_state,
NULL, NULL);
}
/**

View file

@ -7,8 +7,9 @@
#ifndef ZEPHYR_INCLUDE_INIT_H_
#define ZEPHYR_INCLUDE_INIT_H_
#include <device.h>
#include <toolchain.h>
#include <kernel.h>
#include <zephyr/types.h>
#ifdef __cplusplus
extern "C" {
@ -29,11 +30,85 @@ extern "C" {
#define _SYS_INIT_LEVEL_SMP 4
#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
* 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__)
/**
* @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
*
@ -43,35 +118,13 @@ extern "C" {
*
* @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
* DEVICE_AND_API_INIT for details.
* Z_INIT_ENTRY_DEFINE for details.
*/
#define SYS_INIT(init_fn, level, prio) \
DEVICE_AND_API_INIT(Z_SYS_NAME(init_fn), "", init_fn, \
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)
Z_INIT_ENTRY_DEFINE(Z_SYS_NAME(init_fn), init_fn, NULL, level, prio)
#ifdef __cplusplus
}

View file

@ -1,10 +1,5 @@
/* 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)
SECTION_DATA_PROLOGUE(sw_isr_table,,)
{
@ -18,12 +13,10 @@
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
#endif
/* verify we don't have rogue .init_<something> initlevel sections */
SECTION_DATA_PROLOGUE(initlevel_error,,)
SECTION_DATA_PROLOGUE(devices,,)
{
DEVICE_INIT_UNDEFINED_SECTION()
}
ASSERT(SIZEOF(initlevel_error) == 0, "Undefined initialization levels used.")
DEVICE_SECTIONS()
} GROUP_DATA_LINK_IN(RAMABLE_REGION, ROMABLE_REGION)
SECTION_DATA_PROLOGUE(initshell,,)
{

View file

@ -1,5 +1,10 @@
/* 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)
SECTION_PROLOGUE(sw_isr_table,,)
{
@ -12,6 +17,14 @@
*(_SW_ISR_TABLE_SECTION_NAME)
} GROUP_LINK_IN(ROMABLE_REGION)
#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
SECTION_PROLOGUE(_CTOR_SECTION_NAME,,)
{
@ -66,14 +79,6 @@
__app_shmem_regions_end = .;
} 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,,)
{
__net_l2_start = .;

View file

@ -45,7 +45,7 @@
*/
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
#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 DEVICE_BUSY_BITFIELD() \
FILL(0x00) ; \
@ -57,37 +57,53 @@
#endif
/*
* generate a symbol to mark the start of the device initialization objects for
* the specified level, then link all of those objects (sorted by priority);
* ensure the objects aren't discarded if there is no direct reference to them
* generate a symbol to mark the start of the objects array for
* the specified object and level, then link all of those objects
* (sorted by priority). Ensure the objects aren't discarded if there is
* no direct reference to them
*/
#define DEVICE_INIT_LEVEL(level) \
__device_##level##_start = .; \
KEEP(*(SORT(.init_##level[0-9]))); \
KEEP(*(SORT(.init_##level[1-9][0-9]))); \
#define CREATE_OBJ_LEVEL(object, level) \
__##object##_##level##_start = .; \
KEEP(*(SORT(.##object##_##level[0-9]))); \
KEEP(*(SORT(.##object##_##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 (i.e. ordered by level, sorted by priority within a level)
*/
#define DEVICE_INIT_SECTIONS() \
__device_init_start = .; \
DEVICE_INIT_LEVEL(PRE_KERNEL_1) \
DEVICE_INIT_LEVEL(PRE_KERNEL_2) \
DEVICE_INIT_LEVEL(POST_KERNEL) \
DEVICE_INIT_LEVEL(APPLICATION) \
DEVICE_INIT_LEVEL(SMP) \
__device_init_end = .; \
DEVICE_BUSY_BITFIELD() \
#define INIT_SECTIONS() \
__init_start = .; \
CREATE_OBJ_LEVEL(init, PRE_KERNEL_1) \
CREATE_OBJ_LEVEL(init, PRE_KERNEL_2) \
CREATE_OBJ_LEVEL(init, POST_KERNEL) \
CREATE_OBJ_LEVEL(init, APPLICATION) \
CREATE_OBJ_LEVEL(init, SMP) \
__init_end = .; \
/* define a section for undefined device initialization levels */
#define DEVICE_INIT_UNDEFINED_SECTION() \
#define INIT_UNDEFINED_SECTION() \
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
* their shell commands are automatically initialized by the kernel.

View file

@ -9,17 +9,20 @@
#include <sys/atomic.h>
#include <syscall_handler.h>
extern struct device __device_init_start[];
extern struct device __device_PRE_KERNEL_1_start[];
extern struct device __device_PRE_KERNEL_2_start[];
extern struct device __device_POST_KERNEL_start[];
extern struct device __device_APPLICATION_start[];
extern struct device __device_init_end[];
extern const struct init_entry __init_start[];
extern const struct init_entry __init_PRE_KERNEL_1_start[];
extern const struct init_entry __init_PRE_KERNEL_2_start[];
extern const struct init_entry __init_POST_KERNEL_start[];
extern const struct init_entry __init_APPLICATION_start[];
extern const struct init_entry __init_end[];
#ifdef CONFIG_SMP
extern struct device __device_SMP_start[];
extern const struct init_entry __init_SMP_start[];
#endif
extern struct device __device_start[];
extern struct device __device_end[];
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
extern u32_t __device_busy_start[];
extern u32_t __device_busy_end[];
@ -27,71 +30,72 @@ extern u32_t __device_busy_end[];
#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
* created by the DEVICE_INIT() macro using the specified level.
* The linker script places the device objects in memory in the order
* @details Invokes the initialization routine for each init entry object
* created by the INIT_ENTRY_DEFINE() macro using the specified level.
* 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
* off and the next one begins.
*
* @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 struct device *config_levels[] = {
__device_PRE_KERNEL_1_start,
__device_PRE_KERNEL_2_start,
__device_POST_KERNEL_start,
__device_APPLICATION_start,
static const struct init_entry *levels[] = {
__init_PRE_KERNEL_1_start,
__init_PRE_KERNEL_2_start,
__init_POST_KERNEL_start,
__init_APPLICATION_start,
#ifdef CONFIG_SMP
__device_SMP_start,
__init_SMP_start,
#endif
/* End marker */
__device_init_end,
__init_end,
};
const struct init_entry *entry;
for (info = config_levels[level]; info < config_levels[level+1];
info++) {
for (entry = levels[level]; entry < levels[level+1]; entry++) {
struct device *dev = entry->dev;
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) {
/* Initialization failed. Clear the API struct so that
* device_get_binding() will not succeed for it.
*/
info->driver_api = NULL;
} else {
z_object_init(info);
if (dev) {
/* Initialization failed. Clear the API struct
* so that device_get_binding() will not succeed
* for it.
*/
dev->driver_api = NULL;
}
}
}
}
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
* device names are stored in ROM (and are referenced by the user
* 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++) {
if ((info->driver_api != NULL) &&
(info->config->name == name)) {
return info;
for (dev = __device_start; dev != __device_end; dev++) {
if ((dev->driver_api != NULL) &&
(dev->name == name)) {
return dev;
}
}
for (info = __device_init_start; info != __device_init_end; info++) {
if (info->driver_api == NULL) {
continue;
}
if (strcmp(name, info->config->name) == 0) {
return info;
for (dev = __device_start; dev != __device_end; dev++) {
if ((dev->driver_api != NULL) &&
(strcmp(name, dev->name) == 0)) {
return dev;
}
}
@ -126,8 +130,8 @@ int device_pm_control_nop(struct device *unused_device,
void device_list_get(struct device **device_list, int *device_count)
{
*device_list = __device_init_start;
*device_count = __device_init_end - __device_init_start;
*device_list = __device_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)
{
if (atomic_test_bit((const atomic_t *)__device_busy_start,
(chk_dev - __device_init_start))) {
(chk_dev - __device_start))) {
return -EBUSY;
}
return 0;
@ -158,7 +162,7 @@ void device_busy_set(struct device *busy_dev)
{
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
atomic_set_bit((atomic_t *) __device_busy_start,
(busy_dev - __device_init_start));
(busy_dev - __device_start));
#else
ARG_UNUSED(busy_dev);
#endif
@ -168,7 +172,7 @@ void device_busy_clear(struct device *busy_dev)
{
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
atomic_clear_bit((atomic_t *) __device_busy_start,
(busy_dev - __device_init_start));
(busy_dev - __device_start));
#else
ARG_UNUSED(busy_dev);
#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_device_do_config_level(_SYS_INIT_LEVEL_POST_KERNEL);
z_sys_init_run_level(_SYS_INIT_LEVEL_POST_KERNEL);
#if CONFIG_STACK_POINTER_RANDOM
z_stack_adjust_initialized = 1;
#endif
@ -234,7 +234,7 @@ static void bg_thread_main(void *unused1, void *unused2, void *unused3)
#endif
/* 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
/* 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
z_smp_init();
z_sys_device_do_config_level(_SYS_INIT_LEVEL_SMP);
z_sys_init_run_level(_SYS_INIT_LEVEL_SMP);
#endif
#ifdef CONFIG_BOOT_TIME_MEASUREMENT
@ -488,8 +488,8 @@ FUNC_NORETURN void z_cstart(void)
#endif
/* perform basic hardware initialization */
z_sys_device_do_config_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_1);
z_sys_init_run_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);
#ifdef CONFIG_STACK_CANARIES
z_early_boot_rand_get((u8_t *)&stack_guard, sizeof(stack_guard));

View file

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