USB プロジェクト - ReportDescriptorの解析 [USB]
HIDのフリをするプログラムが出来たので、何が書いてあるのか解析します。 (普通は、逆だな。)
参考文献
- Device Class Definition for Human Interface Devices (HID) Firmware Specification—6/27/01
- HID Usage Tables 10/28/2004 Version 1.12
ReportDescriptorを読む
ファームウェアには、HIDに特有のReportDescriptorと呼ばれるデータが入っていて、 ホストの要求に応じてデータを返します。 このデータは、PCとの送受信に使われるパケットのフォーマットを規定していて、 賢いホストさまが、構文解析を行ってデバイスごとに適切なパケットを送受信してくれるという ありがたい仕組みになっています。 サンプルプログラムのこの部分には、一切コメントが無かったので、 勉強がてら解析することにしました。
05 01 - Global Usage Page = $01 (Generic Desktop) 09 06 - Local Usage = $06 (Keyboard)
Usageは、このデバイスの用途を指定します。 ここでは、Keyboardが指定されています。 Usageは、32ビットのデータで、Usage Pageは、その上位16ビットを指します。 と、仕様書には書いてあるのだけど、これは8ビットデータだよな。 まあ、いいか。
A1 01 - Main Collection = $01 (Application)
ここから、"End Collection"までの部分でパケットのフォーマットを指定します。 実際に送受信されるパケットの構造は、HIDの仕様書に書いてあります。
05 07 - Global Usage Page = $07 (Keyboard/Keypad Page) 19 E0 - Local Usage Minimum = $E0 (Left Control) 29 E7 - Local Usage Maximum = $E7 (Right GUI) 15 00 - Global Logical Minimum = $00 (0) 25 01 - Global Logical Maximum = $01 (1) 75 01 - Global Report Size = $01 (1 bit) 95 08 - Global Report Count = $08 (8 fields) 81 02 - Main Input = $02 (no NULL position; preferred state; linear; no wrap; absolute; variable; data)
最初の部分は、8ビットの送信データです。 Hutで定義されている8個のキーの状態をPCに知らせます。
ビット位置 | Usageコード | 対応するキー |
---|---|---|
0 | $E0 | 左のCtrl |
1 | $E1 | 左のShift |
2 | $E2 | 左のAlt |
3 | $E3 | 左のGUI(PC/ATではWindowsキー) |
4 | $E4 | 右のCtrl |
5 | $E5 | 右のShift |
6 | $E6 | 右のAlt |
7 | $E7 | 右のGUI(PC/ATではメニューキー) |
このファーム・ウェアでは、 それぞれのキーの状態をKbBuffer[0]に入れると、 それをPCが引き取ってしかるべき動作を行ってくれます。
95 01 - Global Report Count = $01 (1 field) 75 08 - Global Report Size = $08 (8 bits) 81 01 - Main Input = $01 (no NULL position; preferred state; linear; no wrap; absolute; array; constant)
この部分は、KbBuffer[1]に相当します。 8ビットの定数データが入ってくるだけで、PCでは何も処理されないはずです。
95 05 - Global Report Count = $05 (5 fields) 75 01 - Global Report Size = $01 (1 bit) 05 08 - Global Usage Page = $08 (LED) 19 01 - Local Usage Minimum = $01 (Num Lock) 29 05 - Local Usage Maximum = $05 (Kana) 91 02 - Main Output = $02 (non volatile; no NULL position; preferred state; linear; no wrap; absolute; variable; data)
ここから、唐突にPCから送られてくるデータの定義が始まります。 この部分は、LedBufferの下位5ビットに相当するデータです。 ファーム・ウェアがLedBufferの内容に従って、「何か」を行います。 それぞれのビットは、以下のように割り当てられます。
ビット位置 | Usageコード | 対応するLED |
---|---|---|
0 | $01 | Num Lock |
1 | $02 | Caps Lock |
2 | $03 | Scroll Lock |
3 | $04 | Compose |
4 | $05 | Kana |
ComposeとKanaは、 最近のキーボードには見当たりませんね。 LEDには、"Spinning"や"Message Waiting"なんていうものもあるので、 「ディスク回ってます」「メールが来たよ」インジケータがすぐに出来てしまうかも知れません。
95 01 - Global Report Count = $01 (1 field) 75 03 - Global Report Size = $03 (3 bits) 91 01 - Main Output = $01 (non volatile; no NULL position; preferred state; linear; no wrap; absolute; array; constant)
次の部分で、残りの上位3ビットを定義しています。 この部分には、定数データ(0)が入ってくるはずです。
95 06 - Global Report Count = $06 (6 fields) 75 08 - Global Report Size = $08 (8 bits) 15 00 - Global Logical Minimum = $00 (0) 25 65 - Global Logical Maximum = $65 (101) 05 07 - Global Usage Page = $07 (Keyboard Keypad Page) 19 00 - Local Usage Minimum = $00 (no event) 29 65 - Local Usage Maximum = $65 (Application) 81 00 - Main Input = $00 (no NULL position; preferred state; linear; no wrap; absolute; array; constant)
再び、PCに送るデータの宣言が入っています。 ここは、KbBuffer[2]からKbBuffer[7]に相当する部分で、それぞれにUsageコードが入ります。 つまり同時に6個のキー入力を伝えることが出来るというわけです。 入れることのできるコードは、102種類で、101タイプ・キーボードのすべてのキーに対応しています。 どのキーも押されていなければ、$00(no event)を入れます。
C0 - Main End Collection
データ(Collection)の終端をあらわす印で、デスクリプタは、完結します。
HIDデバイスに関する情報はこちら
HIDに関する資料などは、このサイトにあります。
ここには、"HID Descriptor Tool"なんてものもあって、 16進数と格闘することなくデスクリプタを記述することの出来るツールなどがそろっています。 なんだ、これをそのままコピーしたのか。 納得です。
char ReportDescriptor[63] = { 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x08, // REPORT_SIZE (8) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x01, // REPORT_SIZE (1) 0x05, 0x08, // USAGE_PAGE (LEDs) 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 0x29, 0x05, // USAGE_MAXIMUM (Kana) 0x91, 0x02, // OUTPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x03, // REPORT_SIZE (3) 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) 0x95, 0x06, // REPORT_COUNT (6) 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x65, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0 // END_COLLECTION };
デスクリプタはここから来たらしい
HIDの仕様書の最後には、サンプル・デスクリプタが記載されています。 今回調べたファームウェアは、キーボードのサンプル・デスクリプタと一字一句同じでした。
2008-02-06 22:13:05 追記
他のUsageでLEDを操作できるか?
Message Waitingを使おうとしたのだけれど、失敗しました。 Telephonyに入っているところをみると、 どうやら「留守番電話にメッセージあり」という意味だったようです。
Spinningも使えませんでした。 こちらは、Mediaに入っているので、 「Mediaデバイス」のフリをさせないと情報が来ないのだろうと思います。
Muteもダメ。 これも、Consumerデバイスに分類されているので、 それなりのデバイスのフリをさせなくてはならないようです。
だったら、Keyboardに分類されている Shiftならいけるだろうと考えましたが、 そもそも、Shiftインジケータがどんな条件で点灯するのかがわからず、 操作できませんでした。
と、4連発で失敗してしまいました。 HIDインターフェースでLEDを光らせるのは、難しいようなので、 次は、マウス・インターフェースにでも挑んでみます。
コメント 0