summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-06-13 21:49:01 +0200
committerHans de Goede <hdegoede@redhat.com>2013-06-19 11:04:15 +0200
commitd4e993192aa81a1867bddf90d53cf750ec21e7b1 (patch)
tree1ba35c380272b8f492629c2a9614a602573018b9
parentfc51f19341af70508b94c06ff763842b396ea72e (diff)
downloadlibusb-d4e993192aa81a1867bddf90d53cf750ec21e7b1.tar.gz
linux_usbfs: Add support for new disconnect-and-claim ioctl
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>
-rw-r--r--libusb/os/linux_usbfs.c26
-rw-r--r--libusb/os/linux_usbfs.h10
-rw-r--r--libusb/version_nano.h2
3 files changed, 36 insertions, 2 deletions
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index e4ff79d..6a4a3f1 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -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;
diff --git a/libusb/os/linux_usbfs.h b/libusb/os/linux_usbfs.h
index 26f052d..499bab7 100644
--- a/libusb/os/linux_usbfs.h
+++ b/libusb/os/linux_usbfs.h
@@ -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;
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 60306bf..42b9c0e 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10742
+#define LIBUSB_NANO 10743