add functionality for querying and detaching kernel driver
authorDaniel Drake <dsd@gentoo.org>
Sun, 4 May 2008 15:51:23 +0000 (16:51 +0100)
committerDaniel Drake <dsd@gentoo.org>
Sun, 4 May 2008 15:51:23 +0000 (16:51 +0100)
libusb/core.c
libusb/libusb.h
libusb/libusbi.h
libusb/os/linux_usbfs.c

index 2dce681..2225106 100644 (file)
@@ -716,6 +716,50 @@ API_EXPORTED int libusb_reset_device(libusb_device_handle *dev)
        return usbi_backend->reset_device(dev);
 }
 
+/** \ingroup dev
+ * Determine if a kernel driver is active on an interface. If a kernel driver
+ * is active, you cannot claim the interface, and libusb will be unable to
+ * perform I/O.
+ *
+ * \param dev a device handle
+ * \param interface the interface to check
+ * \returns 0 if no kernel driver is active
+ * \returns 1 if a kernel driver is active
+ * \returns LIBUSB_ERROR code on failure
+ * \see libusb_detach_kernel_driver
+ */
+API_EXPORTED int libusb_kernel_driver_active(libusb_device_handle *dev,
+       int interface)
+{
+       usbi_dbg("interface %d", interface);
+       if (usbi_backend->kernel_driver_active)
+               return usbi_backend->kernel_driver_active(dev, interface);
+       else
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup dev
+ * Detach a kernel driver from an interface. If successful, you will then be
+ * able to claim the interface and perform I/O.
+ *
+ * \param dev a device handle
+ * \param interface the interface to detach the driver from
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active
+ */
+API_EXPORTED int libusb_detach_kernel_driver(libusb_device_handle *dev,
+       int interface)
+{
+       usbi_dbg("interface %d", interface);
+       if (usbi_backend->detach_kernel_driver)
+               return usbi_backend->detach_kernel_driver(dev, interface);
+       else
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
 /** \ingroup lib
  * Initialize libusb. This function must be called before calling any other
  * libusb function.
index 09401c2..5cce3fb 100644 (file)
@@ -525,7 +525,8 @@ enum libusb_error {
        LIBUSB_ERROR_BUSY = -5,
        LIBUSB_ERROR_TIMEOUT = -6,
        LIBUSB_ERROR_NO_MEM = -7,
-       LIBUSB_ERROR_OTHER = -8,
+       LIBUSB_ERROR_NOT_SUPPORTED = -8,
+       LIBUSB_ERROR_OTHER = -9,
 };
 
 /** \ingroup asyncio
@@ -663,6 +664,9 @@ int libusb_set_interface_alt_setting(libusb_device_handle *dev,
 int libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint);
 int libusb_reset_device(libusb_device_handle *dev);
 
+int libusb_kernel_driver_active(libusb_device_handle *dev, int interface);
+int libusb_detach_kernel_driver(libusb_device_handle *dev, int interface);
+
 /* async I/O */
 
 /** \ingroup asyncio
index bf4aa19..2f70053 100644 (file)
@@ -284,6 +284,12 @@ struct usbi_os_backend {
                unsigned char endpoint);
        int (*reset_device)(struct libusb_device_handle *handle);
 
+       /* optional */
+       int (*kernel_driver_active)(struct libusb_device_handle *handle,
+               int interface);
+       int (*detach_kernel_driver)(struct libusb_device_handle *handle,
+               int interface);
+
        void (*destroy_device)(struct libusb_device *dev);
 
        int (*submit_transfer)(struct usbi_transfer *itransfer);
index e67d7b6..40a32f1 100644 (file)
@@ -516,6 +516,51 @@ static int op_reset_device(struct libusb_device_handle *handle)
        return 0;
 }
 
+static int op_kernel_driver_active(struct libusb_device_handle *handle,
+       int interface)
+{
+       int fd = __device_handle_priv(handle)->fd;
+       struct usbfs_getdriver getdrv;
+       int r;
+
+       getdrv.interface = interface;
+       r = ioctl(fd, IOCTL_USBFS_GETDRIVER, &getdrv);
+       if (r) {
+               if (errno == ENODATA)
+                       return 0;
+
+               usbi_err("get driver failed error %d errno %d", r, errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 1;
+}
+
+static int op_detach_kernel_driver(struct libusb_device_handle *handle,
+       int interface)
+{
+       int fd = __device_handle_priv(handle)->fd;
+       struct usbfs_ioctl command;
+       int r;
+
+       command.ifno = interface;
+       command.ioctl_code = IOCTL_USBFS_DISCONNECT;
+       command.data = NULL;
+
+       r = ioctl(fd, IOCTL_USBFS_IOCTL, &command);
+       if (r) {
+               if (errno == ENODATA)
+                       return LIBUSB_ERROR_NOT_FOUND;
+               else if (errno == EINVAL)
+                       return LIBUSB_ERROR_INVALID_PARAM;
+
+               usbi_err("detach failed error %d errno %d", r, errno);
+               return LIBUSB_ERROR_OTHER;
+       }
+
+       return 0;
+}
+
 static void op_destroy_device(struct libusb_device *dev)
 {
        unsigned char *nodepath = __device_priv(dev)->nodepath;
@@ -1158,6 +1203,9 @@ const struct usbi_os_backend linux_usbfs_backend = {
        .clear_halt = op_clear_halt,
        .reset_device = op_reset_device,
 
+       .kernel_driver_active = op_kernel_driver_active,
+       .detach_kernel_driver = op_detach_kernel_driver,
+
        .destroy_device = op_destroy_device,
 
        .submit_transfer = op_submit_transfer,