摘要
OpenHarmony系統中使用了liteos-m、liteos-a、linux三種內核,工作隊列是linux內核引入的一種異步處理機制。本文對liteos-a內核下工作隊列的實現原理進行分析,并對芯海科技的PPG芯片CS1262接入OpenHarmony過程中對工作隊列的使用方法進行總結分享。
工作隊列工作隊列(Workqueue)是linux內核引入的一種異步進程調用的機制,允許內核代碼請求在將來某個時間調用一個函數。在內核源碼的documentation中也有對workqueue的說明,路徑為Documentation/core-api/workqueue.rst。網絡上也有很多對工作隊列機制的總結文章可以學習,《Linux workqueue工作原理》講解的就比較詳細。簡單理解就是先創建一個隊列用于存放work,并創建一個處理線程叫worker;用戶進程通過調用接口往隊列里面添加work驅動著worker來處理,如下:
而OpenHarmony系統根據不同的使用場景使用了liteos-m/liteos-a/linux三種內核。
這里通過CS1262驅動的實現,對liteos-a內核中工作隊列的使用及實現做一下分析。
工作隊列的使用方法
Sensor設備作為外接設備重要組成模塊,Sensor驅動模型為上層Sensor服務系統提供穩定的Sensor基礎能力接口,包括Sensor列表查詢、Sensor啟停、Sensor訂閱及去訂閱,Sensor參數配置等功能。傳感器驅動模型總體框架如圖1所示。
OpenHarmony系統對workqueue提供了幾個接口,以方便用戶的使用。
1. 調用HdfWorkQueueInit,傳入queue名稱,創建并初始化一個workqueue
2. 調用HdfWorkInit,可以理解為初始化一個work的模板,主要記錄處理這個queue里面work的回調函數func以及參數para信息,類似于linux的work_struct
3. 通過調用HdfAddWork往workqueue中添加work,觸發調用與此queue關聯的回調函數func
步驟1~2可以在CS1262驅動HdfDriverEntry對象的Init接口中看到
int32_t InitPpgDriver(struct HdfDeviceObject *device)
{
CHECK_NULL_PTR_RETURN_VALUE(device, HDF_ERR_INVALID_PARAM);
struct PpgDrvData *drvData = (struct PpgDrvData *)device->service;
CHECK_NULL_PTR_RETURN_VALUE(drvData, HDF_ERR_INVALID_PARAM);
if (HdfWorkQueueInit(&drvData->ppgWorkQueue, HDF_PPG_WORK_QUEUE) != HDF_SUCCESS) {
HDF_LOGE("%s: Ppg init work queue failed", __func__);
return HDF_FAILURE;
}
if (HdfWorkInit(&drvData->ppgWork, PpgDataWorkEntry, drvData) != HDF_SUCCESS) {
HDF_LOGE("%s: Ppg create thread failed", __func__);
return HDF_FAILURE;
}
drvData->initStatus = true;
drvData->enable = false;
drvData->detectFlag = false;
HDF_LOGI("%s: init Ppg driver success", __func__);
return HDF_SUCCESS;
}
步驟3在CS1262的中斷處理函數中,有數據需要上報時會產生一個中斷,中斷處理中添加一個work通過工作隊列機制來實現數據的上報。
static int32_t PpgReadInt(uint16_t gpio, void *data)
{
struct PpgDrvData *drvData = PpgGetDrvData();
CHECK_PPG_INIT_RETURN_VALUE(drvData, HDF_ERR_NOT_SUPPORT);
if (!drvData->enable) {
HDF_LOGE("%s: ppg not enabled", __func__);
return HDF_SUCCESS;
}
if (!HdfAddWork(&drvData->ppgWorkQueue, &drvData->ppgWork)) {
HDF_LOGE("%s: Ppg add work queue failed", __func__);
return HDF_FAILURE;
}
return HDF_SUCCESS;
}
CS1262工作對列中work的回調接口實現,兩個主要功能:獲取數據,數據上報。
static void PpgDataWorkEntry(void *arg)
{
int32_t ret;
struct PpgDrvData *drvData = (struct PpgDrvData *)arg;
uint16_t readLen = 0;
CHECK_NULL_PTR_RETURN(drvData);
CHECK_NULL_PTR_RETURN(drvData->chipData.opsCall.ReadData);
ret = drvData->chipData.opsCall.ReadData(g_fifoBuf, sizeof(g_fifoBuf), &readLen);
if ((ret != HDF_SUCCESS) || (readLen > sizeof(g_fifoBuf))) {
HDF_LOGE("%s: Ppg read data failed", __func__);
return;
}
if (PpgReportInt(g_fifoBuf, readLen) != HDF_SUCCESS) {
HDF_LOGE("%s: Cs1262ReportInt fail", __func__);
}
}
說明:當前CS1262驅動已提交PR,還未正式上庫
Liteos-a內核中工作隊列的實現
1.HdfWorkQueueInit接口
(1)在liteos-a系統源碼中搜索,接口定義如下:
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
int32_t HdfWorkQueueInit(HdfWorkQueue *queue, char *name)
{
......
queue->realWorkQueue = create_singlethread_workqueue(name);
......
return HDF_SUCCESS;
}
(2)create_singlethread_workqueue接口實現如下
源文件:kernel/liteos_a/bsd/compat/linuxkpi/include/linux/workqueue.h
宏定義:
#define create_singlethread_workqueue(name) \
linux_create_singlethread_workqueue(name)
源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代碼如下:
struct workqueue_struct *linux_create_singlethread_workqueue(char *name)
{
return __create_workqueue_key(name, 1, 0, 0, NULL, NULL);
}
(3)__create_workqueue_key中初始化化了一個event后面會用;創建一個workqueueThread線程用來處理這個workqueue里的所有work
源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代碼如下:
struct workqueue_struct *__create_workqueue_key(char *name,
int singleThread,
int freezeable,
int rt,
struct lock_class_key *key,
const char *lockName)
{
......
(VOID)LOS_EventInit(&wq->wq_event);
if (singleThread) {
cwq = InitCpuWorkqueue(wq, singleThread);
ret = CreateWorkqueueThread(cwq, singleThread);
} else {
LOS_MemFree(m_aucSysMem0, wq->cpu_wq);
LOS_MemFree(m_aucSysMem0, wq);
return NULL;
}
if (ret) {
destroy_workqueue(wq);
wq = NULL;
}
return wq;
}
(4)LOS_EventInit就是liteos系統的task之間通信的事件機制實現
(5)CreateWorkqueueThread就是調用的liteos的LOS_TaskCreate來創建一個Task(也即thread),處理函數為WorkerThread,如下
源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代碼如下:
STATIC UINT32 CreateWorkqueueThread(cpu_workqueue_struct *cwq, INT32 cpu)
{
......
taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WorkerThread;
......
ret = LOS_TaskCreate(&cwq->wq->wq_id, &taskInitParam);
......
return LOS_OK;
}
STATIC VOID WorkerThread(cpu_workqueue_struct *cwqParam)
{
cpu_workqueue_struct *cwq = cwqParam;
for (;;) {
if (WorkqueueIsEmpty(cwq)) {
(VOID)LOS_EventRead(&(cwq->wq->wq_event),0x01,LOS_WAITMODE_OR|LOS_WAITMODE_CLR,LOS_WAIT_FOREVER);
}
RunWorkqueue(cwq);
}
}
線程處理函數里面就是一個死循環,當workqueue中為空時,代碼會阻塞在LOS_EventRead處,讀一個還未發生的事件時,代碼就會在此處一直阻塞,直到事件發生;
(6)在事件發生(有work可以處理)時,就會調用真正的處理接口RunWorkqueue,對work調用回調函數(例如上面CS1262中的PpgDataWorkEntry)
源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
代碼如下:
STATIC VOID RunWorkqueue(cpu_workqueue_struct *cwq)
{
......
if (!WorkqueueIsEmpty(cwq)) {
......
func = work->func;
func(work);
......
}
LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
}
2.HdfWorkInit接口
(1)接口實現如下
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
int32_t HdfWorkInit(HdfWork *work, HdfWorkFunc func, void *para)
{
......
wrapper = (struct WorkWrapper *)OsalMemCalloc(sizeof(*wrapper));
if (wrapper == NULL) {
HDF_LOGE("%s malloc fail", __func__);
return HDF_ERR_MALLOC_FAIL;
}
realWork = &(wrapper->work.work);
wrapper->workFunc = func;
wrapper->para = para;
INIT_WORK(realWork, WorkEntry);
work->realWork = wrapper;
return HDF_SUCCESS;
}
(2)從這里看這個處理函數func賦值給了wrapper->workFunc,而在INIT_WORK中將WorkEntry接口賦值給了work的func
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
#ifdef WORKQUEUE_SUPPORT_PRIORITY
#define INIT_WORK(work, callbackFunc) do { \
INIT_LIST_HEAD(&((work)->entry)); \
(work)->func = (callbackFunc); \
(work)->data = (atomic_long_t)(0); \
(work)->work_status = 0; \
(work)->work_pri = OS_WORK_PRIORITY_DEFAULT; \
} while (0)
#else
#define INIT_WORK(work, callbackFunc) do { \
INIT_LIST_HEAD(&((work)->entry)); \
(work)->func = (callbackFunc); \
(work)->data = (atomic_long_t)(0); \
(work)->work_status = 0; \
} while (0)
#endif
(3)從前面分析,有work需要處理時調用的就是這個(work)->func;而這里看這個接口的值是WorkEntry,怎么跟驅動側輸入的處理接口聯系的呢?就是這個WorkEntry實現的
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
static void WorkEntry(struct work_struct *work)
{
struct WorkWrapper *wrapper = NULL;
if (work != NULL) {
wrapper = (struct WorkWrapper *)work;
if (wrapper->workFunc != NULL) {
wrapper->workFunc(wrapper->para);
} else {
HDF_LOGE("%s routine null", __func__);
}
} else {
HDF_LOGE("%s work null", __func__);
}
}
從這里看就是調用的wrapper->workFunc,也即HdfWorkInit接口傳入的回調函數。
3.HdfAddWork接口
(1)接口定義如下
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
bool HdfAddWork(HdfWorkQueue *queue, HdfWork *work)
{
......
return queue_work(queue->realWorkQueue, &((struct WorkWrapper *)work->realWork)->work.work);
}
(2)queue_work接口是一個宏,定義如下
源文件:drivers/adapter/khdf/liteos/osal/src/osal_workqueue.c
代碼如下:
#define queue_work(wq, work) \
linux_queue_work(wq, work)
源文件:kernel/liteos_a/bsd/compat/linuxkpi/src/linux_workqueue.c
linux_queue_work最終調用的接口是InsertWork,中間的調用如下
linux_queue_work()
|--> QueueWorkOn()
|--> QueueWork()
|-->InsertWork()
接口實現:
STATIC VOID InsertWork(cpu_workqueue_struct *cwq, struct work_struct *work,
struct list_head *head, UINT32 *intSave)
{
#ifdef WORKQUEUE_SUPPORT_PRIORITY
WorkListAdd(&work->entry, head, work->work_pri);
#else
WorkListAddTail(&work->entry, head);
#endif
LOS_SpinUnlockRestore(&g_workqueueSpin, *intSave);
(VOID)LOS_EventWrite(&(cwq->wq->wq_event), 0x01);
LOS_SpinLockSave(&g_workqueueSpin, intSave);
}
從實現上看這里就是寫了一個Event,還記得前面在init里面一個阻塞在讀Event的嗎?這里的LOS_EventWrite就代表了事件發生,然后就可以正常處理work了
總結
OpenHarmony的liteos-a內核中工作隊列的實現就是參照linux內核的實現,只是底層使用的是嵌入式系統中事件處理機制。
OpenHarmony在內核上層做了一層封裝(OSAL),在使用linux和liteos-a內核時工作隊列的使用方式統一,驅動開發時無感知。
芯海科技作為OpenHarmony項目群B類捐贈人,已加入DriverFramework SIG和DevBoard SIG。在DriverFramework SIG中負責心率傳感器PPG驅動模型和HDI的實現。CS1262是芯海科技最新推出的一款用于光電血管容積圖(PPG)信號采集的高端模擬前端(AFE),通過SPI總線與主控通信。關鍵性能指標達到業界一流水平:
◆ 高精度測量:最高PPG SNR高達110dB
◆ 超強抗干擾:PSRR ≥ 90dB(0.5Hz~10MHz 范圍內的Boost噪聲)
◆ 低功耗:83uA@100Hz@4Ch@2階環境光
◆ 全膚色支持:單路LED Driver最大電流可達125mA,兩路合并后支持250mA
◆ 高可靠:支持過溫保護/關鍵寄存器保護/LED過流保護/SPI通訊可靠性check
◆ 易用性:支持1/2階環境光消減/硬件佩戴檢測/自動調光/動態配置刷新
-
芯片
+關注
關注
456文章
51166瀏覽量
427206 -
OpenHarmony
+關注
關注
25文章
3744瀏覽量
16573
發布評論請先 登錄
相關推薦
評論