ベアメタルRaspberry PiのUSBドライバの続きです。
csudで、Raspberry Pi Zero Wに直接接続したキーボードを取り外すと処理が正しく行えない問題を修正しました。
root hubからデバイスを取り外した際の「変更あり」フラグが設定できていなかったのが原因でした。
Raspberry Pi Zero WにUSBハブをつないで、そこに接続したキーボードを外したときは、正常に取り外し処理が行われます。
この処理は、概ね以下のような流れで行われています。
UsbCheckForChange
→HubCheckForChange
→HubCheckConnection
→HubPortGetStatus
→UsbControlMessage
→HcdSumbitControlMessage
HcdChannelSendWait // Setup
HcdChannelSendWait // Data
HcdChannelSendWait // Status
HubPortConnectionChanged
→HubPortGetStatus
HubChangePortFeature
HubPortGetStatusでデバイスの接続状況を確認しています。
この処理はデバイスへUSB経由でStatusコマンドを送ることで行われています。
一方、root hubからデバイスを取り外す場合はHcdSumbitControlMessageの内部で以下のように分岐しています。
この場合は、接続処理は正しく行われますが、取り外し処理が行われません。
if (pipe.Device == RootHubDeviceNumber) {
return HcdProcessRootHubMessage(device, pipe, buffer, bufferLength, request);
}
この結果、HcdSumbitControlMessage以降の流れは以下のようになります。
→HcdSumbitControlMessage //ここまでは同じ
→HcdProcessRootHubMessage
switch (request->Request) {
case GetStatus:
switch (request->Type) {
case 0xa3:
// Statusを取得
}
}
root hubのポート(Zero WではオンボードのUSBコネクタに相当)の接続状況のチェックはHcdProcessRootHubMessageで行っていますが、この処理はUSBでStatusメッセージを送る代わりに、USBコントローラのレジスタ(0x0440)の値を読み出しています。
詳しく見てみると、USBコントローラのレジスタには、USBポートの状況を表す2つのフラグ
・Port.Connect(bit 0; デバイスが接続されていればtrue)
・Port.ConnectDetected(bit 1; デバイスが新たに接続されたときにtrue)
があり、後者はデバイスが「なし→あり」のときはtrueになりますが「あり→なし」のときはtrueにならないようです。
しかし、csudの実装ではPort.ConnectDetectedのレジスタ値を「ポートの接続状況に変化があった」フラグとして用いているため、デバイスの取り外しが検出できていませんでした。
このあたり、ultiboのフォーラムの以下のやりとりが参考になりました。
usb keyboards not recognized on "Raspberry PI A+" – ultibo.org
この議論に対応する、ultiboのUSBドライバのパッチは以下のコミットと思われます。
Fixes for Raspberry Pi A/A+/Zero USB handling · ultibohub/Core@ba3c908
csudとはだいぶ構成が違うライブラリなので、csudへそのまま適用するのは無理そうですが、コメントを読むだけでもヒントになりそうです。
csudでの上記の不具合の解決方法ですが、HubPortGetStatusを呼ぶ前と呼んだ後とでPort.Connectの値の変化をチェックするようにしました。
パッチを当てたcsudは以下のリポジトリで公開しています。
boochow/csud: Chadderz's Simple USB Driver for Raspberry Pi
なお、時間がたつとハングアップする問題は、Pollループに少しウエイトを入れるようにしたら起こらなくなったので、デバイスへの過負荷が原因だったのかもしれません。
これで、Pi Zero WにUSBキーボードを直結しても問題なく動作するようになったと思われます。
コメント