首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

HID Core 设备驱动模型与生命周期

HID Core 设备驱动模型与生命周期

1. HID bus

hid_bus_type 定义:

  • .match = hid_bus_match
  • .probe = hid_device_probe
  • .remove = hid_device_remove
  • .uevent = hid_uevent

sysfs modalias:

1
hid:bBUSgGROUPvVENDORpPRODUCT

2. 传输创建设备

1
2
3
4
5
6
7
8
9
10
hid_allocate_device()
-> device_initialize()
-> dev.bus = hid_bus_type
-> init driver_input_lock / ll_open_lock / kref

transport fills:
bus/group/vendor/product/version/country
name/phys/uniq/parent/ll_driver

hid_add_device()

销毁必须用 hid_destroy_device(),最终由 device release 释放。

3. add_device

  1. hid_lookup_quirk()
  2. hid_ignore() 过滤;
  3. 检查 transport 必须实现 raw_request
  4. ll_driver->parse() 读取原始 report descriptor;
  5. hid_scan_report() 初步确定 group;
  6. 设置 HID device 名;
  7. debugfs 注册;
  8. device_add() 触发 driver core 匹配。

4. 匹配

hid_match_one_id() 比较:

  • bus;
  • group;
  • vendor;
  • product。

专用驱动与 generic 驱动通过 match() 和 reprobe 机制协调。专用驱动后加载时,Core 可重新探测原 generic 设备。

5. Probe

1
2
3
4
5
6
hid_device_probe()
-> driver_input_lock
-> hid_match_device()
-> reload quirks
-> hdev->driver = hdrv
-> hdrv->probe()

没有自定义 probe 时:

1
2
3
4
hid_open_report()
-> hid_hw_start(HID_CONNECT_DEFAULT)
-> ll_driver->start()
-> hid_connect()

6. Connect mask

hid_connect() 可建立:

  • HID_CLAIMED_INPUT
  • HID_CLAIMED_HIDRAW
  • HID_CLAIMED_HIDDEV
  • HID_CLAIMED_DRIVER

非 USB 设备会屏蔽 HIDDEV。只有 raw_event 的专用驱动也可不连接其它 listener。

7. Open/close

多个上层消费者共享底层 open:

1
2
3
4
5
6
hid_hw_open()
-> ll_open_lock
-> if first: ll_driver->open()

hid_hw_close()
-> if last: ll_driver->close()

ll_open_count 避免 input 与 hidraw 分别打开时重复启停 transport。

8. Remove

1
2
3
4
5
6
7
8
9
hid_device_remove()
-> driver_input_lock
-> hdrv->remove() or hid_hw_stop()
-> hid_close_report()
-> hdev->driver = NULL

transport disconnect
-> hid_destroy_device()
-> device_del + put_device

hid_hw_stop() 先断开 input/hidraw/hiddev,再调用 transport stop()

9. 并发

锁/引用 作用
driver_input_lock semaphore 输入上报与 probe/remove 串行
ll_open_lock mutex 底层 open count
device refcount sysfs/driver core 生命周期
hid_device::kref HID 专用引用
driver dyn_lock 动态 ID 列表
debug list spinlock debugfs reader

中断上报若无法获得 driver_input_lockhid_input_report() 返回 -EBUSY,避免 remove 与 event 并发访问已解绑驱动。

Report 描述符解析与报告处理

Report 描述符解析与报告处理

1. HID 报告模型

HID descriptor 描述数据的位级布局,而不是固定 C 结构:

1
2
3
4
Report Type
└─ Report ID
└─ Field
└─ Usage(s)

类型:

  • Input:设备到 host;
  • Output:host 到设备;
  • Feature:双向配置状态。

2. Item

短/长 item 按 type 分为:

  • Main:Input/Output/Feature/Collection/End Collection;
  • Global:Usage Page、Logical/Physical Min/Max、Report Size/Count/ID、Unit;
  • Local:Usage、Usage Min/Max、Designator、String、Delimiter。

parser 使用 global stack、local usage list 和 collection stack。

3. 解析链

1
2
3
4
5
6
7
8
hid_open_report()
-> optional driver->report_fixup()
-> hid_parse_report()
-> fetch_item()
-> hid_parser_main/global/local()
-> hid_add_field()
-> hid_register_field()
-> hid_apply_multiplier()

传输在 hid_add_device() 前读取 dev_rdesc;专用驱动可在正式 parse 前修复错误字节。

4. 结构

hid_report 包含 report ID、type、bit size、field array。
hid_field 包含:

  • report offset/size/count;
  • logical/physical min/max;
  • unit/exponent;
  • flags;
  • value array;
  • usages。

hid_usage 保存 HID usage code、collection index、input code/type 映射。

5. 收包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
hid_input_report(type, data, size, interrupt)
-> driver->raw_event() # 可修改/消费原始包
-> hid_report_raw_event()
-> locate report by ID
-> clamp/zero-pad to expected size
-> hidraw copy
-> hid_process_report()
-> extract signed/unsigned bit fields
-> variable/array handling
-> hid_process_event()
-> driver->event()
-> hidinput_hid_event()
-> driver->report()
-> hidinput_report_event()

raw_event 只有负值会中止本次处理;零和正值都会继续进入通用 report 解析。按 usage 调用的 event() 则在返回非零时停止该 usage 的通用 input/hiddev 处理,其中负值还会记录错误。

6. 数组与变量

  • Variable:每个 field element 对应一个 usage/value;
  • Array:value 是 usage selector,如键盘按键数组;
  • Relative:产生相对事件;
  • Absolute:产生绝对坐标;
  • Null state:允许无有效 selector。

键盘数组需比较前后 report,生成 press/release。

7. Report ID

若 descriptor 声明任一 report ID,传输数据首字节通常是 ID。Core 的 report_enum->numbered 控制偏移。编号和非编号 report 混用或长度错误是常见固件问题。

8. 输出编码

1
2
3
4
5
hid_set_field()
-> field->value[]
-> hid_output_report()
-> implement() write bit fields
-> ll_driver request/raw_request/output_report

调用者必须遵守 report type、ID、长度和 logical range。

9. 安全边界

报告描述符和输入报告都来自外设,属于不可信输入。Core 限制 descriptor/report 大小、collection 深度、usage 数量并检查位偏移。专用 report_fixup 也必须验证 rsize 后再访问固定 offset。

10. 可观测

1
2
3
4
cat /sys/bus/hid/devices/*/report_descriptor > rdesc.bin
hid-decode rdesc.bin
mount -t debugfs none /sys/kernel/debug
ls /sys/kernel/debug/hid/

debugfs 节点依赖 CONFIG_DEBUG_FS

HID Input 事件映射与输出控制

HID Input 事件映射与输出控制

1. 映射目标

hid-input.c 把 HID Usage 转为 Linux input:

HID Linux input
Keyboard/Keypad EV_KEY
Button BTN_* / KEY_*
Generic Desktop X/Y EV_RELEV_ABS
Wheel/Consumer REL_WHEEL / media keys
Digitizer ABS、touch、tool
LED EV_LED

2. Connect

1
2
3
4
5
6
hid_connect(HID_CONNECT_HIDINPUT)
-> hidinput_connect()
-> walk input reports/fields/usages
-> hidinput_allocate()
-> hidinput_configure_usage()
-> input_register_device()

一个 HID device 可因多个 Application Collection 或 HID_QUIRK_MULTI_INPUT 生成多个 input device。

3. Usage 映射扩展点

专用驱动可实现:

  • input_mapping():改写或忽略 usage;
  • input_mapped():通用映射完成后的调整;
  • event():在通用输入事件前处理;
  • report():整份 report 完成后处理。

返回值语义需按 HID API:错误使用会造成重复事件或 usage 丢失。

4. 运行时上报

hid_process_event() 调专用 driver event,再进入 hidinput_hid_event()。report 完成后 hidinput_report_event() 对相关 input device 执行 sync,并处理工具释放等状态。

5. Absolute 参数

Core 根据 logical/physical range、unit 和 exponent 设置:

  • input_set_abs_params()
  • resolution;
  • fuzz/flat;
  • multitouch axis。

损坏 descriptor 会导致坐标、分辨率或方向错误,通常由 report fixup 或专用映射修正。

6. Output/LED

键盘 LED 等输出可由 input event 回调更新 HID Output report:

1
2
3
4
5
userspace EV_LED
-> HID input event
-> set field value
-> hid_hw_request(HID_REQ_SET_REPORT)
-> transport

USB 可走 interrupt OUT 或 control;I2C HID 使用 SET_REPORT/输出寄存器。

7. Force Feedback

三类常见实现:

  • HID PID 标准:usbhid/hid-pidff.c
  • input_ff_create_memless() 的设备专用 rumble;
  • 专用协议,如 Logitech/Sony/PlayStation/Nintendo。

FF output report 可能包含强度、时长、effect slot。产品不使用时可裁剪相关专用驱动。

8. 电池

CONFIG_HID_BATTERY_STRENGTH 使 HID battery usage 注册 power_supply,并读取 Feature/Input report。无线手柄、键鼠可由 upower 等查看。

电池值可能存在:

  • 自定义量程;
  • UNKNOWN/NOT_CHARGING;
  • Feature report 查询延迟;
  • 设备专用缩放 quirk。

9. 多键与恢复

数组 report 通过旧值/新值差异释放和按下按键。暂停恢复时 hid_check_keys_pressed() 可用于判断仍有按键,避免错误 autosuspend。设备拔出由 input unregister 释放状态。

10. 调试

1
2
3
4
cat /proc/bus/input/devices
evtest /dev/input/eventX
libinput debug-events
getevent -l

Android getevent 看到的是 input 层结果,不能直接证明原始 HID report 正确;必要时同时抓 hidraw/debugfs。

USB HID 传输与错误恢复

USB HID 传输与错误恢复

1. 枚举

drivers/hid/usbhid/hid-core.c 注册 USB interface driver:

1
2
3
4
5
6
USB interface probe
-> usbhid_probe()
-> validate HID interface/endpoints
-> hid_allocate_device()
-> fill BUS_USB, VID/PID, phys/name, ll_driver
-> hid_add_device()

HID device 通常按 interface,而不是整台 USB device 创建;复合设备可产生多个 HID 节点。

2. Descriptor

usbhid_parse()

  1. 获取 HID descriptor;
  2. 找 report descriptor 长度;
  3. control GET_DESCRIPTOR;
  4. hid_parse_report()

HID_QUIRK_NOGET、broken descriptor 等 quirk 会改变初始化。

3. 启动

usbhid_start()

  • 计算最大 report 大小;
  • 分配 input/control/output buffer;
  • 分配 interrupt IN URB;
  • 可选 interrupt OUT URB;
  • 初始化 queue、timer、work;
  • 获取初始 report(除 quirk 禁止)。

4. Input

1
2
3
4
interrupt IN URB complete
-> status/actual_length check
-> hid_input_report(HID_INPUT_REPORT)
-> resubmit URB

Input endpoint 通常周期轮询。高速/全速 interval 处理错误会影响 CPU 和延迟,部分设备有 interval quirk。

5. Output 与 Feature

  • interrupt OUT:低延迟 output report;
  • control endpoint:GET_REPORT/SET_REPORT;
  • usbhid_request() 排入 control/output queue;
  • usbhid_raw_request() 同步等待;
  • usbhid_output_report() 发送原始输出。

同一 endpoint 的请求必须串行,queue 完成后唤醒等待者。

6. Open/close

usbhid_open() 第一个消费者启动 input URB 并取得 runtime PM;usbhid_close() 最后一个消费者停止或允许 autosuspend。HID_QUIRK_ALWAYS_POLL 使设备即使无人打开也持续轮询。

7. 错误恢复

USB 错误包括:

  • -EPIPE stall:clear halt/reset;
  • protocol/timeout;
  • unplug/shutdown;
  • repeated I/O errors。

usbhid 使用 timer/work 和 reset 标志恢复 input/output/control queue。永久断开时必须停止 URB,防止 completion 访问已释放对象。

8. PM

  • autosuspend;
  • suspend/resume;
  • reset_resume;
  • remote wakeup;
  • hid_driver_suspend/resume/reset_resume

键盘/鼠标作为唤醒源时需 USB host、hub 和设备均支持 wakeup。

9. HIDDEV

usbhid/hiddev.c 是 USB 专属旧接口 /dev/usb/hiddevN,提供解析后的 usage/report ioctl。它不等同于 hidraw,且非 USB transport 不支持。

10. Boot Protocol

usbkbd.cusbmouse.c 只支持简化 Boot Protocol。Kconfig 要求在不使用完整 USB HID 时由 EXPERT 选择,功能有限,正常 RK3588 产品应使用 usbhid。

11. 观测

1
2
3
4
5
lsusb -t
lsusb -v -d VID:PID
cat /sys/kernel/debug/usb/devices
usbmon
dmesg | grep -Ei 'usbhid|hid-generic|input'

I2C HID 与 RK3588 设备树

I2C HID 与 RK3588 设备树

1. 分层

1
2
3
4
5
i2c-hid-of / of-elan / of-goodix / acpi
|
i2c-hid-core
|
HID Core

RK3588 使用 Device Tree,主入口是 CONFIG_I2C_HID_OF;Elan/Goodix 特殊电源时序需要对应 OF glue。

本树 rk3588*.dts(i) 中未检索到标准 compatible = "hid-over-i2c" 节点;tablet 文件出现的 chip_type = <0x01> /* HID IIC */ 属于其它厂商触控驱动属性,不能据此认定它走通用 i2c-hid-of。本章说明的是可用框架和新增标准 HID-I2C 设备时的接入方式。

2. Probe

OF glue 解析:

  • HID descriptor register 地址;
  • IRQ;
  • reset GPIO;
  • regulator;
  • post-power-on delay;
  • wakeup。

随后调用 I2C HID core:

1
2
3
4
5
6
7
power_up
-> fetch HID descriptor
-> validate descriptor/length/registers
-> request threaded IRQ
-> hid_allocate_device()
-> fill BUS_I2C and ll_driver
-> hid_add_device()

3. HID descriptor

I2C HID descriptor给出:

  • report descriptor length/register;
  • input/output register;
  • command/data register;
  • max input/output length;
  • VID/PID/version。

这些字段来自不可信外设,core 会做长度和寄存器边界检查。

4. 输入中断

1
2
3
4
5
6
GPIO IRQ threaded handler
-> i2c_hid_get_input()
-> read input register
-> first 2 bytes = little-endian packet length
-> strip length
-> hid_input_report()

I2C transaction 可睡眠,因此使用 threaded IRQ。IRQ polarity/type 由 DT 和 controller 配置。

5. Output/Feature

i2c_hid_set_or_send_report() 根据 report type、ID 和长度生成命令:

  • GET_REPORT;
  • SET_REPORT;
  • RESET;
  • SET_POWER;
  • 直接 output register 写入。

大 report 需按 descriptor 的 max output length 分配 buffer。

6. Start/open

i2c_hid_start() 根据已解析 report 重新计算最大包并分配 buffers。i2c_hid_open() 清屏蔽状态并允许 IRQ;close 可停止输入,但 wakeup/quirk 可能保留硬件。

7. Reset/PM

  • i2c_hid_execute_reset()
  • i2c_hid_hwreset()
  • power sleep/on 命令;
  • regulator/GPIO power cycle;
  • suspend 时 disable IRQ 或 enable IRQ wake;
  • resume 时重新上电、reset、恢复 driver。

触控屏恢复后无响应,常见原因是 reset delay、IRQ 状态、电源时序或 I2C controller runtime PM。

8. DT 示例

1
2
3
4
5
6
7
8
9
10
touchscreen@xx {
compatible = "hid-over-i2c";
reg = <0xXX>;
hid-descr-addr = <0x0001>;
interrupt-parent = <&gpioX>;
interrupts = <PIN IRQ_TYPE_LEVEL_LOW>;
vdd-supply = <&vcc>;
post-power-on-delay-ms = <20>;
wakeup-source;
};

通用 hid-over-i2c binding 不允许 reset-gpios;需要复位时应使用本树已有的 Elan/Goodix 专用 compatible/binding,或为具体芯片补充合规 binding,不能把任意属性塞进通用节点。属性以本树 binding 为准。

9. RK3588 排查

1
2
3
4
i2cdetect -y BUS                 # 对活跃触控设备谨慎
cat /proc/interrupts
cat /sys/bus/i2c/devices/*/uevent
dmesg | grep -Ei 'i2c-hid|hid-over-i2c|elan|goodix'

不要在驱动已绑定时反复用 i2cget/i2cdump 干扰协议状态。

HIDRAW、UHID、HIDDEV 用户接口

HIDRAW、UHID、HIDDEV 用户接口

1. 三者不同

接口 方向 作用
hidraw kernel HID device ↔ userspace client 访问真实设备原始报告
UHID userspace transport ↔ kernel HID Core 用户态创建虚拟 HID
HIDDEV USB HID ↔ userspace client USB 专属旧式解析接口

2. hidraw

CONFIG_HIDRAWhidraw.c 合入 hid.o,创建 /dev/hidrawN

输入

1
2
3
4
hid_report_raw_event()
-> hidraw_report_event()
-> copy report into each open file queue
-> wake poll/read/fasync

每个 open 有独立的 64 项 ring。ring 满时 hidraw_report_event() 跳过该 reader 的新 report,因此慢消费者会丢包;每个已入队项按实际 report 长度单独分配内存。

输出/ioctl

  • write():Output report;
  • HIDIOCSFEATURE/HIDIOCGFEATURE
  • HIDIOCGRAWINFO
  • HIDIOCGRAWNAME/PHYS/UNIQ
  • HIDIOCGRDESCSIZE/HIDIOCGRDESC

report ID 0 的首字节语义仍需遵循 hidraw ABI。

3. UHID

/dev/uhid 允许用户态实现 low-level transport:

1
2
3
4
5
userspace UHID_CREATE2
-> hid_allocate_device()
-> set BUS_VIRTUAL + ll_driver
-> worker hid_add_device()
-> normal HID driver/input/hidraw appears

用户态发送 UHID_INPUT2,内核调用 hid_input_report()。内核的 open/output/get_report/set_report 作为事件发给用户态,回复通过对应 reply event 返回。

4. UHID 并发

  • create 在 worker 中执行,避免写 syscall 内发生复杂 driver bind;
  • event queue + waitqueue;
  • report request 使用 sequence ID 等待回复;
  • destroy/release 唤醒 pending request;
  • 用户态 daemon 崩溃时虚拟 HID 必须完整移除。

UHID 是“用户态传输”,不是让用户态编写 kernel HID driver;若只需访问真实 raw report,应使用 hidraw。

5. HIDDEV

CONFIG_USB_HIDDEV 构建进 usbhid,设备为 /dev/usb/hiddevN,传统主设备号 180 的区间。它暴露 report/field/usage 枚举与事件,主要服务 UPS、monitor controls 等旧应用。

限制:

  • 仅 USB;
  • ABI 较旧;
  • 事件不是完全不解释的原始字节;
  • 新应用通常优先 hidraw。

6. 权限

这些字符设备可:

  • 注入键盘/鼠标(UHID);
  • 读取安全密钥/输入;
  • 修改 Feature report;
  • 控制设备固件模式。

建议:

  • udev 只授权明确 VID/PID;
  • 不把 /dev/uhid/dev/hidraw* 无条件暴露给不可信容器;
  • 使用 cgroup device/SELinux/AppArmor;
  • 守护进程 drop privileges;
  • 验证 report 长度和 ID。

7. 选择

1
2
3
4
读取专有 USB/I2C 报告       -> hidraw
用户态 Bluetooth/网络 HID -> UHID
兼容旧 UPS/monitor 程序 -> HIDDEV
普通键鼠/触控 -> evdev/libinput

蓝牙 HIDP 与其它传输边界

蓝牙 HIDP 与其它传输边界

1. HID Core 不等于 USB HID

hid_device 的 bus 可为 USB、Bluetooth、I2C、Virtual、sensor hub 等。传输层只需实现 hid_ll_driver 契约:

  • parse;
  • start/stop;
  • open/close;
  • request/raw_request;
  • output_report;
  • power/idle(可选)。

2. Bluetooth HIDP

代码主要在:

1
net/bluetooth/hidp/

而不是 drivers/hid/CONFIG_BT_HIDP 建立 Bluetooth L2CAP control/interrupt channel,将远端 descriptor 和 reports 接入 HID Core。

概念路径:

1
2
3
4
5
6
7
8
Bluetooth connection/session
-> hid_allocate_device(BUS_BLUETOOTH)
-> hid_add_device()
-> HID generic/vendor driver

L2CAP interrupt data
-> HIDP transaction decode
-> hid_input_report()

输出/Feature 通过 HIDP control channel transaction。

3. HID over GATT

BLE HID over GATT 常由 BlueZ 在用户态处理后通过 UHID 注入,内核并非所有情况下都有独立 HOGP HID transport。分析蓝牙键鼠需同时检查 BlueZ 日志和 /dev/uhid

4. Sensor Hub

  • Intel ISH:drivers/hid/intel-ish-hid/
  • AMD SFH:drivers/hid/amd-sfh-hid/
  • HID sensor 语义:hid-sensor-hub.c
  • 各传感器 consumer 多位于 IIO 等目录。

这些 x86 平台 transport 对 RK3588 通常无关,但 HID sensor usage 模型仍可复用。

5. Hyper-V/Surface

hid-hyperv.csurface-hid/ 是特定虚拟化/平台通道。源码存在不表示 ARM64 RK3588 产品启用。

6. SPI HID

drivers/hid/ 树没有一个与 I2C HID 对称的通用 spi-hid/ 构建目录。某些 SPI 触控/键盘可能:

  • 使用 input 专用驱动;
  • 使用厂商 MFD;
  • 在其它目录实现 HID transport;
  • 由用户态/UHID 代理。

不能从 HID core 推断板上 SPI 外设自动受支持。

7. 传输共同约束

  • raw_requesthid_add_device() 强制要求;
  • parse 必须提供 dev_rdesc
  • input 可以在 IRQ/软中断/线程上下文进入;
  • start/stop 与 connect/disconnect 成对;
  • open/close 有共享引用;
  • PM 必须保证 report request 与断电不竞态;
  • max report buffer 不得超过 transport 能力。

8. RK3588 Bluetooth

主配置启用 CONFIG_BT_HIDP=y。实际还依赖:

  • UART/USB Bluetooth controller;
  • firmware;
  • BlueZ;
  • pairing/security;
  • input/hid vendor driver;
  • Android Bluetooth service(如使用 Android)。

调试:

1
2
3
4
btmon
bluetoothctl info
cat /sys/bus/hid/devices/*/uevent
dmesg | grep -Ei 'bluetooth|hidp|uhid'

Multitouch 触控与手写设备

Multitouch 触控与手写设备

1. HID multitouch

hid-multitouch.c 处理符合或接近 HID Digitizers 规范的触摸屏/触控板。它不是传输驱动,可绑定 USB HID 或 I2C HID。

2. 核心 Usage

  • Contact ID;
  • Tip Switch;
  • In Range;
  • Confidence;
  • X/Y;
  • Contact Count/Maximum;
  • Width/Height;
  • Pressure;
  • Scan Time;
  • Button。

设备常因 descriptor 不规范需要 class/VID/PID quirk。

3. Slot 模型

1
2
3
4
5
6
7
HID report contacts
-> raw_event / event
-> map Contact ID to MT slot
-> input_mt_slot()
-> input_mt_report_slot_state()
-> report ABS_MT_*
-> input_mt_sync_frame()

Protocol B 使用稳定 slot,减少 userspace 自行追踪 contact。

4. 触摸屏与触控板

类型 特点
Direct touchscreen 坐标直接对应显示,INPUT_PROP_DIRECT
Indirect touchpad 指针手势,INPUT_PROP_POINTER
Hybrid 需 class/quirk 决定

错误分类会导致 libinput 使用错误策略。

5. Feature 初始化

multitouch probe 可能读取/设置:

  • input mode;
  • contact maximum;
  • latency mode;
  • button type。

某些设备不支持 GET_REPORT,需 HID_QUIRK_NOGET 或专用 quirk,避免 probe 卡住。

6. 专用手写板

驱动 特点
Wacom wacom_wac.c/wacom_sys.c pen/touch、工具 ID、pressure、battery/LED
hid-uclogic-* 修复大量国产数位板 descriptor
hid-waltop.c Waltop tablets
hid-ntrig.c N-Trig digitizer
hid-rmi.c Synaptics RMI over HID

专用驱动可能同时生成 pen、pad、touch 多个 input device。

7. 坐标

关键校验:

  • logical max 是否匹配面板分辨率;
  • physical max/unit 是否合理;
  • X/Y 是否交换/反向;
  • pressure 范围;
  • contact ID 稳定性;
  • report rate;
  • 显示旋转由 DRM/input userspace 或 DT 属性协调。

8. RK3588

rockchip_linux_defconfig 显式启用 HID_MULTITOUCHI2C_HID_OF,适合 I2C HID 触控屏。是否绑定 hid-multitouch 取决于 descriptor group 和设备 ID,并非所有 I2C 触摸控制器都使用 HID;大量芯片仍使用 drivers/input/touchscreen/ 专用驱动。

9. 调试

1
2
3
4
5
libinput list-devices
libinput debug-events
evtest
cat /sys/class/input/eventX/device/name
cat /sys/bus/hid/devices/*/report_descriptor > rdesc.bin

检查 raw report 正常但 evdev 异常时,重点看 descriptor、multitouch class 和 input mapping。

厂商专用 HID 驱动与 Quirk 机制

厂商专用 HID 驱动与 Quirk 机制

1. 为什么需要专用驱动

真实设备经常:

  • report descriptor 错误;
  • 使用 vendor usage page;
  • report 长度/ID 非标准;
  • 需要初始化命令;
  • 有电池、LED、FF、触摸板等扩展;
  • 同一 USB receiver 聚合多个逻辑设备。

2. 两级修正

Core quirk

hid-quirks.c 按 bus/VID/PID 设置 HID_QUIRK_*,例如:

  • NOGET
  • NO_INIT_REPORTS
  • ALWAYS_POLL
  • MULTI_INPUT
  • HIDINPUT_FORCE
  • HIDDEV_FORCE
  • INCREMENT_USAGE_ON_DUPLICATE

专用驱动

通过 hid_driver 回调:

  • report_fixup() 修 descriptor;
  • input_mapping() 改 usage;
  • raw_event() 解私有 report;
  • probe() 执行握手;
  • remove() 清资源;
  • PM 恢复协议状态。

3. 动态 quirk

Core 支持模块参数/动态列表覆盖设备 quirk,适合临时验证。量产应尽量形成明确 VID/PID 修复,并评估是否会影响同 ID 不同硬件版本。

4. 驱动族

驱动 主要能力
hid-logitech-dj Unifying/DJ receiver 多设备
hid-logitech-hidpp HID++、电池、滚轮、功能
hid-playstation 新 PlayStation 控制器
hid-sony 较旧 Sony 设备
hid-nintendo Switch controller、IMU/LED/FF
hid-steam Steam Controller
hid-apple Fn/键盘布局、设备 quirk
hid-microsoft Microsoft 键鼠特殊键
hid-wiimote-* Wii Remote 子模块
hid-roccat-* 游戏外设

5. Receiver

无线 receiver 常在一个物理 USB HID 上复用多个设备。专用驱动可能创建 child HID device,并用 report ID/device index 路由。生命周期必须保证 receiver 断开时先停止 child I/O。

6. 游戏手柄

常见扩展:

  • rumble/force feedback;
  • RGB/player LED;
  • battery;
  • accelerometer/gyro;
  • touchpad;
  • unique MAC/serial;
  • firmware version。

hid-sony 与新 hid-playstation 可能覆盖不同产品,不能仅按厂商名同时强制绑定。

7. 桥接设备

hid-cp2112.chid-ft260.chid-mcp2221.c 将 HID vendor protocol 暴露为 I2C/GPIO/UART 等内核子系统。启用后远不止 input,权限与移除并发要按总线控制器看待。

8. 绑定判断

1
2
3
readlink /sys/bus/hid/devices/DEVICE/driver
cat /sys/bus/hid/devices/DEVICE/modalias
modprobe -c | grep 'hid:b'

不要只看 lsusb 推断实际 driver;同一 USB interface 的 usb driver 是 usbhid,而其上 HID bus driver 可能是 hid-generic 或厂商驱动。

9. 风险

  • fixup 固定 offset 未检查 descriptor size;
  • raw_event 长度未检查;
  • 同步 Feature request 卡死;
  • remove 与 work/timer 并发;
  • battery/LED work 在断开后访问 transport;
  • 过宽 ID table 抢占 generic 或其它专用驱动。

HID Sensor Hub 与传感器框架

HID Sensor Hub 与传感器框架

1. 定位

HID Sensor Usage Tables 可描述 accelerometer、gyro、ALS、magnetometer、temperature 等。hid-sensor-hub.c 解析 sensor collection 并把不同 usage 暴露给 consumer。

2. 架构

1
2
3
4
5
6
ISH/SFH/USB HID sensor transport
-> HID Core
-> hid-sensor-hub
-> MFD child / HID sensor helper
-> IIO or other sensor class
-> userspace

具体 IIO sensor driver通常位于 drivers/iio/,不全部位于 drivers/hid/

3. Sensor collection

每个 Physical/Application collection 可形成 sensor hub 子设备。关键字段:

  • sensor type usage;
  • data fields;
  • report state;
  • power state;
  • report interval;
  • sensitivity/change threshold;
  • timestamp。

4. Feature

配置常通过 Feature report:

  • reporting state;
  • power state;
  • report interval;
  • sensitivity;
  • batch/flush(依设备)。

数据通常从 Input report 分发给注册 callback。

5. hid-sensor-custom

为没有标准专用 consumer 的 custom sensor 提供较通用接口。启用它不代表所有 vendor sensor 都自动获得稳定 ABI,仍需理解 usage 和数据格式。

6. Intel/AMD transport

intel-ish-hid/amd-sfh-hid/ 通过平台固件/IPC 创建虚拟 HID sensor device。这些目录在本树存在,但 RK3588 ARM64 通常不构建或不适用。

7. RK3588 边界

RK3588 板载 IMU/光感常通过普通 I2C/SPI IIO 驱动,而非 HID sensor hub。判断依据应为 DTS compatible 和最终 .config,不能因 hid-sensor-hub.c 存在就认为板载传感器走 HID。

USB/Bluetooth 外接 sensor 仍可能走 HID。

8. 并发

  • raw report 可能在 IRQ/transport thread;
  • consumer callback不能执行不允许的睡眠;
  • feature request需要与 PM/stop 串行;
  • unregister 前停止 report 并等待 callback;
  • timestamp 要区分设备时间与 host arrival。

9. 调试

1
2
3
find /sys/bus/iio/devices -maxdepth 2 -type f
cat /sys/bus/hid/devices/*/uevent
dmesg | grep -Ei 'hid-sensor|sensor-hub|ish|sfh'

HID raw report、sensor hub 字段和最终 IIO channel 是三层,需要逐层核对。