#author("2016-11-15T00:20:08+09:00","default:afuruta","afuruta")
#author("2017-09-02T11:03:45+09:00","default:afuruta","afuruta")
* HID Class の全体図 [#m2a1a245]
HID のドキュメントは &ogfileone(Documentation/hid); 以下にあります。HID の全体図は &ogfileone(Documentation/hid/hid-transport.txt); にあります。
#pre{{
 +-----------+  +-----------+            +-----------+  +-----------+
 | 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 の場合 [#i888df82]
USB の場合で全体図のブロックとファイルの対応を見ていきます。次の表のように "I/O Driver" が USB core &ogfileone(drivers/usb/core); の &ogfileone(drivers/usb/core/message.c); が主に該当します。"Transport Driver" が &ogfileone(drivers/hid/usb/hid-core.c); に該当します。"HID Core" が &ogfileone(drivers/hid/hid-core.c); (こちらも hid-core.c という名前なので要注意), &ogfileone(drivers/hid/hid-input.c); に該当します。キーボードやマウスの場合は "Generic Driver" が &ogfileone(drivers/hid/hid-generic.c); に該当します。

|ブロック|主なファイル|機能|h
|I/O Driver|&ogfileone(drivers/usb/core); の &ogfileone(drivers/usb/core/message.c);|ハードウエア入出力をする|
|Transport Driver|&ogfileone(drivers/hid/usbhid/hid-core.c);|ハードウエア入出力をデバイスのバスに依存しない様に抽象化する|
|HID Core|&ogfileone(drivers/hid/hid-core.c);&br;&ogfileone(drivers/hid/hid-input.c);|Report Descriptor を解釈する&br;Report を input event に変換する&br;HID class driver を登録・削除する&br;HID class device を登録・削除する|
|Generic Driver|&ogfileone(drivers/hid/hid-generic.c);|殆ど何もしないドライバ、HID Core の機能で十分な場合に使う。|
|Custom Driver|&ogfileone(drivers/hid-*,drivers/hid);|特別な対応が必要なデバイスのドライバ|

#textbox(note,Boot Interface Subclass デバイス){{ 
USB HID device のうち Boot Interface Subclass デバイスに対して専用のドライバ &ogfileone(drivers/hid/usbhid/usbkbd.c); と &ogfileone(drivers/hid/usbhid/usbmouse.c); があります。これらは input class device として動作します。Linux kernel 内では HID class device (あるいは driver) ではありません。
}}
** Generic Driver の実装と主要な処理 [#dbbcd142]
&ogfileone(drivers/hid/hid-generic.c); の実装をみると殆ど何もしていません。Report Descriptor の解釈、Report を input event に変換する処理は "HID core" を構成する &ogfileone(drivers/hid/hid-core.c);, &ogfileone(drivers/hid/hid-input.c); で行われています。Application と直接 I/O する処理は &ogfileone(drivers/hid/hidraw.c); です。USB HID device は &ogfileone(drivers/hid/usbhid/hiddev.c); も Application と直接 I/O できます。
** HID User-space I/O driver [#o6030db0]
Application が HID device として機能できるよう、仮想的なデバイス &ogfileone(drivers/hid/uhid.c); があります。
** Transport Driver の実装 [#j3cd8abc]
"Transport Driver" の実装を見ていきます。"Transport Driver" は &ogdefs(struct hid_ll_driver,hid_ll_driver); を構成するメンバが指している関数です。

#code(c,/struct\s*hid_ll_driver\s*{/../^};$/,ogfileone:/include/linux/hid.h);

I2C, bluetooth, USB, Hyper-V(仮想環境内で外界と繋がった HID デバイス), User space I/O driver (uhid) の "Transport Driver" は次の表に示すように実装されています。

|BUS|"Transport Driver" すなわち &ogdefs(struct hid_ll_driver,hid_ll_driver); の実装|h
|I2C|&ogdefs(i2c_hid_ll_driver);|
|bluetooth|&ogdefs(hidp_hid_driver);|
|USB|&ogdefs(usb_hid_driver);|
|Hyper-V|&ogdefs(mousevsc_ll_driver);|
|uhid|&ogdefs(uhid_hid_driver);|

&ogdefs(struct hid_ll_driver,hid_ll_driver); のメンバとそのラッパー関数の関係は次のようになっています。詳細な調査は後回しにします。

|メンバ(メソッド)|説明|ラッパ関数|HID Core 内部使用|h
|start|start underlaying HW|&ogdefs(hid_hw_start(),hid_hw_start);|&ogdefs(hid_device_probe(),hid_device_probe);|
|stop|stop underlaying HW|&ogdefs(hid_hw_stop(),hid_hw_stop);|&ogdefs(hid_device_remove(),hid_device_remove);|
|open|signal underlaying HW to start delivering events|&ogdefs(hid_hw_open(),hid_hw_open);|&ogdefs(hidinput_open(),hidinput_open);|
|close|signal underlaying HW to stop delivering events|&ogdefs(hid_hw_close(),hid_hw_close);|&ogdefs(hidinput_close(),hidinput_close);|
|power|requests underlying HW to go into given power mode|&ogdefs(hid_hw_power(),hid_hw_power);||
|parse|this method is called only once to parse the device data||&ogdefs(hid_add_device(),hid_add_device);|
|request|send report request to device|&ogdefs(hid_hw_request(),hid_hw_request);||
|wait|wait for buffered io to complete|&ogdefs(hid_hw_wait(),hid_hw_wait);||
|raw_request|send report request to device|&ogdefs(hid_hw_raw_request(),hid_hw_raw_request);|&ogdefs(hidinput_led_worker(),hidinput_led_worker);, &ogdefs(hidinput_led_worker(),hidinput_led_worker);|
|output_report|send output report to device|&ogdefs(hid_hw_output_report(),hid_hw_output_report);||
|idle|send idle request to device|&ogdefs(hid_hw_idle(),hid_hw_idle);||

USB HID device (&ogfileone(drivers/hid/usbhid/hiddev.c);) が提供する直接 I/O 機能 &ogdefs(struct hiddev_fops,hiddev_fops); 向けに &ogdefs(struct hid_device, hid_device); に関数を指すメンバがあります。

#code(c,/hiddev_connect/../hiddev_report_event/,ogfileone:/include/linux/hid.h);
次のように実装されています。

|hid_device メンバ(メソッド)|呼び出し元|h
|&ogrefs(hiddev_connect);|&ogdefs(hid_connect(),hid_connect);|
|&ogrefs(hiddev_disconnect);|&ogdefs(hid_disconnect(), hid_disconnect);|
|&ogrefs(hiddev_hid_event);|&ogdefs(mt_touch_event(),mt_touch_event);,&br;&ogdefs(ntrig_event(),ntrig_event);,&br;&ogdefs(hid_process_event(),hid_process_event);|
|&ogrefs(hiddev_report_event);|&ogdefs(hid_report_raw_event(), hid_report_raw_event);|

#textbox(note,hid_device 構造体が持つメソッドについて){{
hiddev_connect .. hiddev_report_event メンバは HID Class driver (device) の全体的な構造としてみると I2C, bluetooth, Hyper-V, uhid いずれのバスでも関数を実装することが可能な様に見えます。実質的には USB device だけに使われる実装です。
}}
** Custom Driver 向け Hook 機能 [#v7d5f9b2]
"Custom Driver" 向けに HID プロトコルを進める随所に hook 関数を仕込めるように &ogdefs(struct hid_driver,hid_driver); メンバーの一部は次の表のように関数を指すポインタになっています。詳細な解析は後回しにします。

|メンバ (メソッド)|説明|HID Core 内部の呼び出し元(一部は Transport Driver)|h
|&ogsearch(probe,".probe",/hid/);|new device inserted|&ogdefs(hid_device_probe(),hid_device_probe);|
|&ogsearch(remove,".remove",/hid/);|device removed (NULL if not a hot-plug capable driver)|&ogdefs(hid_device_remove(),hid_device_remove);|
|&ogrefs(report_table,report_table,/hid/);|on which reports to call raw_event (NULL means all)|&ogdefs(hid_match_report(),hid_match_report,/hid/);, &ogdefs(hid_input_report(),hid_input_report); この中で raw_event の呼び出し判定をしている。|
|&ogrefs(raw_event,raw_event,/hid/);|if report in report_table, this hook is called (NULL means nop)|&ogdefs(hid_input_report(),hid_input_report);|
|&ogrefs(usage_table,usage_table,/hid/);|on which events to call event (NULL means all)|&ogdefs(hid_match_usage(),hid_match_usage); &ogdef(hid_process_event(),hid_process_event); この中で event の呼び出しを判定している|
|&ogrefs(usage_table,usage_table,/hid/);|on which events to call event (NULL means all)|&ogdefs(hid_match_usage(),hid_match_usage); &ogdefs(hid_process_event()); この中で event の呼び出しを判定している|
|&ogsearch(event,".event",/hid/);|if usage in usage_table, this hook is called (NULL means nop)|&ogdefs(hid_process_event(),hid_process_event);|
|&ogsearch(report,".report",/hid/);|this hook is called after parsing a report (NULL means nop)|&ogdefs(hid_report_raw_event(),hid_report_raw_event);|
|&ogrefs(report_fixup,report_fixup,/hid/);|called before report descriptor parsing (NULL means nop)|&ogdefs(hid_open_report(),hid_open_report);|
|&ogrefs(input_mapping,input_mapping,/hid/);|invoked on input registering before mapping an usage|&ogdefs(hidinput_configure_usage(),hidinput_configure_usage);|
|&ogrefs(input_mapped,input_mapped,/hid/);|invoked on input registering after mapping an usage|&ogdefs(hidinput_configure_usage(), hidinput_configure_usage);|
|&ogrefs(input_configured,input_configured,/hid/);|invoked just before the device is registered|&ogdefs(hidinput_connect(),hidinput_connect);|
|&ogrefs(feature_mapping,feature_mapping,/hid/);|invoked on feature registering|&ogdefs(report_features(),report_features);|
|&ogrefs(suspend,suspend,hid*);|invoked on suspend (NULL means nop)|&ogdefs(usbhid hid_suspend(),hid_suspend);, &ogdefs(i2c_hid_suspend(),i2c_hid_suspend);|
|&ogrefs(resume,resume,/hid/);|invoked on resume if device was not reset (NULL means nop)|&ogdefs(hid_resume_common(),hid_resume_common);|
|&ogrefs(reset_resume,reset_resume,/hid/);|invoked on resume if device was reset (NULL means nop)|&ogdefs(usbhid hid_reset_resume(),hid_reset_resume);, &ogdefs(i2c_hid_resume(),i2c_hid_resume);|

** HID デバイスを接続した場合の流れ [#g7505464]
HID class device の登録と HID class driver の関係を見ていきましょう。

USB, bluetooth, I2C, 他 接続にて HID device と認識し、HID class device として &ogdefs(hid_allocate_device(),hid_allocate_device);, &ogdefs(hid_add_device(),hid_add_device); にてデバイスを登録すると、&ogdefs(hid_ignore(),hid_ignore); によって HID class device として扱うか判断されます。判断した結果エラーになることもあります。Linux Kernel の中では珍しい動作です。

#code(c,/int\s*hid_add_device/../^}$/,ogfileone:/drivers/hid/hid-core.c);

#textbox(note,hid_ignore_list に含まれているデバイスのドライバ){{
&ogdefs(hid_ignore(),hid_ignore); はいくつかの条件判定と除外リスト &ogdefs(hid_ignore_list); で構成されています。除外されたデバイスの一部は別のドライバで扱います。例えば &ogfileone(drivers/input/mouse/synaptics_usb.c);, &ogfileone(drivers/usb/misc/ldusb.c); は、&ogdefs(hid_ignore_list); に含まれるデバイスのドライバです。
}}

&ogdefs(hid_add_device(),hid_add_device); は "Transport Driver" の &ogdefs(struct hid_ll_driver,hid_ll_driver);->parse を呼び出し Report Descriptor の取得を行います。続けて &ogdefs(hid_have_special_driver); を &ogdefs(hid_match_id(),hid_match_id); に渡して "Generic Driver" と同じ動作で良ければ(すなわち、&ogdefs(hid_have_special_driver); の要素に無ければ) &ogdefs(hid_scan_report(),hid_scan_report); を呼び出して Report Descriptor をパースします。&ogdefs(device_add(),device_add); を呼び出し、&ogfileone(drivers/base/dd.c); の &ogdefs(device_attach(),device_attach); にてドライバと照合する処理を始めます。

#textbox(note, hid_have_special_driver の要素に格納したデバイスと hid_*.c の struct hid_driver.id_table 要素について){{
&ogdefs(hid_have_special_driver); の要素に格納してあるデバイスは "Custom Driver" の実装である &ogfile(/hid-.*/);(ドライバソースの正規表現です) の &ogrefs(struct hid_driver,hid_driver); の id_table メンバが指す配列の要素にも格納してあります。2 重に記述する必要が有り、保守や driver を module として組み込む場合に注意が必要です。
}}

#textbox(note,ドライバを後から組み込んだ場合について){{
&ogdefs(hid_register_driver(),hid_register_driver); にてデバイスが接続された後、ドライバを組み込んだ場合は、&ogdefs(bus_add_driver(),bus_add_driver);, &ogdefs(driver_attach(),driver_attach); を経てデバイスと照合する処理を始めます。
}}

デバイスとドライバの照合は &ogdefs(hid_bus_match(),hid_bus_match); で行います。適合したと判断したドライバがあれば、&ogdefs(hid_device_probe(), hid_device_probe); が呼ばれます。&ogdefs(hid_device_probe(), hid_device_probe); の実装は巧妙な点が多いです。probe が呼ばれた時点で &ogdefs(struct hid_device,hid_device); hdev の driver メンバが != NULL になっている場合があります。Hyper-V の mouse ドライバ (&ogfileone(drivers/hid/hid-hyperv.c);) の場合だけです。下のコード断片は &ogdefs(hid_device_probe(), hid_device_probe); です。

#code(c,/int\s*hid_device_probe/../^}$/,ogfileone:/drivers/hid/hid-core.c);

#textbox(thought,hid_match_device()をもう一度呼んでいる){{
&ogdefs(hid_match_device(),hid_match_device); をもう一度呼ぶ理由はなぜだろうか?もっともらしい理由が見つかっていないです。&ogfile(drivers/base/dd.c); の処理で &ogdefs(hid_match_device(),hid_match_device); を呼び出しドライバは適合していると確認済みです。強いて言うならば、&ogdefs(struct hid_driver,hid_driver); の id_table から適合デバイスを見つけ出す処理を "Custom Driver" それぞれで実装する負担を減らすためかと思っています。
}}

HID class driver &ogdefs(struct hid_driver, hid_driver); に probe 関数を指すメンバがあります。これは珍しいことです。しかも、実装は必須ではありません。probe 関数が有る場合は、既定動作の Report Descriptor の解釈(&ogdefs(hid_open_report(),hid_open_report);) と 接続処理(&ogdefs(hid_hw_start(),hid_hw_start);) の代わりに probe 関数を呼び出します。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS