WSL2 で ESP32 に接続する (CP210x のドライバーサポート)


## はじめに

2021年秋にMicrosoftが紹介していた [WSL2でUSBデバイスと接続する方法](https://devblogs.microsoft.com/commandline/connecting-usb-devices-to-wsl/) を用いて、WSL2上の ESP-IDF で ESP32 に flash, monitor できることを確認しました。

注意点としては、
ESP32の開発ボードである [ESP32-DevKitC](https://www.espressif.com/en/products/devkits/esp32-devkitc/overview) では、記事の方法だけでは flash や monitor ができないことです。

原因は、WSL2標準のLinuxカーネルが CP210x 用のドライバーを無効化されていることです。
自分でカーネルをビルドし直して、それを WSL に読ませることで解決します。

この記事は特に 上記の問題点の所に焦点を当てた内容となっています。
ほぼ [forum](https://askubuntu.com/questions/1373910/ch340-serial-device-doesnt-appear-in-dev-wsl/) の受け売りですが、日本語情報が見つからなかったのでここにメモしておきます。


## 検証環境

* Ubuntu 20.04 (Microsoft Store で入手)
* ESP32-DevKit-C (他にも、CP210x を搭載しているボードなら同じ方法で対応できると思います)


## 手順

1. usbipd 機能の設定
1. Linux Kernel をビルドする
1. WSLから自作カーネルを読み込ませる
1. (Docker向け) Docker に device を指定する 

## 1. usbipd 機能の設定

具体的な手順は下記にまとまっているので、ここでは省略します。

* [dorssel/usbipd-win (GitHub)](https://github.com/dorssel/usbipd-win)
* [Connecting USB devices to WSL (Microsoft)](https://devblogs.microsoft.com/commandline/connecting-usb-devices-to-wsl/)
* [WSL2 で USB (usbipd-win)](https://lowreal.net/2021/11/16/1)

しかし、上記の対応を済ませても、ESP32-DevKitC との接続はできませんでした。
どうやら、ESP32(CP2102) はWSLで検知しているものの、シリアル通信デバイスとして認識されていないようです。


```bash:WSL2側
$ dmesg
[34209.280578] usb 1-1: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice= 1.00
[34209.280580] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[34209.280581] usb 1-1: Product: CP2102 USB to UART Bridge Controller
[34209.280582] usb 1-1: Manufacturer: Silicon Labs
[34209.280583] usb 1-1: SerialNumber: 0001
# "/dev/tty** に attach しました！" みたいな文言が出てこない

$ lsusb # "CP2102 USB to UART Bridge Controller" が見つからない
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```


## 2. Linux Kernel をビルドする

はじめにも書きましたが、CP210x 系のドライバーが Kernel で無効化されていることが問題の原因です。
必要な作業自体は単純で、Kernel Configuration でチェックボックス１か所に * をつけるだけで有効化したカーネルを作成できます。

まず、WSL2に必要なパッケージをインストールします。

```bash:WSL2
$ sudo apt update && sudo apt install -y build-essential flex bison libssl-dev libelf-dev dwarves libncurses-dev
```

次に WSL2 用 Linux Kernel のソースをダウンロードして、Kernel Configuration を開きます。

```bash:WSL2
$ git clone https://github.com/microsoft/WSL2-Linux-Kernel.git
$ cd WSL2-Linux-Kernel
$ make menuconfig KCONFIG_CONFIG=Microsoft/config-wsl
```

Device Drivers -> USB support -> USB Serial Converter support -> USB CP210x familyof UART Bridge Controllers に * のチェックを入れます。

※ キー操作は下記の通りにできます

* 上下キー: 選択項目の変更 (上下移動)
* Enter キー: 下の階層に移動
* Space キー: チェックボックスの切り替え
* 左右キー: 下部メニューの選択


![スクリーンショット 2022-01-06 110152.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96165/adc72e12-61e2-1331-0012-5c4e56ef80d0.png)
↓
![スクリーンショット 2022-01-06 110135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96165/3ac3d8ae-d183-d8da-112c-c87e8cbcc8fc.png)
↓
![スクリーンショット 2022-01-06 110113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96165/c5897d16-531f-4279-b412-0f9a74d58cd3.png)
↓
![スクリーンショット 2022-01-06 110040.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96165/a6cca791-796d-eab3-6678-ce36c63bad28.png)


Save -> Exit したあとに、 `make` コマンドを実行すれば、カーネルが作成されます。
これを、Windows から閲覧できるディレクトリに配置してください。

```bash
$ make
...(略)...

Kernel: arch/x86/boot/bzImage is ready  (#1)
$ cp arch/x86/boot/bzImage /mnt/c/Users/{あなたのユーザー名}/wsl_kernel
```

Windows 側のホームディレクトリの`.wslconfig`に下記を追記します。`.wslconfig`がなければ作成してください。

```txt:.wslconfig
[wsl2]
kernel = C:\\Users\\{あなたのユーザー名}\\wsl_kernel
```

WSLを再起動すれば、これ以降は読み込めるようになるはずです。
再起動した状態なので、usbipd から attach することもお忘れなく！

```shell:Windows
> wsl --shutdown
> wsl
$ 
```

```shell:管理者権限でコマンドプロンプトまたはPowerShell
> usbipd wsl attach --busid ${busid}
```


```bash:WSL2
$ dmesg | tail -10
[   21.778229] usb 1-1: new full-speed USB device number 2 using vhci_hcd
[   21.858058] vhci_hcd: vhci_device speed not set
[   21.928043] usb 1-1: SetAddress Request (2) to port 0
[   21.965415] usb 1-1: New USB device found, idVendor=10c4, idProduct=ea60, bcdDevice= 1.00
[   21.965419] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[   21.965420] usb 1-1: Product: CP2102 USB to UART Bridge Controller
[   21.965422] usb 1-1: Manufacturer: Silicon Labs
[   21.965423] usb 1-1: SerialNumber: 0001
[   21.969170] cp210x 1-1:1.0: cp210x converter detected
[   21.978875] usb 1-1: cp210x converter now attached to ttyUSB0 #<- /dev/ttyUSB0 からアクセスできるようになった！

$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 10c4:ea60 Silicon Labs CP210x UART Bridge # <- 出てきた！
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```


## 4. (Docker) device を指定する

純粋な WSL2 からアクセスする場合には、すでに接続されていますが、
Docker からアクセスする場合には、さらに Docker コンテナ起動時にデバイスを伝える必要があります。

```bash
$ docker run -it --device=/dev/ttyUSB0:/dev/ttyUSB0 ${Docker名}


# python $IDF_PATH/components/esptool_py/esptool/esptool.py -p /dev/ttyUSB0 write_flash @flash_project_args
esptool.py /dev/ttyUSB0 write_flash --flash_mode dio --flash_freq 40m --flash_size 2MB 0x8000 partition_table/partition-table.bin 0x1000 bootloader/bootloader.bin 0x10000 my_custom_app.bin    
esptool.py v3.3-dev
Serial port /dev/ttyUSB0
Connecting.................
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting....
Detecting chip type... ESP32
Chip is ESP32-D0WDQ6 (revision 1)
Features: WiFi, BT, Dual Core, Coding Scheme None
Crystal is 40MHz

# python $IDF_PATH/tools/idf_monitor.py --port /dev/ttyUSB0 my_custom_app.elf 
/root/esp/esp-idf/tools/idf_monitor.py:518: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
  if StrictVersion(serial.VERSION) < StrictVersion('3.3.0'):
--- idf_monitor on /dev/ttyUSB0 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun  8 2016 00:22:57

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7208
load:0x40078000,len:14880
```


## 参考

* [CH340 Serial device doesn't appear in /dev/ (WSL) (ask Ubuntu)](https://askubuntu.com/questions/1373910/ch340-serial-device-doesnt-appear-in-dev-wsl/)
* [CH340 Serial device doesn't appear in /dev/ #7649 (microsoft
/
WSL)](https://github.com/microsoft/WSL/issues/7649)