解决WSL2默认不支持CAN设备的问题

前言

最近在开发一个AMR(Autonomous Mobile Robot)机器人,一般出于方便考虑,可以在PC上进行开发,而我因为主力PC是windows系统,所以一般用wsl2解决需要linux发行版的地方,比如这里需要用到的ros框架,我现在正在使用Isaac sim配合ros2进行windows下的机器人开发。

由于不想装双系统,以及我相信wsl是最好的linux发行版(doge),所以这里也是用wsl2进行开发,但是在使用一个pcan的设备时遇到了问题。pcan原本是在linux下免驱的,可以通过canutils、以及socketcan等工具非常方便的使用。但是在wsl2就遇到了一些问题,主要是

  1. usb设备在windows下,wsl无法访问
  2. wsl内核没有提供can设备的支持

解决:usb设备在windows下,wsl无法访问

首先,根据我所购买的usb to can设备,其在插上设备后,在windows设备管理器中是可以显示XCAN-USB的,且带有无驱动的问号icon。如果安装了windows下的驱动程序,则可以显示PCAN-USB且能正常使用。

因为我这里肯定不在windows下使用,所以跳过了这一部分。

幸运的是,我曾在去年或前年注意到,有用户需要在WSL中调试蓝牙外设,而该外设位于Windows系统下。为此,他们使用了一个开源的桥接工具,将USB设备映射到WSL中。

我今天在检索的时候,发现这个工具已经在Microsoft的官方Learn文档中。

https://learn.microsoft.com/zh-cn/windows/wsl/connect-usb

读者可以直接通过上面这个链接完成把USB设备共享到WSL内。

这主要是基于USBIPD-WIN这个开源项目实现的,完成安装后可以使用如下命令列出所有连接到 Windows 的USB设备。usbipd的list命令和bind命令可能需要管理员模式。

usbipd list
此图片的 alt 属性为空;文件名为 usbipd.webp

根据开源工具和微软的文档,可以使用 usbipd bind –busid=<BUSID> 命令来共享设备,从而允许它附加到 WSL。例如我上图中的BUSID为6-4的设备就已经被设置为了Shared表示该设备已经共享。随后还需要使用 usbip attach –remote=<HOST> –busid=<BUSID> 命令将usb设备附加到wsl2中。例如我上图中的BUSID为6-3的设备就已经被设置为了Attached,表示该设备已经共享。

参考微软文档,如果要附加到wsl,可以的命令内容为

usbipd bind --busid <busid>          # 将指定usb设备设为共享
usbipd attach --wsl --busid <busid>  # 将指定usb设备附加到wsl
usbipd detach --busid <busid>        # 将指定usb设备从wsl断开

需要注意的是,USB设备附加到WSL后,Windows将无法使用。仅bind命令需在Windows终端管理员模式下执行,设置后无需重复操作。每次断开连接后,需使用attach命令重新附加USB设备到WSL。

打开 Ubuntu(或首选的 WSL 命令行),使用以下命令列出附加的 USB 设备:

lsusb

例如我这里可以看到刚刚所连接的一个PCAN-USB设备。在 WSL 中完成设备使用后,可物理断开 USB 设备,或者从windows的终端运行 usbipd detach 命令。

本来我已经事情到这里就已经结束了,可以顺利使用can设备了,结果我在运行 ip link show type can 命令时发现,并没有can类型的设备。因此,后续的can启用命令,包括can-utils等工具也无法使用。

过程

经过一番检索,相关的资料比较少,wsl的github issue上内容也很多,不过也算好不容易找到了几种解决办法。

根据 Peak-System 的社区文章,有人遇到了和我一样的现象。不过有人表示:(WSL) 并不是一个真正的完全运行的 Linux 系统,因此无法使用,驱动也可能无法使用。

不过后面有人说内核版本大于6.0的linux_kernel有这个支持,且他解决了这个问题。我发现我的wsl2的linux_kernel内核版本是5.x的,因此参考 微软的文档 编译了一个6.6的内核,但是not work。

后来也在github的issue上看到有人提到了这个问题,但是没有给出解决方案。CAN bus functionality 这个issue中表示它的版本已经支持了can设备,只要使用usbipd那个工具就可以了。就很奇怪。

直到我看到这个Issue:CAN Bus/USBCAN(canable) support,我大概意识到了可能是需要在内核中启用CAN网络的支持,且前一个issue也提到了他曾自己编译了一个wsl kernel。

随后我在两个地方(reddit: SocketCAN/can-utils on WindowsEnabling SocketCAN on WSL2 walkthrough)看到了关于在内核中启用虚拟can部分的内容。以及一篇CSDN的文章 WSL2使用USB转CAN(canable)。虽然由于csdn这个文章我打不开,但是看目录,我认为实现顺序基本一致:把USB附加到WSL、查看WSL内核版本、下载WSL源码、修改配置、编译并使用。

是的,这是三四年前就有解决方案的问题。

解决:wsl内核没有提供can设备的支持

下载WSL源码

首先,查看自己的wsl2内核版本(可选):

luheqiu@LuHeQiu-PC:~$ uname -r
5.15.167.4-microsoft-standard-WSL2

比如我这里运行 uname -r 后是5.15.167.4的内核。

可选的原因是换成哪个内核都不影响,我试过了,换成6.6版本的内核,还是5.15的内核,都可以正常运行,而且换内核也非常方便。(毕竟是在windows上操作,乐。哪怕内核有问题也不担心开不了机,大不了换回去就行了。)

打开 WSL2-Linux-Kernel 的github源码网址,切换到5.15.y的分支。

可以看到,我这里最新的提交正好就是5.15.167.4。这也是因为我的wsl2的版本已经是最新的ltsc版本了。

你可以选择直接拉取指定的分支以节约存储,例如:

git clone --branch linux-msft-wsl-5.15.y https://github.com/microsoft/WSL2-Linux-Kernel.git

不过我这里是拉取了默认的master分支(因为忘了),然后切换到目标分支:

git clone https://github.com/microsoft/WSL2-Linux-Kernel.git  # 克隆仓库
cd WSL2-Linux-Kernel                                # 进入文件夹
git checkout --track origin/linux-msft-wsl-5.15.y   # 切换到远程分支(同时本地也会自动创建)

我这里仓库大概下载了2GB多一些。

修改配置

参考github页面的readme,编译wsl2 kernel安装一些前置的库,同时可能还需要安装 ncurses 库,因为这是打开配置菜单menuconfig必须依赖的库。

sudo apt install build-essential flex bison dwarves libssl-dev libelf-dev # 编译kernel需要的库
sudo apt install libncurses5-dev libncursesw5-dev                         # 使用配置菜单需要的库

使用下述三个命令开始配置编译选项:

cat /proc/config.gz | gunzip > .config
make prepare modules_prepare
make menuconfig 

这几个命令从压缩的 /proc/config.gz 文件中检索内核配置信息,将其解压缩,并将其保存到名为 .config 的文件中。然后根据这个配置文件,使用make构建系统打开菜单配置界面,就是下面这个蓝灰的框框。

使用方向键和Enter进行选择,使用Y包含功能到内核、使用N排除功能,使用M把功能作为独立模块特性编译。

根据GPT的解释,当选择M时,该功能会被编译为一个独立的内核模块,该功能不会在内核启动时加载,而是可以在运行时根据需要动态加载或卸载。

所以,如果你需要在内核启用的时候,一直启用这些模块,就需要使用Y,此时功能的前面显示为*,如果需要按需启用,且设置为M后,每当使用can的时候需要额外的命令启用can功能。

注意此处有坑,可以跳转到文章最后一章了解

CAN的相关配置在Networking support子选项内,我按M启用了:

  • <M> CAN bus subsystem support

然后按Enter进入CAN bus subsystem support的子选项,确保启用了

  • <M> Raw CAN Protocol

其他的不做修改,然后继续进入CAN Device Drivers子选项内,启用了

  • <M> Virtual Local CAN Interface #其实现在我感觉这个不需要,好像不需要虚拟can
  • <*> CAN bit-timing calculation #确保这个启用的
  • <*> CAN devices debugging messages #确保这个启用的

另外这个页面还有一个串口转CAN设备的支持,因为我没有用所以没看,但我看到有的教程中提到了这个(也有一些没有)。

  • Serial /USB serial CAN Adaptors (slcan)

slcan就是把can模拟成串口的意思,应该是适配一些slcan的can模块用的。但是理论上slcan都模拟成串口了应该可以直接作为usb设备使用才是。也许会有一些更高级的can支持把。

然后我还继续进入了CAN USB interfaces子选项,启用了

  • <M> 8 devices USB2CAN interface
  • <M> PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD

这个是其他的教程里没有提及,但我感觉我的场景需要的,因为我用到了PCAN,反正加了没坏处,我就加上了,另外第一个似乎是同时启用这个页面的8个设备接口,因为我数了数,正好有八个可以支持的设备接口,所以可能这两个择一即可。但是我输入H查看help,提示好像指的其实是(http://www.8devices.com)这个网址内的8个设备。但我觉得问题不大,都加上。

有些其他的相关配置,可以自行参考HELP选择,help信息都很丰富。

最后Enter一下Save,然后Exit即可保存配置并退出。

编译并使用

最后进入编译流程:

make modules
sudo make modules_install
make -j $(nproc)
sudo make install

编译完成并运行了install后,应该已经把一些必要的模块和库安装了。此外编译后应该还可以得到两类内核文件,一个在工程根目录下的vmlinux,还有一个是 arch/x86_64/boot/目录下的 bzImage(这个目录下的bzImage我看指向了arch/x86/boot/下的bzImgae,可能这两个是一样的),这两类文件我都测试了都可以工作,但是一个体积10-20MB、一个800MB,不知道有什么区别。当然,我也问了GPT。

在编译 WSL2 的 Linux 内核时,生成的 vmlinux 和 bzImage 是两种不同形式的内核文件,它们各有用途和特性:

  1. vmlinux
    用途:
    它是未经压缩和封装的 Linux 内核映像,包含完整的调试信息。
    通常用于内核调试和分析,例如使用 GDB 或 perf 工具时。
    WSL2 内核开发者可以借助它查看内核内部的符号和源代码映射。
    特点:
    体积较大:因为它包含所有符号表和调试信息。
    未经压缩:适合在开发和调试阶段直接使用。
  2. bzImage
    用途:
    它是用于实际引导系统的压缩和封装内核映像,用于启动内核时加载到内存中。
    在 WSL2 环境中,这个文件是最终实际运行的内核文件。
    特点:
    体积较小:通过压缩算法(通常是 gzip 或 bz2)显著缩减体积,去掉了调试符号。
    适合引导:封装了加载器代码,用于引导加载内核。
    WSL2 会使用此文件作为运行内核的载体。

—— 内容来源于Chatgpt4,不保证真实性

我这里选择arch/x86_64/boot/下的内核映像,是因为我的电脑是x86架构的。其实网上一堆教程都用的vmlinux,我之所以产生是不是应该用bzImage,是因为我一开始按照微软官方文档编译linux 6.6内核的时候,它使用的是bzImage。

使用下面这个命令把镜像移到d盘,我一开始移到c盘,但使用sudo也提示无权限,乐。

cp arch/x86_64/boot/bzImage /mnt/d/

另外,也可以直接在windows文件资源管理器中操作,但注意此时需要复制arch/x86/boot/下的bzImage,因为x86_64目录下的是一个硬链接,只有几字节,不能用的。

然后再windows的终端中关闭wsl:

wsl --shutdown

修改wsl配置文件,该文件路径位于用户目录下的.wslconfig。

例如我的为:C:\Users\MyUserName\.wslconfig

修改配置如下:

[wsl2]
kernel=C:\\wsl_kernel\\wsl_kernel_5.15.167.4\\bzImage

这里kenel后面需要填写为你的bzImage内核映像。这个切换内核映像还是比较丝滑的,就算kernel有问题,大不了把这个配置删了,它又会使用原来的正常kernel。也不担心内核映像异常导致系统无法启动,还要修boot。

在WSL2中使用CAN设备

现在回到开始,先使用usbipd attach工具把can usb设备附加到wsl,如果显示已经attach了,但是之前wsl重启过了,可能没有识别到,建议先detach解除附加,再使用attach重新附加到wsl。

此时wsl内依然可以看到这个usb设备。

luheqiu@LuHeQiu-PC:~$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 0c72:000c PEAK System PCAN-USB
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
luheqiu@LuHeQiu-PC:~$

此外,此时也可以看到在网络中识别到了can设备,只不过暂时是关闭的,state为DOWN。

luheqiu@LuHeQiu-PC:~$ ip link list type can
7: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can

我开始插入第二个can设备,重复上述流程,此时可以看到两个can设备:

luheqiu@LuHeQiu-PC:~$ ip link list type can
7: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can
8: can1: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can

然后设置can0设备的波特率并启用can0设备:

luheqiu@LuHeQiu-PC:~$ sudo ip link set can0 down
luheqiu@LuHeQiu-PC:~$ sudo ip link set can0 type can bitrate 1000000
luheqiu@LuHeQiu-PC:~$ sudo ip link set can0 up
luheqiu@LuHeQiu-PC:~$ ip link list type can
9: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
    link/can
10: can1: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can

可以看到can0设备已经正常启动了,注意要先设置波特率后才能启用。(这个地方卡了我半天,我一直up发现启动不了,问了gpt,gpt说可能内核配置不对,可恶。)

重复上述流程启用can1设备,并安装can-utils开始收发测试。

sudo apt-get install can-utils

这里有出现了第二个坑,因为之前跟着网上其他人的解决方案做的,我把所有的模块都设置成了M,结果使用can-utils的时候,一直有问题,提示:

luheqiu@LuHeQiu-PC:~$ sudo candump can0
socket: Address family not supported by protocol

后来才知道,那个需要按需启用的意思是需要自己输入命令启动的。

sudo modprobe can         #启用can支持
sudo modprobe can_raw     #启用can协议

把两个can设备的CANH和CANL相互连接,开启两个终端,一个终端运行candump can0监听can0,一个终端运行cansend can1 666#010203040A往can1执行发送。此时可以看到数据正常的收发了

评论

  1. darkarc
    7 天前
    2024-11-28 10:54:36

    tql,全文最详细的帮助文章

    • 博主
      darkarc
      7 天前
      2024-11-28 11:08:10

      你是真捧hhh

发送评论 编辑评论


|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇