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:
parent
48b784abb3
commit
8d7bb8ffd8
11 changed files with 304 additions and 250 deletions
|
@ -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) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
180
include/device.h
180
include/device.h
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
105
include/init.h
105
include/init.h
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,,)
|
||||
{
|
||||
|
|
|
@ -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 = .;
|
||||
|
|
|
@ -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.
|
||||
|
|
100
kernel/device.c
100
kernel/device.c
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue