HID のドキュメントは Documentation/hid 以下にあります。HID の全体図は Documentation/hid/hid-transport.txt にあります。
USB の場合で全体図のブロックとファイルの対応を見ていきます。次の表のように "I/O Driver" が USB core drivers/usb/core の drivers/usb/core/message.c が主に該当します。"Transport Driver" が drivers/hid/usb/hid-core.c に該当します。"HID Core" が drivers/hid/hid-core.c (こちらも hid-core.c という名前なので要注意), drivers/hid/hid-input.c に該当します。キーボードやマウスの場合は "Generic Driver" が drivers/hid/hid-generic.c に該当します。
ブロック | 主なファイル | 機能 |
I/O Driver | drivers/usb/core の drivers/usb/core/message.c | ハードウエア入出力をする |
Transport Driver | drivers/hid/usbhid/hid-core.c | ハードウエア入出力をデバイスのバスに依存しない様に抽象化する |
HID Core | drivers/hid/hid-core.c drivers/hid/hid-input.c | Report Descriptor を解釈する Report を input event に変換する HID class driver を登録・削除する HID class device を登録・削除する |
Generic Driver | drivers/hid/hid-generic.c | 殆ど何もしないドライバ、HID Core の機能で十分な場合に使う。 |
Custom Driver | drivers/hid-* | 特別な対応が必要なデバイスのドライバ |
drivers/hid/hid-generic.c の実装をみると殆ど何もしていません。Report Descriptor の解釈、Report を input event に変換する処理は "HID core" を構成する drivers/hid/hid-core.c, drivers/hid/hid-input.c で行われています。Application と直接 I/O する処理は drivers/hid/hidraw.c です。USB HID device は drivers/hid/usbhid/hiddev.c も Application と直接 I/O できます。
Application が HID device として機能できるよう、仮想的なデバイス drivers/hid/uhid.c があります。
ノート
USB HID device のうち Boot Interface Subclass デバイス専用のドライバ drivers/hid/usbhid/usbkbd.c と drivers/hid/usbhid/usbmouse.c があります。これらは input class device として動作します。Linux kernel 内では HID class device (あるいは driver) ではありません。
"Transport Driver" の実装を見ていきます。"Transport Driver" は struct hid_ll_driver を構成するメンバが指している関数です。 I2C, bluetooth, USB, Hyper-V(仮想環境内で外界と繋がった HID デバイス), User space I/O driver (uhid) の "Transport Driver" は次の表に示すように実装されています。
BUS | "Transport Driver" すなわち struct hid_ll_driver の実装 |
I2C | i2c_hid_ll_driver |
bluetooth | hidp_hid_driver |
USB | usb_hid_driver |
Hyper-V | mousevsc_ll_driver |
uhid | uhid_hid_driver |
struct hid_ll_driver のメンバとそのラッパー関数の関係は次のようになっています。詳細な調査は後回しにします。
メンバ(メソッド) | 説明 | ラッパ関数 | HID Core 内部使用 |
start | start underlaying HW | hid_hw_start() | hid_device_probe() |
stop | stop underlaying HW | hid_hw_stop() | hid_device_remove() |
open | signal underlaying HW to start delivering events | hid_hw_open() | hidinput_open() |
close | signal underlaying HW to stop delivering events | hid_hw_close() | hidinput_close() |
power | requests underlying HW to go into given power mode | hid_hw_power() | |
parse | this method is called only once to parse the device data | hid_add_device() | |
request | send report request to device | hid_hw_request() | |
wait | wait for buffered io to complete | hid_hw_wait() | |
raw_request | send report request to device | hid_hw_raw_request() | hidinput_led_worker(), hidinput_led_worker() |
output_report | send output report to device | hid_hw_output_report() | |
idle | send idle request to device | hid_hw_idle() |
USB HID device (drivers/hid/usbhid/hiddev.c) が提供する直接 I/O 機能 drivers/hid/usbhid/hiddev.c 向けに struct hid_device に関数を指すメンバが次のように実装されています。
hid_device メンバ(メソッド) | 呼び出し元 |
hiddev_connect | hid_connect() |
hiddev_disconnect | hid_disconnect() |
hiddev_hid_event | mt_touch_event(), ntrig_event(), hid_process_event() |
hiddev_report_event | hid_report_raw_event() |
ノート
hiddev_connect .. hiddev_report_event メンバは HID Class driver (device) の全体的な構造としてみると I2C, bluetooth, Hyper-V, uhid いずれのバスでも関数を実装することが可能な様に見えます。実質的には USB device だけに使われる実装です。
"Custom Driver" 向けに HID プロトコルを進める随所に hook 関数を仕込めるように struct hid_driver メンバーの一部は次の表のように関数を指すポインタになっています。詳細な解析は後回しにします。
メンバ (メソッド) | 説明 | HID Core 内部の呼び出し元(一部は Transport Driver) |
probe | new device inserted | hid_device_probe() |
remove | device removed (NULL if not a hot-plug capable driver) | hid_device_remove() |
report_table | on which reports to call raw_event (NULL means all) | hid_match_report(), hid_input_report() この中で raw_event の呼び出し判定をしている。 |
raw_event | if report in report_table, this hook is called (NULL means nop) | hid_input_report() |
usage_table | on which events to call event (NULL means all) | hid_match_usage() &ogdef(hid_process_event(),hid_process_event); この中で event の呼び出しを判定している |
event | if usage in usage_table, this hook is called (NULL means nop) | hid_process_event() |
report | this hook is called after parsing a report (NULL means nop) | hid_report_raw_event() |
report_fixup | called before report descriptor parsing (NULL means nop) | hid_open_report() |
input_mapping | invoked on input registering before mapping an usage | hidinput_configure_usage() |
input_mapped | invoked on input registering after mapping an usage | hidinput_configure_usage() |
input_configured | invoked just before the device is registered | hidinput_connect() |
feature_mapping | invoked on feature registering | report_features() |
suspend | invoked on suspend (NULL means nop) | usbhid hid_suspend(), i2c_hid_suspend() |
resume | invoked on resume if device was not reset (NULL means nop) | hid_resume_common() |
reset_resume | invoked on resume if device was reset (NULL means nop) | usbhid hid_reset_resume(), i2c_hid_resume() |
HID class device の登録と HID class driver の関係を見ていきましょう。
HID class driver struct hid_driver に probe 関数を指すメンバがあります。これは珍しいことです。しかも、実装は必須ではありません。USB, bluetooth, I2C, 他 接続にて HID device と認識し、HID class device として hid_allocate_device(), hid_add_device() にて登録すると、hid_ignore() によって HID class device として扱うか判断されます。
hid_ignore() はいくつかの条件判定と除外リスト hid_ignore_list で構成されています。除外されたデバイスの一部は別のドライバで扱います。
と hid_bus_match() で細かく行います。
hid_ignore() は HID class device のうち HID として扱わないデバイスか照合し、hid_add_device() をエラーにします。
hid_device_probe() が呼ばれ、この中で driver が未対応ならば hid_match_device() を呼び出し適切なドライバと対応付け、probe を呼び出します。
ノート: hid_device_probe() が呼び出された時点でドライバが未対応なのが多くの場合です。対応済みなのは hyperv(drivers/hid/hid-hyperv.c) mouse ドライバの場合だけです。
クラスドライバは接続されたデバイスと通信する機能を struct hid_ll_driver に格納した関数ポインタを介して抽象化し、デバイスを probe する(デバイスの機能を確認して初期化し必要なメモリなどのリソースを確保する)役割を担っています。
USB HID デバイスを例として見ていきます。USB HID ドライバのコアは drivers/hid/usbhid/hid-core.c です。ここでは USB デバイスのドライバとして usb_register() (対は usb_deregister()) を使って登録します。struct usb_driver の id_table は HID interface です。USB デバイスを probe する処理 usbhid_probe() で、hid_allocate_device(), hid_add_device() (対は hid_destroy_device()) を使って USB デバイスを HID class デバイスとして登録します。
usbhid_probe() で行われる HID デバイス登録は巧妙です。struct hid_device 構造体の ll_driver メンバーに USB pipe 通信処理を抽象化する関数群を保持した構造体 struct hid_ll_driver を指すポインタを格納しています。
hid_bus_match() にて行われるデバイスとドライバの突き合わせ処理が行われません。
Game Controller や HID 様式の入出力デバイスは USB の VendorID, ProductID で識別します。HID_USB_DEVICE() マクロで識別用のテーブルを作成します。識別用のテーブルを伴った struct hid_driver を hid_register_driver(), (対は hid_unregister_driver()) または module_hid_driver() で HID クラスドライバとして登録します。