Patchwork [1/2] usb-linux: fix device path aka physical port handling

login
register
mail settings
Submitter Gerd Hoffmann
Date May 10, 2011, 10:30 a.m.
Message ID <1305023443-8722-2-git-send-email-kraxel@redhat.com>
Download mbox | patch
Permalink /patch/94966/
State New
Headers show

Comments

Gerd Hoffmann - May 10, 2011, 10:30 a.m.
The device path isn't just a number.  It specifies the physical port
the device is connected to and in case the device is connected via
usb hub you'll have two numbers there, like this: "5.1".  The first
specifies the root port where the hub is plugged into, the second
specifies the port number of the hub where the device is plugged in.
With multiple hubs chained the string can become longer.

This patch renames devpath to port and makes it a string.   It also
adapts the sysfs parsing code accordingly.  The "info usbhost" monitor
command now prints bus number, (os-assigned) device address and physical
port for each device.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 usb-linux.c |   38 ++++++++++++++++++++------------------
 1 files changed, 20 insertions(+), 18 deletions(-)
Markus Armbruster - May 11, 2011, 8:52 a.m.
Good stuff, just a few questions.

Gerd Hoffmann <kraxel@redhat.com> writes:

> The device path isn't just a number.  It specifies the physical port
> the device is connected to and in case the device is connected via
> usb hub you'll have two numbers there, like this: "5.1".  The first
> specifies the root port where the hub is plugged into, the second
> specifies the port number of the hub where the device is plugged in.
> With multiple hubs chained the string can become longer.

"5.1"?  Do you mean "5-1"?

I think you're talking about the file names in /sys/bus/usb/devices.
Kernel names them in drivers/usb/core/usb.c's usb_alloc_dev().

Roots:
		dev->devpath[0] = '0';
		dev_set_name(&dev->dev, "usb%d", bus->busnum);

Hubs:
		if (parent->devpath[0] == '0') {
			snprintf(dev->devpath, sizeof dev->devpath,
				"%d", port1);
		} else {
			snprintf(dev->devpath, sizeof dev->devpath,
				"%s.%d", parent->devpath, port1);
		}
		dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);

On my system:

$ ls /sys/bus/usb/devices
1-0:1.0  2-0:1.0  3-0:1.0  4-0:1.0  5-0:1.0  usb1  usb2  usb3  usb4  usb5

> This patch renames devpath to port and makes it a string.   It also
> adapts the sysfs parsing code accordingly.  The "info usbhost" monitor
> command now prints bus number, (os-assigned) device address and physical
> port for each device.
>
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  usb-linux.c |   38 ++++++++++++++++++++------------------
>  1 files changed, 20 insertions(+), 18 deletions(-)
>
> diff --git a/usb-linux.c b/usb-linux.c
> index 72fe6f5..9543a69 100644
> --- a/usb-linux.c
> +++ b/usb-linux.c
> @@ -62,7 +62,7 @@ struct usb_ctrlrequest {
>      uint16_t wLength;
>  };
>  
> -typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
> +typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port,
>                          int class_id, int vendor_id, int product_id,
>                          const char *product_name, int speed);
>  
> @@ -79,6 +79,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
>  #define USBPROCBUS_PATH "/proc/bus/usb"
>  #define PRODUCT_NAME_SZ 32
>  #define MAX_ENDPOINTS 15
> +#define MAX_PORTLEN 8

Where does 8 come from?  For what it's worth, kernel's struct usb_device
has char devpath[16].

>  #define USBDEVBUS_PATH "/dev/bus/usb"
>  #define USBSYSBUS_PATH "/sys/bus/usb"
>  
> @@ -155,7 +156,7 @@ typedef struct USBHostDevice {
>      /* Host side address */
>      int bus_num;
>      int addr;
> -    int devpath;
> +    char port[MAX_PORTLEN];
>      struct USBAutoFilter match;
>  
>      QTAILQ_ENTRY(USBHostDevice) next;
> @@ -1092,7 +1093,7 @@ static int usb_linux_get_configuration(USBHostDevice *s)
>          char device_name[32], line[1024];
>          int configuration;
>  
> -        sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
> +        sprintf(device_name, "%d-%s", s->bus_num, s->port);
>  
>          if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
>                                  device_name)) {
> @@ -1138,7 +1139,7 @@ static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
>          char device_name[64], line[1024];
>          int alt_setting;
>  
> -        sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
> +        sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
>                  (int)configuration, (int)interface);
>  
>          if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
> @@ -1257,7 +1258,7 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
>  }
>  
>  static int usb_host_open(USBHostDevice *dev, int bus_num,
> -                         int addr, int devpath, const char *prod_name)
> +                         int addr, char *port, const char *prod_name)
>  {
>      int fd = -1, ret;
>      struct usbdevfs_connectinfo ci;
> @@ -1283,7 +1284,7 @@ static int usb_host_open(USBHostDevice *dev, int bus_num,
>  
>      dev->bus_num = bus_num;
>      dev->addr = addr;
> -    dev->devpath = devpath;
> +    strcpy(dev->port, port);
>      dev->fd = fd;
>  
>      /* read the device description */
> @@ -1655,8 +1656,9 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
>  {
>      DIR *dir = NULL;
>      char line[1024];
> -    int bus_num, addr, devpath, speed, class_id, product_id, vendor_id;
> +    int bus_num, addr, speed, class_id, product_id, vendor_id;
>      int ret = 0;
> +    char port[MAX_PORTLEN];
>      char product_name[512];
>      struct dirent *de;
>  
> @@ -1672,8 +1674,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
>              if (!strncmp(de->d_name, "usb", 3)) {
>                  tmpstr += 3;
>              }

Sloppy parsing, but that's not your fault.

> -            if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) {
> -                goto the_end;
> +            if (sscanf(tmpstr, "%d-%7[0-9.]", &bus_num, port) < 2) {
> +                continue;
>              }
>  
>              if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {

The old sscanf() succeeds if at least one item is assigned, i.e. tmpstr
starts with an integer.  I suspect this is broken for roots.  Consider
d_name "usb1": tmpstr is "1", sscan() returns 1, and devpath remains
uninitialized.  It's passed to the func() callback.  Bug?  If yes, the
commit message should mention it.

The new sscan() succeeds only if both items are assigned, i.e. tmpstr
starts with an integer, '-', and up to 7 of [0-9.].  Unlike the old one,
it fails for roots.  Intentional?

[...]
Gerd Hoffmann - May 12, 2011, 9:17 a.m.
On 05/11/11 10:52, Markus Armbruster wrote:
> Good stuff, just a few questions.
>
> Gerd Hoffmann<kraxel@redhat.com>  writes:
>
>> The device path isn't just a number.  It specifies the physical port
>> the device is connected to and in case the device is connected via
>> usb hub you'll have two numbers there, like this: "5.1".  The first
>> specifies the root port where the hub is plugged into, the second
>> specifies the port number of the hub where the device is plugged in.
>> With multiple hubs chained the string can become longer.
>
> "5.1"?  Do you mean "5-1"?

No.

> 			snprintf(dev->devpath, sizeof dev->devpath,
> 				"%s.%d", parent->devpath, port1);
                                  ^^^^^

> $ ls /sys/bus/usb/devices
> 1-0:1.0  2-0:1.0  3-0:1.0  4-0:1.0  5-0:1.0  usb1  usb2  usb3  usb4  usb5

Boring, nothing plugged in here ;)

Laptop docking stations often have a usb hub built in.  If you have one 
place your laptop there, then connect a usb device to one of the ports 
of the docking station.  You'll see a file named like this:

   1-5.3

where "1" is the bus number, "5" is the port number of the root hub and 
"3" is the port number of the docking station hub.

>> @@ -79,6 +79,7 @@ typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
>>   #define USBPROCBUS_PATH "/proc/bus/usb"
>>   #define PRODUCT_NAME_SZ 32
>>   #define MAX_ENDPOINTS 15
>> +#define MAX_PORTLEN 8
>
> Where does 8 come from?

Pulled out of thin air.  Should be enougth, you can build chains with 
three usb hubs (anyone ever did in practice? did the devices still 
work?), getting port addresses like "1.2.3.4", and it still fits in.

> For what it's worth, kernel's struct usb_device
> has char devpath[16].

We can make that 16 too to be on the really safe side.

>> @@ -1672,8 +1674,8 @@ static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
>>               if (!strncmp(de->d_name, "usb", 3)) {
>>                   tmpstr += 3;
>>               }
>
> Sloppy parsing, but that's not your fault.

I think this can be zapped now, the new sscanf will fail on them and 
skip the entries anyway.

>> -            if (sscanf(tmpstr, "%d-%d",&bus_num,&devpath)<  1) {
>> -                goto the_end;
>> +            if (sscanf(tmpstr, "%d-%7[0-9.]",&bus_num, port)<  2) {
>> +                continue;
>>               }
>>
>>               if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
>
> The old sscanf() succeeds if at least one item is assigned, i.e. tmpstr
> starts with an integer.  I suspect this is broken for roots.  Consider
> d_name "usb1": tmpstr is "1", sscan() returns 1, and devpath remains
> uninitialized.  It's passed to the func() callback.  Bug?  If yes, the
> commit message should mention it.

Indeed.

> The new sscan() succeeds only if both items are assigned, i.e. tmpstr
> starts with an integer, '-', and up to 7 of [0-9.].  Unlike the old one,
> it fails for roots.  Intentional?

Yes, now the roots are skipped.  You can't assign them anyway, so it is 
pretty pointless to loom at them and to list them in "info usbhost".

cheers,
   Gerd

Patch

diff --git a/usb-linux.c b/usb-linux.c
index 72fe6f5..9543a69 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -62,7 +62,7 @@  struct usb_ctrlrequest {
     uint16_t wLength;
 };
 
-typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, char *port,
                         int class_id, int vendor_id, int product_id,
                         const char *product_name, int speed);
 
@@ -79,6 +79,7 @@  typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath,
 #define USBPROCBUS_PATH "/proc/bus/usb"
 #define PRODUCT_NAME_SZ 32
 #define MAX_ENDPOINTS 15
+#define MAX_PORTLEN 8
 #define USBDEVBUS_PATH "/dev/bus/usb"
 #define USBSYSBUS_PATH "/sys/bus/usb"
 
@@ -155,7 +156,7 @@  typedef struct USBHostDevice {
     /* Host side address */
     int bus_num;
     int addr;
-    int devpath;
+    char port[MAX_PORTLEN];
     struct USBAutoFilter match;
 
     QTAILQ_ENTRY(USBHostDevice) next;
@@ -1092,7 +1093,7 @@  static int usb_linux_get_configuration(USBHostDevice *s)
         char device_name[32], line[1024];
         int configuration;
 
-        sprintf(device_name, "%d-%d", s->bus_num, s->devpath);
+        sprintf(device_name, "%d-%s", s->bus_num, s->port);
 
         if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue",
                                 device_name)) {
@@ -1138,7 +1139,7 @@  static uint8_t usb_linux_get_alt_setting(USBHostDevice *s,
         char device_name[64], line[1024];
         int alt_setting;
 
-        sprintf(device_name, "%d-%d:%d.%d", s->bus_num, s->devpath,
+        sprintf(device_name, "%d-%s:%d.%d", s->bus_num, s->port,
                 (int)configuration, (int)interface);
 
         if (!usb_host_read_file(line, sizeof(line), "bAlternateSetting",
@@ -1257,7 +1258,7 @@  static int usb_linux_update_endp_table(USBHostDevice *s)
 }
 
 static int usb_host_open(USBHostDevice *dev, int bus_num,
-                         int addr, int devpath, const char *prod_name)
+                         int addr, char *port, const char *prod_name)
 {
     int fd = -1, ret;
     struct usbdevfs_connectinfo ci;
@@ -1283,7 +1284,7 @@  static int usb_host_open(USBHostDevice *dev, int bus_num,
 
     dev->bus_num = bus_num;
     dev->addr = addr;
-    dev->devpath = devpath;
+    strcpy(dev->port, port);
     dev->fd = fd;
 
     /* read the device description */
@@ -1655,8 +1656,9 @@  static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
 {
     DIR *dir = NULL;
     char line[1024];
-    int bus_num, addr, devpath, speed, class_id, product_id, vendor_id;
+    int bus_num, addr, speed, class_id, product_id, vendor_id;
     int ret = 0;
+    char port[MAX_PORTLEN];
     char product_name[512];
     struct dirent *de;
 
@@ -1672,8 +1674,8 @@  static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
             if (!strncmp(de->d_name, "usb", 3)) {
                 tmpstr += 3;
             }
-            if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) {
-                goto the_end;
+            if (sscanf(tmpstr, "%d-%7[0-9.]", &bus_num, port) < 2) {
+                continue;
             }
 
             if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) {
@@ -1725,7 +1727,7 @@  static int usb_host_scan_sys(void *opaque, USBScanFunc *func)
                 speed = USB_SPEED_FULL;
             }
 
-            ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id,
+            ret = func(opaque, bus_num, addr, port, class_id, vendor_id,
                        product_id, product_name, speed);
             if (ret) {
                 goto the_end;
@@ -1816,7 +1818,7 @@  static int usb_host_scan(void *opaque, USBScanFunc *func)
 
 static QEMUTimer *usb_auto_timer;
 
-static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr, char *port,
                               int class_id, int vendor_id, int product_id,
                               const char *product_name, int speed)
 {
@@ -1852,7 +1854,7 @@  static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath,
         }
         DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
 
-        usb_host_open(s, bus_num, addr, devpath, product_name);
+        usb_host_open(s, bus_num, addr, port, product_name);
     }
 
     return 0;
@@ -1974,8 +1976,8 @@  static const char *usb_class_str(uint8_t class)
     return p->class_name;
 }
 
-static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
-                            int vendor_id, int product_id,
+static void usb_info_device(Monitor *mon, int bus_num, int addr, char *port,
+                            int class_id, int vendor_id, int product_id,
                             const char *product_name,
                             int speed)
 {
@@ -1996,8 +1998,8 @@  static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
         break;
     }
 
-    monitor_printf(mon, "  Device %d.%d, speed %s Mb/s\n",
-                bus_num, addr, speed_str);
+    monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                   bus_num, addr, port, speed_str);
     class_str = usb_class_str(class_id);
     if (class_str) {
         monitor_printf(mon, "    %s:", class_str);
@@ -2012,14 +2014,14 @@  static void usb_info_device(Monitor *mon, int bus_num, int addr, int class_id,
 }
 
 static int usb_host_info_device(void *opaque, int bus_num, int addr,
-                                int devpath, int class_id,
+                                char *path, int class_id,
                                 int vendor_id, int product_id,
                                 const char *product_name,
                                 int speed)
 {
     Monitor *mon = opaque;
 
-    usb_info_device(mon, bus_num, addr, class_id, vendor_id, product_id,
+    usb_info_device(mon, bus_num, addr, path, class_id, vendor_id, product_id,
                     product_name, speed);
     return 0;
 }