openharmony中hdf框架的驅動消息機制的實現原理

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;
}
  • 在此段代碼中還可見有文件描述符的處理,即應用中我們用的selectpollepoll等。

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函數,有需要詳細了解的可以直接百度,想了解簡單使用方法的可以參考這篇文章。

使用示例

驅動消息機制管理開發

  1. 將驅動配置信息(device_info.hcs)中服務策略policy字段設置為2(SERVICE_POLICY_CAPACITY,驅動對內核態和用戶態都發布服務)。

    device_sample :: Device {policy = 2;permission = 0644;...
    }
    
  2. 配置驅動信息中的服務設備節點權限(permission字段)是框架給驅動創建設備節點的權限,默認是0666,驅動開發者根據驅動的實際使用場景配置驅動設備節點的權限。

    權限值含義
    0666所有用戶都可以讀寫該設備節點
    0644所有者可以讀寫,所屬組和其他用戶可以讀取
    0640所有者可以讀寫,所屬組可以讀取,其他用戶無法訪問
    0600只有所有者可以讀寫,其他用戶無法訪問
  3. 在服務實現過程中,實現服務基類成員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);
    
  4. 驅動定義消息處理函數中的cmd類型

    #define SAMPLE_WRITE_READ 1    // 讀寫操作碼1
    
  5. 用戶態獲取服務接口并發送消息到驅動

    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;
    }
    
  6. 用戶態接收該驅動上報的消息

    //用戶態編寫驅動上報消息的處理函數。
    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;
    }
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/70647.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/70647.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/70647.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leaflet實現歷史軌跡播放效果

效果圖如下&#xff1a; 效果實現&#xff1a; 1、添加完整軌跡線&#xff0c;藍色的 this.echoLine L.polyline(points, { weight: 8 }).addTo(this.map) 2、添加實時軌跡線&#xff0c;初始狀態置空 this.realEchoLine L.polyline([], { weight: 12, color: "#FF9…

JAVAEE一>Spring IoC和DI詳解

目錄 Spring容器說明&#xff1a;Ioc容器優勢&#xff1a;DI介紹&#xff1a;從Spring獲取對象&#xff1a;獲取對象的方法&#xff1a;關于上下文的概念&#xff1a; Controller注解&#xff08;控制層&#xff1a;接收參數并響應&#xff09;&#xff1a;Service注解&#xf…

(四)趣學設計模式 之 原型模式!

目錄 一、 啥是原型模式&#xff1f;二、 為什么要用原型模式&#xff1f;三、 原型模式怎么實現&#xff1f;四、 原型模式的應用場景五、 原型模式的優點和缺點六、 總結 &#x1f31f;我的其他文章也講解的比較有趣&#x1f601;&#xff0c;如果喜歡博主的講解方式&#xf…

完美解決:.vmx 配置文件是由 VMware 產品創建,但該產品與此版 VMware Workstation 不兼容

參考文章&#xff1a;該產品與此版 VMware Workstation 不兼容&#xff0c;因此無法使用 問題描述 當嘗試使用 VMware Workstation 打開別人的虛擬機時&#xff0c;可能會遇到以下報錯&#xff1a; 此問題常見于以下場景&#xff1a; 從其他 VMware 版本&#xff08;如 ESX…

Linux——安裝Git的方法

安裝Git的命令&#xff1a; yum -y install git查看Git的版本&#xff1a; git --version

編程小白沖Kaggle每日打卡(13)--kaggle學堂:<機器學習簡介>基礎數據探索

Kaggle官方課程鏈接&#xff1a;Basic Data Exploration 本專欄旨在Kaggle官方課程的漢化&#xff0c;讓大家更方便地看懂。 Basic Data Exploration 加載并理解您的數據。 使用Pandas熟悉您的數據 任何機器學習項目的第一步都是熟悉數據。您將使用Pandas庫進行此操作。Pand…

從零開始的網站搭建(以照片/文本/視頻信息通信網站為例)

本文面向已經有一些編程基礎&#xff08;會至少一門編程語言&#xff0c;比如python&#xff09;&#xff0c;但是沒有搭建過web應用的人群&#xff0c;會寫得盡量細致。重點介紹流程和部署云端的步驟&#xff0c;具體javascript代碼怎么寫之類的&#xff0c;這里不會涉及。 搭…

【Java項目】基于SpringBoot的【高校校園點餐系統】

【Java項目】基于SpringBoot的【高校校園點餐系統】 技術簡介&#xff1a;采用Java技術、MySQL數據庫、B/S結構實現。 系統簡介&#xff1a;高校校園點餐系統是一個面向高校師生的在線點餐平臺&#xff0c;主要分為前臺和后臺兩大模塊。前臺功能模塊包括&#xff08;1&#xff…

Django check_password原理

check_password 是 Django 提供的一個用于密碼校驗的函數&#xff0c;它的工作原理是基于密碼哈希算法的特性。 Django 的 make_password 函數在生成密碼哈希時&#xff0c;會使用一個隨機的 salt&#xff08;鹽值&#xff09;。這個 salt 會與密碼一起進行哈希運算&#xff0…

Vulnhun靶機-kioptix level 4-sql注入萬能密碼拿到權限ssh連接利用mysql-udf漏洞提權

目錄 一、環境搭建信息收集掃描ip掃描開放端口掃描版本服務信息指紋探測目錄掃描 二、Web滲透sql注入 三、提權UDF提權修改權限 一、環境搭建 然后選擇靶機所在文件夾 信息收集 本靶機ip和攻擊機ip 攻擊機&#xff1a;192.168.108.130 靶機&#xff1a;192.168.108.141 掃描…

PHP 會話(Session)實現用戶登陸功能

Cookie是一種在客戶端和服務器之間傳遞數據的機制。它是由服務器發送給客戶端的小型文本文件&#xff0c;保存在客戶端的瀏覽器中。每當瀏覽器向同一服務器發送請求時&#xff0c;它會自動將相關的Cookie信息包含在請求中&#xff0c;以便服務器可以使用這些信息來提供個性化的…

PAT 甲級 1090 Highest Price in Supply Chain

構造一個二維數組 v &#xff0c;v[i] 存放指向 i 的所有元素。 構造隊列 q 存放每個待讀取的節點。 構造數組 high 存放每個節點的高度&#xff08;第幾級經銷商&#xff09; #include<iostream> #include<queue> #include<cmath> using namespace std; …

DeepSeek掘金——SpringBoot 調用 DeepSeek API 快速實現應用開發

Spring Boot 實現 DeepSeek API 調用 1. 項目依賴 在 pom.xml 中添加以下依賴: <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>&l…

算法——數學建模的十大常用算法

數學建模的十大常用算法在數學建模競賽和實際問題解決中起著至關重要的作用。以下是這些算法的具體信息、應用場景以及部分算法的C語言代碼示例&#xff08;由于篇幅限制&#xff0c;這里只給出部分算法的簡要代碼或思路&#xff0c;實際應用中可能需要根據具體問題進行調整和擴…

推薦幾款SpringBoot項目手腳架

作為程序員、一般需要搭建項目手腳架時、都會去Gitee或Github上去找、但是由于Github在國內并不穩定、所以就只能去Gitee去上查找。 不同語言檢索方式不一樣、但是也類似。 Gitee WEB應用開發 / 后臺管理框架 芋道源碼 ELADMIN 后臺管理系統 一個基于 Spring Boot 2.7.1…

智能自動化新紀元:AI與UiPath RPA的協同應用場景與技術實踐

智能自動化新紀元&#xff1a;AI與UiPath RPA的協同應用場景與技術實踐 引言 在數字化轉型的浪潮中&#xff0c;企業對于自動化技術的需求已從簡單的任務執行轉向更復雜的智能決策。傳統RPA&#xff08;Robotic Process Automation&#xff09;通過模擬人類操作處理重復性任務…

數據結構:動態數組vector

vector 是 C 標準庫的動態數組。 在C語言中一般初學者會使用malloc&#xff0c;int[n]等方式來創建靜態數組&#xff0c;但是這種方式繁瑣且容易出錯。我們做算法題一般使用動態數組vector&#xff0c; 并且在刷題網站的題目給的輸入一般也是vector類型。 示例&#xff1a;vect…

基于深度學習的信號濾波:創新技術與應用挑戰

一、引言 1.1 研究背景 隨著科技的不斷發展&#xff0c;信號處理領域面臨著越來越復雜的挑戰。在眾多信號處理技術中&#xff0c;基于深度學習的信號濾波技術逐漸嶄露頭角&#xff0c;成為研究的熱點。 基于深度學習的信號濾波在信號處理領域具有至關重要的地位。如今&#…

前端八股——JS+ES6

前端八股&#xff1a;JSES6 說明&#xff1a;個人總結&#xff0c;用于個人復習回顧&#xff0c;將持續改正創作&#xff0c;已在語雀公開&#xff0c;歡迎評論改正。

醫院安全(不良)事件上報系統源碼,基于Laravel8開發,依托其優雅的語法與強大的擴展能力

醫院安全&#xff08;不良&#xff09;事件上報系統源碼 系統定義&#xff1a; 規范醫院安全&#xff08;不良&#xff09;事件的主動報告&#xff0c;增強風險防范意識&#xff0c;及時發現醫院不良事件和安全隱患&#xff0c;將獲取的醫院安全信息進行分析反饋&#xff0c;…