libusb: Add auto-detach-kernel-driver functionality
authorHans de Goede <hdegoede@redhat.com>
Thu, 13 Jun 2013 19:21:14 +0000 (21:21 +0200)
committerHans de Goede <hdegoede@redhat.com>
Wed, 19 Jun 2013 09:04:14 +0000 (11:04 +0200)
Add auto-detach-kernel-driver functionality, and a
libusb_set_auto_detach_kernel_driver() function.

Note that I went with a libusb_set_auto_detach_kernel_driver() function,
rather then with a libusb_enable_auto_detach_kernel_driver(), so that apps
can also disable it again. This is necessary to handle 2 corner cases:

1) When an app wants to do a libusb_set_configuration after claiming 1 or
more interfaces, it needs to first release the interface(s), and in this
case libusb_release_interface() should *not* (re-)attach the kernel driver

2) Some usb classes use multiple interfaces for one function, ie usb-audio
devices do this. In this case attaching the driver will fail until all
interfaces are released, so the app should first release all interfaces, and
only then (re-)attach the kernel driver.

auto-detach-kernel-driver functionality is still useful for these apps, since
doing libusb_detach_kernel_driver() followed by libusb_claim_interface() in
2 separate calls is inherently racy, but they need to be able to disable the
auto-detach functionality before releasing interfaces to be able to properly
handle the 2 described corner cases.

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

index 1c3748d..c902747 100644 (file)
@@ -1361,7 +1361,14 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev,
  * endpoint halts cleared, toggles reset).
  *
  * You cannot change/reset configuration if your application has claimed
- * interfaces - you should free them with libusb_release_interface() first.
+ * interfaces. It is advised to set the desired configuration before claiming
+ * interfaces.
+ *
+ * Alternatively you can call libusb_release_interface() first. Note if you
+ * do things this way you must ensure that auto_detach_kernel_driver for
+ * <tt>dev</tt> is 0, otherwise the kernel driver will be re-attached when you
+ * release the interface(s).
+ *
  * You cannot change/reset configuration if other applications or drivers have
  * claimed interfaces.
  *
@@ -1383,6 +1390,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev,
  * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
  */
 int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev,
        int configuration)
@@ -1398,6 +1406,9 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev,
  * It is legal to attempt to claim an already-claimed interface, in which
  * case libusbx just returns 0 without doing anything.
  *
+ * If auto_detach_kernel_driver is set to 1 for <tt>dev</tt>, the kernel driver
+ * will be detached if necessary, on failure the detach error is returned.
+ *
  * Claiming of interfaces is a purely logical operation; it does not cause
  * any requests to be sent over the bus. Interface claiming is used to
  * instruct the underlying operating system that your application wishes
@@ -1414,6 +1425,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev,
  * interface
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns a LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
  */
 int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev,
        int interface_number)
@@ -1447,6 +1459,9 @@ out:
  * This is a blocking function. A SET_INTERFACE control request will be sent
  * to the device, resetting interface state to the first alternate setting.
  *
+ * If auto_detach_kernel_driver is set to 1 for <tt>dev</tt>, the kernel
+ * driver will be re-attached after releasing the interface.
+ *
  * \param dev a device handle
  * \param interface_number the <tt>bInterfaceNumber</tt> of the
  * previously-claimed interface
@@ -1454,6 +1469,7 @@ out:
  * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
  * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_set_auto_detach_kernel_driver()
  */
 int API_EXPORTED libusb_release_interface(libusb_device_handle *dev,
        int interface_number)
@@ -1678,6 +1694,38 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev,
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
+/** \ingroup dev
+ * Enable/disable libusbx's automatic kernel driver detachment. When this is
+ * enabled libusbx will automatically detach the kernel driver on an interface
+ * when claiming the interface, and attach it when releasing the interface.
+ *
+ * Automatic kernel driver detachment is disabled on newly opened device
+ * handles by default.
+ *
+ * On platforms which do not have LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER
+ * this function will return LIBUSB_ERROR_NOT_SUPPORTED, and libusbx will
+ * continue as if this function was never called.
+ *
+ * \param dev a device handle
+ * \param enable whether to enable or disable auto kernel driver detachment
+ *
+ * \returns LIBUSB_SUCCESS on success
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \see libusb_claim_interface()
+ * \see libusb_release_interface()
+ * \see libusb_set_configuration()
+ */
+int API_EXPORTED libusb_set_auto_detach_kernel_driver(
+       libusb_device_handle *dev, int enable)
+{
+       if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+
+       dev->auto_detach_kernel_driver = enable;
+       return LIBUSB_SUCCESS;
+}
+
 /** \ingroup lib
  * Set log message verbosity.
  *
index e6c46fd..cb0e32a 100644 (file)
@@ -130,6 +130,8 @@ EXPORTS
   libusb_release_interface@8 = libusb_release_interface
   libusb_reset_device
   libusb_reset_device@4 = libusb_reset_device
+  libusb_set_auto_detach_kernel_driver
+  libusb_set_auto_detach_kernel_driver@8 = libusb_set_auto_detach_kernel_driver
   libusb_set_configuration
   libusb_set_configuration@8 = libusb_set_configuration
   libusb_set_debug
index a56a687..d7cc40f 100644 (file)
@@ -1390,6 +1390,8 @@ int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev,
        int interface_number);
 int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev,
        int interface_number);
+int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(
+       libusb_device_handle *dev, int enable);
 
 /* async I/O */
 
index d5e5c59..c9b402d 100644 (file)
@@ -317,6 +317,7 @@ struct libusb_device_handle {
 
        struct list_head list;
        struct libusb_device *dev;
+       int auto_detach_kernel_driver;
        unsigned char os_priv
 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
        [] /* valid C99 code */
index a27ed44..e4ff79d 100644 (file)
@@ -1317,7 +1317,7 @@ static int op_set_configuration(struct libusb_device_handle *handle, int config)
        return LIBUSB_SUCCESS;
 }
 
-static int op_claim_interface(struct libusb_device_handle *handle, int iface)
+static int claim_interface(struct libusb_device_handle *handle, int iface)
 {
        int fd = _device_handle_priv(handle)->fd;
        int r = ioctl(fd, IOCTL_USBFS_CLAIMINTF, &iface);
@@ -1336,7 +1336,7 @@ static int op_claim_interface(struct libusb_device_handle *handle, int iface)
        return 0;
 }
 
-static int op_release_interface(struct libusb_device_handle *handle, int iface)
+static int release_interface(struct libusb_device_handle *handle, int iface)
 {
        int fd = _device_handle_priv(handle)->fd;
        int r = ioctl(fd, IOCTL_USBFS_RELEASEINTF, &iface);
@@ -1407,7 +1407,7 @@ static int op_reset_device(struct libusb_device_handle *handle)
           getting bound to the in kernel driver if any). */
        for (i = 0; i < USB_MAXINTERFACES; i++) {
                if (handle->claimed_interfaces & (1L << i)) {
-                       op_release_interface(handle, i);
+                       release_interface(handle, i);
                }
        }
 
@@ -1428,7 +1428,7 @@ static int op_reset_device(struct libusb_device_handle *handle)
        /* And re-claim any interfaces which were claimed before the reset */
        for (i = 0; i < USB_MAXINTERFACES; i++) {
                if (handle->claimed_interfaces & (1L << i)) {
-                       r = op_claim_interface(handle, i);
+                       r = claim_interface(handle, i);
                        if (r) {
                                usbi_warn(HANDLE_CTX(handle),
                                        "failed to re-claim interface %d after reset", i);
@@ -1530,6 +1530,40 @@ static int op_attach_kernel_driver(struct libusb_device_handle *handle,
        return 0;
 }
 
+static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
+       int interface)
+{
+       int r;
+
+       r = op_detach_kernel_driver(handle, interface);
+       if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND)
+               return r;
+
+       return claim_interface(handle, interface);
+}
+
+static int op_claim_interface(struct libusb_device_handle *handle, int iface)
+{
+       if (handle->auto_detach_kernel_driver)
+               return detach_kernel_driver_and_claim(handle, iface);
+       else
+               return claim_interface(handle, iface);
+}
+
+static int op_release_interface(struct libusb_device_handle *handle, int iface)
+{
+       int r;
+
+       r = release_interface(handle, iface);
+       if (r)
+               return r;
+
+       if (handle->auto_detach_kernel_driver)
+               op_attach_kernel_driver(handle, iface);
+
+       return 0;
+}
+
 static void op_destroy_device(struct libusb_device *dev)
 {
        struct linux_device_priv *priv = _device_priv(dev);
index 5a98334..60306bf 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10741
+#define LIBUSB_NANO 10742