1.1 HOG概述
HOGP是HID Over GATT Profile的缩写,即蓝牙HID设备是通过BLE的GATT来实现HID协议的。
常见的蓝牙鼠标、蓝牙键盘、蓝牙手柄,它们都属于HID设备,但与有线设备不同的是,有线鼠标等设备属于USB HID设备,而蓝牙鼠标等设备属于Bluetooth HID设备,即协议是一样的,只是通信方式不同。
1.2 HOG设备的广播数据
- tpye 0x01:蓝牙的FLAG信息,0x05表示设备仅支持BLE,不支持经典蓝牙,广播类型为可被发现(有限时间)。
- type 0x03:UUID16_ALL,0x1218是16位的HID服务的UUID,这里已经初步表明设备是一个蓝牙HID设备。
- type 0x19:GAP的apperance,即设备的外观,0xC103表示设备是一个蓝牙键盘(Keyboard)。
- type 0x09:蓝牙设备的全名,该手柄的设备名叫"HP K10GW KB5.0"。
连接上后,展示出了一部分gatt server。
1.3 Linux环境配置
- 内核对蓝牙HID的配置,一部分是蓝牙部分,一部分是hid。
2. Bluez协议栈添加enable-hog
1.4 数据链路
1.4.1 Bluez创建HID设备
1.启动bluetoothctl工具,搜索并连接该设备。Bluez会发现PnP ID服务,读取PnP ID服务可以获取设备的制造商信息,例如VIP和PID,串口会有相应的打印。在向内核注册HID设备时,VIP和PID是非常重要的参数。
bluetoothd[536]: profiles/deviceinfo/dis.c:read_pnpid_cb() source: 0x01 vendor: 0x1949 product: 0x0402 version: 0x0000
2.当Bluez发现HID服务时,hog-lib.c中的char_discovered_cb函数会被调用,该函数会解析HID服务下所有特征值,其中有一部是比对report_map_uuid,report_map_uuid是0x2A4B。
hog-lib.c
primary_cb --> hog_attach_hog --> bt_hog_attach --> discover_char --> char_discovered_cb -->
读取report map,回调函数report_map_read_cb里通过/dev/uhid节点向内核注册hid设备。
1.4.2 内核创建HID设备
- 在内核配置中开启uhid的支持后,会生成一个/dev/uhid设备节点,用户层可以通过该文件与hid驱动通信。Bluez的bt_uhid_send函数就是向这个节点写入UHID_CREATE消息。
- 内核的hid驱动,kernel/linux-5.15/drivers/hid/uhid.c, uhid_dev_create会唤醒专门添加uhid设备的工作队列uhid_device_add_worker,该工作队列会调用hid_add_device尝试添加HID设备,hid_add_device函数会比对要注册的设备的VIP和PID,看是通用设备还是特殊设备,往下具体没有深究,接下来还会注册一个debug节点,/sys/kernel/debug/hid/events,方便看到gatt上报给hid的数据。device_add函数是个通用的注册设备函数,属于linux的bus框架,细节没关注,最终会在/dev/input/目录下会多了一个event4,就是代表该蓝牙设备,应用程序通过这个节点获取hid设备的驱动解析后数据。
1.4.3 数据链路
整条数据链路:按下键盘的某个按键,bluetoothd进程收到手柄的notify数据,bluetoothd通过/dev/uhi向HID系统发送UHID_INPU消息,HID驱动会根据report map将数据解析成对应的input_evevt事件并上报,用户层解析/dev/input/目录下对应的event即可获取键盘的状态。
1.5 测试方法
1.蓝牙启动
2.bluetoothctl
3.power on
4.scan on
5.scan off
6.pair xx:xx:xx
7.yes
按下键盘某个键,cat节点信息看到:
都有event数据,证明链路已通。
测试结果:pass
上面的显示乱码,是因为数据是struct input_event结构体记录内容,不是字符串,所以不能直接打出来,但能证明收到事件了。
如果想解析这些内容,需要应用处理或调试工具。
libevdev:这是一个 C 语言库,提供了处理输入设备事件的高级接口。它可以让您以编程方式读取和解析 HID 键盘上报的事件。
evtest:这是一个命令行工具,用于测试和调试输入设备。它可以读取和显示输入设备事件,包括 HID 键盘上报的事件。
下面展示evtest工具的效果: