openharmony中hdf框架的驱动消息机制的实现原理
在分析hdf框架时发现绕来绕去的,整体梳理画了一遍流程图,发现还是有点模糊甚至不清楚如何使用的,详细的每个点都去剖析细节又过于消耗时间,所以有时间便从功能应用的角度一块块的去梳理。
此文为参考官方源码(oh5.0版本)中的docs\zh-cn\device-dev\driver\driver-hdf-manage.md驱动开发手册,将驱动消息机制这个小章节拿出来,单独做剖析的。官方手册中只涉及了如何使用,未涉及具体的原理。本文会先整体说下实现原理,然后结合驱动开发手册将涉及使用hdf接口函数的部分再往下剖了一下,目的是了解具体的实现逻辑。
概述
HDF框架提供统一的驱动消息机制,支持用户态应用向内核态驱动发送消息,也支持内核态驱动向用户态应用发送消息,用于当用户态应用和内核态驱动需要交互的场景。
使用分析
由原理可知消息机制的功能主要有以下两种:
- 用户态应用发送消息到驱动。
- 用户态应用接收驱动主动上报事件。
表2 消息机制接口
方法 | 描述 |
---|---|
struct HdfIoService *HdfIoServiceBind(const char *serviceName); | 用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息。 |
void HdfIoServiceRecycle(struct HdfIoService *service); | 用户态释放驱动的服务,与HdfIoServiceBind对应 |
int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | 用户态程序注册接收驱动上报事件的操作方法。 |
int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data) | 驱动主动上报事件接口。当驱动服务调用此函数发送消息时,所有通过 HdfDeviceRegisterEventListener注册了监听器的用户级应用程序都将收到该消息。 |
HdfIoServiceBind
用户态获取驱动的服务,获取该服务之后通过服务中的Dispatch方法向驱动发送消息(见示例),函数的声明在接口文件中hdf_core\interfaces\inner_api\core\hdf_io_service_if.h,驱动开发者可以直接包含调用。
struct HdfIoService *HdfIoServiceBind(const char *serviceName)
|-->HdfIoServiceAdapterObtain(serviceName) //获取设备服务接口的适配器函数
|-->svcMgr = DevSvcManagerClntGetInstance();//获取设备服务管理实例
|-->HdfDeviceObject *deviceObject = svcMgr->devSvcMgrIf->GetObject(svcMgr->devSvcMgrIf, serviceName);//通过设备服务管理器获取设备对象
|-->DevSvcManagerGetObject //通过获取设备服务管理实例的创建过程可知上述函数的回调为此函数
|-->serviceRecord = DevSvcManagerSearchServiceLocked(inst, serviceKey)//从设备服务管理器中搜索该名称的服务
|-->return serviceRecord->value //返回设备对象
|-->HdfIoServiceKClient *kClient = HdfHdfIoServiceKClientInstance(deviceObject)//通过设备对象获取设备服务的客户端实例
|-->kDispatcher = {.Dispatch = HdfKIoServiceDispatch,};
|-->struct HdfIoServiceKClient *client = OsalMemCalloc(sizeof(struct HdfIoServiceKClient))//分配内存
|-->if (deviceObject->service->Open(&client->client) != HDF_SUCCESS)//回调驱动层的open函数
|-->client->ioService.dispatcher = &kDispatcher//绑定具体的接口,
|-->return &kClient->ioService//返回设备服务接口
HdfIoServiceRecycle
用户态释放驱动的服务,与HdfIoServiceBind对应,声明在接口文件中hdf_core\interfaces\inner_api\core\hdf_io_service_if.h,驱动开发者可以直接包含调用。
void HdfIoServiceRecycle(struct HdfIoService *service)
|-->HdfIoServiceAdapterRecycle(service)
|-->HdfIoServiceKClient *kClient = CONTAINER_OF(ioService, struct HdfIoServiceKClient, ioService)//根据ioService反推HdfIoServiceKClient对象①
|-->kClient->client.device->service->Release(&kClient->client)//客户端设备的释放②
|-->OsalMemFree(kClient)//对应HdfIoServiceBind函数中的HdfHdfIoServiceKClientInstance
①:关于反推函数CONTAINER_OF需要详细的了解的可以参考这篇文章
②:客户端设备的释放是在设备构建过程中构建的io服务的设备接口(IDeviceIoService),从整体的流程图中方便看,但太大了没法放上来,后续有时间整理了这部分再重新放上链接。✒️
HdfDeviceRegisterEventListener
用户态程序注册接收驱动上报事件的操作方法
int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener)
|-->return HdfDeviceRegisterEventListenerWithSchedPolicy(target, listener, SCHED_OTHER)//为设备服务注册事件监听器,并指定事件处理线程的调度策略
|-->struct HdfSyscallAdapter *adapter = CONTAINER_OF(target, struct HdfSyscallAdapter, super)//从HdfIoService结构体中获取其所属的HdfSyscallAdapter实例
|-->if (!AddListenerToAdapterLocked(adapter, listener)) //将事件监听器添加到适配器中
|--> ret = HdfIoServiceGroupThreadStart(adapter->group, policy) //如果适配器属于一个服务组(adapter->group不为空)便启动服务组的线程,并指定调度策略(policy即传入的SCHED_OTHER)
|--> if (HdfIoServiceGroupThreadInit(group) != HDF_SUCCESS) //对线程的初始化
|-->HdfDevListenerThreadDoInit(thread)
|-->int32_t ret = OsalThreadCreate(&thread->thread, HdfDevEventListenTask, thread)//创建监听线程
|-->//....路径较多省略了
|-->int32_t HdfDevEventDispatchLocked(
const struct HdfDevListenerThread *thread, struct HdfSyscallAdapter *adapter, const struct HdfWriteReadBuf *bwr)
|-->(void)listener->callBack(listener->priv, bwr->cmdCode, sbuf)//此处进行监听事件的回调①
|--> int32_t ret = HdfDevListenerThreadStart(group->thread)//初始化设备监听线程,并启动一个线程来处理设备事件
|-->int32_t ret = HdfListenThreadInitPollFds(thread)//初始化监听的文件描述符列表
|-->if (HdfAdapterStartListenIoctl(thread->pfds[i].fd)) {//启动监听
|-->if (OsalThreadStart(&thread->thread, &config) != HDF_SUCCESS) {//启动线程
return ret;
|-->if (HdfIoServiceStartListen(adapter, policy) != HDF_SUCCESS)//如果适配器不属于服务组,启动事件处理线程,并指定调度策略
|-->return HdfDevListenerThreadStart(adapter->thread)//初始化设备监听线程,并启动一个线程来处理设备事件
SCHED_OTHER是linux系统中默认的进程调度策略,想详细了解进程调度策略可以参考这篇文章
在本文后续的使用示例中可见此函数的使用的目的,主要是为了将事件进行回调,其中回调部分可参看本段代码的①处。
static struct HdfDevEventlistener listener = {
.callBack = OnDevEventReceived,
.priv ="Service0"
};
if (HdfDeviceRegisterEventListener(serv, &listener) != 0) {
HDF_LOGE("fail to register event listener");
return HDF_FAILURE;
}
- 在此段代码中还可见有文件描述符的处理,即应用中我们用的select
、
poll或
epoll等。
HdfDeviceSendEvent
驱动主动上报事件接口,驱动服务调用此函数发送消息时,所有通过 HdfDeviceRegisterEventListener注册了监听器的用户级应用程序都将收到该消息。声明的头文件在hdf_core\interfaces\inner_api\host\shared\hdf_device_desc.h,意味着用户态可以直接包含调用。
int32_t HdfDeviceSendEvent(const struct HdfDeviceObject *deviceObject, uint32_t id, const struct HdfSBuf *data)
|-->struct HdfDeviceNode *deviceNode = CONTAINER_OF(deviceObject, struct HdfDeviceNode, deviceObject)
|-->adapter = (struct HdfVNodeAdapter *)(((struct DeviceNodeExt *)deviceNode)->ioService)
|-->return HdfVNodeAdapterSendDevEvent(adapter, NULL, id, data)
|-->ret = VNodeAdapterSendDevEventToClient(client, id, data)//将设备事件从驱动程序发送到客户端(通常是用户态应用程序或服务)
|-->event = OsalMemAlloc(sizeof(struct HdfDevEvent));//分配事件对象
|--> event->data = HdfSbufCopy(data);//给事件对象赋值
|-->DListInsertTail(&event->listNode, &vnodeClient->eventQueue) //将事件对象插入到客户端的事件队列
|-->wake_up_interruptible(&vnodeClient->pollWait)//唤醒等待事件的客户端线程
- 唤醒等待事件的客户端线程的wake_up_interruptible函数为linux内核的api函数,有需要详细了解的可以直接百度,想了解简单使用方法的可以参考这篇文章。
使用示例
驱动消息机制管理开发
将驱动配置信息(device_info.hcs)中服务策略policy字段设置为2(SERVICE_POLICY_CAPACITY,驱动对内核态和用户态都发布服务)。
device_sample :: Device { policy = 2; permission = 0644; ... }
配置驱动信息中的服务设备节点权限(permission字段)是框架给驱动创建设备节点的权限,默认是0666,驱动开发者根据驱动的实际使用场景配置驱动设备节点的权限。
权限值 含义 0666
所有用户都可以读写该设备节点 0644
所有者可以读写,所属组和其他用户可以读取 0640
所有者可以读写,所属组可以读取,其他用户无法访问 0600
只有所有者可以读写,其他用户无法访问 在服务实现过程中,实现服务基类成员IDeviceIoService中的Dispatch方法。
#include <hdf_log.h> #include <hdf_device_io.h> #include <hdf_device_desc.h> #include <hdf_sbuf.h> // 假设的其他服务函数 int32_t SampleDriverServiceA(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply) { HDF_LOGI("SampleDriverServiceA called"); return HDF_SUCCESS; } int32_t SampleDriverServiceB(struct HdfDeviceIoClient *client, struct HdfSBuf *data, struct HdfSBuf *reply) { HDF_LOGI("SampleDriverServiceB called"); return HDF_SUCCESS; } // I/O 请求处理函数 int32_t SampleDriverDispatch(struct HdfDeviceIoClient *client, int cmdCode, struct HdfSBuf *data, struct HdfSBuf *reply) { HDF_LOGI("SampleDriverDispatch called with cmdCode: %d", cmdCode); // 根据 cmdCode 处理不同的命令 switch (cmdCode) { case 1: HDF_LOGI("Handling command 1"); // 处理命令 1 的逻辑 const char *msg = "Hello from driver"; struct HdfSBuf *eventData = HdfSbufObtainDefaultSize(); // 创建事件数据缓冲区 if (eventData == NULL) { HDF_LOGE("fail to obtain sbuf for event data"); return HDF_DEV_ERR_NO_MEMORY; } if (!HdfSbufWriteString(eventData, msg)) { // 写入事件数据 HDF_LOGE("fail to write event data"); HdfSbufRecycle(eventData); return HDF_FAILURE; } break; case 2: HDF_LOGI("Handling command 2"); // 处理命令 2 的逻辑 break; default: HDF_LOGE("Unknown command code: %d", cmdCode); return HDF_ERR_INVALID_PARAM; } // 上报事件 int ret = HdfDeviceSendEvent(client->device, cmdCode, eventData); HdfSbufRecycle(eventData); // 释放事件数据缓冲区 return HDF_SUCCESS; } // 驱动绑定函数 int32_t SampleDriverBind(struct HdfDeviceObject *device) { HDF_LOGI("SampleDriverBind called"); if (device == NULL) { HDF_LOGE("Invalid device object"); return HDF_FAILURE; } // 定义服务接口 static struct ISampleDriverService sampleDriverA = { .ioService.Dispatch = SampleDriverDispatch, .ServiceA = SampleDriverServiceA, .ServiceB = SampleDriverServiceB, }; // 将服务接口绑定到设备对象 device->service = (struct IDeviceIoService *)&sampleDriverA; return HDF_SUCCESS; } // 驱动卸载函数 void SampleDriverUnload(struct HdfDeviceObject *device) { HDF_LOGI("SampleDriverUnload called"); // 在这里释放资源或执行清理操作 } // 驱动描述符 struct HdfDriverEntry g_sampleDriver = { .moduleVersion = 1, .Bind = SampleDriverBind, .Unload = SampleDriverUnload, .moduleName = "sample_driver", }; // 驱动模块入口 SYSEXPORT_DRIVER(g_sampleDriver);
驱动定义消息处理函数中的cmd类型。
#define SAMPLE_WRITE_READ 1 // 读写操作码1
用户态获取服务接口并发送消息到驱动。
int SendMsg(const char *testMsg) { if (testMsg == NULL) { HDF_LOGE("test msg is null"); return HDF_FAILURE; } struct HdfIoService *serv = HdfIoServiceBind("sample_driver");// 绑定到驱动服务 if (serv == NULL) { HDF_LOGE("fail to get service"); return HDF_FAILURE; } struct HdfSBuf *data = HdfSbufObtainDefaultSize();// 分配数据和响应缓冲区 if (data == NULL) { HDF_LOGE("fail to obtain sbuf data"); return HDF_FAILURE; } struct HdfSBuf *reply = HdfSbufObtainDefaultSize(); if (reply == NULL) { HDF_LOGE("fail to obtain sbuf reply"); ret = HDF_DEV_ERR_NO_MEMORY; goto out; } if (!HdfSbufWriteString(data, testMsg)) {// 写入数据到数据缓冲区 HDF_LOGE("fail to write sbuf"); ret = HDF_FAILURE; goto out; } int ret = serv->dispatcher->Dispatch(&serv->object, SAMPLE_WRITE_READ, data, reply);// 调用服务的 Dispatch 函数发送请求 if (ret != HDF_SUCCESS) { HDF_LOGE("fail to send service call"); goto out; } out: HdfSbufRecycle(data); HdfSbbufRecycle(reply); HdfIoServiceRecycle(serv); return ret; }
用户态接收该驱动上报的消息。
//用户态编写驱动上报消息的处理函数。 static int OnDevEventReceived(void *priv, uint32_t id, struct HdfSBuf *data) { OsalTimespec time; OsalGetTime(&time); HDF_LOGI("%{public}s received event at %{public}llu.%{public}llu", (char *)priv, time.sec, time.usec); const char *string = HdfSbufReadString(data); if (string == NULL) { HDF_LOGE("fail to read string in event data"); return HDF_FAILURE; } HDF_LOGI("%{public}s: dev event received: %{public}d %{public}s", (char *)priv, id, string); return HDF_SUCCESS; } //用户态注册接收驱动上报消息的操作方法。 int RegisterListen() { struct HdfIoService *serv = HdfIoServiceBind("sample_driver"); if (serv == NULL) { HDF_LOGE("fail to get service"); return HDF_FAILURE; } static struct HdfDevEventlistener listener = { .callBack = OnDevEventReceived, .priv ="Service0" }; if (HdfDeviceRegisterEventListener(serv, &listener) != 0) { HDF_LOGE("fail to register event listener"); return HDF_FAILURE; } ...... HdfDeviceUnregisterEventListener(serv, &listener); HdfIoServiceRecycle(serv); return HDF_SUCCESS; }