在分析openharmony的hdf框架的设备驱动加载器(IDriverLoader)时发现在创建实例时会首先判断一下是否完成了驱动入口的构建(HdfDriverEntryConstruct),如果没有构建会重新构建,这与我开始以为的不一致(我一直以为是采用的linux内核方式,只是由hdf来将驱动注册进去),oh将这部分由hdf框架接管了。下面我便将这部分实现驱动程序的动态加载和管理的技术细节进行分析。
实现驱动程序的动态加载和管理主要过程如下:
- 驱动注册:通过
HDF_DRIVER_INIT
宏将驱动入口点注册到.hdf.driver
段。 - 驱动加载:由设备管理器解析配置文件并调用驱动的
Bind
和Init
函数。 - 服务发布与订阅:通过
HdfDeviceNodePublishService
和HdfDeviceSubscribeService
实现。 - 驱动卸载:通过
Release
函数释放驱动资源。
在使用前首先看下驱动是如何应用的(有个大概的使用思路)
struct HdfDriverEntry g_i2cManagerEntry = {//驱动程序的入口信息
.moduleVersion = 1,//驱动模块的版本号
.Bind = I2cManagerBind,//用于将驱动与设备对象关联
.Init = I2cManagerInit,//用于完成驱动的初始化操作
.Release = I2cManagerRelease,//用于释放驱动占用的资源
.moduleName = "HDF_PLATFORM_I2C_MANAGER", //必须与 device_info.hcs 文件中的设备名称一致
};
HDF_INIT(g_i2cManagerEntry);//用于将驱动程序的入口结构注册到 HDF 框架中
下面分别介绍动态加载和管理的四个主要过程。
驱动注册
在 OpenHarmony 中,驱动程序通过 HDF_INIT宏注册到 .hdf.driver
段中。这个宏将驱动程序的入口点信息存储到特定的内存段,供运行时加载。
在hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h文件中我们可以看到实际HDF_INIT宏实际调用的为HDF_DRIVER_INIT。
#define HDF_INIT(module) HDF_DRIVER_INIT(module)
- HDF_DRIVER_INIT说明
结合hdf_core\framework\core\common\include\host\hdf_device_section.h代码的宏定义
//这用于确保驱动程序的入口点不会被编译器优化掉
#define USED_ATTR __attribute__((used))
//用于将变量或函数放置到指定的内存段(section)中
//将驱动程序的入口点信息存储到 .hdf.driver 段中
//这个段在运行时会被 HDF 框架扫描,以获取驱动程序的入口点
#define HDF_SECTION __attribute__((section(".hdf.driver")))
可得注册一个驱动程序模块
#define HDF_DRIVER_INIT(module) \
const size_t USED_ATTR module##HdfEntry HDF_SECTION = (size_t)(&(module))
定义一个全局变量(module##HdfEntry),其名称由驱动模块名(module)拼接而成。使用 USED_ATTR 确保该变量不会被优化掉。使用 HDF_SECTION 将该变量放置到 .hdf.driver 段中。将该变量的值设置为驱动模块的地址(&(module))。这样,驱动程序的入口点信息就会被存储到 .hdf.driver 段中,供运行时扫描和加载。
此时便有个疑问,我们只知道驱动程序的入口点信息就会被存储到 .hdf.driver 段中,那我们如何访问或使用它呢?
答案就在hdf_core\framework\core\common\include\host\hdf_device_section.h文件中的以下两个函数中
/**
* 这个宏定义用于获取指定变量的地址。
* 声明一个外部变量(extern type name)。
* 返回该变量的地址(&(name))。
* 这个宏主要用于定义 _hdf_drivers_start 和 _hdf_drivers_end,标记驱动程序入口点的范围。
*/
#define HDF_DRIVER_SEC_NAME(type, name) \
({extern type name; \
&(name); \
})
/**通过 HDF_DRIVER_SEC_NAME 宏,获取 _hdf_drivers_start 和 _hdf_drivers_end 的地址。
这些地址在运行时被 HDF 框架用来遍历 .hdf.driver 段中的驱动程序入口点。
HDF 框架可以通过 _hdf_drivers_start 和 _hdf_drivers_end 之间的范围,动态加载所有注册的驱动程序。
*/
#define HDF_DRIVER_BEGIN() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_start)
#define HDF_DRIVER_END() HDF_DRIVER_SEC_NAME(size_t, _hdf_drivers_end)
经过对上述代码的理解我们知道如何来使用了,但是此时还有一个疑问没有解决,**那就是_hdf_drivers_start和_hdf_drivers_end变量在哪定义的呢?它在哪被赋值了呢?**通过对代码的搜索可以看到在链接器脚本(vmlinux.lds.S
)中有相关定义。如下图所示:
这段代码的作用是:
- 定义一个名为
.init.hdf_table
的段。 - 在该段的起始位置设置
_hdf_drivers_start
符号。 - 将所有标记为
.hdf.driver
的数据(即驱动程序入口点)放入该段。 - 在该段的结束位置设置
_hdf_drivers_end
符号。
这部分的内容有点绕为了更好的总结在这儿弄了个使用demo,可以供参考理解:
// 定义一个驱动程序模块
struct HdfDriverEntry MyDriver;
// 注册驱动程序模块
HDF_DRIVER_INIT(MyDriver);
// 在代码中使用 HDF_DRIVER_BEGIN 和 HDF_DRIVER_END 获取驱动程序范围
size_t *start = HDF_DRIVER_BEGIN();
size_t *end = HDF_DRIVER_END();
// 遍历驱动程序入口点
for (size_t *ptr = start; ptr < end; ptr++) {
struct HdfDriverEntry *driver = (struct HdfDriverEntry *)(*ptr);
// 加载驱动程序
}
驱动加载
驱动加载过程由 HDF 框架的设备管理器(Device Manager)和设备主机(Device Host)协同完成,这部分详细的过程比较复杂(本周应该能写完这篇汇总的),此处只列出核心点:
设备管理器初始化:
static int __init DeviceManagerInit(void) { int ret = DeviceManagerStart(); if (ret < 0) { HDF_LOGE("%s start failed %d", __func__, ret); } else { HDF_LOGD("%s start success", __func__); } return ret; } late_initcall(DeviceManagerInit);
设备管理器在系统启动时通过
late_initcall
初始化,并开始加载驱动。驱动加载流程:
- 设备管理器解析配置文件(
device_info.hcs
),获取设备和驱动的映射关系。 - 根据配置文件中的
preload
字段,决定驱动是按需加载还是按序加载。 - 遍历
.hdf.driver
段,获取驱动程序的入口地址。 - 调用驱动的
Bind
和Init
函数完成初始化:
- 设备管理器解析配置文件(
代码调用过程如下:
int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode)
{
const struct HdfDriverEntry *driverEntry = devNode->driver->entry;
int ret = DeviceDriverBind(devNode);
if (ret != HDF_SUCCESS) {
return ret;
}
ret = driverEntry->Init(&devNode->deviceObject);
if (ret != HDF_SUCCESS) {
return HDF_DEV_ERR_DEV_INIT_FAIL;
}
ret = HdfDeviceNodePublishService(devNode);
if (ret != HDF_SUCCESS) {
return HDF_DEV_ERR_PUBLISH_FAIL;
}
return ret;
}
服务发布与订阅
HDF 框架支持服务发布和订阅机制,允许驱动程序在初始化后发布服务接口,供其他模块使用
服务发布:
int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode);
服务订阅:
其他模块可以通过 HdfDeviceSubscribeService
订阅驱动服务。当驱动加载完成后,框架会将服务接口发布给订阅者
int HdfDeviceSubscribeService(struct HdfDeviceObject *deviceObject, const char *serviceName, struct SubscriberCallback callback);
驱动卸载
和驱动加载相对应的卸载驱动的函数为HdfDeviceUnlaunchNode,当驱动被卸载或初始化失败时,HDF 框架会调用驱动的 Release
函数释放资源:
static void HdfDeviceUnlaunchNode(struct HdfDeviceNode *devNode)
{
....
if (devNode->driver != NULL) {
driverEntry = devNode->driver->entry;//从设备节点中获取驱动程序对象(devNode->driver)
}
if (driverEntry != NULL && driverEntry->Release != NULL) {
driverEntry->Release(&devNode->deviceObject);//如果驱动程序的入口结构和 Release 函数指针都有效,调用 Release 函数释放驱动占用的资源,即驱动中传入的release函数
}
....
}