WSL2環境からUSBシリアルデバイスを使う

WSL2環境からUSBシリアルデバイスを使う方法についてのメモ書きです。

標準WSL2環境のままではUSBデバイスを認識することができないのですが、ここに書いたusbipd-winツールを使用すればUSBデバイスをWindowsホスト側で使うかWSL2側で使うかを切り替えることができるようになります。

また、Arduino、ESP32、Spresenseといったボードの開発でWSL2を使用する場合、標準のWSL2 kernel(5.10.74.3)にはUSBシリアルドライバが含まれていないので、そのままでは使えません。USBシリアルドライバを有効にしたWSL2 kernelを作成する必要があります。ここに書いた方法でWSL2 kernelを更新することで、WSL2環境からUSBシリアルターミナルを表示したり、WSL2環境からUSBシリアル経由でプログラムの書き込みを実行することができるようになります。

## 事前環境

* Windows10 or Windows11 (私は Windows10 を使用)
* WSL2 kernel 5.10.60.1 以上がインストールされていること
 * 参考: [WSL2でSpresense開発環境を構築する](https://qiita.com/baggio/items/56bea3eb2b231364d7cf)

## インストール

### usbipd-winツールをインストールする

1. [usbipd-winのリリースページ](https://github.com/dorssel/usbipd-win/releases)から 最新の`usbipd-win_バージョン.msi` (例:`usbipd-win_1.3.0.msi`)をダウンロードする
* `usbipd-win_バージョン.msi` を実行してWindowsPCへインストールする

### WSL2環境へUSBIPツールをインストールする

1. WSL2コマンドプロンプトから`linux-tools`と`hwdata`をaptコマンドでインストールする

```shell:WSL
$ sudo apt install linux-tools-5.4.0-77-generic hwdata
```

2. `usbip`コマンドを実行するためにsudo権限を変更します。`visudo`コマンドでsudoersファイルを編集するために`nano`エディタを起動します

```shell:WSL
$ sudo visudo
```

3. `secure_path`に`/usr/lib/linux-tools/5.4.0-77-generic`を追加します

```shell:sudoersファイルを編集する
Defaults secure_path="/usr/lib/linux-tools/5.4.0-77-generic:/usr/local/sbin:..."
```

# USBデバイスの接続方法

1. PowerShellを管理者権限で起動します
2. WindowsPCとUSB機器を接続した状態で、USBデバイスリストを表示します
 * ここでの例は`BUSID`に`1-1`に`Silicon Labs CP210x USB to UART Bridge`が表示されています
 * `STATE`は`Not attached`状態になっています

```shell:PowerShell(例)
PS C:\windows\system32> usbipd wsl list
BUSID  DEVICE                                         STATE
1-1    Silicon Labs CP210x USB to UART Bridge (COM4)  Not attached
1-3    USB 入力デバイス                                 Not attached
```

3. USBデバイスのうちWSL2から使用するデバイスの`BUSID`を選択してattachします
 * ここでの例は`BUSID`に`1-1`を指定します
 * attach後にもう一度list表示すると`STATE`が`Attached`状態に変更されていることを確認できます

```shell:PowerShell(例)
PS C:\windows\system32> usbipd wsl attach --busid 1-1
[sudo] password for mario: （ここでパスワードを入力する）
```

```shell:PowerShell(例)
PS C:\windows\system32> usbipd wsl list
BUSID  DEVICE                                         STATE
1-1    Silicon Labs CP210x USB to UART Bridge (COM4)  Attached - Ubuntu-20.04
1-3    USB 入力デバイス
```

4. WSL2側で、`lsusb`コマンドを実行する
 * `Silicon Labs CP210x UART Bridge`が接続されていることが確認できます

```shell:WSL(例)
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 10c4:ea60 Silicon Labs CP210x UART Bridge
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```
:::note warn
ただしこの時点では、CP210xドライバがインストールされておらず、/dev/ttyUSB0デバイスが見つかりません。/dev/ttyUSB0として利用するためには、後述する WSL2 kernelのビルドが必要になります
:::
5. もし、USB接続をWindows側に戻したいときは、`BUSID`を指定してdetachを実行するか、もしくはUSBを抜き差しすると元に戻ります
 * ここでの例は`BUSID`に`1-1`を指定してdetachを実行しています
 * detach後にもう一度list表示すると`STATE`が`Not attached`状態に変わっていることが分かります

```shell:PowerShell(例)
PS C:\windows\system32> usbipd wsl detach --busid 1-1
```

```shell:PowerShell(例)
PS C:\windows\system32> usbipd wsl list
BUSID  DEVICE                                         STATE
1-1    Silicon Labs CP210x USB to UART Bridge (COM4)  Not attached
1-3    USB 入力デバイス                                 Not attached
```

# CP210X USBドライバを認識させるためにkernelをリビルドする

1. Kerelビルドに必要なツールをaptコマンドでインストールします

```shell:WSL
$ sudo apt install build-essential flex bison libssl-dev libelf-dev dwarves libncurses-dev
```

2. WSL2リポジトリをクローンします

```shell:WSL
$ git clone https://github.com/microsoft/WSL2-Linux-Kernel.git
$ cd WSL2-Linux-Kernel
```

3. menuconfigを開いて`CONFIG_USB_SERIAL_CP210X`を有効にします

```shell:WSL
$ make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
```
![wsl-menuconfig.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432852/0906ce09-bcbc-5148-91a3-da4a2197fd03.png)
 * Spresenseで使用する`USB CP210x family of UART Bridge Controllers`(`CONFIG_USB_SERIAL_CP210X`)は次の階層にあります

```
-> Device Drivers
　　-> USB support (USB_SUPPORT [=y])
　　　　-> USB Serial Converter support (USB_SERIAL [=y])                                                       
```

4. WSL2 kernelをビルドします

```shell:WSL
$ make -j4 KCONFIG_CONFIG=Microsoft/config-wsl
```

5. ビルドしたイメージをWindowsディレクトリにコピーします
 * ユーザー名のmarioは、自分のユーザー名に置き換えてください

```shell:WSL(例)
$ cp arch/x86/boot/bzImage /mnt/c/Users/mario/wsl_kernel
```

6. Windows側に`.wslconfig`ファイルを作成して、`kernel=`でさきほど作成した`wsl_kernel`を指定します

```shell:WSL(例)
$ nano /mnt/c/Users/mario/.wslconfig
```

```shell:.wslconfig編集(例)
[wsl2]
kernel = C:\\Users\\mario\\wsl_kernel
```

7. WSLを一旦シャットダウンします

```shell:PowerShell
PS C:\windows\system32> wsl --shutdown
```

8. WSL2を再び起動します
 * WSL2起動後にバージョンを確認すると新しいカーネルで起動していることを確認できます
 * `/proc/config.gz` で `CONFIG_USB_SERIAL_CP210X`が有効になっていることを確認できます

```shell:WSL
$ cat /proc/version
Linux version 5.10.74.3-microsoft-standard-WSL2+ (mario@DESKTOP-UKT9H1D) (gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1 SMP Fri Jan 7 00:32:37 JST 2022
```

```shell:WSL
$ zcat /proc/config.gz | grep CONFIG_USB_SERIAL_CP210X
CONFIG_USB_SERIAL_CP210X=y
```

9. [USBデバイスの接続方法](#usbデバイスの接続方法) をもう一度実行します
 * USBシリアルデバイスドライバが正常に動作していれば`/dev/ttyUSB0`が表示されます

## TIPS

### `usbipd`を実行したときに`usbipd: warning: Server is currently not running.`が表示される

```shell:PowerShell
PS C:\windows\system32> usbipd wsl list
BUSID  DEVICE                                         STATE
1-1    Silicon Labs CP210x USB to UART Bridge (COM4)  Not attached
1-3    USB 入力デバイス                                 Not attached
usbipd: warning: Server is currently not running.
```

`usbipd-win_バージョン.msi`を起動して`Repair`を実行すると治ります
![usbip-win-repair.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/432852/775bc184-9b3d-2b4c-8ca1-74a0b7770ce2.png)

## メモ追記(2022/06/05)

WSL2をUbuntu22.04に更新したときにusbipdの利用手順が変わっていたことに気が付いたので追記。

[usbipd-winのリリースページ](https://github.com/dorssel/usbipd-win/releases)から最新版`usbipd-win_2.3.0.msi`を落としてきて更新する。

あとはこちらのWikiページを参考に、

https://github.com/dorssel/usbipd-win/wiki/WSL-support

```shell:WSL
$ sudo apt upgrade
$ sudo apt update
```

```shell:WSL
$ sudo apt install linux-tools-virtual hwdata
$ sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-tools/*/usbip 20
```