2007年12月20日

libusb と HID

Windows 流にいえば COM ポート (Linux なら tty )で通信できる CDC は便利ではある。でも、AVRの コードが大きくなったり、約束ごとを決めないと、その装置がどんな装置か判別できないというデメリットもある。また、汎用インターフェイスなので遅い .. かも知れない。

USB ライターでいうと、AVR-DoperUSBasp あと USBtinyはみな libusb や (HID インターフェイス)でアクセスするので、デバイスの種類は指定しても、(COMポートの番号など)デバイスそのものを指定しなくとも使える。

こういう ことができるのが、USBのメリットだが、CDC を使いつつ、どんな装置か判別することはできないだろうか?

というわけで、まずは、AVR-Doper や USBtiny そして USBasp がどんな風に使うのか調べてみた。これらは avrdude でサポートされている。avrdude のコードを見ればだいたいのことは分かる。

libusb の open/close

接続されているすべてのデバイスをスキャンして、VENDOR_ID と PRODUCT_ID が一致するものを選ぶのが普通のやり方らしい。
CLASS とかで判断する方法もあるらしいが、汎用デバイスを作るわけではないので、知る必要もないだろう。


usb_init();
usb_find_busses();
usb_find_devices();
for(bus=usb_get_busses(); bus; bus=bus->next){
for(dev=bus->devices; dev; dev=dev->next){
if(dev->descriptor.idVendor == VENDOR_ID
&& dev->descriptor.idProduct == PRODUCT_ID){
handle = usb_open(dev);
       break;
}
}
}


open したあとさらにオマジナイが結構必要。列挙すると、

usb_set_configration(handle,...);
usb_claim_interface(handle,...);
-- 失敗したとき usb_detach_kernel_driver_np() などで
    ドライバを解放する。


close もそう単純ではない。

usb_release_interface(handle,...);
usb_reaet(usb_handle);
usb_close(usb_handle);


usb_release_interface() はともかく、usb_reset() までしなければならないのか? なぜそうするかよく分からない。

libusb での制御方法

コントロールエンドポイント に対して、ベンダー依存のリクエストを usb_control_msg() で送って制御する方法がある。これは、USBtiny や USBasp のやりかた。ファームウェアが簡単なのがメリット。

そして、コントロールエンドポイント に対してusb_control_msg() を使って制御するのは同じだが、HID クラスを定義し、HID の GET_REPORT / SET_REPORT で制御する方法がある。これは、AVR-Doper が使っているやりかた。この範囲で使うならば、WIN32API を使っても制御できるのがメリットだが、HID の定義が面倒そう。

さらに、usb_bulk_read()/usb_bulk_write() を使って バルクエンドポイントに対して送受信することで制御する方法もある。avrdude の jtagmkII(含 AVRDRAGON) や stk500v2 の制御をみると .. COM ポート以外に、libusb を使って制御する方法を提供している。

ちなみに、usb_control_msg() は、ほとんど SETUP パケットを自分で組み立てるイメージ。GET_DESCRIPTOR とかを直接 CALL したりできるかわりに、SETUP の通信について知識が必要。

WIN32API での制御方法

AVR-Doper での open の方法は次のようになっている。


HidD_GetHidGuid();
SetupDiGetClassDevs();
for(i=0;; i++) {
SetupDiEnumDeviceInterfaces();
SetupDiGetDeviceInterfaceDetail();
CreateFile();
HidD_GetAttributes();
if (deviceAttributes.VenderID == VENDOR_ID
&& deviceAttributes.ProductID == PRODUCT_ID) {
break;
}
}


関数の機能も名前も違うが、libusb の open の仕方と良く似ている。

デバイスとの送受信は、HidD_SetFeature()/HidD_GetFeature() を使用する。libusb 版では、usb_control_msg()をつかって同じ機能を実現している。

さらに説明しておくと、AVR-Doper は HidD_SetFeature()/HidD_GetFeature() を 使って COM ポートのような ストリーム(というか擬似シリアルデバイス?)を実装している。その上で 上位の STK500V2 プロトコルが流れるようだ。

そういう処理をするのも面倒そうだが、HID でベンダー依存の情報を流すためには、デスクリプタの定義が必要でそれも面倒そうだ。AVR-Doper では、定義が 60バイトぐらいある。

CDC を使いつつ、どんな装置か判別できるか?

すくなくとも、Linux ではできないようだ。/dev/ttyACMXX というデバイスと libusb で得られる情報のマッピングができない。

WIN32API だと、API は知らないが、分かるような気がする。
しかし、libusb とは関係ない API だと思う。

Linux で出来ない以上 WIN32API で出来てもあまり嬉しくはない。今度は逆を考えてみる。


libusb を使って、CDC のように使えるか?

まず、AVRCDC や USB162 のドライバのデスクリプタだけを ベンダークラスに 変更したファームウェアを作って、普通に使えるか確認してみた。多少怪しいところがあるが、usb_bulk_read()/usb_bulk_write() でちゃんと送受信できた。

usb_detach_kernel_driver_np() を使ってドライバを解放すると、
(変更しない)AVRCDC や USB162 そのものでも送受信できる。

しかし... usb_bulk_read()/usb_bulk_write()を使った制御は、遅かった。カーネルドライバだと、複数の要求を submit して非同期に処理することができたが、libusb だと同期型になるので遅い。
性能は、2.4 オリジナル CDC ドライバ並かそれ以下 ... 処理の内容を考えてみれば当然かも知れない。

CDC のようには使えるが、非同期での処理が出来ない。スレッドを使えば read と write を非同期でできるのかも知れないのだが、複数の read , 複数の write 要求を同時に出すことは難しいのではないかと思える。

結論

性能を求めるなら、専用のドライバを用意するのが一番だろう。普通そこまでやれないので、チューニングした CDC ドライバを使うのが次善だと思う。USB ならではの便利な機能はあきらめるしかない。

しかし、性能が必要なければ、CDC ベースのファームウェアのデスクリプタだけ変えて、ベンダークラスにすることで、装置の判別や その他付加機能を簡単に付けることができる。

USBasp 流に、バルクエンドポイントをなくしてしまって、usb_control_msg()だけで制御するのも簡単で良いかも知れない。


ただ、HID を汎用に使うのは、すごく面倒。本来の目的どおりボタンを作ったり、キーボード・マウスを作るのでなければ、無理して HID を使う必要はないと思う。


(補足)この記事の最初の版は、自分でも何が書いてあるかわからないぐらいひどかったので、書き直しました。
posted by すz at 21:25| Comment(3) | TrackBack(0) | 日記
この記事へのコメント
メルアドは公開しないで下さいね。
シモンさんのAVRminiProg Test (FULL 2008-04-19)
に興味がありますが、mega16の入手が難しく
mega164pなどに換えると上手くコンパイルできません。
http://www.simonqian.com/en/AVRminiProg/index.html
AT90USB647-16やAT90USB162-16に載せ換えることが出来ればそのほうが早いと思うのですが、どの様なUSB機能の点に注目すればよいでしょうか?
Posted by ノンノ at 2008年08月01日 18:48
AVRminiProg のページを見ましたが、AVR Studio から直接使えたり、JTAG 書き込みに対応していたり、さらにはAVR32に対応していたりしてなかなか良さそうですね。今は XMEGA に興味があるので、XMEGA に対応すればとても嬉しいかも。

それはともかく、AT90USB162等に対応させるとすれば ... usbdrv とインターフェイスをできるだけ合わせたほうが良いと思います。そういう目的でいろいろやったのが、このブログの http://suz-avr.sblo.jp/article/7990960.html (usb162-0.1)です。放置していますが、最新のものは、http://nmj.sumomo.ne.jp/suz-avr/USB162/usb162-test4.tgzhttp://nmj.sumomo.ne.jp/suz-avr/acm-driver/avrcdc-perf-0.1.tgz です。お手軽に..というわけにはいきません。本気で改造するつもりであれば(たぶん)参考になると思います。
Posted by すz at 2008年08月14日 21:37
AT90USB162で大分近づいてきました。
ADCがないので外付け12Vを使いますと何とか
pin数が収まります。

今回はHVPPのpinにusbポートを配して
試したのですが、認識できませんでした。
HWBの処理などが不明なのですが、FLIPからは
fuseが書き込めないので、HWBE_0を書いていません。
(PD7_HWB_PAGEL PD0_D+ PD1_D-)
AVRUSBの試験は飛ばして、usbの置き換えをやった方が良さそうですが、何処をさわればよいのかが判りません(エンドポイント名の変更?)

それと、XMEGAも対応された様です。
Posted by ノンノ at 2008年10月15日 13:00
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/30760439
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック