HID Class の全体図 †HID のドキュメントは Documentation/hid 以下にあります。HID の全体図は Documentation/hid/hid-transport.txt にあります。 +-----------+ +-----------+ +-----------+ +-----------+ | Device #1 | | Device #i | | Device #j | | Device #k | +-----------+ +-----------+ +-----------+ +-----------+ \\ // \\ // +------------+ +------------+ | I/O Driver | | I/O Driver | +------------+ +------------+ || || +------------------+ +------------------+ | Transport Driver | | Transport Driver | +------------------+ +------------------+ \___ ___/ \ / +----------------+ | HID Core | +----------------+ / | | \ / | | \ ____________/ | | \_________________ / | | \ / | | \ +----------------+ +-----------+ +------------------+ +------------------+ | Generic Driver | | MT Driver | | Custom Driver #1 | | Custom Driver #2 | +----------------+ +-----------+ +------------------+ +------------------+ USB HID の場合 †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 に該当します。
Boot Interface Subclass デバイス 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) ではありません。 Generic Driver の実装と主要な処理 †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 できます。 HID User-space I/O driver †Application が HID device として機能できるよう、仮想的なデバイス drivers/hid/uhid.c があります。 Transport Driver の実装 †"Transport Driver" の実装を見ていきます。"Transport Driver" は struct hid_ll_driver を構成するメンバが指している関数です。 OpenGrok(linux-4.1.27/include/linux/hid.h:729)
I2C, bluetooth, USB, Hyper-V(仮想環境内で外界と繋がった HID デバイス), User space I/O driver (uhid) の "Transport Driver" は次の表に示すように実装されています。
struct hid_ll_driver のメンバとそのラッパー関数の関係は次のようになっています。詳細な調査は後回しにします。
USB HID device (drivers/hid/usbhid/hiddev.c) が提供する直接 I/O 機能 struct hiddev_fops 向けに struct hid_device に関数を指すメンバがあります。 OpenGrok(linux-4.1.27/include/linux/hid.h:550)
次のように実装されています。
hid_device 構造体が持つメソッドについて hiddev_connect .. hiddev_report_event メンバは HID Class driver (device) の全体的な構造としてみると I2C, bluetooth, Hyper-V, uhid いずれのバスでも関数を実装することが可能な様に見えます。実質的には USB device だけに使われる実装です。 Custom Driver 向け Hook 機能 †"Custom Driver" 向けに HID プロトコルを進める随所に hook 関数を仕込めるように struct hid_driver メンバーの一部は次の表のように関数を指すポインタになっています。詳細な解析は後回しにします。
HID デバイスを接続した場合の流れ †HID class device の登録と HID class driver の関係を見ていきましょう。 USB, bluetooth, I2C, 他 接続にて HID device と認識し、HID class device として hid_allocate_device(), hid_add_device() にてデバイスを登録すると、hid_ignore() によって HID class device として扱うか判断されます。判断した結果エラーになることもあります。Linux kernel の中では珍しい動作です。 OpenGrok(linux-4.1.27/drivers/hid/hid-core.c:2553)
hid_ignore_list に含まれているデバイスのドライバ hid_ignore() はいくつかの条件判定と除外リスト hid_ignore_list で構成されています。除外されたデバイスの一部は別のドライバで扱います。例えば drivers/input/mouse/synaptics_usb.c, drivers/usb/misc/ldusb.c は、hid_ignore_list に含まれるデバイスのドライバです。 hid_add_device() は "Transport Driver" の struct hid_ll_driver->parse を呼び出し Report Descriptor の取得を行います。続けて hid_have_special_driver を hid_match_id() に渡して "Generic Driver" と同じ動作で良ければ(すなわち、hid_have_special_driver の要素に無ければ) hid_scan_report() を呼び出して Report Descriptor をパースします。device_add() を呼び出し、drivers/base/dd.c の device_attach() にてドライバと照合する処理を始めます。 hid_have_special_driver の要素に格納したデバイスと hid_*.c の struct hid_driver.id_table 要素について hid_have_special_driver の要素に格納してあるデバイスは "Custom Driver" の実装である /hid-.*/(ドライバソースの正規表現です) の struct hid_driver の id_table メンバが指す配列の要素にも格納してあります。2 重に記述する必要が有り、保守や driver を module として組み込む場合に注意が必要です。 ドライバを後から組み込んだ場合について hid_register_driver() にてデバイスが接続された後、ドライバを組み込んだ場合は、bus_add_driver(), driver_attach() を経てデバイスと照合する処理を始めます。 デバイスとドライバの照合は hid_bus_match() で行います。適合したと判断したドライバがあれば、hid_device_probe() が呼ばれます。hid_device_probe() の実装は巧妙な点が多いです。probe が呼ばれた時点で struct hid_device hdev の driver メンバが != NULL になっている場合があります。Hyper-V の mouse ドライバ (drivers/hid/hid-hyperv.c) の場合だけです。下のコード断片は hid_device_probe() です。 OpenGrok(linux-4.1.27/drivers/hid/hid-core.c:2105)
hid_match_device()をもう一度呼んでいる hid_match_device() をもう一度呼ぶ理由はなぜだろうか?もっともらしい理由が見つかっていないです。drivers/base/dd.c の処理で hid_match_device() を呼び出しドライバは適合していると確認済みです。強いて言うならば、struct hid_driver の id_table から適合デバイスを見つけ出す処理を "Custom Driver" それぞれで実装する負担を減らすためかと思っています。 HID class driver struct hid_driver に probe 関数を指すメンバがあります。これは珍しいことです。しかも、実装は必須ではありません。probe 関数が有る場合は、既定動作の Report Descriptor の解釈(hid_open_report()) と 接続処理(hid_hw_start()) の代わりに probe 関数を呼び出します。 |