Raspberry Pi用ベアメタルUSBデバイスドライバ「csud」の調査(2)

baremetal-usb-keyboard1.png

昨日に引き続き、Raspberry PiのベアメタルUSBデバイスドライバ「csud」のRPi Zero W対応をしています。
昨日はUSBキーボードがRPi Zeroに直結された場合に、USB2.0のSplitパケットを送信しないように修正しましたが、これだけですとUSBキーボードは認識されるものの、しばらく時間がたつとキーボードに反応しなくなってしまっていました。

プロトコルアナライザで通信の様子を見てみると、ホスト側からのパケットに対してデバイスがNAKを返しているのに、ホスト側は立て続けにパケットを送りつけています。
コードを見てみると、この送信処理はHcdChannelSendWaitOneという関数の中で行われています。
デバイス側の応答に応じた処理の部分が以下のようになっていました。

if (Host->Channel[channel].SplitControl.SplitEnable) {
if (Host->Channel[channel].Interrupt.Acknowledgement) {

for (tries = 0; tries < 3; tries++) {
/* Complete Splitパケットの送信処理 */
}

if (tries == 3) {
MicroDelay(25000);
continue;
} else if (Host->Channel[channel].Interrupt.NegativeAcknowledgement) {
globalTries--;
MicroDelay(25000);
continue;
} else if (Host->Channel[channel].Interrupt.TransactionError) {
MicroDelay(25000);
continue;
}
HcdChannelInterruptToError(device, Host->Channel[channel].Interrupt, false);
} else if (Host->Channel[channel].Interrupt.NegativeAcknowledgement) {
globalTries--;
MicroDelay(25000);
continue;
} else if (Host->Channel[channel].Interrupt.TransactionError) {
MicroDelay(25000);
continue;
}
} else {
HcdChannelInterruptToError(device, Host->Channel[channel].Interrupt, !Host->Channel[channel].SplitControl.SplitEnable);
}

一番外側のifは、Splitパケットが有効ならば、という条件式ですが、then節の内部の処理はデバイスの速度がHigh Speedでない場合に実行すべき処理に見えます。
オリジナルのコードでは、「Splitパケットが有効」と「デバイスの速度がHigh Speedでない」は常に同値だったのですが、昨日行った変更ではSplitを有効にする条件を増やしましたので、両者は同値ではなくなりました。

then節の中で、Splitパケットが有効なときだけ実行すべき部分は、

if (Host->Channel[channel].Interrupt.Acknowledgement) {
}

の内部です。
ここではComplete Splitパケットの送信と、その送信によってエラーが生じた場合の処理を行っています。
その次のelse節以降は、Split制御が無効の場合も実行する必要があります。

そのため、

・外側のifは「if (pipe->Speed != High)」
・内側のifは「if ((Host->Channel[channel].Interrupt.Acknowledgement) && (Host->Channel[channel].SplitControl.SplitEnable))」

へと条件式を変更しました。
これにより、どうやらキーボードが無事に動作するようになりました。

ただ、キーボードを抜くと動作がおかしくなります。
オリジナルのコードでRoot Hubからデバイスを除去することが想定されていないためです。
RPi Zero WにUSBハブを接続している場合は、ハブからキーボードを抜くのは大丈夫ですが、ハブのほうを抜いてしまうと異常動作になります。
この対策については、USBハブ周辺のコードを調べてみる必要があります。

また、時間が経過するとハングアップすることもあるので、まだ実用的なレベルにするにはだいぶ手間がかかりそうです。

コメント