SSブログ

USBプロジェクト - WindowsでHIDデバイスを扱う定石 (2) [USB]このエントリーを含むはてなブックマーク#

1469810

昨日の続きです。 HIDクラス・デバイスを表す「パス名」のリストからスタートです。

STEP4 : 後片付けを忘れずに

前回、入手した「鍵のリスト」は、「パス名のリスト」が手に入った今となっては不要です。 不要になったからといって、放っておくと、メモリがどんどん消費されてしまいます。 不要になったら、きちんと捨てる。 これが、Winodwsの流儀です。

「鍵のリスト」を廃棄するための関数は、 SetupDiDestroyDeviceInfoList()です。

ここまでの手順は、 DeviceManagement.FindDeviceFromGuidにあります。 何も考えずに使わせてもらっても動きます。

STEP5 : お目当てのデバイスを探せ

「パス名のリスト」が手に入ったので、ここからお目当てのデバイスを探します。 探し出すためには、「パス名」を元にファイルを開き、そのファイルの属性(Attribute)を調べます。

ファイルを開く関数は、一般のファイルと同じCreateFile()です。 宣言は、"FileIODeclaration.vb"に記述されています。

ファイルの属性を取り出す関数は、HidD_GetAttributes()です。 「お目当てのデバイス」を見つけるための手がかりとして使っているのが、 ベンダID (VenderID) とプロダクトID (ProductID) です。 この二つのIDが一致したときに、デバイスが見つかったと判断します。

属性を調べ終わったら、ファイルを閉じておきます。 ファイルを閉じる時に使う関数は、CloseHandle()です。

STEP6 : デバイス取り外し時のイベントを受け付けろ

USBデバイスは、動作中に抜き差ししても良いことになっているので、 抜かれた時に発生するイベントを受信する必要があります。 イベントを受信するオブジェクトを指定する関数は、 RegisterDeviceNotification()です。

この関数の呼び出しには、いくつ物手順を踏む必要があります。 お手本プログラムの"hidclass_vs5"の場合は、 "DeviceManagement.RegisterForDeviceNOtifications()"というメソッドにすべて記述されていますので、 そのまま呼び出すだけで十分です。 イベントが発生したら、フォームのWndProc()というメソッドが呼び出されます。

STEP7 : パイプを開け

HIDデバイスとレポートを送受信するために使用されるのは、 WriteFileReadFileという関数です。 そうです。普通のファイルの読み書きと何ら変わらないのです。

そのため、送受信に使用されるパイプを開くときも 普通のファイルと同様にCreateFile関数が使用されます。

STEP8 : データを送信する

データを送信する場合には、WriteFile関数を使用します。 お手本の"hidclass_vs5"では、 送信すべきレポートがOutputReportクラスとして定義されているので、 インスタンスを作成し、Writeメソッドを呼び出したらおしまいです。

STEP9 : データを受信する

データを受信する場合、ReadFile関数を使用します。 受信の場合には、送信の場合と異なり、データの受信が終わるまで次の処理に進むことが出来ないのが常です。 それまで待っているいるのも無駄なので、「データが受信できたら呼んでね。」という意味の コールバックと呼ばれる関数を定義し、InputReportクラスの Readメソッドを呼び出します。

これら送受信にかかる一連の操作は、 ExchangeInputAndOutputReports()に記述されています。 全部理解しなくても十分に使えています。


こうやって、まとめを書いてみると、レポートの送受信のところの理解がまだ甘い事に気が付きます。 特にDelegateが出てくると、とたんにややこしくなります。 まだ、修行が足りないな。

参考サイト

HIDClass Devices
Micro$oftの解説書は、ここにあります。 日本語版もあるのですが、迷訳なので、英語版の方がおすすめです。
http://i-wanna-metamorphose.blogspot.com/2008/02/so-net-blogusb-reportdescri.html
ここにもがんばっている方がいらっしゃいました。
改訂版 プロフェッショナルVB.NETプログラミング
.NET版のVisual BASIC の使い方が解説されています。読めば読むほど「Javaみたい」だと思うのですよね。

nice!(0)  コメント(2)  トラックバック(0)  このエントリーを含むはてなブックマーク#

nice! 0

コメント 2

Tsuneo

>受信の場合には、送信の場合と異なり、データの受信が終わるまで次の処理に進むことが出来ないのが常です。

送信も受信と同じくデータの送信が終わるまで次の処理に進めません(同期呼び出しの場合)。

送信にかかる時間は、
a) 通常のオーバーヘッド
HIDで使用されるインターラプト転送では、エンドポイント・デスクリプタの bInterval で指定される(実際は最も近い2のn乗 ms)時間間隔で通信がスケジュールされます。WriteFile()による送信はこのタイミングが来るまで待たされます。送信終了後、WriteFile() は TRUE を返します。

b) NAK による再送
ホストが送信した時点でデバイスのエンドポイントバッファが空でないと、デバイスのUSBエンジンは送信されたデータを破棄し、NAKを返します。NAKが返ってくる間は、ホストコントローラはbInterval間隔で同じデータを再送し続けます。ファームウェアがエンドポイントバッファを空にすると、送信データがデバイスに受け取られ、送信が終了します。送信終了後、WriteFile() は TRUE を返します。

c) エラーによる再送
USBでは、通信のエラー検出、再送がハードウェアでサポートされています。このエラーによる再送も、bInterval間隔で行われます。初回の送信を含めて3回の再送までに送信が成功すれば、送信終了後 WriteFile() は TRUE を返します。3回失敗すると、WriteFile() は FALSE を返し、GetLastError() は ERROR_CRC を返します。


NAK による再送は、USBにおけるフローコントロールです。RS232でのハンドシェイク(RTS-CTS、DSR-DTR)に相当します。つまりデバイスは、
ー OUTエンドポイントでは、エンドポイントバッファを読み出さないでデータを置いておく
ー INエンドポイントでは、エンドポイントバッファを空にしておく
ことで、ホストとの通信を待たせることができます。
この場合、ホストアプリがWriteFile()やReadFile()をメインスレッドで同期呼び出ししていると、ホストアプリ自体がハングします。そこで非同期呼び出しやサブスレッドが必要になります。


もう1つの特徴は、USBはハードウェアで通信のエラー検出、再送をサポートしている点です。このため、RS232の通信で良く見るコマンドーリプライによる通信の確認は必要ありません。ホストアプリでWriteFile()が成功すれば、データは確実にデバイスに届いています。

Tsuneo
by Tsuneo (2008-03-14 18:07) 

noritan

そうですね、 WriteFile も ReadFile もブロックされるから、何もできなくなりますよね。勘がにぶっているな。

もし、OutputReport.Write と InputReport.Read が、別のスレッドで WriteFile と ReadFile を実行するように実装されていたら、送受信の間に他の処理が出来ると考えていました。どう実装されているかを確認してみます。

> WriteFile()が成功すれば、データは確実にデバイスに届いています。

データを確実に送るために、シーケンス番号つきのプロトコルにしようか、なんて考えていたのですが、そんな必要はないのですね。納得です。

by noritan (2008-03-14 18:46) 

コメントを書く

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

トラックバック 0

トラックバックの受付は締め切りました