寫在前面
主機:Android 5.0+ 內(nèi)核3.4
從機:SensorTile
先上一個效果圖

原理解析
HID事件到Android屏幕上經(jīng)歷了如下過程:
HID => linux kernel input子系統(tǒng) => Android input子系統(tǒng)
HID是標準的輸入?yún)f(xié)議,對于不同的操作系統(tǒng)而言,也有自己的input子系統(tǒng)。
Android層要求
以Android為例,如果要想讓Android系統(tǒng)認為一個輸入設(shè)備是一個觸摸屏,需要該內(nèi)核設(shè)備上報:
// 對于單點觸控而言
ABS_X ABS_Y 和 BTN_TOUCH
// 對于多點觸控而言
ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 絕對軸 以及 BTN_TOUCH
且需要一個配置文件放到/system/usr/idc/中,如/system/usr/idc/HID.idc,內(nèi)容如下指定其為觸摸屏設(shè)備類型:
touch.deviceType = touchScreen
注:標明輸入設(shè)備為觸摸屏還有一個方法輸入設(shè)備報告存在 INPUT_PROP_DIRECT 輸入屬性,但是對于藍牙HID而言,在Android中的實現(xiàn)是基于uhid實現(xiàn)的,要這樣做比較難,所以一般會使用放配置文件的方式。輸入設(shè)備上報的鍵值和輸入屬性可以使用getevent -i查看。
理論依據(jù)來自:https://source.android.com/devices/input/touch-devices
內(nèi)核層要求
Android層要求中提到的ABS_X ABS_Y 和 BTN_TOUCH等等都是需要內(nèi)核去上報的鍵值,內(nèi)核中就需要要求HID設(shè)備按照一定要求上傳HID鍵值,因為內(nèi)核鍵值是由HID鍵值轉(zhuǎn)換過來的。
// drivers/hid/hid-input.c
case HID_UP_DIGITIZER:
switch (usage->hid & 0xff) {
case 0x00: /* Undefined */
goto ignore;
case HID_DG_TIPPRESSURE: /* TipPressure */
if (!test_bit(BTN_TOUCH, input->keybit)) {
device->quirks |= HID_QUIRK_NOTOUCH;
set_bit(EV_KEY, input->evbit);
set_bit(BTN_TOUCH, input->keybit);
}
map_abs_clear(ABS_PRESSURE);
break;
case HID_DG_INRANGE: /* InRange */
switch (field->physical & 0xff) {
case 0x21: map_key(BTN_TOOL_MOUSE); break;
case 0x22: map_key(BTN_TOOL_FINGER); break;
default: map_key(BTN_TOOL_PEN); break;
}
break;
case HID_DG_INVERT: /* Invert */
map_key_clear(BTN_TOOL_RUBBER);
break;
case HID_DG_TOUCH: /* Touch */
case HID_DG_TIPSWITCH: /* TipSwitch */
case HID_DG_TIPSWITCH2: /* TipSwitch2 */
device->quirks &= ~HID_QUIRK_NOTOUCH;
map_key_clear(BTN_TOUCH);
break;
case HID_DG_BARRELSWITCH: /* BarrelSwitch */
map_key_clear(BTN_STYLUS);
break;
case HID_DG_TABLETPICK: /* TabletPick */
map_key_clear(BTN_STYLUS2);
break;
case HID_DG_CONTACTID: /* ContactID */
device->quirks |= HID_QUIRK_MULTITOUCH;
goto unknown;
default: goto unknown;
}
由此可以看到HID描述符中需要按照以下要求:
-
Usage Page為DIGITIZER,對應內(nèi)核代碼中的HID_UP_DIGITIZER; - 需要包含
HID_DG_TOUCH、HID_DG_TIPSWITCH、HID_DG_TIPSWITCH2三者其中之一,才能上報BTN_TOUCH; - 如果包含了
HID_DG_INRANGE則會上傳BTN_TOOL_PEN有可能會導致畫蛇添足;(我在實際測試中確實遇到這個問題,一個上報BTN_TOOL_PEN事件,而不上報BTN_TOUCH導致無法實現(xiàn)真正的點擊事件);
根據(jù)這些分析,將微軟的Sample Report Descriptor for a Touch Digitizer Device (Windows 7)提到的HID描述符簡單的修整,得到了想要的的描述符:
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x04, // USAGE (Touch Screen)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x01, // REPORT_ID (Touch)
0x09, 0x20, // USAGE (Stylus)
0xa1, 0x00, // COLLECTION (Physical)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x03, // REPORT_COUNT (3)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
// 0x09, 0x32, // USAGE (In Range)
// 0x09, 0x47, // USAGE (Confidence)
// 0x95, 0x02, // REPORT_COUNT (1)
// 0x81, 0x02, // INPUT (Data,Var,Abs)
// 0x95, 0x0a, // REPORT_COUNT (10)
// 0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x01, // REPORT_COUNT (1)
0xa4, // PUSH
0x55, 0x0d, // UNIT_EXPONENT (-3)
0x65, 0x00, // UNIT (None)
0x09, 0x30, // USAGE (X)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xb4, // POP
// 0x05, 0x0d, // USAGE PAGE (Digitizers)
// 0x09, 0x48, // USAGE (Width)
// 0x09, 0x49, // USAGE (Height)
// 0x95, 0x02, // REPORT_COUNT (2)
// 0x81, 0x02, // INPUT (Data,Var,Abs)
// 0x95, 0x01, // REPORT_COUNT (1)
// 0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
使用uhid來測試該描述符,測試代碼,確實可以實現(xiàn)觸摸屏的效果了。
注:對于多點觸摸設(shè)備,那么描述符中需要包含HID_DG_CONTACTID即可,這里先不展開分析。
藍牙HID從機
這里使用的是ST的SensorTile,根據(jù)其提供的HID的Demo,修改對應的成對應的描述符,并在while循環(huán)里實現(xiàn)一個從左上角滑動到右下角的模擬操作,這樣就可以實現(xiàn)通過藍牙HID觸摸屏了。
具體的改動見:https://gitee.com/kangear/STM32CubeExpansion_BLE1_V2.8.0/commit/04e5e0af893c1099d1ce459307223735d3658e6d
展望
目前實現(xiàn)了單點觸摸屏,稍候會另起一個文章,講述如何實現(xiàn)多點觸摸屏,到那時才會是真正的藍牙HID觸摸屏。能實現(xiàn)之后,可以基于這個實現(xiàn)物理外掛,把手游的一些控制轉(zhuǎn)換成體感游戲等等。
<完>