USBプロジェクト - HIDデバイス(5) [USB]
複合USBデバイスが、できちゃいました。 まだ、何も仕事はしてくれません。
デスクリプタを整備する
要は、複合デバイスに見えるデスクリプタを記述して、ホストの要求に応じて適切なデスクリプタを返すだけでおしまいです。 記述したコンフィグレーション・デスクリプタは、こんな構成になっています。
- コンフィグレーション・デスクリプタ
- 自前インターフェース・デスクリプタ (#0)
- 自前HIDデスクリプタ
- 自前エンドポイント・デスクリプタ (IN-EP2)
- 自前エンドポイント・デスクリプタ (OUT-EP2)
- 自前インターフェース・デスクリプタ (#0)
- キーボードHIDデスクリプタ
- キーボードエンドポイント・デスクリプタ (IN-EP1)
そして、GET_DESCRIPTORリクエストの処理部分でwIndexの値により、該当するインターフェースのレポート・デスクリプタを返すようにswitch文を追加しました。
キーボードの実装は、無い
キーボードとして動作する部分の実装は、一切行っていません。 TX1Eさえ、アサートしていません。 そのため、トランザクションが到着しても、ひたすらNAKが返っているはずです。 特に文句も言われていないから、こんなもんでいいんだろう。
ついでに、キーボードのレポート・デスクリプタから、"OUTPUT"部分を取り除いてしまいました。 そもそも、SET_REPORTリクエストへの応答処理も書いていません。 果たして、SET_REPORTリクエストが来なかったのか、それともDATAステージのSTALL応答で十分なのか、深くは追求していません。
PCが認識したデバイス
PCがどのようにこのデバイスを認識したのかは、デバイスマネージャで見るとわかります。
このように、「複合デバイス」という仮想デバイスの下に「ヒューマン・インターフェース・デバイス」がぶら下がり、それらが「HID自前デバイス」と「HIDキーボード・デバイス」であることがわかります。 何だ、こんなに簡単な事だったんだ。
HIDD_ATTRIBUTESには、インターフェースが無い
これを、VisualBASICのプログラムで操作すると、LCDに表示が現れます。 でも、このプログラムでは、インターフェース番号を指定している所がありません。 VendorIDとProductIDは、HIDD_ATTRIBUTES構造体に入っていますが、インターフェース番号は入っていません。 何か、他に指定方法があるのでしょうか?
プロジェクト・アーカイブ
いつものように、HID08.zipという名前で保存してお使いください。
付録 : USBプロジェクト索引
- USBプロジェクト - ファームウェアに立ち返る (1)
- USBプロジェクト - ファームウェアに立ち返る (2)
- USBプロジェクト - ファームウェアに立ち返る (3)
- USBプロジェクト - ファームウェアに立ち返る (4)
- USBプロジェクト - ファームウェアに立ち返る (5)
- USBプロジェクト - ファームウェアに立ち返る (6)
- USBプロジェクト - ファームウェアに立ち返る (7)
- USBプロジェクト - ファームウェアに立ち返る (8)
- USBプロジェクト - ファームウェアに立ち返る (9)
- USBプロジェクト - ファームウェアに立ち返る (10)
- USBプロジェクト - ファームウェアに立ち返る (11)
- USBプロジェクト - ファームウェアに立ち返る (12)
- USBプロジェクト - ファームウェアに立ち返る (13)
- USBプロジェクト - HIDデバイス(1)
- USBプロジェクト - HIDデバイス(2)
- USBプロジェクト - HIDデバイス(3)
- USBプロジェクト - 複合デバイスを考えた
- USBプロジェクト - HIDデバイス(4)
- USBプロジェクト - HIDデバイス(5)
参考文献
USBハード&ソフト開発のすべて―USBコントローラの使い方からWindows/Linuxドライバの作成まで (TECHI―Bus Interface)
- 作者: インターフェース編集部
- 出版社/メーカー: CQ出版
- 発売日: 2006/07
- メディア: 単行本
USBターゲット機器開発のすべて―各種USBコントローラの使い方と基本ソフトウェアの作成法 (TECHI―Bus Interface)
- 作者:
- 出版社/メーカー: CQ出版
- 発売日: 2005/08
- メディア: 単行本
>何だ、こんなに簡単な事だったんだ。
いやいや、簡単に思えるのはこれまでの精進の成果です。
USBの実装の変更はそんなに大がかりなものではありません。しかし、どこをどういじれば良いかが自信を持って判るまでが大変なんです。
>でも、このプログラムでは、インターフェース番号を指定している所がありません。 VendorIDとProductIDは、HIDD_ATTRIBUTES構造体に入っていますが、インターフェース番号は入っていません。 何か、他に指定方法があるのでしょうか?
複合デバイスは、ホストアプリ側から見ると独立した複数のデバイスであるかの様に見えます。例えば、HIDとマスストレージの複合デバイスは、HIDなデバイスとマスストレージなデバイスがそれぞれに接続しているように見えます。実際、複合デバイスを構成する各インターフェースに一対一対応で、デバイスドライバのインスタンスが作成されます。これが複合デバイスのうまみですね。つまり、専用のデバイスドライバを開発しないでも、既存のデバイスドライバの組合わせで新たなドライバができてしまうのです。
もちろん元は一つのデバイスですから、完全に独立しているわけではありません。どの部分が独立していてどの部分が共通しているかは、ファームウェアでのデスクリプタの構造やリクエストの処理を見ていけば自ずと明らかになるでしょう。
デスクリプタ上では、
1)各インターフェースは完全に独立しています。
つまり、インターフェースを指定して送られてくるクラスリクエストやエンドポイントを通じた通信は完全に独立している、ということです。
2)デバイス・デスクリプタとコンフィギュレーション・デスクリプタは共通です。
デバイス自体に送られる標準デバイスリクエストは共通です。また、バスリセットやサスペンドなどのイベントも共通です。
さて、このHID x 2の複合デバイスでは、ベンダ仕様なHIDデバイスとキーボードなHIDデバイスが、それぞれに接続しているように見えています。キーボードはFeatureレポートしか直接アクセスすることは無いのですが、あえてホストアプリで捕まえることにしましょう。
ホストアプリでデバイスを捕まえるには、SetupDi-API を駆使します。
まず、HidD_GetHidGuid()で HIDな GUIDを持ってきて、SetupDiGetClassDevs()に喰わせます。すると接続しているHIDなデバイスを全てリストアップしてくれます。問題のベンダ仕様なHIDデバイスとキーボードなHIDデバイスは、それぞれ別のデバイスとしてこのリストにリストアップされているのです。
ループを回してSetupDiEnumDeviceInterfaces()で一つずつリストから取出して、SetupDiGetDeviceInterfaceDetail()でデバイスパスを拾い、さらにCreateFile()でちょっとだけ開けてHidD_GetAttributes()でベンダーID,プロダクトID(VID/PID)を取出します。このVID/PIDが目的のものと一致するば当たりです。この辺までは定型的なコードですね。
問題の2つのHIDデバイスはどちらも同じVID/PIDを持っています(デバイス・デスクリプタは共通)。なので、通常は一つ見つかったらループを抜けるのですが、さらにループをリストの最後まで回して該当するデバイスを全部見つけ出すのです。こうして2つのHIDデバイスを取出すことができます。
では、この2つをどうやって区別するのか。
実はデバイスパスの一部としてインターフェース番号が含まれています。
USB\VID_vvvv&PID_pppp&MI_mm
vvvv: VID、HEXで4桁
pppp: PID、HEXで4桁
mm: インターフェース番号、HEXで2桁
このインターフェース番号を抽出して、区別します。
デバイスパスは、msinfo32.exe(Windowsの組込みコマンド)で見ることができます。
msinfo32 については、例えば、この記事などを見て下さい。
HIDデバイスは、コンポーネント > 入力 の下に居ます。
”トラブル対策のためのコンピュータ情報を効率よく収集する”
http://www.atmarkit.co.jp/fwin2k/win2ktips/787msinfo32/msinfo32.html
Tsuneo
by Tsuneo (2008-04-30 22:54)
Tsuneoさん、いつもありがとうございます。
デバイスのリストを取り出すところまでは、わかるのですが、お目当てのデバイスを選択する部分がどうしてもわかりませんでした。デバイスパスを解析する必要があるんですか?何だか、美しくないな。
オペレーティングシステムは、デバイスのインターフェイス番号を当然知っているはずだから、属性を取り出す方法がわかれば解決と考えていました。デバイスパスの解析は最後の手段かと思っていました。
他の方法として考えたのは、二つのインターフェースに別々の名前を付けてインターフェイス・デスクリプタのiInterfaceにそれぞれ入れておく方法です。こうすると、インターフェイス番号にかかわらず対応することが出来ます。 msdn によると、 iInterface にゼロ以外の値を入れておくと、HidD_GetProductString 関数で名前を取り出す事ができるらしい。試してみるか。
by noritan (2008-05-02 08:59)
HidD_GetProductString は見落としてました。使えそうですね。
というか、以前のWinDDKの解説では、
The HidD_GetProductString routine returns the embedded string of a top-level collection that identifies the manufacturer's product.
としか書いてなかったので、確かめもせずデバイス・デスクリプタが指定するproduct stringを返すものと思っていました。今のMSDNのは随分詳しく改訂されていますね。これはもう一度WDKのUSB周りを端から端まで読み直せってことですか...
USB-IFで同様の回答をしましたが誰も何も言わなかったので、まだあまり知られていないのでしょう。
http://www.usb.org/phpbb/viewtopic.php?t=13799
Tsuneo
by Tsuneo (2008-05-03 15:47)