使用场景描述 (是什么)
这篇记录主要针对如何在linux
下更加简单的使用ftdi2xx
的动态链接库.
在近期的工作中我需要频繁地使用FTDI的设备(ftdi232)进行SPI的读写操作. 每次在调用自己的程序之前都需要按照官方的建议执行以下操作:
lsmod | grep ftdi
rmmod ftdi_sio
在调试过程中拔插设备的操作十分常见, 以至于每天都要输入上述的命令无数次. 为了完成我当一个咸鱼的梦想, 我在今天花费了一些时间研究了一下, 如何从这段烦人的命令中寻获解脱.
既然拥有一个实现咸鱼化的伟大梦想, 我对解决方案有着以下要求:
- 摆脱对
rmmod
的使用 - 摆脱对
sudo
命令的使用 - 我希望能在使用FTDI232芯片的同时, 不影响到其他设备的使用
需要着重说明, 最后一点是过往使用中的另一个痛点: 其实使用ftdi芯片的设备不在少数. 在我的工作环境中还有另外两种设备会使用到ftdi的芯片: 一个是xilinx artix 7 FPGA, 另一个是一条做工优秀和外面那些地摊货不一样的蓝色串口线.
每次需要同时使用ftdi232和其中一个设备时, rmmod ftdi_sio
这条命令就十分的令人感到绝望. 系统中所有依赖于ftdi_sio
驱动的其他驱动, 都会因为依赖不满足也无法工作.
解决方案 (怎么办)
话不多说, 其实已经说了很多, 亮方案.
新建一个叫85-unbind-ftdi.rules
的文件. 这里我想补上一句, :)udev
的规则不能分行真的很治愈强迫症
ACTION=="add", ATTRS{idVendor}=="0403", MODE:="666"
ACTION=="add", ATTRS{idVendor}=="0403", ATTR{interface}=="Single RS232-HS", RUN+="/bin/sh -c '/bin/echo $kernel > /sys/bus/usb/drivers/ftdi_sio/unbind'"
然后将文件按照需求放入目录/run/udev/rules.d
或者/etc/udev/rules.d
中. 为此我写了一个简陋的Makefile.
unbind_ftdi:
mkdir -p /run/udev/rules.d
cp 85-unbind-ftdi.rules /run/udev/rules.d/
unbind_ftdi_persisted:
mkdir -p /etc/udev/rules.d
cp 85-unbind-ftdi.rules /etc/udev/rules.d/
注: 上述文件运行于在ubuntu 18.04
所用知识点的汇总 (为什么)
接下来的内容是以备未来魔改之需而记录的知识点.┗|`O′|┛ 嗷~~
rules 文件解析
上面的文件85-unbind-ftdi.rules
中的内容是一组udev
的规则. 在设备发生某些行为的时候, 会根据相应的规则进行相应的匹配, 然后执行某些命令.
- 上述规则主要生效于新增设备时.
- 第一条规则是赋予文件
666
的权限, 让我免于遭受sudo
的折磨. - 第二条规则是只筛选出我特别针对的设备, 然后执行
unbind
,让我在梦幻中解决烦恼.
文件名的前缀85-
表示规则的序列, 越大的序列越靠后执行, 99那岂不是更美滋滋, 但是我就不.
makefile 文件解析
在udev
的这套系统中, 相关文件可以存放在 /lib/udev
, /etc/udev
和 /run/udev
目录下.lib
目录下存放的是操作系统所需要的规则; etc
目录下存放的是后期加入的规则; run
目录下存放的是runtime
的规则. 所以如果想要持久化, 那就把写好的文件放到etc
目录下, 如果只想要本次开机使用, 那就放到run
目录下.
unbind命令
对于之前ftdi官方推荐的rmmod ftdi_sio
, 我只能说这种做法十分的暴力, 其实使用unbind
就足够了.
通过lsusb -t
找到设备对应的节点, 然后执行echo ${dev_path} > /sys/bus/usb/drivers/ftdi_sio/unbind
.
对应usb设备可以在/sys/bus/usb/devices
目录下找到.
$ lsusb -t
# snip ...
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M
|__ Port 1: Dev 10, If 0, Class=Vendor Specific Class, Driver=ftdi_sio, 480M
# snip ...
$ sudo sh -c "echo 3-1:1.0 > /sys/bus/usb/drivers/ftdi_sio/unbind"
$ lsusb -t
# snip ...
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M
|__ Port 1: Dev 10, If 0, Class=Vendor Specific Class, Driver=, 480M
# snip ...
udev 及 udev rule
我看了一圈, 关于udev rule
最直观的教程就在本地. 可以通过man udev
查看udev
的介绍和规则的具体内容. 然后可以进入/lib/udev/rules.d
目录下, 查阅系统自带的规则, 学习一下先进的科学与技术. 还有就是这个链接也十分的不错, 有介绍 udevadm
的使用.
上面的规则是怎么攒出来的
通过 udevadm info -a ${usb_sysfs_path}
查看每个设备的所有属性. 下面是我所使用设备s的部分属性.
- ftdi232
looking at device '/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0':
KERNEL=="3-1:1.0"
SUBSYSTEM=="usb"
DRIVER=="ftdi_sio"
ATTR{authorized}=="1"
ATTR{bAlternateSetting}==" 0"
ATTR{bInterfaceClass}=="ff"
ATTR{bInterfaceNumber}=="00"
ATTR{bInterfaceProtocol}=="ff"
ATTR{bInterfaceSubClass}=="ff"
ATTR{bNumEndpoints}=="02"
ATTR{interface}=="Single RS232-HS"
ATTR{supports_autosuspend}=="1"
# snip
# ...
- xilinx FPGA
looking at device '/devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0':
KERNEL=="3-1:1.0"
SUBSYSTEM=="usb"
DRIVER=="ftdi_sio"
ATTR{authorized}=="1"
ATTR{bAlternateSetting}==" 0"
ATTR{bInterfaceClass}=="ff"
ATTR{bInterfaceNumber}=="00"
ATTR{bInterfaceProtocol}=="ff"
ATTR{bInterfaceSubClass}=="ff"
ATTR{bNumEndpoints}=="02"
ATTR{interface}=="Digilent USB Device"
ATTR{supports_autosuspend}=="1"
# snip
# ...
- UART
looking at device '/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/1-1.2.4/1-1.2.4:1.0':
KERNEL=="1-1.2.4:1.0"
SUBSYSTEM=="usb"
DRIVER=="ftdi_sio"
ATTR{authorized}=="1"
ATTR{bAlternateSetting}==" 0"
ATTR{bInterfaceClass}=="ff"
ATTR{bInterfaceNumber}=="00"
ATTR{bInterfaceProtocol}=="ff"
ATTR{bInterfaceSubClass}=="ff"
ATTR{bNumEndpoints}=="02"
ATTR{interface}=="FT232R USB UART"
ATTR{supports_autosuspend}=="1"
# snip
# ...
经过多方筛选, 最终ATTR{interface}
获得桂冠作为筛选目标设备的属性. 在实际工作中, 任何可用于筛选的条件只要能满足需求都具备可行性.
其中规则内的$kernel
变量即为属性KERNEL
. 可以观察到, 在目前的情况下, 此属性恰好可以作为unbind
的参数直接进行使用.
如何调试udev
在ubuntu 18.04 上, 直接添加规则就能实时生效, 不需要特别执行udevadm control --reload
.
可以使用 udevadm test ${usb_sysfs_path}
偷偷地观察代码有没有被运行.
一些更新
- bug: 在ubuntu 18.04环境下, udev的规则在(apt update)之后才能生效.