Мне было интересно, как получить уникальный идентификатор запоминающего устройства USB. Я уже знаю, как получить серийный идентификатор SCSI из этого сообщения: USB-drive serial number under linux C++ В сообщении упоминается использование дескриптора устройства для получения идентификатора. Может ли кто-нибудь опубликовать код для определения информации о дескрипторе устройства в Linux?Как однозначно идентифицировать USB-устройство?
ответ
ls -l /dev/disk/by-id
Я делаю это с HAL в C++/C с помощью QT. Нашли в блоге об этом:
How to detect if /dev/* is a USB device
Было бы возможно сделать это с помощью оболочки & HAL.
сделать sudo blkid
и перечислит идентификатор всех подключенных устройств с файловой системой
Чтобы добавить к тому, что все остальные уже сказал:
USB устройства не всегда имеют серийный номер; даже если он присутствует, он не гарантированно является глобально уникальным. (Например, у моей клавиатуры Apple USB нет серийного номера и GoPro cameras all have the same bogus serial number of 123456789ABC
.) Таким образом, не всегда можно однозначно идентифицировать устройство.
С USB-устройством может быть изменено «имя устройства» устройства, в зависимости от порядка подключения устройства. Удивительно мало устройств имеют настоящий серийный номер. Если вы не можете получить уникальный идентификатор от самого устройства, единственное решение зависит от физического адреса соединения. Недостатком этого является то, что адрес изменяется, если вы подключите устройство к другому разъему USB.
Программно вы можете использовать sysfs для получения информации о ядре, связанной с устройством. Sysfs представляет собой файловое системное представление устройств, которое видит их ядро. (Его не реальные файлы на диске)
С его помощью вы можете: - определить тип устройства с идентификатором продукта и поставщика - прочитать серийный номер устройства, если он есть. - прочитайте номер физического соединения на USB-концентраторе
Вы можете начать поиск с вашего типа устройств в/sys/class. В этом примере я использую порт USB → LPT. Но принцип тот же.
$ ls -l /sys/class/usbmisc
lp1 -> ../../devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5/4-1.5:1.0/usbmisc/lp1
lp2 -> ../../devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.6/4-1.6:1.0/usbmisc/lp2
Grap имя устройства из файла uevent:
cat /sys/class/usbmisc/lp1/uevent
MAJOR=180
MINOR=1
DEVNAME=__usb/lp1__
оных/DEV, так что вы получите имя устройства, чтобы открыть: /DEV/USB/lp1
Используйте реальный путь: $ cd -P/sys/class/usbmisc/lp1
Шаг назад 3 ветки:
$ cd ../../../
/sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5
Этот каталог содержит много информации на устройстве:
idProduct и idVendor может быть использован, чтобы однозначно идентифицировать тип устройства.
Если есть файл serial и он содержит уникальный серийный номер, все готово.
В противном случае вы можете использовать физическое соединение в качестве идентификатора, которое является именем этого каталога «4-1.5« Оно уникально для физического подключения и, как вы уже сказали, измените, если вы подключите устройство к другому порт.
Обобщая Simon Rigét's answer, я придумал эту функцию bash, которая, учитывая необязательный идентификатор поставщика и идентификатор продукта, возвращает список имен узлов устройства, связанных с этим идентификатором поставщика и идентификатором продукта, если он указан.
getDevNodes() {
if [ -n "$1" ] && [ "$1" != "no_class" ]; then
2>/dev/null find -L /sys/class/$1 -maxdepth 2 -mindepth 2 -name uevent -exec realpath "{}" +
else
find /sys/devices -name uevent
fi | {
if [ -z "$1" ]; then
readarray -t lines < <(find /sys/class -maxdepth 2 -mindepth 2 -type l -print -exec realpath "{}" +)
local -i count=${#lines[@]} sys_dev=count/2 sys_class=0
local -A classes
while [ $sys_dev -lt $count ]; do
class="${lines[$sys_class]#/*/*/}"
class="${class%/*}"
classes["${lines[$sys_dev]}"]="$class"
sys_dev+=1
sys_class+=1
done
fi
readarray -t uevents
for u in "${uevents[@]}"; do
DEVNAME=; DEVTYPE=no_type; while IFS="=" read key value; do {
[ "$key" = "DEVNAME" ] && DEVNAME=/dev/"$value"
} || {
[ "$key" = "DEVTYPE" ] && DEVTYPE="$value"
}; done < "$u"
if [ -n "$DEVNAME" ]; then
path="${u%/uevent}"
while [ "$path" != "/sys/devices" ] && ! [ -f "$path"/idVendor ]; do
path="${path%/*}"
done
[ "$path" != "/sys/devices" ] && {
read readIdVendor < "$path"/idVendor
read readIdProduct < "$path"/idProduct
} || {
readIdVendor=----
readIdProduct=----
}
echo "${1:-${classes[${u%/uevent}]:-no_class}}" "$DEVTYPE" "$readIdVendor" "$readIdProduct" "$DEVNAME"
fi
done
} | grep "^${1:-[[:graph:]]\+} ${2:-[[:graph:]]\+} ${3:-....} ${4:-....}" | cat
}
Например, это то, что мой lsusb говорит мне:
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 008: ID 0bda:b719 Realtek Semiconductor Corp.
Bus 001 Device 006: ID 0bda:57b5 Realtek Semiconductor Corp.
Bus 001 Device 004: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
Bus 001 Device 097: ID 1004:6344 LG Electronics, Inc. G2 Android Phone [tethering mode]
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Так что, если я хотел бы видеть, какие устройства узлы связаны с 0x1004 Vendor ID и 0x6344 идентификатор продукта я бы выполните следующие действия:
$ getDevNodes "" "" 1004 6344
no_class usb_device 1004 6344 /dev/bus/usb/001/097
tty no_type 1004 6344 /dev/ttyACM0
Таким образом, мы получили два узла устройства, один из которых из класса TTY, не DEVTYPE, а другие неизвестного класса, но с devtype usb_device.
Можно также дать только идентификатор поставщика, как это:
$ getDevNodes "" "" 0bda
no_class usb_device 0bda 0129 /dev/bus/usb/001/004
no_class usb_device 0bda b719 /dev/bus/usb/001/008
no_class no_type 0bda 57b5 /dev/media0
video4linux no_type 0bda 57b5 /dev/video0
input no_type 0bda 57b5 /dev/input/event14
no_class usb_device 0bda 57b5 /dev/bus/usb/001/006
Если бы я только хотел video4linux класса устройств, чей поставщик идентификатор 0bda, то я бы сделать следующее:
$ getDevNodes video4linux "" "" 0bda
video4linux no_type 0bda 57b5 /dev/video0
Аргументы в основном являются фильтрами по всему списку узлов устройств и связанной с ними информации. Опуская один из этих аргументов или используя пустую строку "" в качестве аргумента, отключает фильтр для этого конкретного аргумента.
Аргументы даются в следующем порядке: 1: класс, 2: тип, 3: идентификатор поставщика, 4: идентификатор продукта.
Здесь следует облегченной версии выше функции, которая работает быстрее за счет некоторых функциональных возможностей: узлы устройства печатаются без дополнительной информации и нет фильтра для данного типа устройства.
getDevNodesLite() {
if [ -n "$1" ]; then
2>/dev/null find -L /sys/class/$1 -maxdepth 2 -mindepth 2 -name uevent -exec realpath "{}" +
else
find /sys/devices -name uevent
fi | {
if [ -n "$2" ]; then
readarray -t uevents
for u in "${uevents[@]}"; do
path="${u%/uevent}"
while [ "$path" != "/sys/devices" ] && ! [ -f "$path"/idVendor ]; do
path="${path%/*}"
done
[ "$path" != "/sys/devices" ] && read readValue < "$path"/idVendor && [ "$readValue" = "$2" ] && {
if [ -n "$idProduct" ]; then
read readValue < "$path"/idProduct && [ "$readValue" = "$3" ]
fi
} && echo "$u"
done
else
cat
fi
} | {
readarray -t uevents
[ ${#uevents[@]} -gt 0 ] && sed -n 's,DEVNAME=\(.*\),/dev/\1,p' "${uevents[@]}"
}
}