一、Android BLE HID 大致框架
開局一張圖,文章全靠編:
以 Android BLE 架構簡單分析下 UHID 機制,圖中①②不探索,畢竟 bluedroid 這個東西也不是很快就能分析透徹的。
二、分析開始
在Android連接中藍牙設備后,會向內核 UHID 發送 creat 消息,中間的橋梁是 /dev/uhid 節點。
UHID驅動會調用到 uhid_dev_create 來初始化bus、vendor、product等信息,並拷貝 report 報文(這個東西很重要,用來描述BLE設備都支持什么操作,例如:INPUT)
進而調用到 uhid_dev_create2 ,期間會 alloc hid 設備,並在最后利用工作隊列中將 hid device 注冊到 hid bus 當中。
有了 device,和對應driver匹配之后才能有后戲,那么它來了 hid_generic ,ANY_BUS、ANY_ID,來吧,它是個 device 就能匹配。
熟悉的總線設備驅動模型,那么之后將調用 bus 的 probe 回調,它是 hid_device_probe ,插入一段簡短的代碼湊字。
1 static int hid_device_probe(struct device *dev) 2 { 3 struct hid_driver *hdrv = to_hid_driver(dev->driver); 4 struct hid_device *hdev = to_hid_device(dev); 5 ... 6 if (!hdev->driver) { 7 id = hid_match_device(hdev, hdrv); 8 if (id == NULL) { 9 ret = -ENODEV; 10 goto unlock; 11 } 12
13 hdev->driver = hdrv; 14 if (hdrv->probe) { 15 ret = hdrv->probe(hdev, id); 16 } else { /* default probe */
17 ret = hid_open_report(hdev); 18 if (!ret) 19 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 20 } 21 if (ret) { 22 hid_close_report(hdev); 23 hdev->driver = NULL; 24 } 25 } 26 ... 27 }
hid_open_report 用來解析我們前面說的比較重要的那個 report 報文,適配 HID 設備的所有 capability,它也會檢查 report 是否完成通過 COLLECTION 與 END_COLLECTION 的匹配,檢查不通過后就沒有下方了。
hid_hw_start 是創建給 Android 傳送信息的 event 通道,
1 int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask) 2 { 3 int error; 4
5 error = hdev->ll_driver->start(hdev); 6 if (error) 7 return error; 8
9 if (connect_mask) { 10 error = hid_connect(hdev, connect_mask); 11 if (error) { 12 hdev->ll_driver->stop(hdev); 13 return error; 14 } 15 } 16
17 return 0; 18 }
這里的 ll_driver 指的是 uhid_hid_driver ,ll_driver 會發回 UHID_START 消息通知內核已准備好。hid 是真正的主角,在其內部注冊了 input_device 和 hidraw 字符設備,至此所有的准備工作已就緒。
創建 OK,內核會有類似這樣的打印:
1 [ 0000.0000] input: BLE Remote as /devices/virtual/misc/uhid/0006:0095:0001.0006/input/input9 2 [ 0000.0000] hid-generic 0006:0095:0001.0006: input,hidraw3: BLUETOOTH HID v1.11 Device [BLE Remote] on
那再來看看 Bluedroid 是怎么將解析后的數據信息發到 event 的。
Bluedroid 中會調用 bta_hh_co_write 通過 UHID_INPUT 來向內核發送 HID 信息,進而內核調用到 uhid_dev_input 來將上層發過來的 HID 信息寫入到 event 節點和 hidraw 節點,具體細節暫不做深入分析。