doc: kernel: drivers: Update with DEVICE_API macros

Update the device driver API documentation with the new DEVICE_API
macro definitions and how to use them.

Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
This commit is contained in:
Pieter De Gendt 2024-05-03 16:26:03 +02:00 committed by Benjamin Cabé
commit d6b1de1cc8

View file

@ -68,21 +68,24 @@ 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_DEFINE()`
:c:macro:`DEVICE_DEFINE()`
Create device object and related data structures including setting it
up for boot-time initialization.
:c:func:`DEVICE_NAME_GET()`
:c:macro:`DEVICE_NAME_GET()`
Converts a device identifier to the global identifier for a device
object.
:c:func:`DEVICE_GET()`
:c:macro:`DEVICE_GET()`
Obtain a pointer to a device object by name.
:c:func:`DEVICE_DECLARE()`
:c:macro:`DEVICE_DECLARE()`
Declare a device object. Use this when you need a forward reference
to a device that has not yet been defined.
:c:macro:`DEVICE_API()`
Wrap a driver API declaration to assign it to its respective linker section.
.. _device_struct:
Driver Data Structures
@ -97,8 +100,8 @@ split into read-only and runtime-mutable parts. At a high level we have:
struct device {
const char *name;
const void *config;
const void *api;
void * const data;
const void *api;
void * const data;
};
The ``config`` member is for read-only configuration data set at build time. For
@ -122,6 +125,9 @@ Most drivers will be implementing a device-independent subsystem API.
Applications can simply program to that generic API, and application
code is not specific to any particular driver implementation.
If all driver API instances are assigned to their respective API linker section
use :c:macro:`DEVICE_API_IS()` to verify the API's type.
A subsystem API definition typically looks like this:
.. code-block:: C
@ -129,29 +135,28 @@ A subsystem API definition typically looks like this:
typedef int (*subsystem_do_this_t)(const struct device *dev, int foo, int bar);
typedef void (*subsystem_do_that_t)(const struct device *dev, void *baz);
struct subsystem_api {
__subsystem struct subsystem_driver_api {
subsystem_do_this_t do_this;
subsystem_do_that_t do_that;
};
static inline int subsystem_do_this(const struct device *dev, int foo, int bar)
{
struct subsystem_api *api;
__ASSERT_NO_MSG(DEVICE_API_IS(subsystem, dev));
api = (struct subsystem_api *)dev->api;
return api->do_this(dev, foo, bar);
return DEVICE_API_GET(subsystem, dev)->do_this(dev, foo, bar);
}
static inline void subsystem_do_that(const struct device *dev, void *baz)
{
struct subsystem_api *api;
__ASSERT_NO_MSG(DEVICE_API_IS(subsystem, dev));
api = (struct subsystem_api *)dev->api;
api->do_that(dev, baz);
DEVICE_API_GET(subsystem, dev)->do_that(dev, baz);
}
A driver implementing a particular subsystem will define the real implementation
of these APIs, and populate an instance of subsystem_api structure:
of these APIs, and populate an instance of subsystem_driver_api structure using
the :c:macro:`DEVICE_API()` wrapper:
.. code-block:: C
@ -165,9 +170,9 @@ of these APIs, and populate an instance of subsystem_api structure:
...
}
static struct subsystem_api my_driver_api_funcs = {
static DEVICE_API(subsystem, my_driver_api_funcs) = {
.do_this = my_driver_do_this,
.do_that = my_driver_do_that
.do_that = my_driver_do_that,
};
The driver would then pass ``my_driver_api_funcs`` as the ``api`` argument to