PEEE802.11

半端者

USBバルク転送の挙動(ZLP: Zero Length Packet)

USB機器のデバイスドライバ書いてる人なら当たり前の話だとは思うんだけど、専門外の自分が調べる機会があったのでメモ。

USBの転送モードはいくつかあって、そのうちの一つにバルク転送というものがある。大容量のデータをやりとりするのに適した転送モードで、記憶装置とのデータのやりとりに使われたりする。このバルク転送を使ってPC(ホスト側)から携帯端末(デバイス側)にデータを送っていたのだけど、あるサイズのデータを送ると、デバイスが応答を停止してしまう現象が起きた。あるサイズというのは、512バイト、1024バイト、1536バイト、…つまり512バイトの倍数である。

最初は、受け取ったデータを処理をするためにデバイス側に追加した自分のコードが悪さしてるのでは?と思ったのだが、いろいろ検索してみるとどうも違うらしい。

まず、USB2.0 High Speedバルク転送のデバイスのエンドポイントのバッファサイズは512バイトである。そして、USB 2.0 の仕様の5.8.3でバルク転送について以下のように書かれている。

endpoint must always transmit data payloads with a data field less than or equal to the endpoint’s reported wMaxPacketSize value. When a bulk IRP involves more data than can fit in one maximum-sized data payload, all data payloads are required to be maximum size except for the last data payload, which will contain the remaining data. A bulk transfer is complete when the endpoint does one of the following:

  • Has transferred exactly the amount of data expected.
  • Transfers a packet with a payload size less than wMaxPacketSize or transfers a zero-length packet

When a bulk transfer is complete, the Host Controller retires the current IRP and advances to the next IRP.

つまり、バッファサイズより大きなデータを送る際はバッファサイズいっぱいまで詰めるだけ詰めて分割して送り、期待するデータ量を送り終わったら、もしくはバッファサイズより小さいパケットが来たら転送が終了する、ということらしい。

ここで曲者なのが期待するデータ量をどうやって知るかということ。USBバルク転送の仕様では、これから送るデータ量をやりとりする方法は記述されていない。ネット上を調べた感じ、どうもデバイスやドライバの実装依存のようなのだ。自分が調べていたドライバやデバイスには、特にそういう実装がないように見える。つまり、送る/受け取るデータ量を事前に知ることができない。ということで、データ転送を終了する条件はただ一つ、バッファサイズより小さいパケットが来ること、になる。自分が直面した問題は、バッファサイズ = 512バイトと同じパケットを送ると、デバイスが転送の途中だと判断し、いつまでも次のパケットを待っている状態になっていた、ということだった。

この問題を解決するための一般的な方法が、上記引用にも書いてあるzero-length packet (ZLP)である。ホスト側が、全部のデータを送り終わり、かつ送ったデータがデバイス側のバッファサイズの倍数だったら、データペイロードが空のパケットを送り、デバイスに転送が終了したことを知らせる。この実装だと、デバイス側がどれだけデータが送られてくるかを知らなくてもよい。

自分が担当しているのはデバイス側の実装だけなので、ホスト側のドライバを書いているチームに実装してもらわなければならない… デバイス側はAndroid Linuxのコードを流用してるから、そのままシンプルにキープしておきたいなあ。

ちょっと古くてUSB2.0までしか載ってないけど、以下の書籍で調べた。訳書だけど自然な日本語でわかりやすかった。

USB 2.0とUSB On-The-Goを含むカスタムUSBデバイス開発のすべて USBコンプリート[第3版]

USB 2.0とUSB On-The-Goを含むカスタムUSBデバイス開発のすべて USBコンプリート[第3版]