Chrome App は、chrome.usb という API を通じて低レベルなUSBデバイスドライバをJavaScriptで書くことが可能になっている。chrome.usb は libusb 相当の API を提供している。
つまり、自分で作った USB デバイスと通信して、ブラウザ(といっても App だけど)からそれのI/Oを操作でき、LED を HTML+CSS+JS で UI を作って操作したり、ミサイルも JavaScript で飛ばせる。
そこで、AVR (200円ぐらいのワンチップマイコン) とその上で動くソフトウェアUSB実装である V-USB でカスタムデバイスを作成し、Chrome App から呼んでみた。
デバイス側
だいぶ前に実験した結果、Mac OS においては HID デバイスとしてUSBデバイスを作ると OS の管理下におかれ、libusb などからアクセスする手段が殆どなくなってしまうことがわかっていた。libhid 的なものがあればいいのだけれど、すくなくともまともにメンテされているのは当時見当らなかったので諦めた。
今回は教訓をいかして最初からHIDではないカスタムクラスのUSBデバイスにすることとした。
デバイス側の実装及び V-USB の設定ファイルは以下の通り
- https://github.com/cho45/AVR-V-USB-Vendor-Skelton/blob/master/main.c
- https://github.com/cho45/AVR-V-USB-Vendor-Skelton/blob/master/usbconfig.h
AVR ATmega168、16MHz で動かして試している。
HID にしなくともデバイス側の実装はあまり大差ない。
USB_CFG_DEVICE_CLASS と USB_CFG_INTERFACE_CLASS を 0xff (vendor) にしておかないと claim_interface に失敗したので忘れずに変えておく必要がありそう。
ホスト側
ホスト側では、まず libusb + ruby における実装を書いて試した。
interrupt_transfer はブロックをとれるみたいなのだが、うまく呼ばれなかったので深追いせず普通に呼んでいる。
libusb を使うにしても USB のプロトコルの知識が必要になるのが面倒
そして Chrome App の実装も書いた。主な実装箇所は以下
最初どうしても openDevice が成功せず途方にくれていたが、Chrome 自体を再起動することで成功するようになった。諦めなくてよかった。
ちなみに公式?にある usb のサンプルは API が最新に追従していないので参考にしないほうがいい。
USB プロトコルまわり
知っておかなければいけないこと
- USB は基本的に片方向の通信
- CONTROL 転送は1度のトランザクションが双方向で Stop-and-wait ARQ みたいな感じになってる
- 必ずホストから先に通信が始まる (つまりデバイス側からデータを push することはできない)
- USB には転送方法がいくつかある・それぞれ再送があったりなかったりするので必要に応じて使う
- CONTROL 転送
- ISOCHRONOUS 転送
- BULK 転送
- INTERRUPT 転送
参照