device: refactor to simplify maintenance
DEVICE_AND_API_INIT and DEVICE_DEFINE are identical except that DEVICE_DEFINE adds a parameter providing the device pm control function, while DEVICE_AND_API_INIT does not. This requires duplicate implementations where if CONFIG_DEVICE_POWER_MANAGEMENT is enabled then DEVICE_AND_API_INIT delegates to DEVICE_DEFINE with a dummy pm control function, and if it is not enabled then DEVICE_DEFINE discards the parameter and delegates to DEVICE_AND_API_INIT. DEVICE_INIT is like DEVICE_AND_API_INIT but doesn't provide an API. Clean this up by refactoring so that DEVICE_DEFINE is the core implementation, providing with and without device power management right next to each other where they can be compared and maintained. Redefine DEVICE_INIT and DEVICE_AND_API_INIT delegate to DEVICE_DEFINE. Also remove duplicate code by extracting the variations due to enabling device power management into macros. Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
This commit is contained in:
parent
2fe35e8013
commit
72ebf70543
2 changed files with 88 additions and 102 deletions
|
@ -68,22 +68,28 @@ The following APIs for device drivers are provided by :file:`device.h`. The APIs
|
|||
are intended for use in device drivers only and should not be used in
|
||||
applications.
|
||||
|
||||
:c:func:`DEVICE_INIT()`
|
||||
create device object and set it up for boot time initialization.
|
||||
:c:func:`DEVICE_DEFINE()`
|
||||
Create device object and related data structures including setting it
|
||||
up for boot-time initialization.
|
||||
|
||||
:c:func:`DEVICE_AND_API_INIT()`
|
||||
Create device object and set it up for boot time initialization.
|
||||
This also takes a pointer to driver API struct for link time
|
||||
pointer assignment.
|
||||
Like :c:func:`DEVICE_DEFINE()` but without support for device power
|
||||
management.
|
||||
|
||||
:c:func:`DEVICE_INIT()`
|
||||
Like :c:func:`DEVICE_AND_API_INIT()` but without providing an API
|
||||
pointer.
|
||||
|
||||
:c:func:`DEVICE_NAME_GET()`
|
||||
Expands to the full name of a global device object.
|
||||
Converts a device identifier to the global identifier for a device
|
||||
object.
|
||||
|
||||
:c:func:`DEVICE_GET()`
|
||||
Obtain a pointer to a device object by name.
|
||||
|
||||
:c:func:`DEVICE_DECLARE()`
|
||||
Declare a device object.
|
||||
Declare a device object. Use this when you need a forward reference
|
||||
to a device that has not yet been defined.
|
||||
|
||||
.. _device_struct:
|
||||
|
||||
|
@ -106,7 +112,7 @@ split into read-only and runtime-mutable parts. At a high level we have:
|
|||
The ``config_info`` member is for read-only configuration data set at build time. For
|
||||
example, base memory mapped IO addresses, IRQ line numbers, or other fixed
|
||||
physical characteristics of the device. This is the ``config_info`` structure
|
||||
passed to the ``DEVICE_*INIT()`` macros.
|
||||
passed to ``DEVICE_DEFINE()`` and related macros.
|
||||
|
||||
The ``driver_data`` struct is kept in RAM, and is used by the driver for
|
||||
per-instance runtime housekeeping. For example, it may contain reference counts,
|
||||
|
@ -337,10 +343,11 @@ the IRQ handler argument and the definition of the device itself.
|
|||
Initialization Levels
|
||||
*********************
|
||||
|
||||
Drivers may depend on other drivers being initialized first, or
|
||||
require the use of kernel services. The DEVICE_INIT() APIs allow the user to
|
||||
specify at what time during the boot sequence the init function will be
|
||||
executed. Any driver will specify one of four initialization levels:
|
||||
Drivers may depend on other drivers being initialized first, or require
|
||||
the use of kernel services. :c:func:`DEVICE_DEFINE()` and related APIs
|
||||
allow the user to specify at what time during the boot sequence the init
|
||||
function will be executed. Any driver will specify one of four
|
||||
initialization levels:
|
||||
|
||||
``PRE_KERNEL_1``
|
||||
Used for devices that have no dependencies, such as those that rely
|
||||
|
@ -382,7 +389,7 @@ System Drivers
|
|||
**************
|
||||
|
||||
In some cases you may just need to run a function at boot. Special ``SYS_*``
|
||||
macros exist that map to ``DEVICE_*INIT()`` calls.
|
||||
macros exist that map to ``DEVICE_DEFINE()`` calls.
|
||||
For ``SYS_INIT()`` there are no config or runtime data structures and there
|
||||
isn't a way
|
||||
to later get a device pointer by name. The same policies for initialization
|
||||
|
@ -392,8 +399,11 @@ For ``SYS_DEVICE_DEFINE()`` you can obtain pointers by name, see
|
|||
:ref:`power management <power_management_api>` section.
|
||||
|
||||
:c:func:`SYS_INIT()`
|
||||
Run an initialization function at boot at specified priority.
|
||||
|
||||
:c:func:`SYS_DEVICE_DEFINE()`
|
||||
Like :c:func:`DEVICE_DEFINE` without an API table and constructing
|
||||
the device name from the init function name.
|
||||
|
||||
Error handling
|
||||
**************
|
||||
|
|
154
include/device.h
154
include/device.h
|
@ -33,14 +33,14 @@ extern "C" {
|
|||
* @brief Expands to the full name of a global device object
|
||||
*
|
||||
* @details Return the full name of a device object symbol created by
|
||||
* DEVICE_INIT(), using the dev_name provided to DEVICE_INIT().
|
||||
* DEVICE_DEFINE(), using the dev_name provided to DEVICE_DEFINE().
|
||||
*
|
||||
* It is meant to be used for declaring extern symbols pointing on device
|
||||
* objects before using the DEVICE_GET macro to get the device object.
|
||||
*
|
||||
* @param name The same as dev_name provided to DEVICE_INIT()
|
||||
* @param name The same as dev_name provided to DEVICE_DEFINE()
|
||||
*
|
||||
* @return The expanded name of the device object created by DEVICE_INIT()
|
||||
* @return The expanded name of the device object created by DEVICE_DEFINE()
|
||||
*/
|
||||
#define DEVICE_NAME_GET(name) (_CONCAT(__device_, name))
|
||||
|
||||
|
@ -50,24 +50,46 @@ extern "C" {
|
|||
* @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.
|
||||
* @details Invokes DEVICE_DEFINE() with no power management support
|
||||
* (@p pm_control_fn), no API (@p api), and a device name derived from
|
||||
* the @p init_fn name (@p dev_name).
|
||||
*/
|
||||
#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, \
|
||||
DEVICE_DEFINE(Z_SYS_NAME(init_fn), drv_name, init_fn, \
|
||||
pm_control_fn, \
|
||||
NULL, NULL, level, prio, NULL)
|
||||
|
||||
/**
|
||||
* @def DEVICE_INIT
|
||||
*
|
||||
* @brief Create device object and set it up for boot time initialization.
|
||||
* @brief Invoke DEVICE_DEFINE() with no power management support (@p
|
||||
* pm_control_fn) and no API (@p api).
|
||||
*/
|
||||
#define DEVICE_INIT(dev_name, drv_name, init_fn, \
|
||||
data, cfg_info, level, prio) \
|
||||
DEVICE_DEFINE(dev_name, drv_name, init_fn, \
|
||||
device_pm_control_nop, \
|
||||
data, cfg_info, level, prio, NULL)
|
||||
|
||||
/**
|
||||
* @def DEVICE_AND_API_INIT
|
||||
*
|
||||
* @brief Invoke DEVICE_DEFINE() with no power management support (@p
|
||||
* pm_control_fn).
|
||||
*/
|
||||
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, \
|
||||
data, cfg_info, level, prio, api) \
|
||||
DEVICE_DEFINE(dev_name, drv_name, init_fn, \
|
||||
device_pm_control_nop, \
|
||||
data, cfg_info, level, prio, api)
|
||||
|
||||
/**
|
||||
* @def DEVICE_DEFINE
|
||||
*
|
||||
* @brief Create device object and set it up for boot time initialization,
|
||||
* with the option to device_pm_control. In case of Device Idle Power
|
||||
* Management is enabled, make sure the device is in suspended state after
|
||||
* initialization.
|
||||
*
|
||||
* @details This macro defines a device object that is automatically
|
||||
* configured by the kernel during system initialization. Note that
|
||||
|
@ -83,90 +105,26 @@ extern "C" {
|
|||
*
|
||||
* @param init_fn Address to the init function of the driver.
|
||||
*
|
||||
* @param pm_control_fn Pointer to device_pm_control function.
|
||||
* Can be empty function (device_pm_control_nop) if not implemented.
|
||||
*
|
||||
* @param data Pointer to the device's private data.
|
||||
*
|
||||
* @param cfg_info The address to the structure containing the
|
||||
* configuration information for this instance of the driver.
|
||||
*
|
||||
* @param level The initialization level, See Z_INIT_ENTRY_DEFINE for details.
|
||||
* @param level The initialization level. See SYS_INIT() for
|
||||
* details.
|
||||
*
|
||||
* @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, \
|
||||
data, cfg_info, level, prio, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* @def DEVICE_AND_API_INIT
|
||||
* SYS_INIT() for details.
|
||||
*
|
||||
* @brief Create device object and set it up for boot time initialization,
|
||||
* with the option to set driver_api.
|
||||
*
|
||||
* @copydetails DEVICE_INIT
|
||||
* @param api Provides an initial pointer to the API function struct
|
||||
* used by the driver. Can be NULL.
|
||||
* @details The driver api is also set here, eliminating the need to do that
|
||||
* 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 Z_DECL_ALIGN(struct device) \
|
||||
DEVICE_NAME_GET(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
|
||||
* DEVICE_DEFINE macro so that caller of hook functions
|
||||
* need not check device_pm_control != NULL.
|
||||
*/
|
||||
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
|
||||
level, prio, api) \
|
||||
DEVICE_DEFINE(dev_name, drv_name, init_fn, \
|
||||
device_pm_control_nop, data, cfg_info, level, \
|
||||
prio, api)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def DEVICE_DEFINE
|
||||
*
|
||||
* @brief Create device object and set it up for boot time initialization,
|
||||
* with the option to device_pm_control. In case of Device Idle Power
|
||||
* Management is enabled, make sure the device is in suspended state after
|
||||
* initialization.
|
||||
*
|
||||
* @copydetails DEVICE_AND_API_INIT
|
||||
* @param pm_control_fn Pointer to device_pm_control function.
|
||||
* Can be empty function (device_pm_control_nop) if not implemented.
|
||||
*/
|
||||
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#define DEVICE_DEFINE(dev_name, drv_name, init_fn, pm_control_fn, \
|
||||
data, cfg_info, level, prio, api) \
|
||||
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), \
|
||||
}; \
|
||||
Z_DEVICE_DEFINE_PM(dev_name) \
|
||||
static Z_DECL_ALIGN(struct device) \
|
||||
DEVICE_NAME_GET(dev_name) __used \
|
||||
__attribute__((__section__(".device_" #level STRINGIFY(prio)))) = { \
|
||||
|
@ -174,14 +132,11 @@ extern "C" {
|
|||
.config_info = (cfg_info), \
|
||||
.driver_api = (api), \
|
||||
.driver_data = (data), \
|
||||
.device_pm_control = (pm_control_fn), \
|
||||
.pm = &_CONCAT(__pm_, dev_name), \
|
||||
Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) \
|
||||
}; \
|
||||
Z_INIT_ENTRY_DEFINE(_CONCAT(__device_, dev_name), init_fn, \
|
||||
(&_CONCAT(__device_, dev_name)), level, prio)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @def DEVICE_GET
|
||||
*
|
||||
|
@ -585,6 +540,27 @@ static inline int device_pm_put_sync(struct device *dev) { return -ENOTSUP; }
|
|||
* @}
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
||||
#define Z_DEVICE_DEFINE_PM(dev_name) \
|
||||
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), \
|
||||
};
|
||||
#define Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn) \
|
||||
.device_pm_control = (pm_control_fn), \
|
||||
.pm = &_CONCAT(__pm_, dev_name),
|
||||
#else
|
||||
#define Z_DEVICE_DEFINE_PM(dev_name)
|
||||
#define Z_DEVICE_DEFINE_PM_INIT(dev_name, pm_control_fn)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue