鸿蒙的驱动子系统-4-驱动配置文件的分析 原创 精华
鸿蒙的驱动子系统-4-驱动配置文件的分析
liangkz 2021.08.25
前文《小型系统驱动示例程序的编译和验证》提到,以UART驱动实例程序为例,可将示驱动程序分为三部分:
1. 设备树的描述文件及驱动的配置描述文件(.hcs)
2. 驱动程序的内核空间部分的实现和编译脚本
3. 驱动程序的用户空间部分的实现和编译脚本
本文将基于Hi3516平台详细分析第一部分驱动的配置文件的相关要点。
在项目根目录执行:find ./ -name *.hcs
./device/hisilicon/hispark_aries/sdk_liteos/config/hdf.hcs
......
./device/hisilicon/hispark_aries/sdk_liteos/config/watchdog/watchdog_config.hcs./device/hisilicon/hispark_taurus/sdk_liteos/config/hdf.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/sdio/sdio_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/input/input_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/spi/spi_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/emmc/emmc_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/device_info/device_info.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/gpio/gpio_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/rtc/rtc_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/i2c/i2c_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/lcd/lcd_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/uart/uart_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/wifi/wlan_platform.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/wifi/wlan_chip_hi3881.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/usb/usb_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/watchdog/watchdog_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/pwm/pwm_config.hcs
./device/hisilicon/hispark_taurus/sdk_liteos/config/dmac/dmac_config.hcs./device/qemu/arm_virt/config/hdf.hcs
......
./device/qemu/arm_virt/config/cfiflash/cfi_config.hcs
./vendor/huawei/hdf/sample/config/spi/spi_config.hcs
./vendor/huawei/hdf/sample/config/device_info/device_info.hcs
./vendor/huawei/hdf/sample/config/gpio/gpio_config.hcs
./vendor/huawei/hdf/sample/config/uart/uart_config.hcs./vendor/hisilicon/hispark_aries/config/hdf.hcs
......
./vendor/hisilicon/hispark_aries/config/wifi/wlan_chip_hi3881.hcs./vendor/hisilicon/hispark_taurus/config/hdf.hcs
./vendor/hisilicon/hispark_taurus/config/input/input_config.hcs
./vendor/hisilicon/hispark_taurus/config/device_info/device_info.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/emmc_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/sdio_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/pwm_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/hdf_test_manager/device_info.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/hdf_test.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/gpio_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/spi_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/uart_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/i2c_test_config.hcs
./vendor/hisilicon/hispark_taurus/config/hdf_test/hdf_config_test.hcs
./vendor/hisilicon/hispark_taurus/config/lcd/lcd_config.hcs
./vendor/hisilicon/hispark_taurus/config/sensor/sensor_config.hcs
./vendor/hisilicon/hispark_taurus/config/sensor/accel/bmi160_config.hcs
./vendor/hisilicon/hispark_taurus/config/sensor/accel/accel_config.hcs
./vendor/hisilicon/hispark_taurus/config/wifi/wlan_platform.hcs
./vendor/hisilicon/hispark_taurus/config/wifi/wlan_chip_hi3881.hcs./drivers/adapter/khdf/liteos/test/tools/hc-gen/test/unittest/02_empty_root_ei/case.hcs
......
./drivers/adapter/khdf/liteos/test/tools/hc-gen/test/unittest/30_include_order/base2.hcs
把hispark_aries、qemu/arm_virt、test/unittest这些关系不大的先去掉,剩下的整理成表格如下:
PartA灰色部分由 //drivers/adapter/khdf/liteos/hdf_lite.mk 文件内对 HAVE_VENDOR_CONFIG 的判断决定了不编译它,又由 LOSCFG_DRIVERS_HDF_TEST 决定编译入口在 PartB/hdf_test/Makefile,而不是PartB/Makefile。
PartB/PartC的蓝色部分,可以由开发者自行配置是否编译,PartB/hdf_test 不编译的话,那编译入口就是PartB/Makefile。
更详细的编译路径分析,见前文《驱动相关模块的编译》。
上表的文件可分为三类:
【3-1】Makefile:编译 hcs文件的入口。简单,下面不再详细分析。
LITEOSTOPDIR = //kernel/liteos_a/
HDF_DRIVER = //adapter/khdf/liteos/hdf_driver.mk 【定义在//drivers/adapter/khdf/liteos/lite.mk:61】
【3-2】device_info.hcs:这是整个平台所有设备信息的配置汇总,设备信息可以分布在不同路径下的若干个device_info.hcs文件中,最终会被hc-gen整合在一起。
device_info.hcs 包含了HDF框架加载驱动所需要的基本信息,基于HDF框架开发的驱动,必须要在device_info.hcs文件中添加对应的设备描述。
官方文档已经对该文件结构已经有很详细的解释了:
root {
device_info {
match_attr = "hdf_manager";
template host { // host模板,继承该模板的节点(如下sample_host)如果使用模板中的默认值,则节点字段可以缺省
//......
}
sample_host :: host{
hostName = "host0"; // host名称,host节点是用来存放某一类驱动的容器
priority = 100; // host启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证host的加载顺序
device_sample :: device { // sample设备节点
device0 :: deviceNode { // sample驱动的DeviceNode节点
policy = 1; // policy字段是驱动服务发布的策略,在驱动服务管理章节有详细介绍
priority = 100; // 驱动启动优先级(0-200),值越大优先级越低,建议默认配100,优先级相同则不保证device的加载顺序
preload = 0; // 驱动按需加载字段,在本章节最后的说明有详细介绍
permission = 0664; // 驱动创建设备节点权限
moduleName = "sample_driver"; // 驱动名称,该字段的值必须和驱动入口结构的moduleName值一致
serviceName = "sample_service"; // 驱动对外发布服务的名称,必须唯一
deviceMatchAttr = "sample_config"; // 驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
}
}
}
}
}
这里仅增加我自己的一些粗浅理解:
- host + hostName:一个host,就是同一类设备驱动的容器。如下面的平台类host,就包含了常见的i2c/gpio/uart/sdio等等,
platform :: host {
hostName = "platform_host";
........
}
还有外设类的host,包括了dipsplay类、input类、network类等等:
//vendor/hisilicon/hispark_taurus/config/device_info/device_info.hcs
Line 19: platform :: host {
Line 204: display :: host {
Line 245: input :: host {
Line 337: network :: host {
Line 360: sensor :: host {
Line 384: storage :: host {
Line 387: media :: host {
- device: 具备相同属性的某一类具体的设备,比如I2C设备、uart设备,
- deviceNode : 某一类具体设备的某些具体的设备节点,如I2C总线上挂着设备0,设备1,它们分别有各自的驱动配置
device_i2c :: device {
device0 :: deviceNode {
......
}
device1 :: deviceNode {
......
}
}
device_uart :: device {
device0 :: deviceNode {
......
}
device1 :: deviceNode {......
}}
接下来就是具体的deviceNode的各个字段了,以前文提到的uart驱动示例程序的deviceNode为例,配置见 //vendor/huawei/hdf/sample/config/device_info/device_info.hcs
device5 :: deviceNode {
policy = 2;
priority = 10;
permission = 0660;
moduleName = "UART_SAMPLE"; //uart_sample_driver
serviceName = "HDF_PLATFORM_UART_5"; //uart_sample_service
deviceMatchAttr = "sample_uart_5"; //uart_sample_config
}
- policy:驱动服务发布的策略,官方文档《驱动服务管理》章节有非常详细的解释。
- priority :驱动启动优先级(0-200),值越大优先级越低: 0~ 49板级驱动,50~149设备驱动,150~200 接口拔插类设备驱动
- preload :驱动加载方式,支持按需加载和按序加载两种方式,官方文档《驱动开发》章节也有非常详细的解释。
- permission :驱动创建设备节点权限,默认是0666
- moduleName = "UART_SAMPLE":驱动名称,该字段的值必须和驱动入口结构的moduleName值一致。
本文开头的“2. 驱动程序的内核空间部分的实现和编译脚本”中的内核实现部分代码中的驱动入口结构 g_sampleUartDriverEntry,代码见 //vendor/huawei/hdf/sample/platform/uart/src/uart_sample.c 文件:
struct HdfDriverEntry g_sampleUartDriverEntry = {
.moduleVersion = 1,
.moduleName = "UART_SAMPLE", //uart_sample_driver
.Bind = SampleUartDriverBind,
.Init = SampleUartDriverInit,
.Release = SampleUartDriverRelease,
};HDF_INIT(g_sampleUartDriverEntry);
- serviceName = "HDF_PLATFORM_UART_5":驱动对外发布服务的名称,必须唯一,这是调用者找到驱动服务的凭证,要完全匹配才能找到并使用对应的驱动服务。
本文开头的“3. 驱动程序的用户空间部分的实现和编译脚本”中的实现部分代码://vendor/huawei/hdf/sample/platform/uart/dispatch/uart_if.c 中
#define UART_DEV_SERVICE_NAME_PREFIX "HDF_PLATFORM_UART_%d" //uart_sample_service
struct DevHandle *UartOpen(uint32_t port) //sample code,port=5
{
......
ret = snprintf_s(serviceName, MAX_DEV_NAME_SIZE + 1,MAX_DEV_NAME_SIZE, UART_DEV_SERVICE_NAME_PREFIX, port);
......
}
- deviceMatchAttr = "sample_uart_5":驱动私有数据匹配的关键字,必须和驱动私有数据配置表中的match_attr值相等
这是 device_info.hcs 设备信息与下面的 xxx_config.hcs 中设备专属资源描述挂钩的凭证,device_info.hcs是设备树的树干和树枝,xxx_config.hcs 内的节点就是一片片树叶,deviceMatchAttr 就是树枝和叶片之间的叶柄。
【3-3】xxx_config.hcs:这是对特定设备专属资源的分别描述,不同类别的设备,各自使用的资源肯定也不同,会有自己的特定描述信息。
例如,//vendor/huawei/hdf/sample/config/uart/uart_config.hcs 文件对该设备节点的描述,上面的 deviceMatchAttr 必须与这里的 match_attr 匹配。
uart_sample {
num = 5;
base = 0x120a0000;
irqNum = 38;
baudrate = 115200;
uartClk = 24000000;
wlen = 0x60;
parity = 0;
stopBit = 0;
match_attr = "sample_uart_5"; //uart_sample_config
}
uart_sample 的其他字段则是对这个设备节点的一些资源的初始化/默认配置了。
这些配置在 HdfDeviceObject 结构体中,通过 property 指向的树形结构来保存:
/** Pointer to the property of the device, which is read by the HDF from the configuration file and
transmitted to the driver. */
const struct DeviceResourceNode *property;
在 SampleUartDriverInit(struct HdfDeviceObject *device)中调用 AttachUartDevice() 再调用GetUartDeviceResource()来读取property树形结构,从中解析出相关字段和值,保存在 struct UartDevice *uartDevice 结构体中,以供调用:
struct UartResource {
uint32_t num; /* UART port num */
uint32_t base; /* UART PL011 base address */
uint32_t irqNum; /* UART PL011 IRQ num */
uint32_t baudrate; /* Default baudrate */
uint32_t wlen; /* Default word length */
uint32_t parity; /* Default parity */
uint32_t stopBit; /* Default stop bits */
uint32_t uartClk; /* UART clock */
unsigned long physBase;
};
struct UartDevice {
struct IDeviceIoService ioService;
struct UartResource resource;
enum UartDeviceState state; /* UART State */
uint32_t uartClk; /* UART clock */
uint32_t baudrate; /* Baudrate */
struct BufferFifo rxFifo;
};
其他类型的设备节点专属资源,有各自定义的API和相关结构体做类似的事情。
对驱动配置文件的更多详情,还是请去官方文档仔细研读,比如驱动服务的管理、消息机制的管理和HCS的语法等等。
好久不见,第一时间赶到追更。