How do I uniquely identify an USB-device?

前端 未结 6 1351
心在旅途
心在旅途 2021-01-13 02:32

I was wondering how to get the unique id of a USB storage device. I already know how to fetch the SCSI serial id from this post : USB-drive serial number under linux C++ Th

相关标签:
6条回答
  • 2021-01-13 03:08
    ls -l /dev/disk/by-id
    
    0 讨论(0)
  • 2021-01-13 03:10

    To add to what everyone else has said:

    USB devices do not always have a serial number; even when one is present, it is not guaranteed to be globally unique. (For instance, my Apple USB keyboard has no serial number, and GoPro cameras all have the same bogus serial number of 123456789ABC.) As such, it is not always possible to uniquely identify a device.

    0 讨论(0)
  • 2021-01-13 03:15

    do sudo blkid and it will list the id of all mounted devices with a filesystem

    0 讨论(0)
  • 2021-01-13 03:19

    Generalizing Simon Rigét's answer, I came up with this bash function that, given optional vendor id and product id, returns a list of device node names, related to that vendor id and that product id if given.

    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
    }
    

    For instance, this is what my lsusb tells me:

    $ 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
    

    So, if I wanted to see which device nodes are associated with the vendor id 0x1004 and the product id 0x6344 I'd do the following:

    $ getDevNodes "" "" 1004 6344
    no_class        usb_device      1004 6344 /dev/bus/usb/001/097 
    tty             no_type         1004 6344 /dev/ttyACM0 
    

    So we've got two device nodes, one of which is of class tty with no devtype and the other is of unknown class but with a devtype usb_device.

    One can also only give the vendor id, like this:

    $ 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 
    

    If I only wanted video4linux class devices whose vendor id is 0bda, then I'd do the following:

    $ getDevNodes video4linux "" "" 0bda
    video4linux     no_type         0bda 57b5 /dev/video0 
    

    Arguments are basically filters over the complete list of device nodes and their associated info. Omitting one of those arguments, or using the empty string "" as an argument, disables the filter for that specific argument.

    Arguments are given in this order: 1: the class, 2: the type, 3: the vendor id, 4: the product id.


    Here follows a lite version of the above function that runs faster at the expense of some functionalities: the device nodes are printed without the additional info and there's no filter for the device type.

    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[@]}"
        }
    }
    
    0 讨论(0)
  • 2021-01-13 03:23

    I suggest to use libusb. You can find the documentation here.

    0 讨论(0)
  • 2021-01-13 03:24

    With USB, the "device name" of a device can change, depending on the order of which, the device was connected. Surprisingly few devices have a real serial number. If you can't get a unique identification from the device itself, the only solution is to depend on the physical address of connection. The drawback on this, is that the address changes, if you plug the device into another USB connector.

    Programmatically you can use sysfs to get the information the kernel has, about the device. Sysfs is a file-system-like representation of devices as the kernel sees them. (Its not real files on the disk)

    With it, you can: - identify the device type with product and vendor ID - read the serial number of the device, if it has one. - read the physical connection number on the USB hub

    You could start by finding your type of devices in /sys/class. In this example I use an USB→LPT port. But the principle is the same.

    $ 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 the device name from the uevent file:

    cat /sys/class/usbmisc/lp1/uevent
    MAJOR=180
    MINOR=1
    DEVNAME=__usb/lp1__
    

    add /dev so you get the device name to open: /dev/usb/lp1

    Use the real path: $ cd -P /sys/class/usbmisc/lp1

    Step back 3 branches:

    $ cd ../../../
    /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.5
    

    This directory contains a lot of the information on the device:

    idProduct and idVendor can be used to uniquely identify the device type.

    If there is a serial file and it contains a unique serial number, you are done.

    Otherwise your option is to use the physical connection as identification, wich is this directory name “4-1.5” It is unique for the physical connection, and will as you already mentioned change if you plug the device to another port.

    0 讨论(0)
提交回复
热议问题