linux_usbfs: Add support for new disconnect-and-claim ioctl
authorHans de Goede <hdegoede@redhat.com>
Thu, 13 Jun 2013 19:49:01 +0000 (21:49 +0200)
committerHans de Goede <hdegoede@redhat.com>
Wed, 19 Jun 2013 09:04:15 +0000 (11:04 +0200)
Currently the linux_usbfs detach_kernel_driver_and_claim() helper function
makes 3 system calls:
1) IOCTL_USBFS_GETDRIVER, to check the driver is not usbfs
2) IOCTL_USBFS_DISCONNECT
3) IOCTL_USBFS_CLAIMINTF

Between each of these calls the state of the interface can change, and
things might not work as expected when it does, iow this is inherently racy.

To fix this a new IOCTL_USBFS_DISCONNECT_CLAIM ioctl has been added to the
kernel a while back, which does all 3 in one. This patch adds support for
this ioctl, with a fall back to the old method for kernels lacking this new
ioctl.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
libusb/os/linux_usbfs.c
libusb/os/linux_usbfs.h
libusb/version_nano.h

index e4ff79d..6a4a3f1 100644 (file)
@@ -1533,8 +1533,32 @@ static int op_attach_kernel_driver(struct libusb_device_handle *handle,
 static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
        int interface)
 {
-       int r;
+       struct usbfs_disconnect_claim dc;
+       int r, fd = _device_handle_priv(handle)->fd;
+
+       dc.interface = interface;
+       strcpy(dc.driver, "usbfs");
+       dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
+       r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc);
+       if (r == 0 || (r != 0 && errno != ENOTTY)) {
+               if (r == 0)
+                       return 0;
+
+               switch (errno) {
+               case EBUSY:
+                       return LIBUSB_ERROR_BUSY;
+               case EINVAL:
+                       return LIBUSB_ERROR_INVALID_PARAM;
+               case ENODEV:
+                       return LIBUSB_ERROR_NO_DEVICE;
+               }
+               usbi_err(HANDLE_CTX(handle),
+                       "disconnect-and-claim failed errno %d", errno);
+               return LIBUSB_ERROR_OTHER;
+       }
 
+       /* Fallback code for kernels which don't support the
+          disconnect-and-claim ioctl */
        r = op_detach_kernel_driver(handle, interface);
        if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND)
                return r;
index 26f052d..499bab7 100644 (file)
@@ -123,6 +123,15 @@ struct usbfs_hub_portinfo {
 #define USBFS_CAP_NO_PACKET_SIZE_LIM   0x04
 #define USBFS_CAP_BULK_SCATTER_GATHER  0x08
 
+#define USBFS_DISCONNECT_CLAIM_IF_DRIVER       0x01
+#define USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER   0x02
+
+struct usbfs_disconnect_claim {
+       unsigned int interface;
+       unsigned int flags;
+       char driver[USBFS_MAXDRIVERNAME + 1];
+};
+
 #define IOCTL_USBFS_CONTROL    _IOWR('U', 0, struct usbfs_ctrltransfer)
 #define IOCTL_USBFS_BULK               _IOWR('U', 2, struct usbfs_bulktransfer)
 #define IOCTL_USBFS_RESETEP    _IOR('U', 3, unsigned int)
@@ -145,6 +154,7 @@ struct usbfs_hub_portinfo {
 #define IOCTL_USBFS_CLAIM_PORT _IOR('U', 24, unsigned int)
 #define IOCTL_USBFS_RELEASE_PORT       _IOR('U', 25, unsigned int)
 #define IOCTL_USBFS_GET_CAPABILITIES   _IOR('U', 26, __u32)
+#define IOCTL_USBFS_DISCONNECT_CLAIM   _IOR('U', 27, struct usbfs_disconnect_claim)
 
 extern usbi_mutex_static_t linux_hotplug_lock;
 
index 60306bf..42b9c0e 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10742
+#define LIBUSB_NANO 10743