- OpenHarmony HDF 平台驱动
1.平台驱动概述
系统平台驱动框架是系统驱动框架的重要组成部分,它基于HDF驱动框架、操作系统适配层(OSAL, operating system abstraction layer)以及驱动配置管理机制,为各类平台设备驱动的实现提供标准模型。
系统平台驱动(PlatformDriver),即平台设备驱动,它用于驱动平台设备(PlatformDevice),为系统及外设驱动提供访接口。这里的平台设备,泛指I2C/UART等总线、以及GPIO/RTC等SOC片内硬件资源。
系统平台驱动框架为外设提供了标准的平台设备访问接口,使其不必关注具体硬件及OS平台;同时为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制。
为实现这个目标,OpenHarmony系统平台驱动框架满足如下特性:
- 统一的平台设备访问接口:对平台设备操作接口进行统一封装,屏蔽不同SOC平台硬件差异以及不同OS形态差异。
- 统一的平台驱动适配接口:为平台设备驱动提供统一的适配接口,使其只关注自身硬件的控制,而不必关注设备管理及公共业务流程。
- 提供设备注册、管理、访问控制等与SOC无关的公共能力。
2.平台驱动框架介绍
OpenHarmony系统平台驱动框架主要由平台接口层、平台核心层以及平台适配层三个部分组成。
-
平台接口层 以API的形式提供标准的平台设备访问接口。
平台接口层以设备句柄加配套API的形式对外提供统一的、标准的访问接口。
设备句柄是DevHandle类型的实例,通过不同设备模块提供的Open/Close方法进行获取、释放。成功获取设备句柄后,即可使用相应的API执行设备操作。例如通过I2cTransfer完成一次I2C数据传输。
这是一种代理模式,即接口层API不直接引用实际设备对象,而是通过DevHandle作为代理,间接访问设备;而所有来自外设驱动的访问,都建议走接口层,以获得最佳的稳定性。
-
平台核心层 提供平台设备模型及公共业务框架。
提供统一适配接口:定义了标准的设备对象模型,驱动程序仅需关注标准对象模型的适配。抽取公共业务框架:将不同设备模块的公共流程、算法加以抽取,使得具体设备驱动更加轻薄。
设备管理:设备注册、注销、设备查找、访问控制。
-
平台适配层 提供特定平台设备的适配驱动,并遵守核心层约束。
驱动具体平台设备硬件,并创建对应的设备模型对象,注册到核心层纳入统一管理。
2.1.平台接口层分析
接口层用DevHandle类型的设备句柄表示一个平台设备对象,然后针对不同类型设备提供一套标准的API方法用于设备访问。那么设备句柄和真实的设备对象如何关联呢?
drivers_hdf_core/framework/include/platform/platform_if.h:39 /**40 * @brief Defines the common device handle of the platform driver.41 *42 * The handle is associated with a specific platform device and is used as the43 * first input parameter for all APIs of the platform driver.44 *45 * @since 1.046 */47 typedef void* DevHandle;
在内核态,这个指针可以直接指向实际设备对象,但是对于某些类型的平台设备,需要在用户态提供同样的DevHandle类型及配套API,而实际设备对象在内核空间,导致无法直接获取和使用内核空间的地址。
解决办法:将平台设备对象实现为一个HDF设备服务,这样借助HDF DeviceManager的设备服务机制,可以在用户态、内核态同时获取到设备服务,而用户态同内核态通信的问题交由HDF DeviceManager处理。此时,DevHandle只需要关联到这个设备服务即可,而void*类型保证了足够的灵活性。
根据DevHandle和设备对象关联方式的不同,接口层的设计有三种模式:
2.1.1.独立服务模式
典型实践是UART模块。
使用范围:在用户态和内核态同时提供API的设备类型,DevHandle同设备对象的关联方式为:
-
用户态:关联到平台设备对应的设备服务;
-
内核态:关联到实际平台设备对象或其设备服务(在内核态两者可互相转换)
每一个设备对象,会独立发布一个设备服务,来处理外部访问,服务收到API的访问请求之后,通过提取请求参数,并调用实际设备对象的相应内部方法。
优点:管理比较简单,因为它借助了HDF DeviceManager的服务管理能力;
缺点:需要为每一个设备对象配置设备节点,以便发布设备服务。
2.1.2.统一服务模式
典型实践是I2C模块。
同一类型的设备对象可能会很多,例如I2C模块,可能同时有十几个控制器。如果采用独立服务的模式,每一个控制器,作为一个平台设备,为其创建一个设备服务,那么将会有十几个服务被创建,不光要配置很多设备节点,而且这些服务还会占用内存资源。
因此为一类设备对象,创建一个平台设备管理器(PlatformManager)对象,并同时对外发布一个管理器服务,由这个管理器服务来统一处理外部访问。当用户需要打开某个设备时,先通过HDF DeviceManager获取到管理器服务,然后管理器服务根据用户指定参数查找到指定设备,并返回一个设备描述符,而这个描述符仍然可以由DevHandle类型表示。
2.1.3.无服务模式
用于不需要在用户态提供API的设备类型或者没有用户态、内核区分的OS系统,其关联方式是DevHandle直接指向设备对象内核态地址。而PlatformManager的实现比较自由,它不需要实现设备服务,只需做好某种类型的设备管理即可,甚至在C语言中,由于无法进行OOP编程,很多模块直接将这个功能面向过程化了,使得没有一个具体的结构体与之对应。
2.2.平台核心层
平台核心层的作用是承上启下,其主要内容包括:
-
提供适配接口:为具体的平台设备驱动提供统一的适配接口
平台驱动框架为不同设备类型,定义了标准的设备对象模型,具体设备驱动只需要关注标准设备对象的适配即可
-
提供设备管理:提供设备的注册、注销、查找等功能、访问控制等能力
核心层会提供一系列内部方法,用于设备的注册、注销,设备对象的查找、获取、释放,以及处理多线程访问。例如当向核心层注册一个I2C控制器对象时,使用I2cCntlrAdd;当希望获取一个I2C控制器对象时,通过I2cCntlrGet并指定控制器编号;当不再使用这个对象时,还需要通过I2cCntlrPut释放。这样做的好处是将每一个具体的操作步骤高度抽象化,减小同平台接口层及平台适配层的耦合面,便于业务解耦、演进。由于业务需求需要对I2cCntlr对象进行引用计数,那么只需要修改I2cCntlrGet/Put这对方法的实现即可,并不会影响平台接口层和平台适配层。
-
公共业务实现:抽取公共的业务流程、算法
凡是跟特定硬件无关的业务逻辑,都会被抽取到核心层,例如RTC时钟的时间格式转换算法,GPIO模块的线程中断实现等等。
2.3.平台适配层实现
适配层提供具体平台硬件设备的驱动,按照核心层定义的模型创建设备对象,并完成对象的初始化(包括必要的成员变量初始化以及钩子方法挂接,以及相关的硬件初始化操作),最后使用核心层提供的注册方法将设备对象注册到核心层纳入统一管理。
3.OpenHarmony系统平台驱动适配
3.1.UART模块适配
UART模块适配的核心环节,是UartHost对象的创建、初始化及注册。UART模块采用的是独立服务模式,要求每一个UartHost对象关联一个HDF设备服务。
1).device_info.hcs: 为每一个UART控制器配置一个HDF设备节点
vendor/hihope/rk3568/hdf_config/khdf/device_info/device_info.hcs:71 device_uart :: device {72 device0 :: deviceNode {73 policy = 2;74 priority = 40;75 permission = 0644;76 moduleName = "HDF_PLATFORM_UART";77 serviceName = "HDF_PLATFORM_UART_0";78 deviceMatchAttr = "rockchip_rk3568_uart_0";79 }80 device1 :: deviceNode {81 policy = 2;82 permission = 0644;83 priority = 40;84 moduleName = "HDF_PLATFORM_UART";85 serviceName = "HDF_PLATFORM_UART_1";86 deviceMatchAttr = "rockchip_rk3568_uart_1";87 }88 device2 :: deviceNode {89 policy = 2;90 permission = 0644;91 priority = 40;92 moduleName = "HDF_PLATFORM_UART";93 serviceName = "HDF_PLATFORM_UART_3";94 deviceMatchAttr = "rockchip_rk3568_uart_3";95 }96 }
说明:
-
policy大于等于1(如需对用户态可见为2,仅内核态可见为1);
-
moduleName需要与驱动Entry中moduleName 保持一致;
-
serviceName必须要按照HDF_PLATFORM_UART_X的格式,X为UART控制器编号;
-
deviceMatchAttr用于配置控制器私有数据,要与uart_config.hcs中对应控制器保持一致,如不需要则忽略。
2). uart_config.hcs:每一个UART控制器配置私有数据
如果控制器需要配置一些私有数据,例如寄存器基地址,初始化波特率等等,可以在uart_config.hcs中配置,该文件将在产品配置目录的hdf.hcs中导入,具体路径可由产品自由配置。
vendor/hihope/rk3568/hdf_config/khdf/platform/rk3568_uart_config.hcs:1 root {2 platform {3 uart_config {4 template uart_device {5 serviceName = "";6 match_attr = "";7 driver_name = "ttyS";8 num = 0;9 }1011 device_uart_0x0000 :: uart_device {12 match_attr = "rockchip_rk3568_uart_0";13 }14 device_uart_0x0001 :: uart_device {15 num = 1;16 match_attr = "rockchip_rk3568_uart_1";17 }18 device_uart_0x0003 :: uart_device {19 num = 3;20 match_attr = "rockchip_rk3568_uart_3";21 }22 }23 }24 }hihope/rk3568/hdf_config/khdf/hdf.hcs:1 #include "device_info/device_info.hcs"2 #include "platform/adc_config_linux.hcs"3 #include "platform/pwm_config.hcs"4 #include "platform/rk3568_watchdog_config.hcs"5 #include "platform/rk3568_uart_config.hcs"6 #include "platform/sdio_config.hcs"7 #include "platform/emmc_config.hcs"8 #include "platform/rk3568_spi_config.hcs"9 #include "input/input_config.hcs"10 #include "wifi/wlan_platform.hcs"11 #include "wifi/wlan_chip_ap6275s.hcs"12 #include "camera/camera_config.hcs"13 #include "sensor/sensor_config.hcs"14 #include "audio/audio_config.hcs"15 #include "audio/codec_config.hcs"16 #include "audio/dai_config.hcs"17 #include "audio/dma_config.hcs"18 #include "audio/dsp_config.hcs"19 #include "audio/analog_headset_config.hcs"20 #include "light/light_config.hcs"21 #include "vibrator/vibrator_config.hcs"22 #include "vibrator/linear_vibrator_config.hcs"23 #include "vibrator/drv2605l_linear_vibrator_config.hcs"24 #include "lcd/lcd_config.hcs"
3).驱动的Entry结构
先执行Bind方法绑定服务,后执行Init 初始化。
drivers_hdf_core/adapter/platform/uart/uart_asr.c:
685 struct HdfDriverEntry g_hdfUartDevice = {
686 .moduleVersion = 1,
687 .moduleName = "HDF_PLATFORM_UART",
688 .Bind = HdfUartDeviceBind,
689 .Init = HdfUartDeviceInit,
690 .Release = HdfUartDeviceRelease,
691 };
692
693 HDF_INIT(g_hdfUartDevice);
- Bind方法调用UartHostCreate创建 UartHost 控制器对象并完成服务绑定。
drivers_hdf_coreframework\support\platform\include\uart\uart_core.h:
struct UartHost {struct IDeviceIoService service;struct HdfDeviceObject *device;uint32_t num;OsalAtomic atom;void *priv;struct UartHostMethod *method;
};drivers_hdf_core/framework/support/platform/src/uart/uart_core.c
635 static int32_t HdfUartDeviceBind(struct HdfDeviceObject *device)
636 {
637 ...
640 return (UartHostCreate(device) == NULL) ? HDF_FAILURE : HDF_SUCCESS;
641 }drivers_hdf_core/framework/support/platform/src/uart/uart_core.c79 struct UartHost *UartHostCreate(struct HdfDeviceObject *device)80 {81 struct UartHost *host = NULL;8288 host = (struct UartHost *)OsalMemCalloc(sizeof(*host));9394 host->device = device;95 device->service = &(host->service);96 host->device->service->Dispatch = UartIoDispatch;97 OsalAtomicSet(&host->atom, 0);98 host->priv = NULL;99 host->method = NULL;
100 return host;
101 }
该方法中将UartHost对象同HdfDeviceObject进行关联:
-
为HdfDeviceObject的service成员进行赋值,使其指向UartHost的IDeviceIoService类型的成员对象;
-
同时为service成员的Dispatch方法赋值。
host->device->service->Dispatch = UartIoDispatch; (后面讨论)
这样做的结果:
-
为HdfDeviceObject对象绑定IDeviceIoService类型的服务对象
-
UartHost和其IDeviceIoService类型的成员对象service可以相互转换
-
通过UartHost对象即可获取HdfDeviceObject对象
-
通过HdfDeviceObject对象即可间接获取UartHost对象(先获取service再转为host)
4). Init方法
完成UartHost对象的初始化
drivers_hdf_core/adapter/platform/uart/uart_asr.c:
643 static int32_t HdfUartDeviceInit(struct HdfDeviceObject *device)
644 {
645 int32_t ret;
646 struct UartHost *host = NULL;
647
652 host = UartHostFromDevice(device);
657 ret = UartDevAttach(host, device);
662 host->method = &g_uartHostMethod;
663 return ret;
664 }518 struct UartHostMethod g_uartHostMethod = {
519 .Init = UartHostDevInit,
520 .Deinit = UartHostDevDeinit,
521 .Read = UartHostDevRead,
522 .Write = UartHostDevWrite,
523 .SetBaud = UartHostDevSetBaud,
524 .GetBaud = UartHostDevGetBaud,
525 .SetAttribute = UartHostDevSetAttribute,
526 .GetAttribute = UartHostDevGetAttribute,
527 .SetTransMode = UartHostDevSetTransMode,
528 };
通过UartHostFromDevice从HdfDeviceObject对象获取之前关联的UartHost对象,然后调用 UartDevAttach方法完成host对象的初始化,最后为host对象挂接钩子方法。
小结:
UART适配关键是在驱动Entry的Bind方法中创建UartHost对象,而且是使用UartHostCreate创建。这个创建动作同时也是注册的动作,因为它将UartHost以HDF设备服务的形式同HdfDeviceObject进行绑定,这样就完成了服务的发布,HDF Manager对设备服务的管理也就是对UartHost的管理,核心层可以通过HDF提供的服务获取接口来访问UartHost。
UART适配采用独立服务模式,每一个UartHost对象同时也是一个设备服务,其优点是可以直接利用HDF Manager进行管理;缺点是需要在device_info.hcs为每一个UartHost对象定义设备节点。
3.2.UART模块主要接口
- drivers/hdf_core/framework/include/platform/uart_if.h
DevHandle UartOpen(uint32_t port) UART获取设备句柄
void UartClose(DevHandle handle) UART释放设备句柄
int32_t UartRead(DevHandle handle, uint8_t *data, uint32_t size) 从UART设备中读取指定长度的数据
int32_t UartWrite(DevHandle handle, uint8_t *data, uint32_t size) 向UART设备中写入指定长度的数据
int32_t UartGetBaud(DevHandle handle, uint32_t *baudRate) UART获取波特率
int32_t UartSetBaud(DevHandle handle, uint32_t baudRate) UART设置波特率
int32_t UartGetAttribute(DevHandle handle, struct UartAttribute *attribute) UART获取设备属性
int32_t UartSetAttribute(DevHandle handle, struct UartAttribute *attribute) UART设置设备属性
int32_t UartSetTransMode(DevHandle handle, enum UartTransMode mode) UART设置传输模式
本文涉及的UART所有接口,支持内核态及用户态使用。
UartOpen 代码分析:
UartOpen->UartGetObjGetByBusNum(port);-> DevSvcManagerClntGetService(name) -> UartHostRequest((struct UartHost *)handle);
- UartGetObjGetByBusNum 调用DevSvcManagerClntGetService,根据name=“HDF_PLATFORM_UART_port” (framework/sample/config/device_info/device_info.hcs) 获取到handle 即host。
- UartHostRequest 调用host->method->Init,即UartHostDevInit(uart_asr.c)
refer to
- git clone https://gitee.com/openharmony/drivers_hdf_core.git
- https://blog.csdn.net/HarmonyOS_666/article/details/140824175
- https://blog.csdn.net/maniuT/article/details/141064333
- https://blog.csdn.net/zxc95279527q/article/details/143062695