2010-12-01 5 views
4

Мне было интересно, как получить уникальный идентификатор запоминающего устройства USB. Я уже знаю, как получить серийный идентификатор SCSI из этого сообщения: USB-drive serial number under linux C++ В сообщении упоминается использование дескриптора устройства для получения идентификатора. Может ли кто-нибудь опубликовать код для определения информации о дескрипторе устройства в Linux?Как однозначно идентифицировать USB-устройство?

ответ

0

сделать sudo blkid и перечислит идентификатор всех подключенных устройств с файловой системой

1

Чтобы добавить к тому, что все остальные уже сказал:

USB устройства не всегда имеют серийный номер; даже если он присутствует, он не гарантированно является глобально уникальным. (Например, у моей клавиатуры Apple USB нет серийного номера и GoPro cameras all have the same bogus serial number of 123456789ABC.) Таким образом, не всегда можно однозначно идентифицировать устройство.

1

С 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« Оно уникально для физического подключения и, как вы уже сказали, измените, если вы подключите устройство к другому порт.

1

Обобщая 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[@]}" 
    } 
}