Apps which deal with devices which also have a kernel driver, need to do
the following:
1) Check which driver is attached, so as to not detach the wrong driver
   (ie detaching usbfs while another instance of the app is using the device)
2) Detach the kernel driver
3) Claim the interface

Where moving from one step to the next for both 1-2 and 2-3 consists of
a (small) race window. So currently such apps are racy and people just live
with it.

To fix this issue, a new ioctl is being added to the Linux kernel which does
steps 1-3 in one race-free call.

This patch adds a new libusbx function for exporting this functionality, note
that currently it just implements it using the old racy ioctls. A follow up
patch will add use of the new ioctl (where available).

Signed-off-by: Hans de Goede <hdego...@redhat.com>
---
 libusb/core.c           | 38 ++++++++++++++++++++++++++++++++++++++
 libusb/libusb.h         |  2 ++
 libusb/libusbi.h        | 16 ++++++++++++++++
 libusb/os/linux_usbfs.c | 23 +++++++++++++++++++++++
 4 files changed, 79 insertions(+)

diff --git a/libusb/core.c b/libusb/core.c
index 4c513f7..89d7c93 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -1568,6 +1568,44 @@ int API_EXPORTED 
libusb_attach_kernel_driver(libusb_device_handle *dev,
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
+/** \ingroup dev
+ * Detach the named kernel driver from an interface, then claim it,
+ * in one atomic operation (if possible, fallback to non atomic
+ * where not). This call is only effective on Linux and returns
+ * LIBUSB_ERROR_NOT_SUPPORTED on all other platforms.
+ *
+ * If driver_name is NULL, any attached driver will be detached, if
+ * driver_name is not NULL, and another driver then named is attached,
+ * the call will fail with a LIBUSB_ERROR_BUSY return.
+ *
+ * This functionality is not available on Darwin or Windows.
+ *
+ * \param dev a device handle
+ * \param interface_number the interface to detach the driver from
+ * \param driver_name name of the driver to detach, or NULL to detach any
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_BUSY if the interface is busy
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED on platforms where the functionality
+ * is not available
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_detach_kernel_driver()
+ * \see libusb_claim_interface()
+ */
+int API_EXPORTED libusb_detach_kernel_driver_and_claim(
+       libusb_device_handle *dev, int interface_number,
+       const char *driver_name)
+{
+       usbi_dbg("interface %d, driver_name %s", interface_number,
+                driver_name ? driver_name : "NULL");
+       if (usbi_backend->detach_kernel_driver_and_claim)
+               return usbi_backend->detach_kernel_driver_and_claim(
+                               dev, interface_number, driver_name);
+       else
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
 /** \ingroup lib
  * Set log message verbosity.
  *
diff --git a/libusb/libusb.h b/libusb/libusb.h
index de31a56..3fdf6dd 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -1032,6 +1032,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_detach_kernel_driver_and_claim(
+       libusb_device_handle *dev, int interface_number, const char *driver);
 
 /* async I/O */
 
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 5ec0761..d195a36 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -792,6 +792,22 @@ struct usbi_os_backend {
        int (*attach_kernel_driver)(struct libusb_device_handle *handle,
                int interface_number);
 
+       /* Detach the named kernel driver from an interface, then claim it,
+        * in one atomic operation (if possible, fallback to non atomic
+        * where not). Optional.
+        *
+        * Return:
+        * - 0 on success
+        * - LIBUSB_ERROR_BUSY if the interface is busy
+        * - LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+        * - LIBUSB_ERROR_NO_DEVICE if the device has been disconnected since
+        *   it was opened
+        * - another LIBUSB_ERROR code on other failure
+        */
+       int (*detach_kernel_driver_and_claim)(
+               struct libusb_device_handle *handle, int interface_number,
+               const char *driver_name);
+
        /* Destroy a device. Optional.
         *
         * This function is called when the last reference to a device is
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 1407866..b6194ce 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -1599,6 +1599,28 @@ static int op_attach_kernel_driver(struct 
libusb_device_handle *handle,
        return 0;
 }
 
+static int op_detach_kernel_driver_and_claim(
+       struct libusb_device_handle *handle, int interface,
+       const char *driver_name)
+{
+       int fd = _device_handle_priv(handle)->fd;
+       struct usbfs_getdriver getdrv;
+       int r;
+
+       if (driver_name) {
+               getdrv.interface = interface;
+               r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+               if (r == 0 && strcmp(driver_name, getdrv.driver) != 0)
+                       return LIBUSB_ERROR_BUSY;
+       }
+
+       r = op_detach_kernel_driver(handle, interface);
+       if (r != 0 && r != LIBUSB_ERROR_NOT_FOUND)
+               return r;
+
+       return op_claim_interface(handle, interface);
+}
+
 static void op_destroy_device(struct libusb_device *dev)
 {
        struct linux_device_priv *priv = _device_priv(dev);
@@ -2547,6 +2569,7 @@ const struct usbi_os_backend linux_usbfs_backend = {
        .kernel_driver_active = op_kernel_driver_active,
        .detach_kernel_driver = op_detach_kernel_driver,
        .attach_kernel_driver = op_attach_kernel_driver,
+       .detach_kernel_driver_and_claim = op_detach_kernel_driver_and_claim,
 
        .destroy_device = op_destroy_device,
 
-- 
1.7.11.4


------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
libusbx-devel mailing list
libusbx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libusbx-devel

Reply via email to