写在前面
原理解析
HID事件到Android屏幕上经历了如下过程:
HID => linux kernel input子系统 => Android input子系统
HID是标准的输入协议,对于不同的操作系统而言,也有自己的input子系统。
Android层要求
以Android为例,如果要想让Android系统认为一个输入设备是一个触摸屏,需要该内核设备上报:
// 对于单点触控而言 ABS_X ABS_Y 和 BTN_TOUCH // 对于多点触控而言 ABS_MT_POSITION_X 和 ABS_MT_POSITION_Y 绝对轴 以及 BTN_TOUCH
且需要一个配置文件放到/system/usr/idc/中,如/system/usr/idc/HID.idc,内容如下指定其为触摸屏设备类型:
touch.deviceType = touchScreen
注:标明输入设备为触摸屏还有一个方法输入设备报告存在 INPUT_PROP_DIRECT 输入属性,但是对于蓝牙HID而言,在Android中的实现是基于uhid实现的,要这样做比较难,所以一般会使用放配置文件的方式。输入设备上报的键值和输入属性可以使用getevent -i查看。
理论依据来自:https://source.android.com/devices/input/touch-devices
内核层要求
Android层要求中提到的ABS_X ABS_Y 和 BTN_TOUCH等等都是需要内核去上报的键值,内核中就需要要求HID设备按照一定要求上传HID键值,因为内核键值是由HID键值转换过来的。
// 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; }
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来测试该描述符,测试代码,确实可以实现触摸屏的效果了。
注:对于多点触摸设备,那么描述符中需要包含HID_DG_CONTACTID即可,这里先不展开分析。
蓝牙HID从机
这里使用的是ST的SensorTile,根据其提供的HID的Demo,修改对应的成对应的描述符,并在while循环里实现一个从左上角滑动到右下角的模拟操作,这样就可以实现通过蓝牙HID触摸屏了。
具体的改动见:https://gitee.com/kangear/STM32CubeExpansion_BLE1_V2.8.0/commit/04e5e0af893c1099d1ce5d3658e6d
展望
目前实现了单点触摸屏,稍候会另起一个文章,讲述如何实现多点触摸屏,到那时才会是真正的蓝牙HID触摸屏。能实现之后,可以基于这个实现物理外挂,把手游的一些控制转换成体感游戏等等。
<完>完>
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/204198.html原文链接:https://javaforall.net
