OpenHarmony小型系統輸入子系統解析
前言
本文將以鼠標為例介紹小型系統上的輸入系統架構以及輸入信號傳遞的整個流程。
一、大致流程
- 輸入設備產生信號,內核接收到并處理信號。
- 內核將處理后的信號傳遞給HDF化后的設備節點。
- HDF對信號進行事件上報,也就是通過HDF的消息機制與用戶空間空間進行交互。
- 最后WMS給用戶程序進行事件分發。
二、內核到HDF
由于硬件到內核這一步與linux沒有太多的不同,在這里不做過多的介紹。
HDF設備的注冊以及后續的輸入信號傳遞都是通過給內核添加補丁來實現的。
kernel/linux/patches/linux-4.19/common_patch/hdf.patch。
+ dev->devType = type;
+ dev->devName = hdev->name;
+ hdev->input_dev = HidRegisterHdfInputDev(dev); //Hid設備注冊接口
+ if (hdev->input_dev == NULL) {
+ printk("%s: RegisterInputDevice failed\n", __func__);
+ }
+ kfree(dev);
+ dev = NULL;
+}
+#endif
HidRegisterHdfInputDev()的主要作用是調用管理器的注冊接口。
drivers/hdf_core/framework/model/input/driver/hdf_hid_adapter.c
void* HidRegisterHdfInputDev(HidInfo *info)
{
InputDevice* inputDev = HidConstructInputDev(info);
if (inputDev == NULL) {
HDF_LOGE("%s: hid construct input Dev failed", __func__);
return NULL;
}
if (InputDriverLoaded()) {
DoRegisterInputDev(inputDev); //將調用管理器的接口RegisterInputDevice()
} else {
CacheHid(inputDev);
}
return inputDev;
}
管理器的RegisterInputDevice()完成分配id、緩存和生成HDF設備節點。
drivers/hdf_core/framework/model/input/driver/hdf_hid_device_managerr.c
int32_t RegisterInputDevice(InputDevice *inputDev)
{
int32_t ret;
HDF_LOGI("%s: enter", __func__);
if (inputDev == NULL) {
HDF_LOGE("%s: inputdev is null", __func__);
return HDF_ERR_INVALID_PARAM;
}
if ((g_inputManager == NULL) || (g_inputManager->initialized == false)) {
HDF_LOGE("%s: dev manager is null or initialized failed", __func__);
return HDF_FAILURE;
}
OsalMutexLock(&g_inputManager->mutex);
ret = AllocDeviceID(inputDev); //分配設備ID
if (ret != HDF_SUCCESS) {
goto EXIT;
}
ret = CreateDeviceNode(inputDev); //創建設備節點,也就是上面所述的hdf_input_eventX
if (ret != HDF_SUCCESS) {
goto EXIT1;
}
ret = AllocPackageBuffer(inputDev); //分配包的緩存
if (ret != HDF_SUCCESS) {
goto EXIT1;
}
AddInputDevice(inputDev);
OsalMutexUnlock(&g_inputManager->mutex);
HDF_LOGI("%s: exit succ, devCount is %d", __func__, g_inputManager->devCount);
return HDF_SUCCESS;
EXIT1:
DeleteDeviceNode(inputDev);
EXIT:
OsalMutexUnlock(&g_inputManager->mutex);
return ret;
}
OpenHarmony小型系統輸入子系統解析-開源基礎軟件社區
2、信號傳遞部分。
上面步驟準備就緒后,如上提及,鼠標的信息傳遞也是通過內核調用了HDF接口向用戶空間傳輸鼠標的事件信號。
補丁調用適配器接口發送輸入事件信息。
hdf.patch:
+#if defined(CONFIG_DRIVERS_HDF_INPUT)
+ if (hid->input_dev) {
//內核獲取到的輸入設備產生的信號,將信號通過事件上報傳遞到HDF
+ HidReportEvent(hid->input_dev, usage->type, usage->code, value);
+ }
+#endif
hdf_hid_adapter.c:
void HidReportEvent(const void *inputDev, uint32_t type, uint32_t code, int32_t value)
{
#ifdef CONFIG_DFX_ZEROHUNG
if (type == EV_KEY && code == KEY_POWER)
hung_wp_screen_powerkey_ncb(value);
#endif
InputDevice *device = (InputDevice *)inputDev;
PushOnePackage(device, type, code, value); //調用了event_hub.c中的接口,先打包,再發送
if (type == EV_KEY && KEY_RESERVED < code && code < KEY_MAX && value == 0 && code == g_kbdcode) {
OsalTimerDelete(&g_timer);
g_kbdcode = 0;
}
if (type == EV_KEY && KEY_RESERVED < code && code < KEY_MAX && value == 1 &&
device->devType == INDEV_TYPE_KEYBOARD) {
g_kbdcode = code;
RepateEvent(device);
}
}
經過event_hub處理進行下一步的傳遞。
// PushOnePackage()在這里的作用就是將事件流打成包的形式進行傳遞
// 代碼過長不展示,PushOnePackage()處理完成包后就開始進入HDF消息機制了
static void SendFramePackages(InputDevice *inputDev) //該函數由PushOnePackage調用
{
struct HdfDeviceObject *hdfDev = inputDev->hdfDevObj;
if (hdfDev == NULL || inputDev->pkgBuf == NULL) {
HDF_LOGE("%s: hdf dev is null", __func__);
return;
}
// HDF消息機制的事件上報接口
int32_t ret = HdfDeviceSendEvent(hdfDev, 0, inputDev->pkgBuf);
}
OpenHarmony小型系統輸入子系統解析-開源基礎軟件社區
三、HDF到WMS
1、WMS簡單介紹。
源碼位置由foundation/graphic/wms變更至/foundation/window/window_manager_lite。
WMS全稱是Window Manager Service,顧名思義,該服務就是用來管理窗口和繪畫光標等操作的。該組件里還包含了另一個必不可少的組件IMS(Input Manager Service),同理,它是用來處理輸入事件的。
由圖可知,IMS也有一個eventhub,很容易想到這邊的eventhub就是用來接收內核態的evenhub發來的輸入信號的。
2、消息機制簡單介紹。
HDF消息機制是建立用戶態應用和內核態驅動的交互。主要功能有兩個,一個是用戶態應用發送消息到驅動,另一個是用戶態應用接收驅動主動上報事件。主要接口有如下:
接口 | 描述 |
struct HdfIoService *HdfIoServiceBind(const char *serviceName); | 用戶態獲取驅動的服務,獲取該服務之后通過服務中的Dispatch方法向驅動發送消息。 |
void HdfIoServiceRecycle(struct HdfIoService *service); | 釋放驅動服務。 |
int HdfDeviceRegisterEventListener(struct HdfIoService *target, struct HdfDevEventlistener *listener); | 用戶態程序注冊接收驅動上報事件的操作方法。 |
int HdfDeviceSendEvent(struct HdfDeviceObject *deviceObject, uint32_t id, struct HdfSBuf *data); | 驅動主動上報事件接口。 |
該消息機制的實現方法大概就是對上面相應設備的事件緩存區進行操作,具體可以查閱代碼。
3、信號傳遞過程。
如上表所示,內核態的eventhub最終調用了事件上報接口,但是事件監聽器不是在IMS中的eventhub注冊的。而是由硬件抽象層drivers/peripheral/input/hal中注冊的。并且封裝成了API供IMS使用。有input_controller,input_manager和input_reporter,分別管理輸入設備的的上電、開關以及事件接收。input_reporter.h里的主要接口如下:
接口 | 描述 |
int32_t (*RegisterReportCallback)(uint32_t devIndex, InputEventCb *callback); | 注冊事件上報的回調函數 |
int32_t (*UnregisterReportCallback)(uint32_t devIndex); | 注銷 |
int32_t (*RegisterHotPlugCallback)(InputHostCb *callback); | 注冊熱插拔事件上報的回調函數 |
int32_t (*UnregisterHotPlugCallback)(void); | 注銷 |
4、IMS注冊事件上報的回調函數。(源碼此處有問題,僅做部分展示)。
// input_event_hub.cpp
ret = inputInterface_->iInputManager->OpenInputDevice(mountDevIndex_[i]); //首先要打開設備
if (ret == INPUT_SUCCESS && inputInterface_->iInputReporter != nullptr) {
callback_.EventPkgCallback = EventCallback; //進行callback綁定
ret = inputInterface_->iInputReporter->RegisterReportCallback(mountDevIndex_[i], &callback_); //以設備號注冊回調函數,此處設備號對應hdf_input_eventX中的X
if (ret != INPUT_SUCCESS) {
GRAPHIC_LOGE("device dose not exist, can't register callback to it!");
return;
}
openDev_ = openDev_ | (1 << i);
}
注冊完畢后,當鼠標觸發了輸入信號,將在回調函數中接收到事件。
OpenHarmony小型系統輸入子系統解析-開源基礎軟件社區
至此,輸入信號從內核傳遞到了用戶空間。
四、WMS到用戶程序
用戶程序與WNS硬件之間的交互體現在UI與WMS之間或者UI直接與HAL層的交互。
OpenHarmony小型系統輸入子系統解析-開源基礎軟件社區
在這里源碼不做過多分析,留一張流程圖。
OpenHarmony小型系統輸入子系統解析-開源基礎軟件社區
高清pdf放在附件里:https://ost.51cto.com/resource/2801。
總結
開源鴻蒙小型系統的輸入功能仍未完善,比如熱插拔問題和屏幕HDF化的設計沒有考慮到無觸控屏的開發板等的問題。于是在這里,我將我學到的知識分享給大家與大家共同討論。