【问题标题】:How to map /proc/bus/usb/devices entry to a /dev/sdX device?如何将 /proc/bus/usb/devices 条目映射到 /dev/sdX 设备?
【发布时间】:2012-05-18 13:43:17
【问题描述】:

我需要知道如何确定 /dev/sdX 设备映射到 /proc/bus/usb/devices 中的哪个条目。基本上,我需要知道给定 U 盘的供应商 ID 和产品 ID(可能没有序列号)。

就我而言,我的闪存驱动器在 /proc/bus/usb/devices 中有以下条目:

T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  6 Spd=480 MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=0781 ProdID=5530 Rev= 2.00
S:  Manufacturer=SanDisk
S:  Product=Cruzer
S:  SerialNumber=0765400A1BD05BEE
C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms

我碰巧知道在我的情况下它是 /dev/sda,但我不确定如何在代码中解决这个问题。我的第一种方法是遍历所有 /dev/sdXX 设备并发出 SCSI_IOCTL_GET_BUS_NUMBER 和/或 SCSI_IOCTL_GET_IDLUN 请求,但返回的信息无法帮助我匹配:

/tmp # ./getscsiinfo /dev/sda
SCSI bus number: 8
ID: 00
LUN: 00
Channel: 00
Host#: 08
four_in_one: 08000000
host_unique_id: 0

我不确定如何使用 SCSI 总线号或 ID、LUN、通道、主机将其映射到 /proc/bus/usb/devices 中的条目。或者我如何从 /proc/bus/usb/001/006 设备获取 SCSI 总线号,它是一个 usbfs 设备,似乎不喜欢相同的 ioctl:

/tmp # ./getscsiinfo /proc/bus/usb/001/006
Could not get bus number: Inappropriate ioctl for device

这是我的小getscsiinfo测试工具的测试代码:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>

struct scsi_idlun
{
    int four_in_one;
    int host_unique_id;
};

int main(int argc, char** argv) {
    if (argc != 2)
        return 1;

    int fd = open(argv[1], O_RDONLY | O_NONBLOCK);
    if (fd < 0)
    {
        printf("Error opening device: %m\n");
        return 1;
    }

    int busNumber = -1;
    if (ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &busNumber) < 0)
    {
        printf("Could not get bus number: %m\n");
        close(fd);
        return 1;
    }

    printf("SCSI bus number: %d\n", busNumber);

    struct scsi_idlun argid;
    if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &argid) < 0)
    {
        printf("Could not get id: %m\n");
        close(fd);
        return 1;
    }

    printf("ID: %02x\n", argid.four_in_one & 0xFF);
    printf("LUN: %02x\n", (argid.four_in_one >> 8) & 0xFF);
    printf("Channel: %02x\n", (argid.four_in_one >> 16) & 0xFF);
    printf("Host#: %02x\n", (argid.four_in_one >> 24) & 0xFF);
    printf("four_in_one: %08x\n", (unsigned int)argid.four_in_one);
    printf("host_unique_id: %d\n", argid.host_unique_id);

    close(fd);
    return 0;
}

有人知道吗?

【问题讨论】:

    标签: c linux usb-drive


    【解决方案1】:

    我相信您可以使用 libudev 库收集此类信息。

    这里有一些关于它的细节:http://www.signal11.us/oss/udev/

    我在上面的网站上发现了类似的东西:

    .. 使用 libudev,我们将能够检查设备,包括其供应商 ID (VID)、产品 ID (PID)、序列号和设备字符串,而无需打开设备。此外,libudev 将告诉我们设备节点在 /dev 内的确切位置,从而为应用程序提供一种强大且独立于分发的设备访问方式。 ...

    【讨论】:

    • 不幸的是,我似乎也无法从 udev 获取 /dev/sdX 节点。为我的 USB 记忆棒返回的开发节点路径 libudev 为空。但我确实得到了它的父级(主机控制器)的路径,但不幸的是,它指向 /proc/bus/usb/001/006 设备。我需要能够挂载这个设备,而且我只能使用适当的 /dev/sdX 设备来做到这一点......而且我不知道 /dev/sdX 设备中的哪个与 /proc/bus/usb/ 相关001/006。 libudev 也没有给我这个信息。
    • 大多数 Linux 系统都将 udev 配置为在插入 USB 密钥时执行操作。也许您可以根据需要调整该配置规则
    【解决方案2】:

    udevadm 能够实现您想要实现的目标。

    udevadm info -a -p $(udevadm info -q path -n /dev/sda)
    

    udevadm 的消息来源会告诉你它是如何完成的。

    【讨论】:

      【解决方案3】:

      这并不是那么容易,也没有很好的记录(至少从高层次的角度来看)。以下内容应该在 3.1 以上版本的内核中工作(至少)。

      我发现最简单(可能不是唯一的方法)是从块设备条目导航并测试每个块设备,直到找到与 USB 条目匹配的那个。

      例如,给定/sys/block中的块设备,如sdb,可以这样找到硬件设备描述符条目:

      # cd /sys/block
      # cd $(readlink sdb); cd ../../../../../..
      # ls -l
      total 0
      drwxr-xr-x 6 root root     0 Aug 14 10:47 1-1:1.0
      -rw-r--r-- 1 root root  4096 Aug 14 10:52 authorized
      -rw-r--r-- 1 root root  4096 Aug 14 10:52 avoid_reset_quirk
      -r--r--r-- 1 root root  4096 Aug 14 10:47 bcdDevice
      -rw-r--r-- 1 root root  4096 Aug 14 10:49 bConfigurationValue
      -r--r--r-- 1 root root  4096 Aug 14 10:47 bDeviceClass
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bDeviceProtocol
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bDeviceSubClass
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bmAttributes
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bMaxPacketSize0
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bMaxPower
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bNumConfigurations
      -r--r--r-- 1 root root  4096 Aug 14 10:49 bNumInterfaces
      -r--r--r-- 1 root root  4096 Aug 14 10:49 busnum
      -r--r--r-- 1 root root  4096 Aug 14 10:52 configuration
      -r--r--r-- 1 root root 65553 Aug 14 10:47 descriptors
      -r--r--r-- 1 root root  4096 Aug 14 10:52 dev
      -r--r--r-- 1 root root  4096 Aug 14 10:49 devnum
      -r--r--r-- 1 root root  4096 Aug 14 10:52 devpath
      lrwxrwxrwx 1 root root     0 Aug 14 10:47 driver -> ../../../../../../bus/usb/drivers/usb
      drwxr-xr-x 3 root root     0 Aug 14 10:52 ep_00
      -r--r--r-- 1 root root  4096 Aug 14 10:47 idProduct
      -r--r--r-- 1 root root  4096 Aug 14 10:47 idVendor
      -r--r--r-- 1 root root  4096 Aug 14 10:52 ltm_capable
      -r--r--r-- 1 root root  4096 Aug 14 10:47 manufacturer
      -r--r--r-- 1 root root  4096 Aug 14 10:49 maxchild
      lrwxrwxrwx 1 root root     0 Aug 14 10:52 port -> ../1-0:1.0/port1
      drwxr-xr-x 2 root root     0 Aug 14 10:52 power
      -r--r--r-- 1 root root  4096 Aug 14 10:47 product
      -r--r--r-- 1 root root  4096 Aug 14 10:52 quirks
      -r--r--r-- 1 root root  4096 Aug 14 10:47 removable
      --w------- 1 root root  4096 Aug 14 10:52 remove
      -r--r--r-- 1 root root  4096 Aug 14 10:47 serial
      -r--r--r-- 1 root root  4096 Aug 14 10:49 speed
      lrwxrwxrwx 1 root root     0 Aug 14 10:47 subsystem -> ../../../../../../bus/usb
      -rw-r--r-- 1 root root  4096 Aug 14 10:47 uevent
      -r--r--r-- 1 root root  4096 Aug 14 10:52 urbnum
      -r--r--r-- 1 root root  4096 Aug 14 10:49 version
      

      (您可以找到有关 USB 描述符 here on the BeyondLogic site. 内容的优秀文档)

      鉴于上述情况,您应该能够将一个或多个 USB 设备字段映射到 /proc/bus/usb/devices 的内容。我发现序列号是最容易匹配的,所以如果你是上面的cat serial,你会得到与列出的相同的序列号:

      T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 0
      D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
      P:  Vendor=0781 ProdID=5575 Rev=01.26
      S:  Manufacturer=SanDisk
      S:  Product=Cruzer Glide
      S:  SerialNumber=4C530100801115115112
      C:  #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=200mA
      I:  If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
      

      如果您转到/sys/block,您可以在每个设备的存储驱动程序 sysfs 条目中列出主机设备条目的完整路径。通常,我使用一些编程方式而不是在 shell 提示符下执行此操作,但在这里您可以看到链接本身:

      # ls -l sd*
      lrwxrwxrwx 1 root root 0 Aug 14 10:45 sda -> ../devices/pci0000:00/0000:00:10.0/host32/target32:0:0/32:0:0:0/block/sda
      lrwxrwxrwx 1 root root 0 Aug 14 10:47 sdb -> ../devices/pci0000:00/0000:00:11.0/0000:02:03.0/usb1/1-1/1-1:1.0/host33/target33:0:0/33:0:0:0/block/sdb
      

      请注意,您不得对链接中看到的数字做出任何假设。根据总线子系统,映射可能完全不同。例如,在 Raspberry Pi 上,它看起来像这样:

      # ls -l sd*
      lrwxrwxrwx 1 root root 0 Aug 13 23:54 sda -> ../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/host3/target3:0:0/3:0:0:0/block/sda
      lrwxrwxrwx 1 root root 0 Aug 13 23:54 sdb -> ../devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/host4/target4:0:0/4:0:0:0/block/sdb
      

      因此,最好的方法是采用顶部列出的方法并相对于存储驱动程序进行导航以找到 USB 设备描述符。

      我很好奇对此有更权威的答案。上面的方法是通过反复试验得出的,但已经在几种不同的设备和内核上运行,没有问题。

      【讨论】:

        【解决方案4】:

        您可以使用 /proc/scsi/scsi,而不是使用用于 usbfs 的 proc/bus/usb。在那里,您可以找到具有特定通道 ID 和 LUN 编号的供应商和序列号。

        【讨论】:

          猜你喜欢
          • 2011-03-30
          • 1970-01-01
          • 2022-12-10
          • 2013-06-21
          • 2022-11-16
          • 2022-11-14
          • 1970-01-01
          • 2011-07-03
          • 1970-01-01
          相关资源
          最近更新 更多