基于W800的HDF驱动框架适配 原创 精华
作者:润和软件 李璐
HDF(Hardware Driver Foundation)驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。
驱动加载
HDF驱动加载包括按需加载和按序加载。
按需加载: HDF框架支持驱动在系统启动过程中默认加载,或者在系统启动之后动态加载。
按序加载: HDF框架支持驱动在系统启动的过程中按照驱动的优先级进行加载。
驱动服务管理
HDF框架可以集中管理驱动服务,开发者可直接通过HDF框架对外提供的能力接口获取驱动相关的服务。
驱动消息机制
HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息。
本文基于W800适配HDF驱动框架,分析轻量系统的HDF驱动框架适配,HDF驱动适配应在内核移植Kconfig适配之后。
驱动启动流程
在系统启动时,HDF驱动框架先启动,通过解析配置文件获取到设备列表,读取".hdf.drivers"段读取到驱动程序(Driver Entry)列表,然后遍历设备列表与驱动程序列表进行匹配,并加载匹配成功的驱动。驱动框架有两大核心管理者:
· DeviceManager 负责设备的管理,包括设备加载、卸载和查询等设备相关功能。
· DeviceServiceManager 负责管理设备发布的接口服务,提供接口服务的发布、查询等功能。
驱动加载主要由 DeviceManager 主导,首先 DeviceManager 要解析配置文件中的 Host 列表,根据 Host 列表中的信息来实例化对应的 Host 对象。Host 解析配置文件获取到关联的设备列表,遍历设备列表去获取与之匹配的驱动程序名称,然后基于.hdf.driver section 获得驱动程序地址。 HDF驱动启动流程如下:
HDF框架启动
在device/soc/winnermicro/wm800/board/app/main.c文件中,DeviceManagerStart()函数为HDF框架入口函数,如下:
......
if (DeviceManagerStart()) { --- HDF初始化
printf("[%s] No drivers need load by hdf manager!",__func__);
}
......
获取驱动程序列表
HDF 驱动框架通过将驱动程序入口符号的地址集中存放到一个特殊的 section 来实现对驱动的索引,这个 section 的开头和末尾插入了_hdf_drivers_start、_hdf_drivers_end 两个特殊符号,用于标记这个 section 的范围,两个特殊符号之间的数据即为驱动实现指针。
在文件 device\soc\winnermicro\wm800\board\ld\w800\gcc_csky.ld中,具体实现如下:
.data : {
. = ALIGN(0x4) ;
__sdata = . ;
__data_start__ = . ;
......
. = ALIGN(4);
_hdf_drivers_start = .;
KEEP(*(.hdf.driver))
_hdf_drivers_end = .;
. = ALIGN(0x4) ;
__edata = .;
__data_end__ = .;
} > REGION_DATA AT > REGION_RODATA
获取设备列表
配置文本编译后会变成二进制格式的配置文件,其中设备相关信息被存放在一个用“hdf_manager”标记的 device_info 配置块中,host 的内容以块的形式在 device_info 块中依次排列,host 块中记录了 host 名称、启动优先级和设备列表信息。 设备信息中的 moduleName 字段将用于和驱动程序入口中的 moduleName 进行匹配,从而为设备匹配到正确的驱动程序。
在文件device\soc\winnermicro\wm800\hdf_config\device_info.hcs中,具体实现如下(以GPIO为例):
evice_info { --- device_info 配置块
match_attr = "hdf_manager";
......
platform :: host { --- host 块
hostName = "platform_host";
priority = 50;
device_gpio :: device {
gpio0 :: deviceNode {
policy = 0;
priority = 45;
moduleName = "WM_GPIO_MODULE_HDF"; --- moduleName 字段
serviceName = "HDF_PLATFORM_GPIO";
deviceMatchAttr = "gpio_config";
}
}
......
在文件drivers\adapter\platform\gpio\gpio_wm.c 中,moduleName 字段与设备信息中的 moduleName字段保持一致,如下:
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_GpioDriverEntry = {
.moduleVersion = 1,
.moduleName = "WM_GPIO_MODULE_HDF", --- moduleName 字段
.Bind = GpioDriverBind,
.Init = GpioDriverInit,
.Release = GpioDriverRelease,
};
HDF_INIT(g_GpioDriverEntry);
驱动程序加载流程
Device Manager 遍历设备列表,当查找到对应驱动实现时,为设备创建 Device 对象实例,如果设备配置中的 policy 字段为需要对外发布的驱动接口(SERVICE_POLICY_CAPACITY),那么驱动的 Bind 接口将首先被调用,用于关联设备和服务实例。然后驱动的 Init 接口将被调用,用于完成驱动的相关初始化工作。如果驱动被卸载或者因为硬件等原因 Init 接口返回失败,Release 将被调用,用于释放驱动申请的各类配置。
HDF驱动框架适配
HDF驱动框架提供了一套应用访问硬件的统一接口,可以简化应用开发,添加HDF组件需要在//vendor/hihope/neptune_iotlink_demo/kernel_configs添加:
LOSCFG_DRIVERS_HDF=y
LOSCFG_DRIVERS_HDF_PLATFORM=y
可在kernel/liteos_m中执行make menuconfig,进入 Driver选项选择适配的外设,例如适配UART,可选中 Enable HDF platform uart driver。
驱动适配相关文件放置在drivers/adapter/platform中,对应有gpio,i2c,pwm,spi,uart,watchdog,都是通过HDF机制加载,以GPIO和UART为例进行详细说明。
GPIO适配
1.芯片驱动适配文件位于drivers/adapter/platform目录,在gpio目录增加gpio_wm.c文件,在BUILD.gn文件中,描述了W800驱动的编译适配。如下:
...
if (defined(LOSCFG_SOC_COMPANY_WINNERMICRO)) {
sources += [ "gpio_wm.c" ]
}
...
2.gpio_wm.c中GPIO驱动实现如下:
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_GpioDriverEntry = {
.moduleVersion = 1,
.moduleName = "WM_GPIO_MODULE_HDF",
.Bind = GpioDriverBind,
.Init = GpioDriverInit,
.Release = GpioDriverRelease,
};
HDF_INIT(g_GpioDriverEntry);
编写一个简单的驱动,首先需要实现驱动程序 (Driver Entry)入口中的三个主要接口:
Bind 接口:实现驱动接口实例化绑定,如果需要发布驱动接口,会在驱动加载过程中被调用,实例化该接口的驱动服务并和 DeviceObject 绑定。 Init 接口:实现驱动的初始化,返回错误将中止驱动加载流程。 Release 接口:实现驱动的卸载,在该接口中释放驱动实例的软硬件资源。
3.在文件device\soc\winnermicro\wm800\hdf_config\device_info.hcs中添加GPIO驱动配置,具体实现如下:
device_info {
match_attr = "hdf_manager";
......
platform :: host {
hostName = "platform_host";
priority = 50;
device_gpio :: device {
gpio0 :: deviceNode {
policy = 0;
priority = 45;
moduleName = "WM_GPIO_MODULE_HDF";
serviceName = "HDF_PLATFORM_GPIO";
deviceMatchAttr = "gpio_config";
}
}
......
4.在device/board/hihope/shields/neptune100/neptune100.hcs添加gpio硬件描述信息, 添加内容如下:
root {
platform {
gpio_config {
match_attr = "gpio_config";
groupNum = 1;
pinNum = 48;
}
}
}
5.在GpioDriverInit获取hcs参数进行初始化,如下:
...
gpioCntlr = GpioCntlrFromHdfDev(device); --- gpioCntlr节点变量就可以获取具体gpio配置
if (gpioCntlr == NULL) {
HDF_LOGE("GpioCntlrFromHdfDev fail\r\n");
return HDF_DEV_ERR_NO_DEVICE_SERVICE;
}
...
UART适配
1.芯片驱动适配文件位于drivers/adapter/platform目录,在uart目录增加uart_wm.c文件,在BUILD.gn文件中,描述了W800驱动的编译适配。如下:
...
if (defined(LOSCFG_SOC_COMPANY_WINNERMICRO)) {
sources += [ "uart_wm.c" ]
}
...
2.uart_wm.c中UART驱动实现如下:
/* HdfDriverEntry definitions */
struct HdfDriverEntry g_UartDriverEntry = {
.moduleVersion = 1,
.moduleName = "W800_UART_MODULE_HDF",
.Bind = UartDriverBind,
.Init = UartDriverInit,
.Release = UartDriverRelease,
};
/* Initialize HdfDriverEntry */
HDF_INIT(g_UartDriverEntry);
3.在文件device\soc\winnermicro\wm800\hdf_config\device_info.hcs中添加UART驱动配置,具体实现如下:
device_info {
match_attr = "hdf_manager";
......
platform :: host {
hostName = "platform_host";
priority = 50;
device_uart :: device {
/*uart0 :: deviceNode {
policy = 1;
priority = 40;
moduleName = "W800_UART_MODULE_HDF";
serviceName = "HDF_PLATFORM_UART_0";
deviceMatchAttr = "uart0_config";
}*/
uart1 :: deviceNode {
policy = 1;
priority = 50;
moduleName = "W800_UART_MODULE_HDF";
serviceName = "HDF_PLATFORM_UART_1";
deviceMatchAttr = "uart1_config";
}
}
......
4.在device/board/hihope/shields/neptune100/neptune100.hcs添加uart硬件描述信息, 添加内容如下:
root {
platform {
uart_config {
/*
uart0 {
match_attr = "uart0_config";
num = 0;
baudrate = 115200;
parity = 0;
stopBit = 1;
data = 8;
}*/
uart1 {
match_attr = "uart1_config";
num = 1;
baudrate = 115200;
parity = 0;
stopBit = 1;
data = 8;
}
}
}
}
5.在UartDriverInit获取hcs参数进行初始化,如下:
...
host = UartHostFromDevice(device);
if (host == NULL) {
HDF_LOGE("%s: host is NULL", __func__);
return HDF_ERR_INVALID_OBJECT;
}
...
参考链接
HDF驱动框架:https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/driver/driver-hdf-manage.md