驱动模型介绍
SDK 中的设备驱动是基于 Zephyr 的驱动模型进行开发的。驱动模型为各个驱动提 供了统一的设备注册、配置、功耗管理等接口。每种类型的驱动程序(UART、I2C、 Watchdog 等)都定义了通用 API 函数,API 函数中再来调用具体设备驱动的实现。 比如下面的 watchdog 驱动的实现:
在 watchdog.h 中定义了 watchdog 这一类设备统一的 API 接口函数
typedef void (*wdt_api_enable)(struct device *dev);
struct wdt_driver_api
{
wdt_api_enable enable;
...
};
static inline void wdt_enable(struct device *dev)
{
const struct wdt_driver_api *api = dev->driver_api;
api->enable(dev);
}
在具体的 watchdog 驱动中实现 API 函数
void wdt_xx_enable(struct device *dev)
{
...
}
static const struct wdt_driver_api wdt_api = {
.enable = wdt_xx_enable,
...
};
驱动初始化
系统初始化时会根据设备初始化参数定义来调用各个设备的初始化函数。 设备驱动使用驱动框架提供的 DEVICE_INIT()、DEVICE_AND_API_INIT() 等宏 接口来定义初始化配置参数。
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, ,cfg_info, level, prio, api) ...
初始化参数意义为:
- dev_name: 设备名
- drv_name: 驱动名
- init_fn: 驱动初始化函数
- data: 驱动运行时自定义参数
- cfg_info: 驱动配置自定义参数
- level: 设备初始化优先级
- prio: 设备中断优先级
- api: 驱动实现的 api 结构体
比如 watchdog 驱动中设备初始化参数定义:
DEVICE_AND_API_INIT(wdt_xx, CONFIG_WDT_XX_DEVICE_NAME, wdt_xx_init, &shared_data, NULL, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_api);
驱动程序可能会依赖其它驱动或内核服务,所以需要能自定义初始化顺序。设备驱动定 义时需要指定初始化等级。下面是几个系统预定义的初始化等级
- PRE_KERNEL_1
用于那些没有任何依赖的设备,例如那些纯粹只需要处理器/SoC 上的 硬件的设备。这些设备在配置期间不需要使用任何内内核服务,因此此 时内核服务还未启动。不过,中断子系统会被配置,因此可以设置中断。 在这个等级上的初始化函数运行在中断栈上面。
- PRE_KERNEL_2
用于那些依赖于已被初始化的 PRE_KERNEL_1 等级的设备的设备。 这些设备在配置期间不使用任何内核服务,因此此时内核服务还未启 动。在这个等级上的初始化函数运行在中断栈上面。
- POST_KERNEL
用于那些在配置期间需要依赖内核服务的设备。在这个等级上的初始化 函数运行在内核主栈的上下文中。
- APPLICATION
用于需要自动配置的应用程序组件(即非内核组件)。这些设别在配置 期间可以使用内核提供的所有服务。在这个等级上的初始化函数运行在 内核主栈的上下文中。
在每个初始化等级,您还需要指定具体的初始化优先级,用于区分相同初始化等级的 初始化顺序。这个优先级是 0 到 99 之间的整数值。优先级越低表示越早被初始化。 优先级必须是一个前面没有补零的或者没有符号的十进制整数字面量或者一个整数 宏定义(例如 #define MY_INIT_PRIO 32)。这里的定义不能用符号表达式(例如 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT + 5)。
评论