先日のエントリに書いたようにWaveshareのETH/USB HUB HAT(B)には、「USBブートするとEthernetを使えなくなる」問題があります。これを解決したので、以下メモとして残しておきます。
原因
Ethernetを使えなくなるのは何らかの理由でETH/USB HUB HAT(B)のEthernet ICであるRTL8152の内部モードが不適切な状態になっているからです。
RTL8152はUSB Ethernetチップです。この種のチップの中にはリセット時に仮想CD-ROMとして動作するデュアル・ファンクションを持っているものが多くあります。これはWindowsユーザーに対してドライバを提供する方法であり、この方法を使うことで製品ベンダーはドライバが入ったCD-ROMを別途用意する必要がなくなります。
RTL8152はリセット時は仮想CD-ROMインターフェースRTL8151として動作しています。RealtekのWindowsドライバはRTL8151を見つけると内部のモードを変更してRTL8152として動作するようにします。モード切替はUSB規格の一部です。
Linuxのドライバにはこのメカニズムはありません。ですので動作しない方が正常です。逆になぜSDカードブートだと動作していたのか、そちらの方が私にはわかっていません。
解決策
上述のようなふるまいをするUSBデバイスは、無線モデムに多く見られました。
そのため、usb_modeswitchというプログラムが開発され、広く使われています。このプログラムはUbuntuには標準でインストールされており、多くの無線モデムをトラブルなく利用できます。
RTL8152がRTL8151と認識されている状態で以下のコマンドを実行すると、RTL8152に切り替わることを確認できます。このコマンドはusb_modeswitchのフォーラムを参考にしています。
usb_modeswitch -v 0bda -p 8151 -V 0bda -P 8152 -M 555342430860d9a9c0000000800006e0000000000000000000000000000000
ワークアラウンド
usb_modeswitchで問題が解決することはわかりました。次はこれをどうやって組み込むかです。
結論から書くと、/etc/udev/rules.d/の下にRTL8151に応じた宣言を置いておき、その中でusb_modeswitchを実行します。実行パラメータは別途/etc/usb_modeswitch.d/の下に置いておきます。
/etc/udev/rules.d/には、40-usb_modeswitch.rulesと名付けたファイルを作ります。サンプルは/lib/udev/usb_modeswitchにありますので同じ形式をなぞります。以下の宣言ではRealtek(idVendor==0bda)のRTL8151(idProduct==8151)を発見すると、usb_modeswitchを起動します。
# /etc/udev/rules.d/40-usb_modeswitch.rules # Custom usb_modeswitch rules # If matched, usb_modeswitch refer the external rules in the /etc/usb_modeswitch.d/xxxx:yyyy # Where xxxx and yyyy are vender ID and device ID. # Realtek RTL8151 ATTR{idVendor}=="0bda", ATTR{idProduct}=="8151", RUN+="usb_modeswitch '/%k'"
udevのrulesに呼応するファイルとして/etc/usb_modeswitch.d/0bda:8151を作ります。サンプルは/usr/share/usb_modeswitchにありますので同じ形式をなぞります。
ファイル名のxxxx:yyyyはUSBのベンダー(xxxx)とプロダクトID(yyyy)を対にしたものです。
# /etc/usb_modeswitch.d/0bda:8151 # Realtek RTL8151 TargetVendor=0x0bda TargetProduct=8152 MessageContent="555342430860d9a9c0000000800006e0000000000000000000000000000000"
以上の二つのファイルを作って再起動すると、RTL8152が正しく認識されるようになります。
まとめ
rulesファイルの’/%k’は、RTL8151のカーネル名(SRn)を示す文字列のはずですが、この文字列が与えられた時の挙動はmanpageには明記されていません。ただ、開発サイトの解説とサンプルを読む限りではこの引数を与えられた時、usb_modeswitchは/etc/usb_modeswitch.d/下にあるxxxx:yyyyファイルを参照しています。xxxxはUSBのベンダーID、yyyyはプロダクトIDです。
説明が甘いせいか多くのサイトでこの辺を手探りで探しています。usb_modeswitchの作者は「新しい設定ファイルがを作ったら送ってくれ」と呼び掛けていますが、Ubuntu Serverの場合はusb_modeswitchのデータをインストールしても展開していません。この辺のエコシステムが機能していない気がします。
Waveshareには設定ファイルを送付済みです。usb_modeswitchにも送りましょうか。