linux: Fix libusb_get_device_speed() not working on wrapped devices
authorHans de Goede <hdegoede@redhat.com>
Mon, 7 Sep 2020 08:30:54 +0000 (10:30 +0200)
committerHans de Goede <hdegoede@redhat.com>
Mon, 14 Sep 2020 07:48:14 +0000 (09:48 +0200)
We don't have a sysfs_dir for wrapped devices, so we cannot read the speed
from sysfs.

The Linux kernel has supported a new ioctl to get the speed directly from
the fd for a while now, use that when we don't have sysfs access.

Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1871818
Reported-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
libusb/os/linux_usbfs.c
libusb/os/linux_usbfs.h

index f5c92c2..f3c188e 100644 (file)
@@ -863,6 +863,26 @@ static int usbfs_get_active_config(struct libusb_device *dev, int fd)
        return LIBUSB_SUCCESS;
 }
 
+static enum libusb_speed usbfs_get_speed(struct libusb_context *ctx, int fd)
+{
+       int r;
+
+       r = ioctl(fd, IOCTL_USBFS_GET_SPEED, NULL);
+       switch (r) {
+       case USBFS_SPEED_UNKNOWN:       return LIBUSB_SPEED_UNKNOWN;
+       case USBFS_SPEED_LOW:           return LIBUSB_SPEED_LOW;
+       case USBFS_SPEED_FULL:          return LIBUSB_SPEED_FULL;
+       case USBFS_SPEED_HIGH:          return LIBUSB_SPEED_HIGH;
+       case USBFS_SPEED_WIRELESS:      return LIBUSB_SPEED_HIGH;
+       case USBFS_SPEED_SUPER:         return LIBUSB_SPEED_SUPER;
+       case USBFS_SPEED_SUPER_PLUS:    return LIBUSB_SPEED_SUPER_PLUS;
+       default:
+               usbi_warn(ctx, "Error getting device speed: %d", r);
+       }
+
+       return LIBUSB_SPEED_UNKNOWN;
+}
+
 static int initialize_device(struct libusb_device *dev, uint8_t busnum,
        uint8_t devaddr, const char *sysfs_dir, int wrapped_fd)
 {
@@ -893,6 +913,8 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
                                usbi_warn(ctx, "unknown device speed: %d Mbps", speed);
                        }
                }
+       } else if (wrapped_fd >= 0) {
+               dev->speed = usbfs_get_speed(ctx, wrapped_fd);
        }
 
        /* cache descriptors in memory */
index 060aa35..1238ffa 100644 (file)
@@ -127,6 +127,14 @@ struct usbfs_streams {
        unsigned char eps[0];
 };
 
+#define USBFS_SPEED_UNKNOWN                    0
+#define USBFS_SPEED_LOW                                1
+#define USBFS_SPEED_FULL                       2
+#define USBFS_SPEED_HIGH                       3
+#define USBFS_SPEED_WIRELESS                   4
+#define USBFS_SPEED_SUPER                      5
+#define USBFS_SPEED_SUPER_PLUS                 6
+
 #define IOCTL_USBFS_CONTROL            _IOWR('U', 0, struct usbfs_ctrltransfer)
 #define IOCTL_USBFS_SETINTERFACE       _IOR('U', 4, struct usbfs_setinterface)
 #define IOCTL_USBFS_SETCONFIGURATION   _IOR('U', 5, unsigned int)
@@ -146,6 +154,8 @@ struct usbfs_streams {
 #define IOCTL_USBFS_DISCONNECT_CLAIM   _IOR('U', 27, struct usbfs_disconnect_claim)
 #define IOCTL_USBFS_ALLOC_STREAMS      _IOR('U', 28, struct usbfs_streams)
 #define IOCTL_USBFS_FREE_STREAMS       _IOR('U', 29, struct usbfs_streams)
+#define IOCTL_USBFS_DROP_PRIVILEGES    _IOW('U', 30, __u32)
+#define IOCTL_USBFS_GET_SPEED          _IO('U', 31)
 
 extern usbi_mutex_static_t linux_hotplug_lock;