Как отличить идентичные адаптеры USB-to-serial?
Я использую несколько идентичных адаптеров USB-to-serial с моим ноутбуком (Ubuntu 9.10). Адаптеры изготовлены Sabrent и построены вокруг интегральной схемы Prolific PL2303, как показано 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, которое фиксирует имя каждого адаптера в зависимости от того, к какому физическому порту на концентраторе подключен адаптер?
7 ответов
Можно ли как-нибудь написать правило udev, которое фиксирует имя каждого адаптера в зависимости от того, к какому физическому порту концентратора подключен адаптер?
Да, как оказалось. Рассмотрим последнюю часть иерархии устройств, показанную во втором примере выше:
глядя на родительское устройство '/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.5': KERNELS == "1-4.5"
SUBSYSTEMS == "USB"
DRIVERS == "USB"
ATTRS {конфигурации} == ""
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 {Скорость} == "12"
ATTRS {busnum} == "1"
ATTRS {devnum} == "7" ATTRS {версия} == "1.10" ATTRS {maxchild} == "0" ATTRS {quirks} == "0x0"
ATTRS {разрешено} == "1"
ATTRS {производитель} == "Prolific Technology Inc."
ATTRS {product} == "USB-последовательный контроллер"
Имя, данное этому устройству ядром (KERNELS=="1-4.5"), указывает, что это устройство подключено к пятому порту концентратора, подключенного к порту четыре на шине 1 (см. Этот FAQ для получения дополнительной информации о том, как декодировать иерархия USB-устройств sysfs). С некоторой помощью из этого руководства по написанию правил udev я придумал следующий набор правил udev для моих преобразователей USB-последовательный порт:
KERNEL == "ttyUSB *", KERNELS == "1-8.1.5", NAME = "ttyUSB0"
KERNEL == "ttyUSB *", KERNELS == "1-8.1.6", NAME = "ttyUSB1"
KERNEL == "ttyUSB *", KERNELS == "1-8.1.1", NAME = "ttyUSB2"
KERNEL == "ttyUSB *", KERNELS == "1-8.1.2", NAME = "ttyUSB3"
Эти правила имеют один очевидный недостаток: они предполагают, что все преобразователи USB-последовательный порт будут подключены к одному и тому же концентратору ("1-8.1. *"). Если преобразователь USB в последовательный порт был подключен к другому порту USB, ему можно было бы присвоить имя "ttyUSB0", что противоречило бы описанной выше схеме именования. Однако, так как я оставляю все преобразователи подключенными к концентратору, я могу жить с этим ограничением.
Хотя это не поможет в этом конкретном случае, некоторым адаптерам назначаются уникальные серийные идентификаторы:
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 года назад, он может не задаться вопросом, но я опубликую его для дальнейшего использования.
Существует способ перепрограммировать серийный номер путем доступа к EEPROM микросхем FTDI, Silicon Labs предоставляет инструмент, но только для Windows:
Страница продукта-> Инструменты-> Утилита настройки фиксированных функций
Инструкцию можно найти на remotehq:
http://remoteqth.com/wiki/index.php?page=How+to+set+usb+device+SerialNumber
Существует также библиотека Unix на Sourceforge. Это только проверено с CP2101 / CP2102 / CP2103, и я не пробовал это лично.
Вы смотрели на содержание /dev/serial/by-id/
? В аналогичной ситуации каждому устройству был присвоен уникальный постоянный идентификатор (я признаю, что не знаю, что оно на самом деле представляет).
Раздражают некоторые
ATTR
-поля, не перечисленные
udevadm
один из которых
ATTR{index}
.
Насколько я могу сказать, для веб-камер это всегда устройство с
ATTR{index}=="0"
что является фактическим, реальным устройством, которое может использоваться программным обеспечением (остальные, как я понимаю, являются метаустройствами , что бы это ни значило). Так, например, следующее правило делает
/dev/webcam
всегда указывать на последнюю подключенную веб-камеру:
SUBSYSTEM=="video4linux", ATTR{index}=="0", GROUP="video", SYMLINK+="webcam"
Использование ответа, а не комментария, так как мне нужно форматирование.
Эти правила имеют один очевидный недостаток: они предполагают, что все преобразователи 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 - это скомпилированная Си-программа.
Программа просто должна печатать текст после определенного момента, чтобы он не был сложным
/* 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;
}
Я написал статью в блоге с более подробной информацией. Это один из серии по настройке среды командного программирования для встраиваемых систем.
Вы можете перечислить последовательные устройства USB, как это
ls -l /sys/bus/usb-serial/devices
total 0
lrwxrwxrwx 1 root root 0 Oct 9 09:10 ttyUSB0 -> ../../../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB0
lrwxrwxrwx 1 root root 0 Oct 9 09:10 ttyUSB1 -> ../../../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5:1.0/ttyUSB1
Две строки заканчиваются на
1-1.3:1.0/ttyUSB0
1-1.5:1.0/ttyUSB1
Это на Raspberry Pi. Я сейчас оставлю устройство ttyUSB1
подключен и вытащить адаптер ttyUSB0
и подключите его к другому порту, затем к другому, а затем обратно к начальному порту
# original setup
['1-1.3:1.0', 'ttyUSB0'] --
['1-1.5:1.0', 'ttyUSB1']
# move it to port above 1.3
['1-1.3:1.0', 'ttyUSB0']
['1-1.5:1.0', 'ttyUSB1']
['1-1.2:1.0', 'ttyUSB2'] --
# move it to port above 1.5
['1-1.3:1.0', 'ttyUSB0']
['1-1.5:1.0', 'ttyUSB1']
['1-1.4:1.0', 'ttyUSB2'] --
# move it back to the original port
['1-1.3:1.0', 'ttyUSB0'] --
['1-1.5:1.0', 'ttyUSB1']
Я не знаю почему 1-1.3:1.0
не отключается при отключении, но я могу с этим смириться, так как я редко меняю адаптеры с одного USB-порта на другой.
Моя проблема заключалась в том, что на Raspberry Pi, который управляет реле затвора через Arduino, подключенный через USB-кабель, и считывает данные датчика окружающей среды через другой Arduino (того же производителя, той же модели), иногда, когда активируются затворы, данные датчика Arduino вышибаются выключен и переназначен с ttyUSB0 на ttyUSB2 (ttyUSB1 - затвор). Я закончил с этим скриптом Python, чтобы не пришлось методом проб и ошибок выяснять, на каком устройстве теперь были данные датчика.
usb_devices = collections.OrderedDict()
usb_device_list = subprocess.check_output('ls -l /sys/bus/usb-serial/devices', shell=True, universal_newlines=True).split('\n')
for usb_device in usb_device_list:
match = re.search("([^/]+)/([^/]+)$", usb_device)
if match:
usb_devices[match.group(1)] = match.group(2)
for key, value in usb_devices.items():
print key, value
# I know that 1.3 is the environment sensor device
if '1-1.3:1.0' in usb_devices:
print '1-1.3:1.0 -->', usb_devices['1-1.3:1.0'] # == ttyUSB0
что дает мне следующий вывод
1-1.3:1.0 ttyUSB0
1-1.5:1.0 ttyUSB1
1-1.3:1.0 --> ttyUSB0
Я выполняю эту проверку только тогда, когда происходят тайм-ауты из-за ошибки соединения.