WSLでUSBデバイスを使う(その1)

WSLでUSBデバイスを使う(その1:準備&USB-Serial編)

概要

WSLでUSBを使う方法。あちこちに情報があるけど、なんとなく自分なりにまとめておく。
本家の説明

ますは、準備とLinuxカーネルのビルドが必要ないUSB-Serialデバイスから。

WSL側の準備

WSLのバージョン

使用したWSLのバージョンは以下。

wsl --version

WSL バージョン: 2.2.4.0
カーネル バージョン: 5.15.153.1-2
WSLg バージョン: 1.0.61
MSRDC バージョン: 1.2.5326
Direct3D バージョン: 1.611.1-81528511
DXCore バージョン: 10.0.26091.1-240325-1447.ge-release
Windows バージョン: 10.0.19045.4529

使用したディストリビューションは「Ubuntu 22.04 LTS」

WSLディストリビューションでsystemdの有効化

[!NOTE] インストールしたタイミングによっては既に有効化されているかも。

USBのホットプラグ処理は systemd + udev がよしなに行ってくれるみたいなので、systemdを有効化しておく。
使用するディストリビューションを起動し、/etc/wsl.confに以下の設定を行う。

デフォルト(古いデフォルト?)のSystemVinitだとバカチョンで動かなかった…udevサービス再起動とかやったら動いたけど。

[boot]
systemd=true

[!NOTE] systemdを有効にするか否かはディストリビューション毎の設定

設定後、WSLをシャットダウン(コマンドプロンプト等からwsl --shutdownを実行)し、
再度ディストリビューションを起動する。

参考:WSL2でsystemctlを使う方法

[!NOTE] systemdが動作しているか確認するには、systemctl is-system-running を実行する。
running(起動中) degraded(起動中だが失敗したサービスなどが存在) と表示されれば動作している。
offline と表示されれば起動していない。
その他使い方についてはぐぐってちょ。

必要なパッケージをインストール

sudo apt install linux-tools-generic hwdata

[!NOTE] linux-tools-generic : usbipコマンド等が入る
hwdata : USB ID等のデータベースが入る
linux-tools-generic インストールしたら自動でhwdata入るけど….
参考にしたサイトにこう書いてあったので。
linux-tools-virtualと書いてあるサイトもあるけど、ubuntu 22.04だとchangelogとcopyrightしか違わないみたい。
 

pushd /usr/lib/linux-tools
sudo ln -s 5.15.0-113-generic $(uname -r)
popd

[!NOTE] あちこちにsudo update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20 でコマンドのバージョン管理を行う例が書かれているけど、なんとなくシンボリックリンクを張る方がしっくりするのでこっちにした。

本来、usbipを実行すると /usr/bin/usbip/usr/lib/linux-tools/≪カーネルバージョン≫/usbip
を起動してくれるらしいのだけど、WSL環境では≪カーネルバージョン≫がカスタマイズバージョンでうまく働かないので
/usr/lib/linux-tools/≪カーネルバージョン≫をインストールされているバージョンに飛ばしてやればいい。
カーネルバージョンが変更されたら動かなくなるけど、そのときは、新しいカーネルバージョン名のシンボリックリンクを作ってやればよい。
もちろん、linux-tools-genericのアップデートが必要か判断して、必要ならアップデートする。

windows側の準備

usbipd-win のインストール

まずはUSBをTCP/IP経由で接続させるためのツール、usbipd-win をwindowsにインストールする。
インストールするには、ダウンロードページ から 最新版(以下の手順で使用したのは4.2.0)のmsiファイルをダウンロードして実行するだけ。

[!NOTE] インストール時PATHが変更されるので、既に開いているコマンドプロンプトorPowerShellは一旦閉じて再実行する。

USBデバイスの操作

WSLで使用したいUSBデバイスはあらかじめPCに接続しておく。

以下の処理はコマンドプロンプトorPowerShellで行う。
ただし、bind/unbindサブコマンドは管理者権限が必要なので、管理者として開いたコマンドプロンプトorPowerShellで実行する。

接続したいデバイスのBusIDを調べる

以下のコマンドを実行する。
結果は例。今回は3-4のUSB Serial Converter を使う。

usbipd list
≪結果≫
Connected:
BUSID  VID:PID    DEVICE                                                        STATE
2-1    1f75:0918  USB 大容量記憶装置                                            Not shared
2-2    056e:700a  Venus USB2.0 Camera                                           Not shared
3-4    0403:6001  USB Serial Converter                                          Not shared

ここで表示されたWSLで使用したいUSBデバイスのBUSID(例えば3-4)を覚えておく。

デバイスを共有設定する

管理者として開いたコマンドプロンプトorPowerShellで以下のコマンドを実行する。

usbipd bind --busid 3-4        # 3-4は接続するデバイスのBusID

[!NOTE] usbipd: warning: USB filter 'USBPcap' is known to be incompatible with this software; 'bind --force' will be required.
と出た時は、一旦unbindして、–forceオプションを追加して実行
(うちのPCはWiresharkインストールしてUSBPcap入ってるからだろうなぁ…)

実行後、usbipd listを実行すると、対象デバイスのステータスが Shared または Shared (forced) になっている

ディストリビューションの起動

接続するためのWSLのディストリビューションを起動しておく。

[!NOTE] 起動してないとusbipd: error: There is no WSL 2 distribution running; keep a command prompt to a WSL 2 distribution open to leave it running.とエラーになる。

[!NOTE] このとき、複数のディストリビューションが起動していると変なことが起こりそうなので(未確認) 対象のディストリビューションだけ起動しておくのが良いと思う。

デバイスを接続する

bindしただけではまだWSL側からはUSBデバイスは見えない。

Windows側から接続する場合

usbipd attach --wsl --busid 3-4             # 3-4は接続するデバイスのBusID

linux側から接続する場合

sudo usbip attach -r 172.20.160.1 -b 3-4    # 172.20.160.1 はWindows側のIPアドレス
                                            # 3-4は接続するデバイスのBusID

[!NOTE] Windows側のIPアドレスはコマンドプロンプトorPowerShellからipconfigコマンドを実行し、
Ethernet adapter vEthernet (WSL): に表示されているIPアドレスを使用する。
でもWSLがミラーネットワークモードになってたらどうなるんだろう???

デバイス接続を確認する

linux側で以下を実行し、接続されたデバイスが表示されていることを確認する。 以下のように接続したデバイスが表示されていればOK
(以下では2行目のFT232 Serial (UART) IC)

lsusb
≪結果≫
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub```

USB-Serialデバイスを接続

上記操作で接続したUSBデバイスがUSB-Serialデバイス(上記の例では FT232 Serial (UART) IC) であれば、 /dev/ttyUSBx(xは数字。通常0から順に割り当てられる)に割り当てられる。

ls -la /dev/ttyUSB*
≪結果≫
crw-rw---- 1 root dialout 188, 0  7月  3 11:02 /dev/ttyUSB0

一応、ログを確認してみる。

dmesg
≪結果≫
・・・・・・
[  189.418189] vhci_hcd vhci_hcd.0: pdev(0) rhport(0) sockfd(3)
[  189.418196] vhci_hcd vhci_hcd.0: devid(196612) speed(2) speed_str(full-speed)
[  189.418249] vhci_hcd vhci_hcd.0: Device attached
[  189.690366] vhci_hcd: vhci_device speed not set
[  189.760405] usb 1-1: new full-speed USB device number 2 using vhci_hcd
[  189.840475] vhci_hcd: vhci_device speed not set
[  189.910358] usb 1-1: SetAddress Request (2) to port 0
[  190.135906] usb 1-1: New USB device found, idVendor=0403, idProduct=6001, bcdDevice= 6.00
[  190.135918] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  190.135921] usb 1-1: Product: FT232R USB UART
[  190.135923] usb 1-1: Manufacturer: FTDI
[  190.135924] usb 1-1: SerialNumber: A50285BI
[  190.511832] usbcore: registered new interface driver ftdi_sio
[  190.511852] usbserial: USB Serial support registered for FTDI USB Serial Device
[  190.511881] ftdi_sio 1-1:1.0: FTDI USB Serial Device converter detected
[  190.511904] usb 1-1: Detected FT232RL
[  190.521353] usb 1-1: FTDI USB Serial Device converter now attached to ttyUSB0       ← /dev/ttyUSB0が割り当てられたことが分かる

デフォルトで使用できるUSB-Serialデバイスは、 Silicon Labs社製 CP210x、FTDI社製シリアルコンバータ(FT232)、CH34x(安い中国製Arduinoで使われているらしい)らしい。

/dev/ttyUSBxが割り当てられていない場合は、対応しているUSB-Serialデバイスか、systemdが有効になっているか、等を確認してください。

[!NOTE] 参考:Windows WSL2 の Ubuntu 22.04 上から USB-UART 経由で M5Stack に書き込みする
上記サイトではudevルールをplatform.ioのサイトから拝借してきているが、上記の製品なら持ってこなくても可。
互換品とかでうまく認識しないときは試してみると動くかも(試してないので無責任発言)。

シリアルポートを使ってみる

シリアルポートを使うツールは色々あると思うけど、とりあえず動いているか確認するならこんな感じ。

sudo pyserial-miniterm --parity N /dev/ttyUSB0 115200

終了するにはCTRL+]を入力する。

[!NOTE] sudoなしで実行するには、自分のアカウントにdialoutグループを追加すればよい。

sudo gpasswd -a $USER dialout

一旦ログアウトして再ログイン必要。

デバイスを接続解除する

WSLをシャットダウンしたら(ディストリビューションのシャットダウンではなくWSL全体)接続解除されるけど、 手動で接続解除するには以下のコマンドを実行する。

Windows側から接続解除する場合

usbipd  detach --busid 3-4              # 3-4は接続するデバイスのBusID

linux側から接続解除する場合

linuxから解除するには、まずポート番号を確認する。

sudo usbip port
≪結果≫
Imported USB devices
====================
Port 00: <Port in Use> at Full Speed(12Mbps)
       Future Technology Devices International, Ltd : FT232 Serial (UART) IC (0403:6001)
       1-1 -> usbip://172.20.160.1:3240/3-4       ← ホストアドレス
           -> remote bus/dev 003/004              ← BusID
           

上記の例ではポート番号は00なので、これをportオプションに指定して以下のように実行する。

sudo usbip detach --port 00

デバイスを共有解除する

USBデバイスを共有解除するには、管理者として開いたコマンドプロンプトorPowerShellで以下のコマンドを実行する。

usbipd unbind --busid 3-4           # 3-4は接続するデバイスのBusID

[!NOTE] デバイスの共有解除しなければ、PCを再起動しても共有設定されたままになる。
ただし、使用しているUSBポートに別のUSBデバイスを挿入すると共有解除されるが、再度同じUSBデバイスを挿入すれば共有設定される。
何言ってるか分からんと思うけど、USBデバイス挿抜してみて確かめてみて。