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

2008年11月4日火曜日

gainer-rubyを NetBSDの箱で動かす

gainer-rubyは rubyで作られたgainerのライブラリであり、rubyを知っていれば非常に簡単にgainerを使うことが出来るのが特徴だ。GUIを使った作品作りとは異なるが、本blogでも twitterとgainerをつなげるなどのエントリで活躍している。ruby自体にさまざまなライブラリが豊富なので twitterやIMなどとの連携を行いやすく、グラフィカルなインスタレーションではないような用途では非常に重宝する。

個人的には温度計を接続し、activerecord経由でDBと連携させて一定時間おきにtwitterに投稿するといった使い方をしている。

個人的なgainer-rubyの問題としては NetBSDをいれた小さい箱で動かなかった点がある。これはシリアルポートのtermiosの設定がうまくいっていないようなので、ここを修正することとした。また、ダウンロードできるgainer-rubyのソースには PGAのゲイン調整のコードが入っていないのでこれも追加したものを作ってみた。動作を確認したNetBSDは 4.0R のi386版である。
PGAのほうは作者にも送ってあるので次のバージョンには反映されるかもしれない。

コードの差分をリンクしておくので、試してみたい方はsvnで checkoutした gainer-ruby にあててみてほしい。PGAのゲインコントロール関数は amp_gain_agnd/amp_gain_dgnd でprocessing のライブラリと対応させてある。シリアルポートはtermiosの設定を変更したが、NetBSDの他にOSX(10.5) でも動作することは確認済みである。


これをつかった作品の全容はまた後ほど

2008年2月17日日曜日

NetBSDで「24時間ワンセグ野郎」(新デバイス)(3)

新デバイスさらに続き。

バイトストリームを眺め続ける人生にもちょっとだけ疲れたので、ここらで絵(と音)でも見てみたい、という願望が大きくなってきた。というわけで、ちょっと真面目に絵と音声を見る部分を書いてみることにした。もし絵とか音声が見えたら、それがちゃんとしたMPEG2TSストリームとして扱えているか?っていう検証になるし。(バイトストリームを検証するためにはちゃんとパーサーと表示系をいちいち書かないといけないから、すぐには対応できないし)
というわけで、新デバイスでも生TSをキャプチャして録画までできることを確認できた。(絵は今とったらこれだった、てだけ、意味はないです)。ワンセグチューナさわっていて初めてまともに絵を見たよ。

下手に書いちゃって動かないのに買っちゃう人がいたら災難だから「新デバイス」とだけ書いていたけど、これで今まで伏せていた製品名をかける。写真のRockridgesoundのDitune(DUS-01)が最近触っていたデバイス。秋葉原だと3000円弱で買える&結構店頭に並んでいるので見たことがある人も多いかもしれない。
外部アンテナはつながらないけど、選択肢の一つとしてはいいかな、と思っている。

NetBSDで「24時間ワンセグ野郎」(新デバイス)(2)

新デバイスの話再び。

前回のエントリー(NetBSDで「24時間ワンセグ野郎」(新デバイス))に、生TSっぽいのが出ているとかいてしまったが、ちょっと寝てから冷静に見直してみると、なんか変だということに気がついた。

簡単にいえば、「先頭の0x47以降は本来受信したはずのデータ列と異なっている」ようだ。キャプチャされたTSの解析結果も載せてあったので、鋭い人なら一目で気がつくような気もするけど、異様にフラグが多かったり、アダプテーションフィールド長が255だったり、(あそこには含まれていないけど、PIDが0x1fffを超えていたり)、していた。

また、

  • だいぶ感度が悪そう。ちょっと、置き場所を変えるだけで、MPEG2TSのパケットでTS_ERRORフラグが立ってしまう。あんまり頻度が高いと、TS_ERROR付きでも眺めてみる、みたいな処理に変えないといけないかもしれない。
とも書いていたが、これもTS_ERRORビットの値がずれているのが問題だったようだ。値を読み替えてからみてみると、感度はそんなに問題ないみたい。

幸いにもMPEG2TSヘッダは固定長で単純かつ、特定の部分に決まった情報が出やすいという特徴があるので、しばらく眺めていると変換のパターンは見えてくる。もう少し解析しないとだめだけど、今のところ、「各バイト単位で特定の値で排他的論理和をとる」っていう単純なルールでもとに戻るようだ。(コンティニュティカウンタの値が1->0->3->2->5と進むところで、XORじゃないか、って推測できたのが決め手だった)。

というわけで、今度こそ生TSだと思われる出力。それっぽくなった。
これで、MPEG2TSパーサーに戻れる。

2008年2月16日土曜日

NetBSDで「24時間ワンセグ野郎」(新デバイス)

このエントリーの一行まとめ: 使えるUSBワンセグチューナーは、実はたくさんある、かも?

本家の昨晩のエントリーでふれられていた「一部で非常に不評なLOG-J200のUSB(笑)」をおもに専門に扱っている:)わけですが、そこに非常に気になる記述があった。

  • ちゃんとエンドポイントは分かれてるしTSのデータはアイソクロナスで送ってきているし ^^; なんといってもコントロールコマンドがシンプル簡単で使いやすい!(笑) それこそ他OSでいじるベースとしては便利そうな感触ですよ。USBXPressではなく今度はEZ-USBベース、そして実売価格は3000円。探せばいろいろあるものです。

しかも入手性がいいようなので注目に値する情報だ。これで、24時間ワンセグ野郎コミュニティ(?)もさらに盛り上がるに違いない。
と、そのエントリーを読んだのが午前2時半すぎ。それまでは、ARIBの資料を眺めながらワンセグのMPEG2TSストリームの制御情報を処理する部分をつらつらと書いていたのだが、デバイスを触りたい気分がいきなり盛り上がってしまった。私も実は、その辺でみかけるたびにUSBデバイスを買ってあって、そろそろ家があふれそうだったりしているので、そのなかでまだ開けていなかった箱をいくつか取り出してUSBストリームを眺める、という作業に従事してみた。

2-3種類眺めてみると、やっぱりいろいろと特徴が見えてくる、その辺の話はまた今度まとめる機会を探すとして、また新しく「188byte単位の情報転送 & 先頭バイトが0x47」のストリームを吐くチューナーを見つけてしまった。俄然盛り上がったので、NetBSD用のデバイスドライバも書いてみた。(おかげで眠い)

このデバイスは、
  • CPUは8051ベース(シリコンラボラトリC8051F342)
  • チューナーはシャープ製
  • アンテナはロッドアンテナのみ
  • 眩しく光る青色LED付き
  • 3000円台のお得な値段で入手可能かも
というハードウェアで、基板を眺める限り、J200よりは出来がよさそう。挙動としては、
  • コマンド用のEndpoingとデータ用のEndpointがわかれている
  • データ用Endpointはアイソクロナス転送で188Byte単位で送ってくる
  • 初期化コマンド等はほとんどいらない。お行儀よくチャネル切り替えをすれば動く
  • 電波が安定するまではストリームを出力しない。チューナーがセットアップされたかどうかを問い合わせてみて0x1がかえってきたら、転送開始、みたいな処理をする。
というかんじで、この前のエントリに書いた不満はおおむね解消されているようだ。

ただ、気になるのが、まだソフトウェアが悪い可能性もあるので何ともいえないけど、だいぶ感度が悪そう。ちょっと、置き場所を変えるだけで、MPEG2TSのパケットでTS_ERRORフラグが立ってしまう。あんまり頻度が高いと、TS_ERROR付きでも眺めてみる、みたいな処理に変えないといけないかもしれない。付属のソフトではそんなに画像が乱れるみたいなことが頻発するわけではないので、もう少し調査が必要。
MPEG2TSのストリームはとれているみたいなので、もう少し調査して詳細をお知らせしたいとおもいます。というわけで、MPEG2TSストリームが出ている証拠も載せておきます。先頭バイトが0x47だけ、ってわけではないので安心。

2008年2月15日金曜日

(ワンセグ)電波時計(の特性)


一発芸のつもりだった「ワンセグ電波時計」だったが、もう少し詳しく特性を調べてみることにした。
ARIBの仕様書では時刻情報(TOT)のデフォルト送出間隔は5秒と決まっているらしいが、その5秒刻みというのがどのくらい正確なのか?というあたりを知りたくなったからだ。

前回のエントリーではNTPで同期しているNetBSDの時刻情報と並べて表示していたが、今回はgettimeofday()を使ってマイクロ秒単位でTOTとTOTの間隔を計測してみた。108個(つまり540秒ほど)連続して受信したTOTの間隔の統計的な値を表に示しておく。

思っていたよりも精度が高いようで、うまくつかえば結構便利かもしれないな、と別の野望も妄想中。簡単な機材で大域時刻同期ができるって結構すごいことなんだよね。

2008年2月14日木曜日

NetBSDで「24時間ワンセグ野郎」(2)

さすがに平日はあまり時間が取れないので、あっと驚くような進捗があるわけではないけど、進んでいないというわけでもないので、メモがてらに書いてみることにした。Log-J200のUSBデバイスとしての感想など。

USBデバイスとしてのLog-J200
ざっくりいうとLog-J100もUOT-100も同じなんだが、どれもこれもUSBデバイスとしてみたときにはあまりいけているデバイスだとは言い難い。一言でいうなら、

  • USBデバイスのファーム設計のセオリーを見直してこい!
と、受け側のソフトを書きながら思わずデバイスに突っ込みをいれてしまうような、そんな仕様がいくつもある。たとえば、
  • 機能とエンドポイントの分離がうまくできてない、とか
  • コマンドを受理しても返答しない、という動作をするコマンドがある
  • なぜかbulk転送になっているMPEG2TSストリームの転送モード、とか
  • なぜか64Byte長で細切れな転送
とか。最初の「エンドポイントの使い方」は、endpoint0でやればよいようなデバイスの制御が、なぜかMPEG2TSストリームをやりとりしているendpoint2にのっている、とか、そのあたりが顕著な例。2つめも問題で、USBのホストコントローラ的には受理したならackを返してほしいと思っているのに、黙ってしまうのでコマンドがストールしたように見えてしまう。必ず受理される、と信じて送らないといけないのはどうかとおもう。
で、性能上問題になるのは3つめと4つめ。MPEG2TSのパケット長は188Byteで、空から約380KBps程度の速度でどんどんと情報が降ってきている。これを受け取るそばからUSBバスの上に流してあげる、というのがUSBワンセグチューナデバイスのお仕事。(たぶん)普通はこんな特性の情報を扱うためには、USBではアイソクロナス転送モードというのを使うことになっている。アイソクロナス転送にするとハンドシェイクやリトライをしないので時間制約に即した転送ができる、ので、次々に発生する事象(音とか映像とか電波とか)を扱うのに向いている。Log-J200(とその仲間たち)はなぜかMPEG2TSストリームの転送にBulkエンドポイントを採用している。Bulkエンドポイントでは転送サイズは64byteまでなので、188Byteの転送をするために少なくとも3回(J200の場合は4回)の転送要求発行しないといけない。

ちょっと計算してみるとわかるのだが、ワンセグのパケットは約1.2msくらいに一回の周期で受信される。つまり、1.2ms以内に4回のトランザクションを必ず発行するような動作をしていないと取りこぼしてしまう。
いまどきの速いコンピュータにとっては1msなんて無限な時間にみえるかもしれないが、実時間動作が苦手なUNIX系のOSではちゃんとやらないと実際に取りこぼしてしまう場合があった。

特に今回はugenをつかったユーザーランドアプリケーションとして書いてみているので、そのあたりは結構シビアだなあ、というのが感想。eggmanさんのlinuxデバイスドライバの話をみて、「やっぱりカーネルデバイスドライバのほうがかっこいいし、オーバーヘッド少なくなるからよいだろうなあ。」と思ったのは内緒だ。いつか気が向いたらkernelのデバイスドライバも書いてみよう。
でも、アイソクロナス転送のしてくれていれば、ユーザランドからさわってもこんなことは起こらないので、やっぱり設計に難がある、っていう気がしている。

ちなみに、内部バッファとかも持っていない設計になっているようで、USBデバイスへのポーリングが遅れるとデータの一部が化ける(nバイトシフトしているように見える&nバイト足りない)。最初は何で先頭にMPEG2TSの先頭バイト(0x47)が現れない場合があるんだろうか?と悩んでしまったが、USBアナライザの時間計測モードをつかってみたら、アクセス間隔がWindowsでみた場合とくらべて長いかった。
複数のkerenlへのリクエストをまとめるとか、小技を使って解決している。

Log-J200の受信感度
外付けアンテナがつくのであんまり問題にならないんだけど、受信したMPEG2TSストリームを数字で眺めていると、数パーセントの割合でエラーが出ているみたい。エラーとしては以下の3パターン。
  • そもそも受信できていない。MPEG2TSのカウンターを計測していると取れなかったことがわかる
  • MPEG2TSのトランスポートエラーインジケーターの値が1になっている。つまり、受信時に「このデータ使わないほうがいいじゃない?」と言ってきている
  • 先頭バイトが0x47じゃない。タイミング制約は満たしているはずだとは思うけど、たまに発生する
特にトランスポートエラーインジケーターは受信を開始してから200パケットくらいに全部ついているので、受信機が設定されてから正しいデータ出すまでに300msくらいはかかるってことかな?

NetBSDの24時間ワンセグ野郎の現状
とまあ、USBレベルでは取りこぼしもなく普通に受信情報を取り出せている。とりあえず受信データMPEG2TSの状態でファイルに書き出すこともできる。とまあ、そのくらい。いまは画像を眺めるというよりは、受信したストリームを数字で見ながら、ヘッダとかペイロードとかのいろいろな部分のパーサーとダンプ関数を書いてみて、「ほほー」とか言いながらにやけている状態。今日の参考資料はARIB-TR-B14(英語版)とかARIB-STD-B10(英語版)。なぜ英語かというと無料で公開されているから。(ARIBの英語版配布サイト

というわけで、外堀はほとんど埋まっているような感じですが、本家みたいな作りこみをできるまでには、まだしばらくかかりそうです。

最後に色気はないけど、動作中のテキスト出力など。ワンセグっていうことでPID=0x1fc8のパケット情報。本当になんかプログラムを書いている、ってい証拠に(なるかな?)。

== MPEG2TS header (PID:0x1fc8) -:P == Cont count:08, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:09, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:10, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:11, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:12, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:13, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:14, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:15, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:00, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:01, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:02, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:03, Flags PUSTART TRANSPRIO
== MPEG2TS header (PID:0x1fc8) -:P == Cont count:04, Flags PUSTART TRANSPRIO

2008年2月12日火曜日

NetBSDで「24時間ワンセグ野郎」(1)

MobileHackerz再起動日記で開発が進められている「24時間ワンセグ野郎」だが、ひと月ほど前のエントリーを見たときから「すげえ」素直に感嘆していた。「これはいけてる!」とおもって、その場でUOT-100を6本ほど買ってあった、ということからも(今はすでに入手困難だということを考えて)本当にぐっときたことは推測してもらえると思う。
ちょっと忙しくしていたので出遅れてしまったのだが、これができるということは、別のOS、たとえばNetBSDでも同じようなアプリケーションを構築できるんじゃないかとおもってやってみた、というのがこのエントリーの内容。
この3連休の時間で始めたことなので、本家の完成度には到底およばないけれど、最近一部でブームも盛り上がってきているようなのでちょっと参戦してみようと思う。

UOT-100を持っているから、といってもそれをそのまま使うのは二番煎じすぎる。本家にもなにか貢献できないかなーとおもって最近どこかで安いワンセグチューナを見かけるたびいくつかを購入してみていた。分解したり、USBのデータストリームを眺めていたりもしたのだが、まだ「これは!」という機種には出会えていない。
今回は、Log-J200をつかってワンセグ野郎をしてみることにする。
Log-J200はUOT-100と似たデバイスで、UOT-100と同様に「生TSをバス上に流している」ので、扱いが簡単だから、というのと、「まだ入手できるから」というのが理由。

いきなりkernelドライバを書いてもいいのだが、「ugenでラピッドプロトタイピング」という話も書いていたし、今回は

  • NetBSD ugenをつかったユーザランドアプリケーションで実装
  • ハードウェア改修なしに複数デバイスに対応
  • (できればネットワークサーバ化)
という感じでいってみている。

先に、現状を書いておくと、昨晩から24時間(当然途中では寝ているしごはんもたべてる)くらい経過したところで、
  • NetBSDで複数デバイス同時に認識
  • Windowsデバイスドライバには非依存
  • とりあえずチャネルは2チャンネルくらいは変更可能
  • 生TSだと思われるファイルを書きだしている(けど、まだみえない)
  • MPEG2TSの簡単なヘッダパーサを書いて、それなりにエラーハンドリングをできるようになりつつある
くらい。パケットダンプを見ている限り、それなりに正しくMPEG2TSのキャプチャはできているようにみえるので、あともう少しで画像がみえるか?という期待でここまで来ている、というところ。Log-J200は分解が大変(というか分解するとたぶん復元できない)ので、ハードウェアを触らなくてもいいというのは大きな利点じゃなかなと(個人的には)おもっている。

というわけで、現状報告を先にしておく。ソースをまとめたら公開予定(BSDライセンス)。
次のエントリーで解析した内容を書く予定。

週末の娯楽としては大変楽しめたし、これからもちょっと楽しめそう。がんばろう。

2008年2月11日月曜日

NetBSDでDoCoMo A2502をつかう(-currentにマージ)

NetBSDでDoCoMo A2502をつかう」で書いたようにNTT DoCoMoの3G/HSDPAモデムFOMA A2502をNetBSDで使えるようにして、patchを本家に送っておいた。
今日(2/11)無事に-currentにマージされたようだ。

D02HWにつづいてA2502も今日以降のNetBSD-currentを入れれば使えるようになる。
A2502もちょっと解析(アンテナレベルの取得とか)を進めていたので、もう少し進んだらまとめてみようとおもっている。

今日はお知らせだけで。

2008年2月7日木曜日

NetBSDでSONY GPS-CS1Kを使う

SONYはGPS-CS1KというGPSロガーを発売している。ずいぶん前(発売直後)に買ってあったのだけど、実はあまり使わずに棚の上のオブジェと化していた。一緒にblogをかいているCUEICHIがGoogle Mapと位置情報で遊んでいたのをみてGPSを持っていることを思い出しGPSを使おうと盛り上がった。
が、なぜかNetBSDでは動かない(OSXでも動かない。。)ので原因を調べてみた。
kerenlからのメッセージと、USBバスアナライザの出力を見る限り

  • UR_BBB_GET_MAX_LUNリクエスト受け取ったあとデバイスの挙動がどうも怪しい
  • しばらくリトライすると復帰する
ようだ。リトライのコードを入れてもいいのだが、他のデバイスへの影響が大きそう。あまり美しくはないがNetBSDのumassの例外コード(umass_quirks)を拡張してUR_BBB_GET_MAX_LUNを発行せずに決め打ちでLUNを指定する、というロジックを導入してみることにした。

その結果、sd0 on umass0として認識されるようになって、mount -t msdos /dev/sd0a /mntでディスクとしてマウントできるようになった。とりあえずpatchはsend-prしてある(kern/37948)。

NetBSDで動くようにはなったが、GPSロガーをどんなふうに使うかはまだ思案中。

こんなデバイスをみるたびに、「Windowsで動けばいい、みたいなデバイスの作り方はしないでほしいなー」と思う。で、同時に「Windowsのホストドライバってなにかあってもあきらめずに頑張るなー」とこっそり感心する。ここは純粋に評価してもいいね。

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月28日月曜日

ugenでラピッドプロトタイピング(*BSDでUSB)

*BSDにはugen(usb generic device)という汎用のUSBデバイスドライバが実装されていて、USBデバイスを刺したときに適切なデバイスドライバが見つからなかったときには、ugenデバイスしてアタッチするようになっている。普通は「なんだugenかよ」と思って「サポートされてないじゃんか」となる(普通じゃない人は「しょうがないからデバイスドライバ書くか」となる)のだが、実はugenとして見えるのはそれはそれで大変意味があることだ。
というわけで、ugenを使ったrapid prototypingについて書いてみようと思う。

ugenが出来ることは、

  • /dev/ugenN.MMというスペシャルファイルを介したUSBデバイスとのエンドポイントとの通信
で、read(), write(), ioctl()をつかって、USBデバイスの概ねほとんどの機能にアクセスできる。USBは「ユニバーサルシリアルバス」なのでread(), write()できれば、適切なアクセス方法をプログラムできれば大体なんでもできるはずだ。
もちろん、ugenもデバイスドライバなので、あるデバイスに適切なデバイスドライバが実装されていてkernelにコンフィグされているときはugenとしてアクセスすることは出来ない。(kernelコンフィグによっては強制的にugenにすることもできる。man ugen参照のこと)。

ugenで実装することとkernelで実装することの利点・欠点をまとめると、

ugen利用の利点:
  • kernelで実装しなくてもいいので、開発が楽
  • kernelに実装しなくていいので、配布や利用が楽
  • APIがあまり変わらないので、kernelバージョンがあがっても追従しやすい
  • ugenっぽいデバイスドライバがあるOSなら移植しやすい(かも)
ugen利用の欠点:
  • 参考になる実装や文書が少ない
  • kernelに取り込まれることは無いので、マージはされない
  • kernelスペースとuserlandのやりとりが多いのでパフォーマンスは期待できないかも
  • kernel内のほかのフレームワークとの連携は難しい
という感じだと理解している。本質的にはkernelで書こうがugen経由で書こうがUSB的にはやることはほとんど変わらないので、kernelをかける人もプロトタイプツールとしてugenというのは検討しても良いんじゃないだろうか。

というわけで簡単な使い方から。基本的なステップは
  • usbctlなどのツールでUSB device descriptorを取得して、エンドポイントの構造をざっくりと理解する。D02HWみたいに何かしたら化けるデバイスもあるので、ugenの-Dオプションを使って全部のconfigurationをdumpしておくと便利。何回もみるので、ファイルに出しておいたり、印刷して脇に置いたりしても良い。usbctlとかusbgenコマンドはpkgsrcのusbutilを入れれば入るはず。
  • デバイスを刺したときにでたugenのデバイスのugenN.00をO_RDWRでopen()する。これがコントロールエンドポイント(EP0)になる。
  • 開いたEP0に対して、ioctlでUSB_GET_DEVICEINFOを送りつけると、device descriptorが取得できる。取得したusb_device_info構造体にvendor IDとproduct IDが入っているのでこれでデバイスの認識をする。複数のデバイスをサポートしていないなら、お目当てのデバイスがみつからなければexitしてしまえばよい。
  • お目当てのデバイスがみつかったら、最初に調べておいたエンドポイントを必要な数だけopen()する。openするファイル名は/dev/ugenN.MMのMMがendpoint番号。endpointがINだったらO_RDONLY、OUTだったらO_WRONLYにするのを忘れずに。IN/OUTだったらO_RDWRで良い。
  • 後は開いたファイルディスクリプタに対して、書き込んだり、ioctlを発行すれば、usbのバスに必要なトランザクションが生成される
と、こんな感じ。

デバイスを探索するコードのサンプルを書いてみると、

#include </dev/usb/usb.h>
int
XXX_match(int fd) {
struct usb_device_info udi;

/* get device information */
if (ioctl (fd, USB_GET_DEVICEINFO, &udi) < 0) {
perror("can't get device information");
return -1;
}
fprintf(stderr, "device info: VID:%#.4x PID:%#.4x\n",
udi.udi_vendorNo, udi.udi_productNo);
/* check the device is XXX */
if (udi.udi_vendorNo != XXX_VID || udi.udi_productNo != XXX_PID){
fprintf(stderr, "can't find any NM30 on %s\n", dc->dev);
return -1;
}
printf("==== Found a XXX ====\n");
return 0;
}

のようになる。

使ってみていて注意しないといけない点は、
  • 結構kernelの奥深くをさわっているらしく、お行儀が悪いとすぐにkernelパニックを引き起こす
ということ。openしたファイルディスクリプタの始末や、書き込むメッセージのサニタライズは最初からそれなりにお行儀よくしておいたほうがいい。

長くなったので続きは次回。

2008年1月22日火曜日

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

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

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

2008年1月9日水曜日

NetBSDでWatchdog timerを使う(CS5535/CS5536)

ちょっと前にNetBSDでWatchdog timerを使う(for ALIX) (1) という一連のエントリーを書いたけれど、年が明けたころからNetBSD-currentへの統合作業をやっていた。手元にあるALIXでは普通に動くのだけど、ほかのボードではどうもうまく動かない(watchdog timerをenableすると一瞬でリブートされてしまう)と言うレポートが担当となったcommiterの人からあがってきてその理由を追ってみたので、メモがてらに何が起こっていたかを書いてみることにする。

  • 元々のコードの仮定:CS5536のMFGPT0はNetBSD kernel以外では使っていない。だから、watchdog timer用のtimerとしてMFGPT0を決めうちで使っても問題無い
動かないといわれても動かないボードは手元に無いので、debugの出力をてんこ盛りにしたテスト用のドライバをつくって、何回かやりとりをしてみた。その結果
  • 何者かがNetBSD kernelがアクセスする前に、MFGPT0を設定して(たぶん、何かの用途に使って)いる。
ことがわかった。MFGPTのsetupレジスタはほとんどのビットフィールドがwrite onceレジスタになっていて、一度設定されるとリセットするまで再設定はできない設計になっている。だから誰かが設定してしまうと、その後は必要な設定を上書きすることができないので事実上watchdog timerとしては利用できない。特に問題なのはタイマーの分周器の設定で、分周比が低すぎるとあっというまにタイマーがまわってしまって、watchdogが動く値に到達してしまう、というのがリブートされる理由だった。たぶん問題になったボードではBIOSなどが何かの用途に設定してしまっているのではないかと推測している。

この問題に対応するためにやったことは、
  • gcscpcib_attach()のwatchdog timerを設定するところで、gcscpcib_scan_mfgpt()という関数(とりあえず書いた)を使って、その時点で使われていないMFGPTを探して使うことにした
  • 必要なら決めうちで利用するMFGPTを指定できるようにした
というあたり。更にマージする前に、もう少しお行儀の良いコードにしようと思って
  • MFGPT関連のレジスタの更新ロジックを、仕様書に指定されている「前準備」−「更新」-「後始末」というシーケンスでやるようにした。
前準備ではカウンタを止めたり、割り込みを禁止したりする必要があり、後始末では関連フラグをクリアしたりしなければならないらしい。手元のALIXではそんなことをしなくても動いていたんだけど、debugする過程で書いてしまったので正式採用した。更に
  • watchdogの間隔を最低値(1秒)にすると運が悪い時にリブートしちゃう問題、が起こらないようにタイマーの精度を1Hzから4Hzにあげた。
  • define一つでタイマーの精度を指定できるようにした
  • まともなdebug messageを埋めた
あたりを入れ込んだ。

元々はAMD CS5536用のコードとしてsend-prしたが、CS5536とCS5535はほとんど一緒、ということもあって共用のドライバーとして扱うことにした。で、名前もOpenBSDのglxpcibからgcscpcibという名前に変更。

というわけでHEADにマージされました。cvswebでは明日以降ならみえるんじゃないかな。

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日金曜日

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

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のプロトコル的にうまくできないか?という方針のまま作業をすすめていくことにする。

続く