From 470b1bc42bf53373ce678fc76bab9160a54d6881 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 4 May 2008 16:51:23 +0100 Subject: [PATCH] add functionality for querying and detaching kernel driver --- libusb/core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ libusb/libusb.h | 6 +++++- libusb/libusbi.h | 6 ++++++ libusb/os/linux_usbfs.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/libusb/core.c b/libusb/core.c index 2dce681..2225106 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -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. diff --git a/libusb/libusb.h b/libusb/libusb.h index 09401c2..5cce3fb 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -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 diff --git a/libusb/libusbi.h b/libusb/libusbi.h index bf4aa19..2f70053 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -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); diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index e67d7b6..40a32f1 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -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, -- 2.7.4