我在笔记本电脑 (Ubuntu 9.10) 上使用了许多相同的 USB 转串口适配器。这些适配器由 Sabrent 制造,基于 Prolific PL2303 IC 构建,如下所示lsusb
:
Bus 001 Device 008: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
Bus 001 Device 007: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
Bus 001 Device 006: ID 067b:2303 Prolific Technology, Inc. PL2303 Serial Port
所显示的属性udevadm
似乎都不是特定适配器所独有的:
foo@bar:~$ udevadm info --attribute-walk --path=/sys/bus/usb-serial/devices/ttyUSB0
looking at device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1/1-4.1:1.0/ttyUSB0':
KERNEL=="ttyUSB0"
SUBSYSTEM=="usb-serial"
DRIVER=="pl2303"
ATTR{port_number}=="0"
looking at parent device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1/1-4.1:1.0':
KERNELS=="1-4.1:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="pl2303"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bNumEndpoints}=="03"
ATTRS{bInterfaceClass}=="ff"
ATTRS{bInterfaceSubClass}=="00"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{modalias}=="usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00"
ATTRS{supports_autosuspend}=="1"
looking at parent device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.1':
KERNELS=="1-4.1"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="538"
ATTRS{idVendor}=="067b"
ATTRS{idProduct}=="2303"
ATTRS{bcdDevice}=="0300"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="12"
ATTRS{busnum}=="1"
ATTRS{devnum}=="6"
ATTRS{version}==" 1.10"
ATTRS{maxchild}=="0"
ATTRS{quirks}=="0x0"
ATTRS{authorized}=="1"
ATTRS{manufacturer}=="Prolific Technology Inc."
ATTRS{product}=="USB-Serial Controller"
<snip>
foo@bar:~$ udevadm info --attribute-walk --path=/sys/bus/usb-serial/devices/ttyUSB1
looking at device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5/1-4.5:1.0/ttyUSB1':
KERNEL=="ttyUSB1"
SUBSYSTEM=="usb-serial"
DRIVER=="pl2303"
ATTR{port_number}=="0"
looking at parent device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5/1-4.5:1.0':
KERNELS=="1-4.5:1.0"
SUBSYSTEMS=="usb"
DRIVERS=="pl2303"
ATTRS{bInterfaceNumber}=="00"
ATTRS{bAlternateSetting}==" 0"
ATTRS{bNumEndpoints}=="03"
ATTRS{bInterfaceClass}=="ff"
ATTRS{bInterfaceSubClass}=="00"
ATTRS{bInterfaceProtocol}=="00"
ATTRS{modalias}=="usb:v067Bp2303d0300dc00dsc00dp00icFFisc00ip00"
ATTRS{supports_autosuspend}=="1"
looking at parent device
'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5':
KERNELS=="1-4.5"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="69"
ATTRS{idVendor}=="067b"
ATTRS{idProduct}=="2303"
ATTRS{bcdDevice}=="0300"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="12"
ATTRS{busnum}=="1"
ATTRS{devnum}=="7"
ATTRS{version}==" 1.10"
ATTRS{maxchild}=="0"
ATTRS{quirks}=="0x0"
ATTRS{authorized}=="1"
ATTRS{manufacturer}=="Prolific Technology Inc."
ATTRS{product}=="USB-Serial Controller"
<snip>
所有适配器都插入单个 USB 集线器。由于我无法区分适配器本身,是否有办法编写一个 udev 规则,根据适配器插入集线器上的哪个物理端口来确定每个适配器的名称?
答案1
有什么方法可以编写一个 udev 规则,根据适配器插入集线器上的哪个物理端口来确定每个适配器的名称?
是的,事实证明确实如此。考虑上面第二个示例中显示的设备层次结构的最后部分:
查看父设备'/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5':KERNELS=="1-4.5"
SUBSYSTEMS=="usb"
DRIVERS=="usb"
ATTRS{configuration}==""
ATTRS{bNumInterfaces}==" 1"
ATTRS{bConfigurationValue}=="1"
ATTRS{bmAttributes}=="80"
ATTRS{bMaxPower}=="100mA"
ATTRS{urbnum}=="69"
ATTRS{idVendor}=="067b"
ATTRS{idProduct}=="2303"
ATTRS{bcdDevice}=="0300"
ATTRS{bDeviceClass}=="00"
ATTRS{bDeviceSubClass}=="00"
ATTRS{bDeviceProtocol}=="00"
ATTRS{bNumConfigurations}=="1"
ATTRS{bMaxPacketSize0}=="64"
ATTRS{speed}=="12"
ATTRS{busnum}=="1"
ATTRS{devnum}=="7" ATTRS{version}==" 1.10" ATTRS{maxchild}=="0" ATTRS{quirks}=="0x0"
ATTRS{authorized}=="1"
ATTRS{manufacturer}=="Prolific Technology Inc."
ATTRS{product}=="USB 串行控制器"
内核赋予该设备的名称 (KERNELS=="1-4.5") 表示该设备插入到与总线 1 上的端口 4 相连的集线器的第五个端口中(请参阅此常见问题解答有关如何解码 sysfs usb 设备层次结构的更多信息)。本指南在编写 udev 规则时,我为我的 USB 转串口转换器制定了以下一组 udev 规则:
内核=="ttyUSB*", 内核=="1-8.1.5", 名称="ttyUSB0"
内核=="ttyUSB*", 内核=="1-8.1.6", 名称="ttyUSB1"
内核=="ttyUSB*", 内核=="1-8.1.1", 名称="ttyUSB2"
内核=="ttyUSB*", 内核=="1-8.1.2", 名称="ttyUSB3"
这些规则有一个明显的缺点:它们假设所有 USB 转串口转换器都将插入同一个集线器(“1-8.1.*”)。如果将 USB 转串口转换器插入另一个 USB 端口,则可能会为其分配名称“ttyUSB0”,这将与上面描述的命名方案相冲突。但是,由于我将所有转换器都插入集线器,因此我可以忍受这个限制。
答案2
虽然在这种特定情况下它没有帮助,但是一些适配器被分配了唯一的序列 ID:
udevadm info -a -n /dev/ttyUSB1 | grep '{serial}'
适配器序列号示例:
ATTRS{serial}=="A6008isP"`
udev 规则将包含:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A6008isP", SYMLINK+="arduino"
答案3
你看过 的内容了吗/dev/serial/by-id/
?在类似情况下,每个设备都被分配了一个唯一的持久 ID(我承认我不知道它实际上代表什么)。
答案4
使用答案而不是评论,因为我需要格式化。
这些规则有一个明显的缺点:它们假设所有 USB 转串口转换器都将插入同一个集线器(“1-8.1.*”)。如果将 USB 转串口转换器插入另一个 USB 端口,则可能会为其分配名称“ttyUSB0”,这将与上面描述的命名方案相冲突。但是,由于我将所有转换器都插入集线器,因此我可以忍受这个限制。
我遇到了这个问题,可以使用一个小型 C 程序来操作 %devpath 或您选择的其他 USB 属性的文本轻松修复。
然后你可以像这样调用该程序:
ACTION!="add|change", GOTO="99-local-end
SUBSYSTEM=="usb", ATTR{idVendor}=="0403", ATTR{idProduct}=="6001", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GOTO="99-local-tty-ftdi"
GOTO="99-local-end"
LABEL="99-local-tty-ftdi"
IMPORT{program}="/usr/local/lib/udev/multiusbserial-id %s{devpath}"
# Hayes-style Modem
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="1", GROUP="dialout", MODE="0660", SYMLINK+="modem"
# Console for network device
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="2", GROUP="wheel", MODE="0660", SYMLINK+="ttyswitch"
# Serial port for software development
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="3", GROUP="eng", MODE="0660", SYMLINK+="ttyrouter"
# Unused
ENV{ID_MULTIUSBSERIAL_DEVNAME_MINOR}=="4", GROUP="wheel", MODE="0660"
LABEL="99-local-end"
其中 multiusbserial-id 是编译后的 C 程序。
该程序只需要在特定点之后打印文本,因此并不复杂
/* multiusbserial.c */
#include <stdio.h>
#include <stdlib.h>
#define PROGRAM_NAME "multiusbserial-id"
#define VARIABLE_PREFIX "ID_MULTIUSBSERIAL_"
int main(int argc, char *argv[])
{
char *p;
int found = 0;
if (argc != 2) {
fprintf(stderr, "Usage: " PROGRAM_NAME " ATTRS{devpath}\n");
exit(1);
}
for (p = argv[1]; *p != '\0'; p++) {
if (*p == '.') {
p++;
found = (*p != '\0');
break;
}
}
if (!found) {
fprintf(stderr, PROGRAM_NAME ": unexpected format\n");
exit(1);
}
printf(VARIABLE_PREFIX "DEVNAME_MINOR=%s\n", p);
return 0;
}
我写了一个博客文章包含更多详细信息。这是设置嵌入式系统团队编程环境系列文章之一。