SSブログ

HIDでHello World [USB]

P1010002.jpgUSBのWindows側のプログラミングにどっぷり嵌る前に、適当なHIDデバイスアプリケーションを試してみようと言う話。

オリジナルはnoritanさんの所で公開されているHIDアプリケーションをベースにやっています。

JB16のエンドポイント1には8byteのバッファが有りますが、キーボードデバイスとして使用した場合、先頭の1byteにはシフトキーやコントロールキーの状態が入ります。
bit7:右GUI、つまりプロパティキー
bit6:右Alt
bit5:右シフト
bit4:右コントロール
bit3:左GUI、つまりWindowsキー
bit2:左Alt
bit1:左シフト
bit0:左コントロール

またキーコードが入る領域は3byte目から8byte目までの6byteとなります。
キーコードは、Make/Breakを表現する為に、コードを転送した後は0を転送しています。これは、同じコードを送りつづけているとリピートとは判断してくれないからです。
例えばAの文字を2回送る時は、A→0→A→0と送る事となります。

プロジェクトファイルは、拡張子をZIPに変更してから解凍して下さい。

※しかしこの事が意味するのは、HIDデバイスって危険な香りがするという事かな。
※なお、キーリピートも含めてキーボードのエミュレーションは現在研究中ですので、内容が修正される事も有ります。
特にバッファの使い方に付いては、実際のキーボードのパケットを覗いてみたいなぁ。



※Low Speedのインタラプト転送の周期は10msではない!。
以下のキャプチャ画面が、INエンドポイントのところでポートをON/OFFさせた波形です。一回の割り込みで極性を変えるので、1周期は割り込み2回分です。
hid_in_timig_02.png
普通10msで語られる事の多い周期ですが、実際には2の階乗で取られる事が多い!という話を詳しく以下のコメントでしてもらいました。キャプチャ画面も半周期(割り込み1回分)が約8msとなっている事が判るかと思います。
※あっ!間違ってACレンジで撮っちゃった。
nice!(0)  コメント(17)  トラックバック(1) 

nice! 0

コメント 17

noritan

危険な香り…実施例

「USBに差し込むと自動的にブラウザを開いて自分のブログページを開く」自作HIDキーボード・デバイスをPCショップのデモ機に片っ端から差し込んで抜き去って行く…ような行為のことですか。

これ以上の危険な行為の実施例は自粛。

大き目の赤いボタンを押すと即座にロックスクリーンがかかる、「上司が来た!ボタン」も作ってみたい。すべてのウィンドウが閉じるのでもいいかな。

by noritan (2008-03-13 08:28) 

hamayan

できます、できます。
キーボードで出来ることは全てできてしまうでしょう。
しかもこれに付いてはセキュリティを掛け難い。

これ以上は自粛。

by hamayan (2008-03-13 09:21) 

noritan

私の想像では、HIDキーボードというのは、「現在押下されているキーを示す」だけの機能しか持っていないのではないかと思っています。それを「押されたままになっている」のか「複数回押された」のかを判断するのは、PC側の仕事であると理解しています。

その傍証となるのが、リピートまでの時間とリピート間隔が指定できる「コントロールパネル:キーボード:速度」です。これは、つまり、PC側でリピートの処理を行っているのだと考えます。

そうすると、無線キーボードで電波障害がおこると、勝手に複数回キー入力がされたりするのかな?

確実にキーボードからの入力を伝えたいのであれば、「五月雨式タイピング・エミュレーション」が必要になるのだろうと思います。

ぜひ、「オリゲー・フェスタ☆68」に、この手のおバカ・ガジェットを並べてみたい。

参考サイト
http://plusd.itmedia.co.jp/pcuser/obaka.html
ITmedia +D PC USER:おバカ

by noritan (2008-03-13 10:41) 

hamayan

> 五月雨式タイピング・エミュレーション

そうそう結構これが悩み所で、2回のインタラプトのタイミング(1回は0を挿入する為)で、一つずつ送ると、これが「はぁ?」ってな感じでゆっくりタイプされます。まあ私とどっこいなスピードですが。
とてもタイピングの達人とは言えない(笑)。

そこでバッファの6byteをフルに使う処理を書いてみたのですが、例えば途中に大文字があった場合、シフトbitを立ててしまうと、全部の文字が大文字になってしまいます。
つまり小文字の中に大文字がある場合、大文字のみ個別で送る必要がある。
小文字のaと大文字のAが同じキーコードって所が問題だな。

人の場合、CAPS LOCKを掛けていなければ、小文字でどんどん打っていて、たまに大文字に切り替える場合、シフトキーを押しながら文字入力、シフトキーを離す。と言ったアクションが入るので、必然的に大文字のみ単体で送られる結果になって、それはそれでよいのかも知れない。

ただマイコンでこれをやると、インタラプトの間隔にバッファをフルにする事は余裕なので、ちょっと工夫が必要になりますね。

それ以外にも、このインタフェースは基本的に人間が入力するものなので、各所でタイミングを取る必要が有りますね。

でもなんか変な物!(面白い物)は作れる予感がする。
by hamayan (2008-03-13 11:15) 

hamayan

> ITmedia +D PC USER:おバカ

グリーンハウス大丈夫か?。
by hamayan (2008-03-13 11:57) 

noritan

私のサンプル・プロジェクトを元にされているので、インタラプト入力のポーリング時間が10msecになっています。これをもっと短くすると、PCからの要求がサクサクと届くと思います。その代わりファームウェアの負担は増えます。

ただ、現行の10msec毎に1パケットの割合でも、目に見えて遅くなるとは思えないので、別のところで時間を喰っているのかもしれません。

ところで、バッファの複数バイトを使った場合、順序どおりにキー入力されたと認識されていますか?バッファに複数のコードを入れた場合、複数のキーを同時に押した状態と認識されるような気がするのですが。

by noritan (2008-03-13 12:42) 

hamayan

> バッファの複数バイトを使った場合、順序どおりにキー入力されたと
> 認識されていますか?

確かに、たしかに、これも確認事項ですね。
多分順番通りに読み取ってくれると思いますが、と言うかそれ以外のロジックが考えられないし、今度シリアルから適当なテキストファイルを送って、それをメモ帳に書き出すプログラムを作ってみようと思っています。
それでファイル比較してみれば、ある程度判ると思う。

インタラプトの周期は基本的に10msなので、それを考えれば遅くはないと思いますが、、、これも確認事項ですね。プログラム上で失敗しているのかもしれません。

by hamayan (2008-03-13 13:05) 

masato

Wikipedia の USB キーボードのスキャンコードの説明を読むと noritan さんの言われたように「バッファに複数のコードを入れた場合、複数のキーを同時に押した状態と認識される」ようです。
by masato (2008-03-13 14:07) 

hamayan

Wikipediaが書いているのは、「Aキーを押したままBキーを押すと」なので、
状態を書くと
最初 A
次 A+B
この様な入力が有った時はABとしろ!と言っているのだと思います。

今回のは最初から
A+B
なので、そうした時に順番はどう評価されるか?なのですが、
バッファの順番がA、Bになっているのに、多分コントローラもこの順番で受けているのに、わざわざABではなくBAと評価するとは思えないと思っております。
順番が守られないなら、バッファの意味無いし。

by hamayan (2008-03-13 14:53) 

Tsuneo

>特にバッファの使い方に付いては、実際のキーボードのパケットを覗いてみたいなぁ。
ハードウェアUSBバスアナライザーを使えば、USBバス上の通信を直接見ることができますが、ソフトウェアモニターでもそこそこ十分な観察ができます。Windows のアプリだと、
Free, open-source:
SnoopyPro http://sourceforge.net/projects/usbsnoop/

Commercial, 1-month eval:
HHD USB Monitor http://www.hhdsoftware.com/Products/home/usb-monitor.html
などなど

これらのソフトウェアモニターは、一旦エニュメレーションが成功すればその後の通常の通信を捕らえることができますが、エニュメレーションの過程そのものを見ることができません。例えば、HHD USB Monitorは、エニュメレーションが成功すればその過程を表示しますが、失敗すると何も出力しません。したがって、バグのあるエニュメレーションのデバッグには使えません。

高価なソフトウェアモニターなら、バグがあるエニュメレーションの過程も捕まえます。
Commercial, 1-month eval:
SourceUSB http://www.sourcequest.com/

BusHound (機能限定Free版あり)
http://www.perisoft.net/bushound/

この値段になると、Beagle などの廉価なハードウェアバスアナライザーとどちらが良いか悩ましいところですね。またEllisysなどの本格的なバスアナライザーも、フルスピード(12Mbps)用なら近い値段です。

Total Phase, Beagle USB 12 Protocol Analyzer
http://www.totalphase.com/products/beagle_usb12/

Ellisys, USB Tracker 110
http://www.ellisys.com/products/usbtr110/index.php

ハードウェアUSBバスアナライザーの利点は、エニュメレーションのデバッグ以外に、通信速度のチューニングに必要な情報が容易に取れる点が挙げられます。
- NAK が見えるので、速度低下がデバイス側の遅れによるのか、ホスト側なのかがわかる
- SOF が見えるので、USBフレーム内のパケットの数およびタイミングがわかる

なお、Linux な人には、ソフトウェアモニターとしてこんなのもあります。
WireShark for USB
http://wiki.wireshark.org/USB/

また、Mac な人には、
"Mac OSX USB Debug Kits Downloads" from developer.apple.com
http://developer.apple.com/hardwaredrivers/download/usbdebug.html


取っかかりは、ソフトウェアモニターで始めるのが良いでしょう。
今時のUSB MCU は大抵のUSBクラスのサンプルがあるので、一から自分でクラスのコードを書く必要はありません。エニュメレーションで失敗するとしても、デスクリプタの設定ミスなど限られた範囲のもので、調べるなり聞くなりですぐ判明します。通常の通信が見えれば十分でしょう。(もしくは、必要な時に評価版でしのぐ :-))
通信パフォーマンスのチューニングに目が行くようになったら、ハードウェアバスアナライザーを検討されると良いでしょう。

ソフトウェアモニターはお互いにコンフリクトする可能性があるので、比較のため複数のアプリをインストールしたいのなら、以前のものをアンインストールしてから別のアプリをインストールした方が良いようです。

Tsuneo
by Tsuneo (2008-03-13 15:51) 

hamayan

詳細なレポート有難うございます。
非常に参考になります。

by hamayan (2008-03-13 17:27) 

Tsuneo

>順番が守られないなら、バッファの意味無いし。

HIDキーボードのINレポートは通常6ヶのキーコードバッファをレポートデスクリプタで設定しますが、これは複数のキーの同時キーインを表現するためのものです。このバッファ上ではキーインの順番は考慮されません。このINレポートは、ある瞬間のユーザーによるキー押し下げ状態のスナップショットを表しているとお考え下さい。
- その瞬間に押し下げられているキーのキーコードを適当な順番でバッファにつめます(どんな順序でもOK)。5ヶ以下なら残りをno event indicated (0x00)で埋めます
- 全くキーが押されていないなら、no event indicated (0x00)で全部のバッファを埋めます。
- 7ヶ以上のキーが押し下げられている場合は、ErrorRollOver (0x01)で6ヶのキーコードバッファ全部を埋めます。

"Device Class Definition for HID 1.11" on USB.org
http://www.usb.org/developers/devclass_docs/HID1_11.pdf
Appendix C: Keyboard Implementation (HID1_11.pdf p62)

"HID Usage Tables 1.12" on USB.org
http://www.usb.org/developers/devclass_docs/Hut1_12.pdf
KEYBOARD/KEYPAD PAGE (0X07) (Hut1_12.pdf p53)

ちなみに、キーコードバッファをレポートデスクリプタで1ヶに指定しても大抵は問題なく動作します。が、6ヶがデファクトスタンダード(HID スペックの例示)なので、6ヶにしておくのが無難です。OSの実装でデバイスドライバーが6ヶの場合しかデバッグしていない、ということは大いにありえます。


では、デバイス側ではどういうタイミングでこのINレポートを作成し、送出するか、という点ですが、これが結構考慮すべき要素が多くてきちんと実装するのは悩ましい。(デバイス側が適当にやってもOSのデバイスドライバーがなんとか動くので普通はそれで良しとされています。)

1) Set_Idle リクエスト
HID デバイスのレポート送出間隔は、INエンドポイントへのトランザクション間隔とは独立に設定されます。つまり、たとえINエンドポイントにホストからトランザクションが来ても、定められたレポート送出間隔に満たなければ(厳密にスペックに従えば)NAKで答えることになります。
HID のスペックはキーボードでは500 msのレポート送出間隔がデフォルトとして推奨されていますが、この数字はどうかと思いますね。実際の送出間隔は、Set_Idle リクエストでOSが設定します。Windowsはエニュメレーションの直後にSet_Idle リクエストを送り、レポート送出間隔infinity に設定します。これは、「キー入力状態に変化があったときにのみレポートを送出する」という意味です。つまり、何らかのキーの押下げあるいは解放でレポートを送出することになります。
なお、MacOS X のキーボードではSet_Idle リクエストで 36 msに設定されます。

7.2.4 Set_Idle Request (HID1_11.pdf p52)


2) bInterval フィールド (エンドポイント デスクリプタ)
INエンドポイントへのトランザクション間隔は、エンドポイント デスクリプタのbIntervalフィールドによってホストに通知されます。最小値(最高速)は、FS(Full-Speed, 12Mbps) では1ms、LS (Low-Speed, 1.5Mbps)では10msです。
ここで問題なのは、たとえ10msにbIntervalを設定しても、大抵の場合実際のトランザクション間隔は 8ms になることが多いという点でしょう。USBのスペックは実際のトランザクション間隔が厳密にbIntervalに設定した間隔になることは保証していません。設定値以下であれば良しとされています。そして、実際のトランザクション間隔はホストコントローラとそのドライバによりけりです。大抵のホスト側の実装は、FS,LSのインターラプトトランスファのトランザクション間隔は、2のn乗(1,2,4,8,16...)msを選びます。つまり、bIntervalでデバイスが指定しても、きっかり10msで実際のトランザクションがくることは保証できないということです。

実際のトランザクションのタイミングは、
- タイマーによるINエンドポイントのエンプティフラグの定期的な監視(ポリング)
- あるいは、INエンドポイントからのインターラプト
によって知る、といった形で実装することが多いですね。SOFインターラプト(1ms間隔)があるMCUでは、このハンドラーの中でINエンドポイントをチェックするのも良くある実装です。NAKインターラプトもありますが、良い実装ではないでしょう(汎用性が無い)。


Windowsでは、レポート送出間隔infinityなので、「変化があったときに送出する」のが原則ですが、変化があったときにすぐさまレポートを送出できるわけではありません。ホストからINトランザクションがまだ来て無くて、以前のレポートがバッファ(FIFO)上に残っていれば、新たなレポートをバッファに書込め無いわけです。
そこで、潔く
- エンドポイントバッファが空ならキースキャンをする(空でなければキースキャンしない)
- 前回のスキャンと比べて変化があれば、レポートを書き込む
という処理をタイマー(あるいはSOFインターラプト)で叩くという実装になります。

ここで、INエンドポイントからのインターラプトをキースキャンイベントのトリガーとして使った場合、デッドロックになる(キーに変化が無いと何も出さない ー 次のインターラプトが発生しない)ので、この方法は使用しないでください。

英文ですが、ここでHID キーボードの実装のまとめを投稿しました。
"USB HID KEYBOARD" on SiLabs Forum
http://www.cygnal.org/ubb/Forum9/HTML/001381.html

Tsuneo
by Tsuneo (2008-03-13 19:49) 

noritan

大変深いお話、ありがとうございます。

私が使っているサンプル・プログラムを見直してみました。

GET_REPORT
HIDの仕様書には、このリクエストの実装は必須となっているのですが、私が使っているサンプル・プログラムには、GET_REPORTの処理がありません。処理されているクラス・リクエストは、SET_REPORTだけです。何で、これで動いているんだろう?
確か、このサンプルは、フォーラムで流れていたものをmasatoさんに教えてもらって、持ってきたものです。変更した覚えがないので、最初から無かったのだと思います。
もう一つのサンプル、CMXのHIDスタックでは、GET_REPORTもSET_IDLEも処理が書かれていました。こちらは、記述量が圧倒的に多くなっています。

もうちょっと、真面目に実装してみますか。
そのためには、何が書いてあるか理解せねば。

by noritan (2008-03-13 22:18) 

DAI

Tsuneoさん、こんばんは。
もうお忘れでしょうが、C8051コメツブマイコンいじっていた頃お世話になりました。
現在は、Freescale社の68系に鞍替えしております。
by DAI (2008-03-13 22:23) 

DAI

hamayanさん、 noritanさん、思わず投稿時間見てしまいました。
私の方はその後あまり進んでいません。
BCBの使い方習得中です。最近はBCBの解説本が無いに等しいですね。

で、現在私がいじくってるThesyconのドライバに付いてくるUSBIOLIBのソース群ですが、BCBでコンパイルして、USBIOLIB.LIBと USBIOLIB.DLLを作ることができました。
これをVC++から使うことができたら、と言う野望です。
使えたら、BCBを持っていない人でもあのでかいフリーのVC++2008を落として来さえすればThesyconドライバが使えるのに、ということです。
私のところは、HDDの容量の関係でVC++2008をインストールするのはためらってます。
しかも、BCBで作ったDLLをVC++で使うことを解説したページが見つからない。
どなたか試される方がいらっしゃるかも知れませんので、私のHPにおいときましょうか。
by DAI (2008-03-13 22:47) 

hamayan

Tsuneoさん有難うございます。
早速10ms周期が実は8msである事を試してみました。

by hamayan (2008-03-13 22:52) 

Tsuneo

ああ、またやってしまった。突然乱入してすいません。ちゃんとご挨拶もしてませんでした。
DAIさん、こんばんは。いえいえ、決して忘れていません。

>GET_REPORT

WindowsはHIDマウス、キーボードの実装で IN エンドポイントがインターフェースにあれば、GET_REPORT( Input )リクエストは出しません。MacOS X もそうです。LinuxはGET_REPORT( Input )を出して、ストールすると文句を言ったはず?、ちょっとあやふやです。
HIDのGET_REPORT( Input )に関しては、USB スペック通りに実装しておけばどんなOSでも確実です。
ただ、他の件についてはWindowsがあちこちでスペックからはずれているので、そうともと言い切れないのがつらいところです。

Vendor specific HID のエニュメレーションにおけるOSよる相違について、ここに投稿しました。
"Difference on HID enumeration on Win, Linux and Mac" on SiLbas Forum
http://www.cygnal.org/ubb/Forum9/HTML/001325.html


>BCB

HIDのホストアプリ用のライブラリはRobert Marquardtによるこのライブラリが定評があります。
Delphi で記述されていますが、もちろんBCBで使えます。

"Human Interface Device controller suite" by Robert Marquardt
http://www.soft-gems.net/index.php?option=com_content&task=view&id=14&Itemid=33


>早速10ms周期が実は8msである事を試してみました。

インターラプトトランスファのトランザクション間隔は、ホストコントローラの仕様の一つであるOHCIでは、はっきりと最も近い2のn乗(ms)にすると規定しています(the closest power of 2)。UHCIおよびEHCIではOHCIほど明確な規定はありませんが、これに準じて実装するのが普通です。

"OpenHCI -- Open Host Controller Interface Specification for USB" on HP.com
http://h18000.www1.hp.com/productinfo/development/openhci.html

OpenHCI
ftp://ftp.compaq.com/pub/supportinformation/papers/hcir1_0a.pdf
Figure 5-5: Runtime Structure of Interrupt Lists

5.2.7.2.1 Polling Rate (p64)
"Interrupt Endpoint Descriptors have a minimum rate for which they need to be scheduled. When this information is provided to Host Controller Driver, it determines the closest power of 2 rate below the endpoints requirement and determines which scheduling queue for that rate has the smallest committed bandwidth."

Tsuneo
by Tsuneo (2008-03-14 00:40) 

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

※ブログオーナーが承認したコメントのみ表示されます。

トラックバック 1

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。