From 72ebf70543fca78740888bc58319a19c2a3d636b Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Tue, 9 Jun 2020 14:39:26 -0500 Subject: [PATCH] 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 --- doc/reference/drivers/index.rst | 36 +++++--- include/device.h | 154 ++++++++++++++------------------ 2 files changed, 88 insertions(+), 102 deletions(-) diff --git a/doc/reference/drivers/index.rst b/doc/reference/drivers/index.rst index 7a25f2cfda7..69d9bcacf36 100644 --- a/doc/reference/drivers/index.rst +++ b/doc/reference/drivers/index.rst @@ -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 ` 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 ************** diff --git a/include/device.h b/include/device.h index 5886d50f35e..8121bb367cf 100644 --- a/include/device.h +++ b/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