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

Ruby on Rails で googlemapをかっこよく



今回はいつものハードウェアとは趣が違って web サービスのテクニックの話である。

ruby on rails上でgooglemapをつかった表示を行うときにすこし気の利いたインタラクティブな動作をさせるところがトピックである。

ruby on rails で googlemapを扱うのに使われる pluginとしてym4r/gm というのがある。geocodingもふくめ非常に簡単に使えるのだが、逆に細かいところに手がとどかない。

事の発端はgooglemapの地図上のマーカをクリックすると吹き出し形式でメッセージが見られるインターフェースを実装したのだが、マウスオーバーで見られるほうがいいのではないかという意見をこのblogの共著のYUO氏から貰ったことであった。

そこでこの機能を実現するためにgoogleを何時間かしらべたが、googlemapの使い方はわかったものの、railsでの ym4rを使った例は英語サイトもふくめて直接的な回答にはいきあたらず、ym4r開発者のサポートBBSでもほぼ1年ほど放置プレイだったので備忘録もふくめて書いておく。

googlemap上 でマウスをつかった特別なアクションを使うためにはaddListenerによってリスナを設定する必要がある。しかしながら、 ym4r/gm のコードをそのまま使ってマーカを設定するだけではこのような設定ができない。リスナを設定するためにはリスナになるオブジェクトを変数として代入し、バインドしてやる必要があるためだ。

declare_init をつかってオブジェクトを変数として宣言しなおしてやる必要がある。そしてこの後で、宣言された変数を使ってリスナを設定するのだが、このときに既存のメソッドは役に立たない。汎用につかえるrecord_initメソッドを使うことにより、 initializeのルーチンの中に自分で設定したjavascriptをそのまま書き込むことができるのでそれを利用することになる。


書き上げたコードの一部を切り出しておくと以下のようになる。

ポイントは markerを変数化するときに#{x.id} をつけることによりすべてのマーカで違う変数名を持つようにすることが1つ目である。もう1つのポイントとしては record_init をつかって直接 googlemapを動かすjavascriptのGEvent.addListener メソッドを記述するところである。また、 変数 pで生成している htmlの終端に改行がはいっていると地図のjavascriptのレンダリングがうまくいかないようなのでちゃんと partialなレンダリングをした後にchompすることを忘れないことが大切なようだ。

さらに今回はlistenerとして mouseover のほかに mouseout を設定することで、マウスがはずれると即座にウインドウが自動で閉じる処理を行っている。


実装例

marker = GMarker.new([x.lat,x.lon],
:title => " #{x.created_at.strftime("%m/%d-%H:%M")}" )
p = render_to_string(:partial => 'msg',
:locals => { :tag => tag , :pm => x }).chomp

@map.declare_init(marker,"marker#{x.id}")
@map.overlay_init(marker)
@map.record_init "GEvent.addListener(marker#{x.id},
'mouseover',
function(){marker#{x.id}.openInfoWindowHtml(\"#{p}\")}); "
@map.record_init "GEvent.addListener(marker#{x.id},
'mouseout',
function(){map.disableInfoWindow();
map.enableInfoWindow();}); "

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

メモ: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くらいでしかないので更に解析と作業を進める。

(続く)