blob: 33e1027c07eea157e8141e670fb38d287450562f [file] [log] [blame]
.. _device_model_api:
Device Driver Model
###################
Introduction
************
The Zephyr kernel supports a variety of device drivers. Whether a
driver is available depends on the board and the driver.
The Zephyr device model provides a consistent device model for configuring the
drivers that are part of a system. The device model is responsible
for initializing all the drivers configured into the system.
Each type of driver (e.g. UART, SPI, I2C) is supported by a generic type API.
In this model the driver fills in the pointer to the structure containing the
function pointers to its API functions during driver initialization. These
structures are placed into the RAM section in initialization level order.
.. image:: device_driver_model.svg
:width: 40%
:align: center
:alt: Device Driver Model
Standard Drivers
****************
Device drivers which are present on all supported board configurations
are listed below.
* **Interrupt controller**: This device driver is used by the kernel's
interrupt management subsystem.
* **Timer**: This device driver is used by the kernel's system clock and
hardware clock subsystem.
* **Serial communication**: This device driver is used by the kernel's
system console subsystem.
* **Entropy**: This device driver provides a source of entropy numbers
for the random number generator subsystem.
.. important::
Use the :ref:`random API functions <random_api>` for random
values. :ref:`Entropy functions <entropy_api>` should not be
directly used as a random number generator source as some hardware
implementations are designed to be an entropy seed source for random
number generators and will not provide cryptographically secure
random number streams.
Synchronous Calls
*****************
Zephyr provides a set of device drivers for multiple boards. Each driver
should support an interrupt-based implementation, rather than polling, unless
the specific hardware does not provide any interrupt.
High-level calls accessed through device-specific APIs, such as
:file:`i2c.h` or :file:`spi.h`, are usually intended as synchronous. Thus,
these calls should be blocking.
Driver APIs
***********
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()`
Create device object and related data structures including setting it
up for boot-time initialization.
:c:func:`DEVICE_NAME_GET()`
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. Use this when you need a forward reference
to a device that has not yet been defined.
.. _device_struct:
Driver Data Structures
**********************
The device initialization macros populate some data structures at build time
which are
split into read-only and runtime-mutable parts. At a high level we have:
.. code-block:: C
struct device {
const char *name;
const void *config;
const void *api;
void * const data;
};
The ``config`` 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`` pointer
passed to ``DEVICE_DEFINE()`` and related macros.
The ``data`` struct is kept in RAM, and is used by the driver for
per-instance runtime housekeeping. For example, it may contain reference counts,
semaphores, scratch buffers, etc.
The ``api`` struct maps generic subsystem APIs to the device-specific
implementations in the driver. It is typically read-only and populated at
build time. The next section describes this in more detail.
Subsystems and API Structures
*****************************
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.
A subsystem API definition typically looks like this:
.. code-block:: C
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_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;
api = (struct subsystem_api *)dev->api;
return api->do_this(dev, foo, bar);
}
static inline void subsystem_do_that(const struct device *dev, void *baz)
{
struct subsystem_api *api;
api = (struct subsystem_api *)dev->api;
api->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:
.. code-block:: C
static int my_driver_do_this(const struct device *dev, int foo, int bar)
{
...
}
static void my_driver_do_that(const struct device *dev, void *baz)
{
...
}
static struct subsystem_api my_driver_api_funcs = {
.do_this = my_driver_do_this,
.do_that = my_driver_do_that
};
The driver would then pass ``my_driver_api_funcs`` as the ``api`` argument to
``DEVICE_DEFINE()``.
.. note::
Since pointers to the API functions are referenced in the ``api``
struct, they will always be included in the binary even if unused;
``gc-sections`` linker option will always see at least one reference to
them. Providing for link-time size optimizations with driver APIs in
most cases requires that the optional feature be controlled by a
Kconfig option.
Device-Specific API Extensions
******************************
Some devices can be cast as an instance of a driver subsystem such as GPIO,
but provide additional functionality that cannot be exposed through the
standard API. These devices combine subsystem operations with
device-specific APIs, described in a device-specific header.
A device-specific API definition typically looks like this:
.. code-block:: C
#include <zephyr/drivers/subsystem.h>
/* When extensions need not be invoked from user mode threads */
int specific_do_that(const struct device *dev, int foo);
/* When extensions must be invokable from user mode threads */
__syscall int specific_from_user(const struct device *dev, int bar);
/* Only needed when extensions include syscalls */
#include <syscalls/specific.h>
A driver implementing extensions to the subsystem will define the real
implementation of both the subsystem API and the specific APIs:
.. code-block:: C
static int generic_do_this(const struct device *dev, void *arg)
{
...
}
static struct generic_api api {
...
.do_this = generic_do_this,
...
};
/* supervisor-only API is globally visible */
int specific_do_that(const struct device *dev, int foo)
{
...
}
/* syscall API passes through a translation */
int z_impl_specific_from_user(const struct device *dev, int bar)
{
...
}
#ifdef CONFIG_USERSPACE
#include <zephyr/syscall_handler.h>
int z_vrfy_specific_from_user(const struct device *dev, int bar)
{
Z_OOPS(Z_SYSCALL_SPECIFIC_DRIVER(dev, K_OBJ_DRIVER_GENERIC, &api));
return z_impl_specific_do_that(dev, bar)
}
#include <syscalls/specific_from_user_mrsh.c>
#endif /* CONFIG_USERSPACE */
Applications use the device through both the subsystem and specific
APIs.
.. note::
Public API for device-specific extensions should be prefixed with the
compatible for the device to which it applies. For example, if
adding special functions to support the Maxim DS3231 the identifier
fragment ``specific`` in the examples above would be ``maxim_ds3231``.
Single Driver, Multiple Instances
*********************************
Some drivers may be instantiated multiple times in a given system. For example
there can be multiple GPIO banks, or multiple UARTS. Each instance of the driver
will have a different ``config`` struct and ``data`` struct.
Configuring interrupts for multiple drivers instances is a special case. If each
instance needs to configure a different interrupt line, this can be accomplished
through the use of per-instance configuration functions, since the parameters
to ``IRQ_CONNECT()`` need to be resolvable at build time.
For example, let's say we need to configure two instances of ``my_driver``, each
with a different interrupt line. In ``drivers/subsystem/subsystem_my_driver.h``:
.. code-block:: C
typedef void (*my_driver_config_irq_t)(const struct device *dev);
struct my_driver_config {
DEVICE_MMIO_ROM;
my_driver_config_irq_t config_func;
};
In the implementation of the common init function:
.. code-block:: C
void my_driver_isr(const struct device *dev)
{
/* Handle interrupt */
...
}
int my_driver_init(const struct device *dev)
{
const struct my_driver_config *config = dev->config;
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
/* Do other initialization stuff */
...
config->config_func(dev);
return 0;
}
Then when the particular instance is declared:
.. code-block:: C
#if CONFIG_MY_DRIVER_0
DEVICE_DECLARE(my_driver_0);
static void my_driver_config_irq_0(const struct device *dev)
{
IRQ_CONNECT(MY_DRIVER_0_IRQ, MY_DRIVER_0_PRI, my_driver_isr,
DEVICE_GET(my_driver_0), MY_DRIVER_0_FLAGS);
}
const static struct my_driver_config my_driver_config_0 = {
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)),
.config_func = my_driver_config_irq_0
}
static struct my_data_0;
DEVICE_DEFINE(my_driver_0, MY_DRIVER_0_NAME, my_driver_init,
NULL, &my_data_0, &my_driver_config_0,
POST_KERNEL, MY_DRIVER_0_PRIORITY, &my_api_funcs);
#endif /* CONFIG_MY_DRIVER_0 */
Note the use of ``DEVICE_DECLARE()`` to avoid a circular dependency on providing
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. :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:
``EARLY``
Used very early in the boot process, right after entering the C domain
(``z_cstart()``). This can be used in architectures and SoCs that extend
or implement architecture code and use drivers or system services that
have to be initialized before the Kernel calls any architecture specific
initialization code.
``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 the kernel services are
not yet available. The interrupt subsystem will be configured however
so it's OK to set up interrupts. Init functions at this level run on the
interrupt stack.
``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 the kernel services are not yet
available. Init functions at this level run on the interrupt stack.
``POST_KERNEL``
Used for devices that require kernel services during configuration.
Init functions at this level run in context of the kernel main task.
``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. Init functions at this level run on
the kernel main task.
Within each initialization level you may specify a priority level, relative to
other devices in the same initialization level. The priority level is specified
as an integer value in the range 0 to 99; lower values indicate earlier
initialization. The priority level 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``).
Drivers and other system utilities can determine whether startup is
still in pre-kernel states by using the :c:func:`k_is_pre_kernel`
function.
System Drivers
**************
In some cases you may just need to run a function at boot. For such cases, the
:c:macro:`SYS_INIT` can be used. This macro does not take any config or runtime
data structures and there isn't a way to later get a device pointer by name. The
same device policies for initialization level and priority apply.
Error handling
**************
In general, it's best to use ``__ASSERT()`` macros instead of
propagating return values unless the failure is expected to occur
during the normal course of operation (such as a storage device
full). Bad parameters, programming errors, consistency checks,
pathological/unrecoverable failures, etc., should be handled by
assertions.
When it is appropriate to return error conditions for the caller to
check, 0 should be returned on success and a POSIX :file:`errno.h` code
returned on failure. See
https://github.com/zephyrproject-rtos/zephyr/wiki/Naming-Conventions#return-codes
for details about this.
Memory Mapping
**************
On some systems, the linear address of peripheral memory-mapped I/O (MMIO)
regions cannot be known at build time:
- The I/O ranges must be probed at runtime from the bus, such as with
PCI express
- A memory management unit (MMU) is active, and the physical address of
the MMIO range must be mapped into the page tables at some virtual
memory location determined by the kernel.
These systems must maintain storage for the MMIO range within RAM and
establish the mapping within the driver's init function. Other systems
do not care about this and can use MMIO physical addresses directly from
DTS and do not need any RAM-based storage for it.
For drivers that may need to deal with this situation, a set of
APIs under the DEVICE_MMIO scope are defined, along with a mapping function
:c:func:`device_map`.
Device Model Drivers with one MMIO region
=========================================
The simplest case is for drivers which need to maintain one MMIO region.
These drivers will need to use the ``DEVICE_MMIO_ROM`` and
``DEVICE_MMIO_RAM`` macros in the definitions for their ``config_info``
and ``driver_data`` structures, with initialization of the ``config_info``
from DTS using ``DEVICE_MMIO_ROM_INIT``. A call to ``DEVICE_MMIO_MAP()``
is made within the init function:
.. code-block:: C
struct my_driver_config {
DEVICE_MMIO_ROM; /* Must be first */
...
}
struct my_driver_dev_data {
DEVICE_MMIO_RAM; /* Must be first */
...
}
const static struct my_driver_config my_driver_config_0 = {
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(...)),
...
}
int my_driver_init(const struct device *dev)
{
...
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
...
}
int my_driver_some_function(const struct device *dev)
{
...
/* Write some data to the MMIO region */
sys_write32(0xDEADBEEF, DEVICE_MMIO_GET(dev));
...
}
The particular expansion of these macros depends on configuration. On
a device with no MMU or PCI-e, ``DEVICE_MMIO_MAP`` and
``DEVICE_MMIO_RAM`` expand to nothing.
Device Model Drivers with multiple MMIO regions
===============================================
Some drivers may have multiple MMIO regions. In addition, some drivers
may already be implementing a form of inheritance which requires some other
data to be placed first in the ``config_info`` and ``driver_data``
structures.
This can be managed with the ``DEVICE_MMIO_NAMED`` variant macros. These
require that ``DEV_CFG()`` and ``DEV_DATA()`` macros be defined to obtain
a properly typed pointer to the driver's config_info or dev_data structs.
For example:
.. code-block:: C
struct my_driver_config {
...
DEVICE_MMIO_NAMED_ROM(corge);
DEVICE_MMIO_NAMED_ROM(grault);
...
}
struct my_driver_dev_data {
...
DEVICE_MMIO_NAMED_RAM(corge);
DEVICE_MMIO_NAMED_RAM(grault);
...
}
#define DEV_CFG(_dev) \
((const struct my_driver_config *)((_dev)->config))
#define DEV_DATA(_dev) \
((struct my_driver_dev_data *)((_dev)->data))
const static struct my_driver_config my_driver_config_0 = {
...
DEVICE_MMIO_NAMED_ROM_INIT(corge, DT_DRV_INST(...)),
DEVICE_MMIO_NAMED_ROM_INIT(grault, DT_DRV_INST(...)),
...
}
int my_driver_init(const struct device *dev)
{
...
DEVICE_MMIO_NAMED_MAP(dev, corge, K_MEM_CACHE_NONE);
DEVICE_MMIO_NAMED_MAP(dev, grault, K_MEM_CACHE_NONE);
...
}
int my_driver_some_function(const struct device *dev)
{
...
/* Write some data to the MMIO regions */
sys_write32(0xDEADBEEF, DEVICE_MMIO_GET(dev, grault));
sys_write32(0xF0CCAC1A, DEVICE_MMIO_GET(dev, corge));
...
}
Device Model Drivers with multiple MMIO regions in the same DT node
===================================================================
Some drivers may have multiple MMIO regions defined into the same DT device
node using the ``reg-names`` property to differentiate them, for example:
.. code-block:: devicetree
/dts-v1/;
/ {
a-driver@40000000 {
reg = <0x40000000 0x1000>,
<0x40001000 0x1000>;
reg-names = "corge", "grault";
};
};
This can be managed as seen in the previous section but this time using the
``DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME`` macro instead. So the only difference
would be in the driver config struct:
.. code-block:: C
const static struct my_driver_config my_driver_config_0 = {
...
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(corge, DT_DRV_INST(...)),
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(grault, DT_DRV_INST(...)),
...
}
Drivers that do not use Zephyr Device Model
===========================================
Some drivers or driver-like code may not user Zephyr's device model,
and alternative storage must be arranged for the MMIO data. An
example of this are timer drivers, or interrupt controller code.
This can be managed with the ``DEVICE_MMIO_TOPLEVEL`` set of macros,
for example:
.. code-block:: C
DEVICE_MMIO_TOPLEVEL_STATIC(my_regs, DT_DRV_INST(..));
void some_init_code(...)
{
...
DEVICE_MMIO_TOPLEVEL_MAP(my_regs, K_MEM_CACHE_NONE);
...
}
void some_function(...)
...
sys_write32(DEVICE_MMIO_TOPLEVEL_GET(my_regs), 0xDEADBEEF);
...
}
Drivers that do not use DTS
===========================
Some drivers may not obtain the MMIO physical address from DTS, such as
is the case with PCI-E. In this case the :c:func:`device_map` function
may be used directly:
.. code-block:: C
void some_init_code(...)
{
...
struct pcie_bar mbar;
bool bar_found = pcie_get_mbar(bdf, index, &mbar);
device_map(DEVICE_MMIO_RAM_PTR(dev), mbar.phys_addr, mbar.size, K_MEM_CACHE_NONE);
...
}
For these cases, DEVICE_MMIO_ROM directives may be omitted.
API Reference
**************
.. doxygengroup:: device_model