ラベル Emobile の投稿を表示しています。 すべての投稿を表示
ラベル Emobile の投稿を表示しています。 すべての投稿を表示

2009年7月2日木曜日

E-mobile H12HW

E-mobileの電話型端末H12HWを試す機会が会ったのでOpenBSDでのデータ通信を試してみた。H12HWは型番が示すようにHUAWEI社の製品で3.6MbpsのHSDPAに対応している。

結果を共有。

準備

以下のようにsys/dev/usb/usbdevsにProduct ID 0x1008を追加して、usbdevs.hとusbdevs_data.hを再生成しておく。
  • product HUAWEI Mobile 0x1008 HUAWEI Mobile
あとはsys/dev/usb/umsm.cのデバイス情報の構造体であるstruct umsm_type umsm_devs[]に
  • {{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_Mobile}, DEV_HUAWEI},
を追加してkernelをリコンパイルする。

結果

普通にumsm(4)のデバイスとして認識されて、pppdでppp接続も確立できた。

いまはopenbsdの次のリリースに向けた作業が進んでいるので、それが落ち着いた頃にopenbsdにはコミットしておく予定。


2009年5月12日火曜日

E-mobile D12HWとD22HW

E-mobileの最近のモデムを触ってみる機会があったのですこし調べてみた。結果を共有。


触ったのはD12HWD22HWで両方ともHUAWEIのOEM製品だ。D22HWはHSUPAでアップリンクが最大1.4Mbpsまで使えるというのが特徴らしい。

OpenBSDで試してみる

HUAWEIの多くのモデムは同じUSBベンダIDとプロダクトIDを共有しているが、今回もその例に漏れず同じIDを利用していた。OpenBSDで試してみたところ、両者とも標準的なHUAWEIプロトコルでUSBマスストレージモードからモデムモードに遷移するようだ。umsm(4)としてデバイスは認識される。pppd経由でネットワーク接続もできたようなので何の工夫もせずに普通につかえる。

メモ:D12HW

D12HWに関してはUSBのディスクリプタの文字列が壊れている部分がある。利用する分には問題はないがUSBディスクリプタの文字列を信用してデバイス認識時に画面に出力してしまうとなんだかよくわからない文字列が表示されてしまうかもしれない。
しょうがないのでOpenBSDではディスクリプタ中の文字列を信用せずにusbdevsの中の文字列を利用するようにUSB_QUIRKS: UQ_NO_STRINGSを設定しておいた。本来はD12HWだけでこのquirksが使われるようにすべきだがみんな同じベンダIDとプロダクトIDを持っているのでほとんど全部のHUAWEIモデムにこのquriksが適用されてしまっている。


2009年4月26日日曜日

E-mobile D21LCをOpenBSDで使う

少し前の話だがE-mobileのD21LCをしばらく借りることができたので、少し解析してOpenBSDで使えるようにしてみた。その時の話をメモとして残しておくことにする。

D21LCは中国のLongcheer社製のHSDPAモデムだ。E-mobileでは最初はHuawei社のモデムを使っていたが、しばらく前から2社の製品を投入している。D21LCも他のE-mobileのモデムと同様に「ゼロインストール」機能が搭載されている。ゼロインストールとはドライバが入っていないOS(つまり初回挿入時)にはモデムをUSBマスストレージデバイスとして認識させて必要なドライバをモデムから直接インストールさせる仕組みである。ドライバが動き始めると、そのドライバがモデムのモードを変更してモデムとして動作するようになる。Windowsなどからは大変便利な機能であるが、ほかのOSで使うためにはちょっとおせっかいだ。

Huawei社のモデムはメモ:D02HW解析で昔書いたように特別なUSBコマンドを発行すると、USBマスストレージモードからモデムモードに動作モードが切り替わった。D21LCの場合はどうだろうか?

D21LC:モデムモードへの切り替え

USBアナライザでUSBトランザクションをモニタしてみると、あるタイミングでデバイスがリセットされるタイミングがあった。これがモデムモードへの切り替えコマンドだろうと推測してもう少し中身を眺めてみる。


D21LCのモード変更コマンドはUSBマスストレージデバイスに対するコマンドとして実装されていた。(コマンドパケットの先頭の0x55 0x53 0x42 0x43:USBCが規定のマジックナンバー)といっても、USBマスストレージデバイスの仕様には定義されていない独自コマンドを発行している。
さらにモデムの動作を眺めてみたが、それ以降は普通の3Gモデムと同様の動作をしているようだ。
というわけで、このシーケンスを発行するようにデバイスドライバを拡張してやれば、ほかのOSでもモデムとして利用できるはずだ。

シリアルポート


D21LCではシリアルポートが3本生成される。モデムポートは3本目(openbsdの場合はttyU2)だった。

OpenBSDのデバイスドライバの拡張

実はすでにOpenBSDのumsm(4): 3Gモデム用デバイスドライバでは、このようなUSBマスストレージ型のモード変更デバイスを扱うための機能をすでに実装してある( umsm_umass_changemode() @umsm.c)。 似たようなデバイスを扱うために実装しておいたものだが、今回はこの関数を少し拡張して利用することにした。
すでにOpenBSDのCVSへマージされている(差分)を参照すればわかるが、上でキャプチャしたコマンドをタイミングよく発行するだけだ。
コマンド発行部分の差分を以下に載せておく。いままで2種類の同様なコマンドがあったので、3種類目として拡張しておいた。










おまけ:D21LC雑感

実際に使ったのは短い期間だったが、D21LCを使って気がついたのは、「デバイスがサポートしていないコマンドを受理するとフリーズすることがある」ということだ。正式に使えるコマンドセットが公開されているわけでもないのでバグというのは言いすぎだが、ちょっとした動作確認でハングアップしてしまうことが多くて困ることが多かった。最初はデバイスドライバ側が悪いのかと思ったが、Windowsでも同じ操作で同じ状態に陥ることが確認されたのでデバイス側に問題があるんだと思っている。

2008年5月13日火曜日

OpenBSDでEmobile D02HWを使う(1)

縁あってOpenBSDの開発者の人たちと一緒にhackする機会があったので、なにかコントリビューションできることはないかなと考えたところ、OpenBSDの3Gモデムのサポートを拡張することにした。
特にD02HWは、本当の名前はE220といって世界各地で使われているメジャーなモデムなので、これがまともに使えるようになるとうれしい人がそれなりにいるだろうから、ちょうど良い。

OpenBSDもNetBSDと同様に、FreeBSD由来のUSBサブシステムを採用しているので、usbデバイスドライバは大きな差異は存在しない。NetBSD用につくったuhmodem(4)をそのまま移植するという手もあったのだけど、OpenBSDにはHSDPAモデム用のドライバとしてumsm(4)がすでに存在していたので、それを拡張する形で実装してみることにした。

umsm(4)(拡張前)
dev/usb/umsm.c(rev.1.21)を眺めてみると、非常にシンプルなドライバになっていた。具体的に言うと、

struct ucom_methods umsm_methods = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};

となっていた。struct ucom_methods は、上位ドライバとなるucom(4)のopen/closeなどの各処理におけるデバイス依存コードへの関数ポインタを渡すための構造体なので、この状態ではシリアルに関する処理はucomでのデフォルト動作だけで実行されることを意味している。

どうしてこれで他のドライバが動作しているのだろうか?と「???」と思いながらざっくりとコードを追ってみたところ、シリアルデバイスのinterrupt endpointに関する処理がざっくりと抜け落ちていることに気がついた。単なるin/out endpointを処理するだけなら、確かにucom(4)における特別な処理は必要ないが、モデムで通信を行う際には自分から始めるだけではなく割り込み処理が必要である。
近くにいたOpenBSD開発者に聞いてみたら「おれが使っているE220で昔だれかが作ったパッチで試した見たところ、たしかにPPPまでは張れることもあるがうまく動かなかった。つながるけど切れたり通信できなかったりしてね。それから使ってない」と言われた。さもありなん、ということでinterrupt endpointの処理を拡張するところから始めることにする。D02HW(E220)以外でも役にたつだろう。

interrupt endpoint処理の追加

endpointの割り当て処理はumsm_attach()の中で行われる。data用のbulk in/out endpointしか処理していなかった部分を、以下のように変更する。また、struct umsm_softcにもinterrupt endpoint周りの状態を保持するための変数を拡張しておく。

if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
sc->sc_intr_number = ed->bEndpointAddress;
sc->sc_isize = UGETW(ed->wMaxPacketSize);
DPRINTF(("%s: find interrupt endpoint for %s\n",
__func__, sc->sc_dev.dv_xname));
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
uca.bulkin = ed->bEndpointAddress;
else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
uca.bulkout = ed->bEndpointAddress;


これで、interrupt endpointを持っているinterfaceがあったら、interrupt endpointを正しく扱うための準備ができる。

次に、ucomをopenしたときに検出したinterrupt endpointをucom割り込みとして利用するためのコードを追加しなければならない。まずucomをopenしたときにusb的にinterrupt endpointを登録するコード(usbd_open_pipe_intr()を呼ぶ)と、closeしたときに後始末(usbd_about_pipe() & usbd_close_pipe()を呼ぶ)をするデバイス依存の関数を追加する。それぞれubsa(4)から持ってきて、umsm_open(), umsm_close()ということにしておく。ucomがopen/closeされるタイミングで呼ばれるデバイス依存関数は、前述のstruct ucom_methodsで指定しておけばよい。

さらに割り込み発生時に呼び出されるデバイス依存関数とその補助関数として、同じくubsa(4)からumsm_intr(), umsm_get_status()を持ってくる。同様にstruct ucom_methodsに指定する。

これで、interrupt endpoint周りの処理は完了。普通のHSDPAモデムのDoCoMo A2502をつなげて試してPPPセッションが正しく張れるようになっていることを確認。とりあえずここまでで半分くらい終了。

ここまでのコードの差分は、OpenBSD CVSwebで確認できる。

次回のエントリーでは、D02HWのモード遷移のための関数の統合について書く予定。

2008年2月7日木曜日

NetBSDでDoCoMo A2502をつかう


別に3Gモデムを攻略するのが趣味ってわけではないが、近くにNTT DoCoMoのHSDPAモデム A2502 (製品情報)があったのでどんなものか眺めてみた。結構あっさりとNetBSDで動くようになったので方法を書いておく。

A2502はAnyData社の製品とのことだが、イーモバイルのD02HW(HAUWEIのE220)みたいにメジャーな製品のOEMというわけではなさそう。検索してもあまり素性がわからないので、とりあえずUSBバスを観測してみたところ、コマンドの系列がこの前書いたD02HWと互換性がありそうだ。というわけで、とりあえずモデムモードになったあとのD02HWと同じように扱ってみたところ通信ができることを確認。

モデムとしては、

  • comポートが3本でてくる。ATコマンドを受理するのは最初の1つだけ。2つめは不明。3つめは網の情報がでているっぽいけど、特に処理するコードは書いていないので見えない。
  • D02HWみたいにUSBマスストレージがついていたりはしない、普通のモデム
  • 変なコマンドを送らなくても、最初からモデムとして動く
なので、D02HWよりは扱いやすい。ただ、不明なvendor specific requestがいくつもあるのでそれがなにかわからないのはちょっと気持ちわるい。

コード的には、いくつか初期化シーケンスを書いて、uhmodemをD02HWとA2502両用になるようにさらに汎用的なドライバとして整理しておいた。やったことは
  • deviceのvendor id, product idの構造体にいくつかの補足情報(comデバイスの数、デバイス種類のフラグ)を持つようにした
  • シリアルデバイスになるインターフェイスごとにちゃんとendpoint haltをかけるようにした。
  • いままで決め打ちだったendpoint 番号をusbフレームワーク的に解決するようにした
  • いままで決め打ちだったデバイス依存リクエストを整理して両方でつかえるようにした
というあたり。見直した結果コードは少しきれいになった。新しいデバイスに対応するのがもっと簡単になりそう。もう少しつかってみてから、NetBSD本家にsend-prしておく予定。寝る前にだせるかな。(追記: done)

とりあえずpppでつなげてみたところ、DoCoMoの網の挙動はイーモバイルの網とは違うことがわかった。
たとえばRTTの変化。イーモバイルはしばらくパケットを投げているとある段階で急にRTTが短くなるが、DoCoMoの場合はステップ的に減っていく。最終的は二つとも同じくらい(70ms-100ms)で収束するが、途中経過が違うのがおもしろかった。
(追記: emobileでもう一度RTTを計ってみたところ500ms → 200ms程度までしか変化しなかった。挙動がかわったのかもしれない。そういう意味ではDoCoMoの網のほうが良い特性かも?)

速度は追いこんでいないけど、SSHでscpして10MBくらいのファイルを転送したところ200KB/secくらいでていた。そんなに悪い数字じゃないね。

追記: NetBSD-pr kern/37978 すぐに試してみたい人はこれを見て手でパッチあててください。最近のcurrentには当たるはず。

2008年1月22日火曜日

NetBSDでEmobile D02HWを使う(-currentにマージ)

何回かにわたってD02HWをNetBSDで使う話を書いていたが、1/21付けでNetBSD(-current)にマージされた。
基本的に「NetBSDでEmobile D02HWを使う(新デバイスドライバ)」でsend-prしたものがおおむねそのままマージされたかんじ。cvsで吸い出すだけで使えるようになったはず。

使う人が出てきたら潜在的な問題とかが出てくるかもしれないけど、一段落。

2008年1月5日土曜日

NetBSDでEmobile D02HWを使う(新デバイスドライバ)

NetBSDでD02HWを使うためにいろいろとやった結果、前回のubsaデバイスドライバへのパッチというアプローチではどうも差分が大きくなりすぎる、という結論に達した。新しく別のデバイスドライバとしたほうがなにかと都合がよいので、uhmodem (USB huawei modem)という名前の新しいデバイスドライバをつくってみた。と、いってもubsaとの共有部分も多いのでそのままコピーして変更するのもいまいちな気がしたので、ubsa.cをubsavar.h, ubsa_common.c, ubsa.cの3つに分解して、uhmodem.cからはubsa_common.cを介してubsaの関数を使うことにした。

とりあえずdmesgを載せておく。

uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
uhmodem0: mass storage only mode, reattach to enable modem
uhmodem0: at uhub0 port 1 (addr 2) disconnected
uhmodem0 detached
uhmodem0 at uhub0 port 1 configuration 1 interface 0
uhmodem0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ucom0 at uhmodem0 portno 0: modem
ucom1 at uhmodem0 portno 1: monitor
umass0 at uhub0 port 1 configuration 1 interface 2
umass0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, 1 lun per target
cd0 at scsibus0 target 0 lun 0: cdrom removable

attach時のメッセージを見てわかるように、uhmodemではD02HW(E220)が提供しているすべての機能(com *2, umass *1)を同時につかえるようになっている。
現状ではumassがつながっても、windowsのデバイスドライバが見えるだけだが、海外のほうでは、あの領域に好きなものを入れれるようなアクティビティもあるようなのでその時に役にたつかもなーくらいな気持ちでいれてみた。
ucom0, ucom1は前者が通信用のttyで、後者がモデムの状況や通信状況をモニターするための監視用ttyだ。ucom0を使っているときでもucom1を同時にアクセスできるので、メモ:D02HW解析に書いたDSFLOWRPTメッセージとか電波状態問い合わせ(^ANQUERY)の回答などを受け取れる。ユーザーインターフェイスアプリケーションを書くときには便利なはず。

というわけで、patchは大きくなったのでここに乗せるのは割愛しておく。すでにNetBSDにはsend-prしてある(kern/37692)ので急ぐ場合はそちらを参照すると良いかも。

とりあえずやることはおわったかなーーー。。

2008年1月4日金曜日

メモ:D02HW解析

イーモバイルのD02HW (Huawei E220のOEM)をNetBSDから使えるように触っている過程で調べたことをメモしておく。

マスストレージモード(電源投入時)
普通に認識すると、USB mass strageデバイスに見える。USB class 8(umass), subclass 6(SCSI), proto 80(BBB)。Interface 0: endpoint: 3(in)-4(out)。マウントするとCDROMにみえて、中身にwindows用のユーティリティとデバイスドライバが入っている。役立たず。

モデムモードへの変換
Host-to-DeviceでSet Future: DEVICE_REMOTE_WAKEUP, wIndex=0x2で送りつける。すると同じvender ID, product IDのままで別のデバイスに化ける。
(wIndex=0x1にしてdataに0x1を入れて送っても同じ結果になるが、違いは不明)

モデムモード(変換後)
モードがモデムモードになると、インターフェイスが3つに増える。

  • インターフェイス1: USB class 255, subclass 255, proto 255。完璧なベンダースペシフィックだが実はシリアル。シリアルデバイスはBELKINのとほとんど同じにみえる(NetBSDだとubsaドライバ)。これがモデムの主インターフェイス。endpoint: 1(intr)-2(in)-2(out)。
  • インターフェイス2:USB class 255, subclass 255, proto 255。インターフェイス1と同様にシリアルデバイス。モデムの観測用インターフェイス。endpoint: 5(in)-5(out)。通信しないからか割り込みが無い。
  • インターフェイス3: USB class 8, subclass 6, proto 80。マスストレージモードのときにインターフェイス1に見えていたUSB mass storage。
モデムの観測用インターフェイス
つなげるとATコマンドを受理する。ステートは主インターフェイスの設定を継承している(たとえばate0と主インターフェイスで打ち込んであるとATコマンドにエコーバックしない)。使い道は2つ。
  • 主インターフェイスで通信をしているときにもATコマンドを受理するので、AT^ANQUERY?(電波強度問い合わせ)などで状態を取得できる
  • 主インターフェイスで通信しているときは定期的(2秒に一回)、^DSFLORRPTという行が出力される。


DSFLORRPT
DSFLORRPTはこんな感じに出力される。(一行で)

^DSFLOWRPT:0000007C, 00000000, 00000000, 000000000001B1AE, 00000000004DD9D2, 0000AA50, 000EBA40

ざっくりと眺めたところ、意味は最初から[通信時間(秒)], [送信スループット(byte/s)], [受信スループット(byte/s)], [総送信量(byte/s)], [総受信量(byte/s)], [なんかのID], [なんかのID]となっているようだ。時間とデータ量は16進数で表現されている。

謎のコマンド
windows PCとD02HWの間のUSBトランザクションを眺めていると、標準的ではない制御トランザクションが観測される。何やっているか不明。
  1. (準備1)Endpoint:2-inにCLEAR_FEATUREリクエスト、ENDPOINT_HALTを送る
  2. (準備2)Endpoint:2-outにCLEAR_FEATUREリクエスト、ENDPOINT_HALTを送る。
  3. InterfaceにType:Class, bRequest=0x02, wValue=0x1, wIndex=0x0, wLength=0x2, Data[]={0x0, 0x0}を送る
  4. InterfaceにType:Class, bRequest=0x22, wValue=0x1, wIndex=0x0, wLength=0x0を送る
  5. InterfaceからType:Class, bRequest=0x21, wValue=0x0, wIndex=0x0で7byte読み込む。普通は{0x0, 0x96, 0x0, 0x0, 0x0, 0x0, 0x8}が見える。
  6. 前のリクエストで取得したバッファの2-3byteを{0x8, 0x7}にして書き戻す。bRequest=0x20
  7. InterfaceにType:Class, bRequest=0x22, wValue=0x3, wIndex=0x0, wLength=0x0を送る
たぶん、
  • bRequest=0x20: E220_CLASS_WRITE
  • bRequest=0x21: E220_CLASS_READ
  • bRequest=0x22: E220_CLASS_SETUP (or CMD)。bVlaue=0x1でstart, bValue=0x3でcommitみたいな感じ?
じゃないかと推測している。問題は読み書きする7byteの意味がわからない点。NetBSDで適当に実装して発行してみているが、何が変わったかはわからない。

通信時のUSBトランザクション
USBのendpointのデータ単位は64byte。TCP/IPで通信をしていると、大体46-48USBパケットがバースト的に送られてくる。モデム側のバッファサイズは3-4KBじゃないかと推測。

(おまけ)イーモバイル網の挙動
PPPで接続して普通にpingをうつと、RTTが300-400msくらいでガッカリする。しかし中身ではいろいろと複雑な制御をしているようで、5pps以上の頻度で同一ホストにパケットを送信すると、10-20パケットくらい経過してからRTTが70ms-100msに変化する。
しばらく通信がないとまたリセットされるので、フローごとに細かい制御をしているようだ。カットスルー?でもしてるんかいな。


とりあえずこのくらい。

NetBSDでEmobile D02HWを使う(スループット改善)(2)

しばらくsys/dev/usbの下を眺めていたが、USB的な律速が他にあるとしても触るにはおおごとになりそうなので、別の部分から攻めることにした。

「Windowsなら速い」という話でふと思いついたのが「TCPの動作の違い」だった。Windowsはいろいろなパラメータ調整でTCPの挙動を変えているが、それがイーモバイルの網の挙動とうまくあっているのかもしれない。または、専用ドライバがWindowsのパラメータを自分好みに変えているのかも。というわけで、NetBSDのネットワーク部分のパラメータを調整してみることにした。これはkernelにpatchを当てるとかいう話ではなく、sysctlで変えれる範囲でおこなった。

とりあえず今のところの/etc/sysctl.confを以下に示す。

net.inet.tcp.mss_ifmtu=1
net.inet.tcp.init_win=8
net.inet.tcp.sack.maxholes=64
net.inet.tcp.sendspace=65536
net.inet.tcp.recvspace=128000
net.inet.udp.recvspace=65536
kern.sbmax=2048000

この中で一番意味があるのがtcp.recvspaceだ。emobile網を使っていて思うのは、
  • 遅延はある程度大きいけど、そんなに落ちない。
ということだ。そんなネットワークではtcpのバッファサイズをある程度大きくしておかないと性能がでない。主記憶の量とどのくらいのセッション数をさばくつもりなのか?というあたりと要相談だが、たぶんいまのemobile網では128KBくらいあれば良いようにみえる。主記憶に余裕があればもう少し増やしておいてもよいかもしれない。recvspaceの大きさはkern.sbmaxに制限されるのでこちらも大きくしておくべき。

ここまでで、調子がよければ300KB/sを超えるようになる。気休めに/etc/ppp/peers/emobileの設定を2行ほど変えた。

# disable compressions
novj
##noccp
##nobsdcomp


実は並列して他にもいろいろな作業をしていたので、ここに書いただけで性能がそこまで上がるかはちょっと自信がないのだが、結果的には200KB/s - 440KB/sくらいは出るようになった。
調子の良い時はwgetで27MBくらいのmp4ファイルを転送して平均で390KB/sくらい、とそこそこの安定したスループットを見せてくれる。
ただ、現状ではWindowsなら300KB/sでているときにNetBSDでは200KB/sしかでない、という現象が発生する。まだ隠れている設定があるのかもしれない。

2008年1月3日木曜日

NetBSDでEmobile D02HWを使う(スループット改善)(1)

このエントリーの一行まとめ:HTTP/TCPで29KB/sしかでなかったけど440KB/s(3.4Mbps)まで改善した。うれしい。


最近のエントリーに結構話題にしたように、いろいろとごにょごにょとして、イーモバイルのD02HW(E220)をNetBSDでも普通に使えるようになった。使えないデバイスが使えるようにする作業をしていると、「使えるようになった!」時点でだいぶ達成感がでてしまってそこでゴールに着いてしまった気になりがちだ。今回もデバイスとして認識できない段階から、まがりなりにもPPPでセッションを張って通信できるようになったわけだから、まあそれでいいや、考えてしまっていた。
ちょっと間をおいて、実際に使ってみようとおもうと、どうも変な感じがする。windowsのNote PCにつなげて使っているときはすぐに見えるwebサイトなどが見えるのがだいぶ遅い。やっぱりスループットを計測してみるか、と重い腰を上げてやってみるた。
wgetコマンドをつかってHTTP/TCPで、ファイルを転送するときの速度を測ってみると………。(プロトコルオーバヘッドがあるので下部層ではあと10%くらい速いはず)。
  • Windows  (Panasonic Let's Note R5): 1.8Mbps-2.5MBps (230KB/s-320KB/s)くらい
  • NetBSD (ALIX 3C): コンスタントに0.225Mbps (29KB/s)
うーん、軽く1/10? と少しショックを感じながら改善作業に取り組むことにした。

2つの計測系で同一なのは「同じD02HWを使っている」「同じ場所で計測」「ほとんど同じ時間に計測」というところ。あとの計算機環境やソフトウェアは全部違っている。本当はできるだけそろえるべきなんだけど、Let's noteにNetBSDを入れるのもめんどくさいので実作業にうつることにした。とりあえず疑うべきは
  • D02HWの設定
  • D02HW用のデバイスドライバ
  • OSのUSBサブシステム
  • PPPの設定
  • PCの性能
あたり。ターゲットとしているALIX3は小さな組み込みボードといえども、500MHz駆動のAMD LX800で動いている。主記憶も256MBある。経験上これくらいの性能でたかだか数Mbpsのトラフィックを裁けないわけがない。USBホストのチップの問題もなさそう。というわけで、5つ目の「PCの性能」は「ほとんどなさそう」だ。

簡単なところが疑っていこうとおもって、次は「D02HW」の設定を追ってみることにした。といってもモデムとして見えるATコマンドでは無くUSBレベルでの話。USBバスアナライザでトランザクションを眺めていると、ベンダ独自のコマンドを使って設定用のレジスタをさわっているようにみえる。仕様書が無いのでこれが何を意味しているかはわからないが、設定しているということは意味があるのかもしれない?とおもってNetBSDからも同じ操作をする関数を実装してみた(どんな風にするかは後日別のエントリーで書く予定)。うーん?別に変わらない様子。

次は「デバイスドライバ」と「OSのUSBサブシステム」。一緒にみた方が良さそうなので同時に始末することにする。たぶん今回の問題の肝は「いつも同じ29KB/sである」ということだろう。windowsで見ていると、結構短い間隔でスループットが大きく変動しているのを観測できるが、NetBSDだとずっときっちりと29KB/sしかでない。つまりある層までは十分な性能があるのにかかわらずどこかに律速(ボトルネック)となる部分があってそれが原因でこんな状況になっている、と推測できる。こんなときにどこをさわればいいか?というのは、そのデータがデバイスからOSの上(tty)までどうやって流れてくるのかを理解して調べていかないといけない。幸いにも今回はD02HW対応でしばらくUSBのデバイスドライバをさわっていたので、ずいぶん勘がはたらくようになっていた。じーっとコードを眺めていると、USBのデータ処理単位とデバイスの単一のパケットサイズを同じ大きさ扱うようになっていた。たとえばUSB_ATTACH()のなかのエンドポイントディスクリプタを読んで上位のucomデバイスの送受信バッファサイズを設定するところ。


if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
uca.bulkin = ed->bEndpointAddress;
uca.ibufsize = UGETW(ed->wMaxPacketSize);
} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
uca.bulkout = ed->bEndpointAddress;
uca.obufsize = UGETW(ed->wMaxPacketSize);
}

単なるシリアルラインならともなく、D02HWは7.2Mbpsの高速シリアルデバイスなのでこれじゃだめだろうという気がする。というわけでその辺のコードをさわる。具体的には、uca(ucom attach arg構造体)のuca.ibufsize / uca.obufsizeをD02HWのパケットサイズ(64Byte)とは独立した大きさにする。試行錯誤の結果、4096Byteくらいがちょうど良い大きさだと判断した。uca構造体を設定する場所で決め打ちで4096を設定しておく。ucomとか関連する関数をチェックしたがそれで問題は無い。

ここまでの作業で、29KB/sec -> 12oKB/secくらいに劇的に改善した。1Mbpsくらいでている計算になる。でもwindowsの1/3くらいでしかないので更に解析と作業を進める。

(続く)

2007年12月26日水曜日

Emobile-Wifiルータ

せっかくそれなりに速い速度のアップリンクを持つイーモバイルのD02HWが使えるようになったので、イーモバイルをアップリンクにする無線アクセスポイントをつくってみた。OSはNetBSD。とりあえず手元にあったusb無線LANアダプタ(バッファローのWLI-U2-SG54HP)をつかっている。ボードはALIX.3C

やることは普通のルータの設定と大体同じ。

  • ルータなのでkernelでipフォワードをenableする。kernelコンフィグレーションファイルでoptions GATEWAYと書いておくか、sysctl -w net.inet.ip.forwarding=1としておく。
  • ipfilterかpfでNATをできるように設定する。pfを使うときはkernelのコンフィグレーションファイルでpseudo-device pfとpseudo-device pflogと書いておく。ipfilterはdisableした記憶がなければ使えるはず。あとは/etc/ipf.confとか/etc/pf.confをかいておく。/etc/rc.confにpf=YESなどを書くのを忘れないように。
  • dhcpdをあげる。/etc/dhcpd.confを設定して、/etc/rc.confにdhcpd=YES dhcpd_flags="無線インターフェイス名"を書いておく。nameserverは自分のwifiインターフェイスのアドレスにしておくとよい。
  • namedをあげる。/etc/rc.confにnamed=YESとかいておく
  • pppの設定をする。前のエントリーを参照のこと。
  • 無線インターフェイスの設定をする。/etc/ifconfig.インターフェイス名というファイルに書いておくと便利。たとえば、rum0を使う場合は、ifconfig rum0 media autoselect mode 11g mediaopt hostapなどと打ち込んで、hostapモードで動かす。IPアドレス、SSIDの設定も忘れずに。
最低限これだけ設定すると、ルータとして動き出す。実用に使うにはもっといろいろと作り込んでいかないといけないが、この辺からスタートするとよいんじゃないかな。
もう少し時間と(切実な必要性)ができたらがんばる予定。

NetBSDでEmobile D02HWを使う(設定編)

NetBSDのpppdのemobileにつなげる方法を教えてもらったので設定してみた。設定をまとめておく。
(まだ最適化はされていないので冗長な記述などがあるかもしれない)

設定するファイルは以下の5つ

  • /etc/ppp/peers/emobile
  • /etc/ppp/chat-emobile
  • /etc/ppp/ip-up
  • /etc/ppp/ip-down
  • /etc/ppp/chap-secret
これらを設定して、ppp call emobile を実行すればいい感じにPPPセッションが確立してくれる。

/etc/ppp/peers/emobile

dtyU0 460800 crtscts
lock
nodetach
hide-password
local
noauth
# dns routing
usepeerdns
defaultroute
noipdefault
# disable compressions
novj
noccp
nobsdcomp
# auth
user "em"
# misc? (xxx)
ipcp-restart 8
ipcp-max-configure 50
ipcp-accept-local
ipcp-accept-remote
# connect script
connect '/usr/sbin/chat -v -f /etc/ppp/chat-emobile -T *99***1#'


/etc/ppp/chat-emobile

ABORT "NO CARRIER"
ABORT "NO DIALTONE"
ABORT "ERROR"
ABORT "NO ANSWER"
ABORT "BUSY"
ECHO ON
SAY "Connecting"
TIMEOUT 15
"" "\d\d\dat\r\dat"
TIMEOUT 5
"OK-\Kat-OK" "at"
OK "at&FE1V1X1&D2&C1S0=0"
OK "at+ipr=230400"
SAY "Let's go"
OK "atdt\T"
CONNECT ''


/etc/ppp/ip-up (chmod 0755 /etc/ppp/ip-upしておくこと)

#!/bin/sh
~
f [ -f /etc/ppp/resolv.conf ]; then
chmod 644 /etc/ppp/resolv.conf
cp /etc/resolv.conf /etc/resolv.conf-
if [ ${USEPEERDNS:-0} -eq 1 ]; then
cp /etc/ppp/resolv.conf /etc/resolv.conf
chmod 644 /etc/resolv.conf
fi
fi


/etc/ppp/ip-down (chmod 0755 /etc/ppp/ip-downしておくこと)

#!/bin/sh
#
if [ -f /etc/resolv.conf- -a ${USEPEERDNS:-0} -eq 1 ]; then
mv /etc/resolv.conf /etc/resolv.conf.old
cp /etc/resolv.conf- /etc/resolv.conf
chmod 644 /etc/resolv.conf
fi

if [ -f /etc/ppp/resolv.conf ]; then
mv /etc/ppp/resolv.conf /etc/ppp/resolv.conf-
fi


/etc/ppp/chap-secret (chmod 0600 /etc/ppp/chap-secretしておくこと)

em * em

USBアナライザとD02HW

「NetBSDでD02HWをつかう」では比較的(というかほぼ全部)試行錯誤でデバイスの設定に必要な条件を推測してデバイスドライバへのパッチを作成した。ハードウェアを触るときによく感じることは「見えないものはわからない」ということだ。当たり前のことだが、電気信号は眼にはみえないので、推測するしかなくそれはいつまでも推測のままで推移してしまう。電圧を測るのにテスターが必要で、時間ドメインの波形をみるのにオシロスコープが必要なように、扱う事象を見るための専用の「アナライザ」が必要になることは多い。実はとある目的のためにUSBバスアナライザが手元にあったので、D02HWのデバイスドライバ作成作業を検証してみることにした。

手元にあるのはellisysUSB Explorer 200だ。これは、USB2.0のHighSpeed(480Mbps)に対応したアナライザで、Windowsソフトウェアと組み合わせることでだいたいすべてのUSBイベントを表示してくれる、という便利な逸品である。ソフトウェアは誰でもダウンロードできるようになっていて、USBバスのキャプチャファイルを入手したときに誰でも表示できるのがうれしい(ちなみにキャプチャファイルの拡張子はUFOだ)。
写真でわかるように、アナライザを解析ソフトが入っているPCにつなげて、デバイスとデバイスをつなげるホストの間にはさむようにアナライザを配置する。これで、デバイスとホスト間のデータをインターセプトするという仕組みになっている。
まず、Windowsで動かしてみたときにUSBバスにどんなトランザクションが発生しているかを見てみた。
NetBSDでEmobile D02HWを使う(3)」にあるUSBのインターフェイスとエンドポイントの情報を見ながら、どのエンドポイントとホストがどんな通信をしているかを眺めてみる。アナライザは、

たとえばこんな情報が表示されて、トランザクションのタイプと中身のデコード結果をだしてくれるので、それとUSB2.0の仕様書(このへんにある)を眺めながら追っていく。赤裸々にどんなトランザクションをやりとりしているかを見ていてわかったことは以下の3つ。

  • NetBSDにつくったパッチは正しい。
  • ATコマンドいろいろ
  • D02HWは実はさらに別のインターフェイスをもっている。
最初のは、結局あのシーケンスを送らないとデバイスは言うことをきいてくれないみたいだ、ということを追認できたということだ。ちゃんとUSB2.0の仕様書を読み込むと、あのメッセージは
  • デバイスへのスタンダードのリクエスト(SET_FEATURE)でHost→Device
  • リクエストタイプはDEVICE_REMOTE_WAKEUP
  • wIndexの0x2はendpoint2を意味する
ということだった。アナライザでの結果をみるとendpoint2はモデムの主通信用のパイプなので、これで意味がわかってすっきりした。
2つめのATコマンドいろいろはたぶんWindowsとかのモデム定義ファイルをみればわかる情報なので大した追加情報ではない。問題は3つめで、確かに「NetBSDでEmobile D02HWを使う(3)」では、モデムとして認識されるようになると3つのインターフェイスディスクリプタが見えている。ひとつはモデムでひとつはUSBストレージデバイスだ、ということもわかっていたが、実はなぜもうひとつみえるのか、は謎のまま残っていた。まだ実際にコードを書いて叩いてみたわけではないのだが、USBのBUSトランザクションを見る限り、

INTERFACE descriptor 1:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=1 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

のendpoint5はもう一本のモデム制御用のパイプらしい。まだ憶測だが、endpoint2でデータ通信しながらでもモデムが管理しているHSDPA由来の制御情報などを並列してとれるのではないかと予想している。これを扱うデバイスドライバかくのはどうしたもんか、と悩みがふえてしまった(しばらくはきっとやらないけどね)。

(12/28追記:実験してみたらモデムモードに入った後は3本ともシリアルらしい。2本目をはやすハックをしてみた、あとで詳しく書く予定)
(12/30追記:やっぱり3本目はusb mass strageだとおもうなあ。コードかいてみるか。。)
(1/4追記:usb mass strageでした。)

というわけで、アナライザを使うとみたいことも見えるけど、まったく気が付いていなかったようなことも見えてしまう、ってことで。今回はおしまい。

2007年12月25日火曜日

NetBSDでEmobile D02HWを使う(4)

Windowsやmacのドライバでは抜き差しせずとも使えるということは、なんらかの方法でこの2つのモードの切り替えができるはずだ。機能の切り替え、はデバイス毎の特有の操作なので一般的な方法は存在しない。USBのバスアナライザとかがあれば、windowsのドライバの挙動などを観察して切り替えるための魔法の言葉を見つけることもできるのだが、休日に家で作業をしていたので手元にそんな便利なものはなかった。
というわけでいろいろと試行錯誤してみることにする。

  • 試行1:D02HW(E220)のファームウェアの立ち上がりが遅くてうまく処理ができていないのでは無いかと思って、attachルーチンのあちこちにdelayを入れてみる。あちこちにusbd_delay_ms(dev, 500); などと書いてみて、挙動がどう変わるか地道な作業をがんばる(2時間くらい)。


/* Find the endpoints */
usbd_delay_ms(dev, 2000);
id = usbd_get_interface_descriptor(sc->sc_iface);
sc->sc_iface_number = id->bInterfaceNumber;

あたりに2000msくらいのdelayをいれると、認識しない→差し直すと認識→次に刺し直すと認識しない→また差し直すと認識、と安定してツンとデレを繰り返すようになった。しかしディレイを入れるだけではどうもこれ以上は進まないようだ。次の方法を考えないといけない、ネットワークで識者(USBシリアルの偉い人)と相談するととりあえずVENDORリクエストなどで適当な値をおくってデバイスをつつくかくすぐるかしてやるのがいい、と助言をうけた。設計している人も人の子なので別に難しい値ではなく0とか1とか2とかそんな値を送ると動き出すことがあるらしい。

というわけで、sys/dev/usb/uplcom.cを参考にして実験用にubsa_reset()という関数をでっちあげてみた。


static usbd_status
ubsa_reset(struct ubsa_softc *sc)
{
usb_device_request_t req;
usbd_status err;
#define UBSA_SET_REQUEST 0x1

req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
req.bRequest = UBSA_SET_REQUEST;
USETW(req.wValue, 0);
USETW(req.wIndex, sc->sc_iface_number);
USETW(req.wLength, 0);

err = usbd_do_request(sc->sc_udev, &req, 0);
if (err) {
printf("%s: send reset fail\n", __func__);
return (EIO);
}

return (0);
}


  • 試行2: ubsa_reset()関数をattachの適当な場所においてみて挙動を見る。が、デバイスからお断りのエラーが返ってくるのでどうもうまくいかないらしい。
  • 試行3: bmRequestType を UT_WRITE_DEVICEにしてbRequest = UR_SET_FEATUREにしてやってみることにする。これはUSBの標準機能なので失敗することはないはず。コードは適当に改変する。
ここで変化が起こった。なんとリクエストを送りつけると、抜き差ししてもモードの変換が発生せずに常にモデムとして見えない状態になる。「逆行してるじゃん」と思うのは素人だ。なにかアクションが返ってきたと言うことは、その近くに正解が隠れている可能性が高い。試行錯誤を繰り返して問題を追い込んでいくことにする。ここまで7時間近く作業をしていてどうもだれぎみだったのが、ちょっとテンションがあがってきた記憶がある。脳内麻薬でもでていたんじゃないかな。しばらくするとリクエストのwIndexの値を0x2にすると、デバイス側がUSBリセットをかけてきて一度デタッチされ、再度OSがアタッチするときにはモデムに化けている、という挙動をするということが判明。ようやくつねにデレにするマジックワードをゲットできた。

あとはコードをまとめるだけだが、最終的には、

/*
* Hauwei E220 needs special request to enable modem function
* XXX: is there more smart methods?
*/
Static usbd_status
ubsa_e220_modechange_request(struct ubsa_softc *sc)
{
#define UBSA_E220_MODE_CHANGE_REQUEST 0x2
usb_device_request_t req;
usbd_status err;

req.bmRequestType = UT_WRITE_DEVICE;
req.bRequest = UR_SET_FEATURE;
USETW(req.wValue, 0x1);
USETW(req.wIndex, UBSA_E220_MODE_CHANGE_REQUEST);
USETW(req.wLength, 0);

DPRINTF(("%s: send e220 mode change request\n", __func__));
err = usbd_do_request(sc->sc_udev, &req, 0);
if (err) {
DPRINTF(("%s: E220 mode change fail\n", __func__));
return (EIO);
}

return (0);
#undef UBSA_E220_MODE_CHANGE_REQUEST
}

みたいな呼び出しをUBSA_ATTACH()の中で

/* Find the endpoints */
/* Hauwei E220 need special request to change its mode to modem */
if (sc->sc_devtype_e220) {
err = ubsa_e220_modechange_request(sc);
if (err) {
printf("%s: failed to change mode: %s\n",
devname, usbd_errstr(err));
sc->sc_dying = 1;
goto error;
}
}

id = usbd_get_interface_descriptor(sc->sc_iface);
sc->sc_iface_number = id->bInterfaceNumber;

こんなふうに呼び出す実装にしてみた。このカーネルでは、


ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ubsa0: HUAWEI E220 need to re-attach to enable modem function
ubsa0: at uhub0 port 2 (addr 2) disconnected
ubsa0 detached
ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ucom0 at ubsa0

みたいに自動的にucom0がアタッチされるようになる。いったんdettachされるのが美しいか?みたいな議論をするともめそうだけど、デバイスの仕様ってことで許してくれないかなーとコッソリ期待している。

tipでucom0につなげてみると、こんな感じにみえる。userland-PPPでemにもつながったので、たぶんこれでサポートのためのハックは完了。実はまだNetBSDのkernel ppp(pppd)の設定がうまくいかないので、設定方法を教えてくれる人も募集中。


#tip dtyU0
connected
at
OK
atI
Manufacturer: huawei
Model: D02HW
Revision: 11.005.08.00.168
IMEI: 3560600XXXXXXXX
+GCAP: +CGSM,+DS,+ES

OK


というわけで、D02HWを動かせるようになるまでに実際にどんな作業をしたかを書いてみた。この手の情報はできた後のコード解説程度が多いが、作業をした過程とか、どんな事を考えて作業したかとか、どんな失敗をしたか、って情報の方が、自分で同じような作業をするときに役に立つ気がしている。
あと、半年たつと自分でも何やったのか忘れることがあるので備忘録にもなることを期待しながら、できる限りこんな感じの作業記録をとってみたいなあーと考えている。

2007年12月24日月曜日

NetBSDでEmobile D02HWを使う(3)

インスタントに「NetBSDでイーモバイルを使う方法」が書いてあるかというとそうでもなくて残念だと思う人もいるかもしれない。今回書いたpatchはすでにNetBSDには送ってあるのだがこれが採用されるかどうかはまだ不明なので、最近の-currentをCVSチェックアウトしただけでは現状では使えない。今回も「どうやってUSBデバイスを動かすようにしたか」というネタが続いているのでその辺に興味があるならおつきあい頂きたい。


現状のubsaドライバではどうもうまくD02HW(E220)をアタッチできないので、もう少し先人達の知恵を借りることにした。FreeBSD系のメーリングリストを眺めるとこんな記述があった:「いったん接続して認識に失敗してから、ゆっくりとUSBコネクタを引き抜いてデタッチされた瞬間にもう一度指し直せば認識する(かも)」(意訳)、最初みたときは何のおまじないかと思っていたのだが、再度好意的に解釈し直すと「電気的な接続を切らずに信号線だけ切りはなして再接続するといんじゃない?」と言っているようにも思える。というわけでやってみた。数回トライすると、本当に認識してucom0が生えてきた。なんで?と思うとこの状態ではさっきまで見えなかったエンドポイントが複数見えるようになっており、その中にインタラプトエンドポイントが含まれている。

usbctlというコマンドでUSBのデバイスの情報を取得してみると、差し直しの前後で全然別物のデバイスになってしまったようにみえる。長いけど抜粋してみるとこんな感じ。

元々の情報


DEVICE addr 2
DEVICE descriptor:
bLength=18 bDescriptorType=device(1) bcdUSB=1.10 bDeviceClass=0 bDeviceSubClass=
0
bDeviceProtocol=0 bMaxPacketSize=64 idVendor=0x12d1 idProduct=0x1003 bcdDevice=0
iManufacturer=1(HUAWEI Technologies) iProduct=2(HUAWEI Mobile) iSerialNumber=0()
bNumConfigurations=1

CONFIGURATION descriptor 0:
bLength=9 bDescriptorType=config(2) wTotalLength=32 bNumInterface=1
bConfigurationValue=1 iConfiguration=0() bmAttributes=a0 bMaxPower=500 mA

INTERFACE descriptor 0:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=0 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=8 bInterfaceSubClass=6
bInterfaceProtocol=80 iInterface=0()

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=3-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=4-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

current configuration 1

差しなおした後の情報


DEVICE addr 2
DEVICE descriptor:
bLength=18 bDescriptorType=device(1) bcdUSB=1.10 bDeviceClass=0 bDeviceSubClass=
0
bDeviceProtocol=0 bMaxPacketSize=64 idVendor=0x12d1 idProduct=0x1003 bcdDevice=0
iManufacturer=1(HUAWEI Technologies) iProduct=2(HUAWEI Mobile) iSerialNumber=0()
bNumConfigurations=1

CONFIGURATION descriptor 0:
bLength=9 bDescriptorType=config(2) wTotalLength=85 bNumInterface=3
bConfigurationValue=1 iConfiguration=0() bmAttributes=a0 bMaxPower=500 mA

INTERFACE descriptor 0:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=0 bAlternateSetting=0
bNumEndpoints=3 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=1-in
bmAttributes=interrupt wMaxPacketSize=16 bInterval=128

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=2-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=2-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

INTERFACE descriptor 1:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=1 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=255 bInterfaceSubClass=255
bInterfaceProtocol=255 iInterface=3(Data Interface)

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=5-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

INTERFACE descriptor 2:
bLength=9 bDescriptorType=interface(4) bInterfaceNumber=2 bAlternateSetting=0
bNumEndpoints=2 bInterfaceClass=8 bInterfaceSubClass=6
bInterfaceProtocol=80 iInterface=0()

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=3-in
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

ENDPOINT descriptor:
bLength=7 bDescriptorType=endpoint(5) bEndpointAddress=4-out
bmAttributes=bulk wMaxPacketSize=64 bInterval=0

current configuration 1


つまり、差しなおしたあとのデバイスには元々見えなかったインターフェイスが生成されていて、それがみえると今回の目的であるモデムとして利用できるようになる、ということらしい。裏技みたいな「ゆっくりやさしくぬいて、素早くさす」みたいなことを何回もやるのは大変なので、USBのプロトコル的にうまくできないか?という方針のまま作業をすすめていくことにする。

続く

NetBSDでEmobile D02HWを使う(2)

CS5536を扱った時は十分に詳しい仕様書が入手できて先がみえる作業(つまり作ればできる)だったが、今回のD02HW(E220)に関しては外部の仕様書が存在しないため、いろいろと試行錯誤が必要となる。幸いにも、「ごにょごにょすると動く」とか「不十分だけど動くかも」といった情報があるし、Linuxでは動いているらしいのであまり深刻にならずにやってみることにしよう(ま、楽しみでやっているわけだし)。
前回のエントリでも出したが、この段階では接続するとこんなメッセージがkernelから出力される。

ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
usbd_set_config_index: illegal index
ubsa0: failed to set configuration: INVAL

問題となっている部分として目につくのが"usbd_set_condig_index: illegal index"の部分だ。ベースとなるsys/dev/usb/ubsa.cを開くと、その部分はUSB_ATTACH(ubsa)の中のこんなコードだった。

/* Move the device into the configured state. */
err = usbd_set_config_index(dev, UBSA_CONFIG_INDEX, 1);
if (err) {
printf("%s: failed to set configuration: %s\n",
devname, usbd_errstr(err));
sc->sc_dying = 1;
goto error;
}

usbd_set_config_index()の実体はsys/deb/usb/usb_subr.cにあるので見てみると、USBのデバイス的に「そんなINDEXはないよ」ということを意味している。ここでデバイスに頑張れといっても無い袖を振ってくれるわけでもないので、ここはUBSA_CONFIG_INDEXの値があっていないのだろうと考えるのが正しそう。もともとの値は1だったので、とりあえず0にしたkernelを作って試してみる。

すると別のメッセージが出るようになる。ubsa.cをみると、それなりにコードの先のほうまで進んでいるようだ。

ubsa0 at uhub1 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
ubsa0: Could not find interrupt in

該当部分のコードは、さっきと同じUSB_ATTACH(ubsa)の中の、

if (sc->sc_intr_number == -1) {
printf("%s: Could not find interrupt in\n", devname);
sc->sc_dying = 1;
goto error;
}

である。ちょっと前の方にもどって追ってみると、USBデバイス(D02HW)からエンドポイントを取得してみたけど必要な割り込み用のエンドポイントをもらえなかった、のであきらめちゃう、ということらしい。割り込み用のエンドポイントがもらえないのは一大事なのでデバイスドライバとしてはあきらめるのは当然だとは思うが、動かしたい人としてはそれでは困る。
可能性としては、

  • このデバイスは割り込みエンドポイントがない変なシリアルデバイスである
  • 本来は割り込みポイントがあるが何らかの問題で見えていない
のどちらかだが、「ガシガシさしなおしていたら見えたことがある」とかいうWebなどでの報告を信じる限り、どちらかというと後者の可能性が高い。もし前者だったとしたらシリアル通信のドライバとして全然別のロジックが必要になるから偶然で動くなんて考えづらいからだ。つまり、
  • 残りのコードはおおむねあっていて、うまくデバイスとしてコンフィグレーションできていない
じゃないかと予測して次のステップに進むことにした。

続く

2007年12月22日土曜日

NetBSDでEmobile D02HWを使う(1)

イーモバイルD02HWが手元に来たので、NetBSDで動かせるようにしてみた。今回はそのあたりの話を書いてみることにする。
D02HWはD01HWとほとんど同じハードウェアで、HUAWEIE220のOEM製品である。今回も手をつける前にとりあえずgoogleとかしてみると、動いたとか動かないとかどうも歯切れがわるい。NetBSDの場合ではcurrent-usersという最新版の開発ブランチのkernelに関する話題を扱うメーリングリストがあるのだが、いくつかのスレッドがあった割には実際のコードには反映されていない状態だった。
まあ、手元に実際のデバイスがあるから、と手をつけてみることにした。

D02HWはUSBのHSDPAモデムだが、モデムの機能の前にデフォルトではUSBストレージデバイスとして認識されるようになっている。たとえば、なんの設定もしていないNetBSDではumassデバイスとして認識・アタッチされて、CDROMとして中身をmountできるようになる。中身はwindows用のデバイスドライバ群で、NetBSDでモデムとして使おうとして思っているときには何の役にもたたない。差し込みのたびにumassとして認識されるのもうるさかったので、まずそのへんをdisableする。umassのdevice driverはsys/dev/usb/umass.cあたりにある。中身をざっと見てみると特定のデバイス毎の特別な設定を書くためのhookが用意されていて、sys/dev/usb/umass_quirks.cに追加できるようになっている。今回は単純にattachされないようにするだけなので、Static const struct umass_quirk umass_quirks[] = {の中に

{ { USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 },
0, 0, 0, 0, 0, NULL, NULL
},

というエントリーを追加した。これで、USBのベンダIDがHUAWEIでプロダクトIDがE220のデバイスはUSBストレージデバイスとして認識されなくなる。NetBSDでは、専用のドライバが存在しないデバイスはugen(汎用USBデバイス)としてattachされるので、umassをdisableすると次からはD02HW(E220)はugenとして認識されるようになる。

このあたりでugenとして認識されたD02HWをユーザーランドからlibusbをつかってなんか操作できないかと、数時間時間を費やしてみたが、得たものは

  • NetBSDのlibusbはugenとして認識されたデバイスしか触れないということを認識
  • libusbを使った基本的なプログラムの書き方と経験
だけで、今回のデバイスドライバつくりに特に役に立つことは(その時点では)なかった。今思えば、あの段階でうまくugen&libusbを使うことで不明なUSBのコマンドを探すような場合に役にたつかも、と思えるようになったので、まあ費やした時間は無駄ではなかった(ことにした)。

と、ここからが本題となる。まずは先人達が到達したところまで追い付かなければならない。ベースとなっているUSBシリアルドライバはubsaドライバ(sys/dev/usb/ubsa.c)なので、このドライバがD02HWのドライバとなるように、Static const struct usb_devno ubsa_devs[] = {


/* HUAWEI E220 / Emobile D0[12]HW */
{ USB_VENDOR_HUAWEI, USB_PRODUCT_HUAWEI_E220 }

と追加する。この段階でkernelをつくってD02HWを接続すると

ubsa0 at uhub0 port 2
ubsa0: HUAWEI Technologies HUAWEI Mobile, rev 1.10/0.00, addr 2
usbd_set_config_index: illegal index
ubsa0: failed to set configuration: INVAL

と表示される。確かに問題がある、というところで概ねこのデバイスを触る準備が整った気になる。ちょっと一休み。

具体的な料理の方法は次のエントリーで説明する予定。