山城 潤<yamajun@{ofug.net, cr.ie.u-ryukyu.ac.jp}>
ここ半年ほど、個人的に行なってきた*BSDからUSB機器をいじる仕事について、
その成果と調査結果について発表する。
(これはあくまで私個人の試行錯誤の記録なので、
ちゃんとした情報は最後に示している参考資料などを見て下さい)
http://ifp-driver.sourceforge.net/
チェコのPavel Krizが、 韓国iRiver社 (日本法人)のMP3プレイヤー iFPシリーズをUNIX(*BSD,Linux)から制御するために開発したプログラム。 コマンドラインから操作する。
関連するプロジェクトには、 Stark ViktorとMichele Baldessariがifp-lineのコードをベースにして書いた ライブラリーlibiriver と、それを利用したGNOME VFSモジュール giriverがある。
私は、2003年4月の中ごろからメーリングリストに各種パッチを送り続け、6月にcvsコミット権を得た。 現在までの主な変更は以下の通り。
今後の課題として、Mac OS X対応があるが(公式GUIマネージャーは既に存在する)、
受信するデータのほとんどがデタラメになってしまう
(エンディアン変換すればいいってものでもない)のが悩み。
ADCから
ダウンロードしたUSB開発キットには、USBロガーらしきものがあるが、
まだ使いこなしていない。
追記(2003-11-15):
Mac OS X対応は完了した。OSXで動かなかった理由は、
libusbを利用するプログラムに必須なインターフェイスのclaim/release処理を
行なっていなかったせいである(*BSD, Linuxではそれでも動作していた)。
この問題は、Johan Andrenのパッチによって改善された。
最近、各社からUSBで接続する無線LANアダプターが出てきているが、 *BSDではまだ使えない。また、 無線LAN内蔵ノートパソコンには、 内部でUSB無線LANモジュールを持っているものもあるらしい (電波を飛ばしたくない時にスイッチ一つでハードウェア的に切り離すためと思われる。 似たようなものにUSB2.0と無線LANが一緒になったCardBusカード (OEM元?)もある)。
これらの機器を*BSDで使わない理由はない!(というか、ノートパソコンの 無線LANが使えないのはもったいないという人も...)ということで、 バッファローの無線LANアダプターWLI-USB-KS11Gを買ったのを機に、 *BSDでもUSB無線LANが使えるようにしようと思った。
開発は、NetBSD-currentをインストールしたIBM ThinkPad上で行なっている。
bus_space_{read,write}_2()
は、
USB機器のドライバーでは使えないので、wi.cを
そのまま使うことはできないそこで、/sys/dev/ic/wi.cの中身を、USBの入出力API に合わせるように移植することでUSB無線LANドライバーを実装しようとしている。
現在は、ヒマと興味がある時のみ開発を進めているので、wi.cの 多くの関数がまだ移植できていません。また、他のUSB Ethernetアダプターの ドライバーから見よう見まねで書いているので、本当にこれで良いのか という自信もあまりありません^^; (というか、wi.cとほとんど同じ役目をするコードを書いてしまうと、 同じようなコードを2つ保守しなければならなくなるという欠点がある)
また、FreeBSD PRESS No.18で、Warner Losh氏がやる気があるようなそぶりを 見せているので、そちらの方が先に出来上がるかも知れません。
ボクがずっとwiドライバに心血をそそいできたから、彼はprism2が載った USB無線デバイスをプレゼントしてくれたんだ。このプレゼントが ボクを楽しませてくれるのか、それともとんでもないフラストレーションを もたらすのか、そのうちわかるだろう。
追記(2003-11-19):
OpenBSD方面で、USB wavelanフロントエンドがcommitされている!
追記(2004-04-01):
FreeBSDでもドライバーが完成したようです。
5.1-RELEASE用で、カーネルソースにパッチを当てて利用します。
私の5.2環境ではコンパイルエラーが出てるけど...
*BSDでドライバーが用意されていない機器は/dev/ugen*として認識される。 これをユーザーランドから利用するソフトウェアを書くには2種類の方法がある。
他のキャラクターデバイスと同様に、 open(2)
で
/dev/ugen*デバイスファイルを開き、
read(2),write(2),ioctl(2)
経由で操作する。
#include <dev/usb/usb.h> ... /* * 長くなるのでデバイスの検索は省略する。 * 利用可能なデバイスを探し、ベンダーIDとプロダクトIDが一致するかどうかを調べる * ここでは、devname = "/dev/ugen0" とする。 */ sprintf(outname, "%s.1", devname); // 出力用デバイス名の生成 sprintf(inname, "%s.2", devname); // 入力用デバイス名の生成 if (fd = open(devname, O_RDWR) == -1) { perror(devname); exit(1); } if ( (outfd = open(outdevname, O_WRONLY)) < 0) { perror(outdevname); close(fd); exit(1); } if ( (infd = open(indevname, O_RDONLY)) < 0) { perror(indevname); close(fd); close(outfd); exit(1); } ioctl(fd, USB_GET_HOGE, &data); ... write(outfd, outbuf, sizeof(outbuf)); ... read(infd, buf, sizeof(buf)); ... close(fd);
http://libusb.sourceforge.net/
libusbは各種OSのUSB関係APIのラッパーなので、 *BSDだけではなく、LinuxやMacOS Xでも使える。 また、ports/pkgsrcでパッケージングされているので、 devel/libusbからインストールできる。
デバイスとの通信には、制御転送(メッセージ程度)を行なう
usb_control_msg()
と、バルク転送(大容量)を行なう
usb_bulk_{read,write}()
を使って行なう。
#include <usb.h> ... struct usb_bus *bus; struct usb_device *dev; usb_dev_handle *dh; usb_init(); usb_find_busses(); /* デバイスの検索 */ for (bus = usb_get_busses(); bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.idVendor == HOGE_VENDOR && dev->descriptor.idProduct == HOGE_PRODUCT) { goto device_found; } } } /* デバイスが見つからなかった場合 */ fprintf(stderr, "Device not found.\n"); exit(1); device_found: dh = usb_open(dev); // デバイス使用開始前に、インターフェイスを要求する。 usb_claim_interface(dh, dev->config->interface->altsetting->bInterfaceNumber); ... usb_control_msg(dh, request_type, request, value, index, msg, sizeof(msg), timeout); ... usb_bulk_read(dh, read_endpoint, readbuf, sizeof(readbuf), timeout); ... usb_bulk_write(dh, write_endpoint, writebuf, sizeof(writebuf), timeout); ... // デバイス使用開始後に、インターフェイスを解放する。 usb_release_interface(dh, dev->config->interface->altsetting->bInterfaceNumber); usb_close(dh);
カーネルレベルでデバイスドライバーを実装しなければならない時(Ethernetなど)は、 /sys/dev/usb/*を参考にして行なう。