linux: Fix libusb_get_device_speed() not working on wrapped devices

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>
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index f5c92c2..f3c188e 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -863,6 +863,26 @@
 	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 @@
 				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 */
diff --git a/libusb/os/linux_usbfs.h b/libusb/os/linux_usbfs.h
index 060aa35..1238ffa 100644
--- a/libusb/os/linux_usbfs.h
+++ b/libusb/os/linux_usbfs.h
@@ -127,6 +127,14 @@
 	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 @@
 #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;