以前調査したように、Raspberry PiのUSBコントローラのドライバには何種類かあり、最もシンプルな「csud」はベアメタルで使うには良さそうですが、Raspberry Pi Zero WではUSB Hubを使わないとデバイスが接続できないという問題がありました。
そこで、昨日作った治具で
(A) Raspberry Pi Zero W → 治具 → USBキーボード
(B) Raspberry Pi Zero W → USBハブ → 治具 → USBキーボード
のそれぞれでcsudを動作させてUSB信号を観測してみたところ、どうやら原因が解ったような気がします。
エラーはデバイスを接続した時点で初期化に失敗するというもので、以下のリンク先の記事にあるSETUPステージで起こります。
USBの基礎知識 ――パケットのフォーマットからプロトコルの詳細まで|Tech Village (テックビレッジ) / CQ出版株式会社
通信内容を(A)と(B)で比較してみたところ、(A)では(B)にはないエラーパケットが観測されました。
(B)では、エラーパケットはフィルタされてUSBキーボードへは送られていませんでした。
Error Packetを除けば(A)と(B)の違いはありません。
このパケットをUSBキーボードが解釈できないため、初期化が失敗しているようです。
このError Packetの中身を見てみると、以下のように規則正しい信号になっています。
つまり、ノイズ等によるエラーパケットではありません。
出現位置も決まっていて、
00000001 00011110 10000000 00000001 00010101 SETUP DATA0 ACK 00000001 00011110 10000001 00000001 00001110
という具合に、SETUPの前後にエラーパケットが入っています。
1バイト目はSYNC、2バイト目はPID、最後はCRCというUSBパケットの形式に沿っていますので、エラーではなくて何かの信号だろうと思われましたので、調べてみたところ、これはUSB2.0で導入されたSplitパケット(PID=0001)だと解りました。
Error Packetと表示されていたのは、解析ソフトがUSB1.1(Low Speed/Full Speed)にしか対応していないためのようです。
USB Protocol: Types of USB Packets and USB Transfers | EngineersGarage
High Speed(480Mbps)で行うUSBハブとの通信を、Start Split(SSPLIT)とComplete Split(CSPLIT)で挟むことにより、USBハブ下部の低速なデバイスとの通信もホスト-USBハブ間ではHigh Speedで行えるようにする機能だそうです。
Split Transaction | ルネサス エレクトロニクス
USB規格書を見ると、SSPLIT、CSPLITは以下のようなフォーマットになっていました。
Hub AddressとPortは左のビットが低位ビット、SCは0ならStart、1ならCompleteです。
SとEは組で、High SpeedとFull Speedのデータ送信順を示すようです。
ETはEndpointのType、UはUnusedです。
この情報を元に、上記のError Packetの中身を見てみると、SETUPの前に送られているパケットがHub address = 1、StartSplit、Port = 0、High Speed Dataが先頭、エンドポイントタイプはControl、というパケットであることがわかります。
同様に、SETUPの後に送られているのは上記に対応するCompleteSplitパケットとなります。
本来、USBハブとの通信だけに使用するはずのSplitですが、csudドライバでは本体に直接接続されるデバイスでは常に使用されるようです。
これはcsudドライバが初代Raspberry Piを前提としているためと思われます。
初代Raspberry PiはオンボードにLAN9512チップが搭載されています。
このチップはEthernet兼USBハブになっていますので、初代Raspberry PiではBCM2835の下部には必ずUSBハブが接続されていました。
逆にUSBハブが無いモデルはEthernetポートが無いモデル、すなわちModelA、A+、Zero、Zero W、Compute Moduleです。
これらのモデルにcsudを使ったソフトウェアを載せて、USBハブ抜きで直接デバイスを接続しようとした場合、上記の問題が起こると思われます。
これらのモデルでcsudを動かすには、ポートにUSBハブを接続した場合とそれ以外のデバイスを接続した場合で処理を分けるように、コードを追加する必要があります。
csudの中身をもう少し調べて検討してみようと思います。
コメント