diff --git a/third_party/libusb/src/libusb/core.c b/third_party/libusb/src/libusb/core.c index e816284..abc4f89 100644 --- a/third_party/libusb/src/libusb/core.c +++ b/third_party/libusb/src/libusb/core.c @@ -1129,6 +1129,83 @@ int API_EXPORTED libusb_open(libusb_device *dev, } /** \ingroup dev + * Open a device and obtain a device handle. A handle allows you to perform + * I/O on the device in question. + * + * Instead of opening the device itself this function accepts an open file + * descriptor that it will take ownership of. + * + * Internally, this function adds a reference to the device and makes it + * available to you through libusb_get_device(). This reference is removed + * during libusb_close(). + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param dev the device to open + * \param fd open file handle to the device + * \param handle output location for the returned device handle pointer. Only + * populated when the return code is 0. + * \returns 0 on success + * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected + * \returns another LIBUSB_ERROR code on other failure + */ +int API_EXPORTED libusb_open_fd(libusb_device *dev, + int fd, + libusb_device_handle **handle) +{ + struct libusb_context *ctx = DEVICE_CTX(dev); + struct libusb_device_handle *_handle; + size_t priv_size = usbi_backend->device_handle_priv_size; + int r; + usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); + + if (!dev->attached) { + return LIBUSB_ERROR_NO_DEVICE; + } + + _handle = malloc(sizeof(*_handle) + priv_size); + if (!_handle) + return LIBUSB_ERROR_NO_MEM; + + r = usbi_mutex_init(&_handle->lock, NULL); + if (r) { + free(_handle); + return LIBUSB_ERROR_OTHER; + } + + _handle->dev = libusb_ref_device(dev); + _handle->auto_detach_kernel_driver = 0; + _handle->claimed_interfaces = 0; + memset(&_handle->os_priv, 0, priv_size); + + r = usbi_backend->open_fd(_handle, fd); + if (r < 0) { + usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r); + libusb_unref_device(dev); + usbi_mutex_destroy(&_handle->lock); + free(_handle); + return r; + } + + usbi_mutex_lock(&ctx->open_devs_lock); + list_add(&_handle->list, &ctx->open_devs); + usbi_mutex_unlock(&ctx->open_devs_lock); + *handle = _handle; + + /* At this point, we want to interrupt any existing event handlers so + * that they realise the addition of the new device's poll fd. One + * example when this is desirable is if the user is running a separate + * dedicated libusbx events handling thread, which is running with a long + * or infinite timeout. We want to interrupt that iteration of the loop, + * so that it picks up the new fd, and then continues. */ + usbi_fd_notification(ctx); + + return 0; +} + +/** \ingroup dev * Convenience function for finding a device with a particular * idVendor/idProduct combination. This function is intended * for those scenarios where you are using libusbx to knock up a quick test diff --git a/third_party/libusb/src/libusb/libusb.h b/third_party/libusb/src/libusb/libusb.h index d144b3e..5d60951 100644 --- a/third_party/libusb/src/libusb/libusb.h +++ b/third_party/libusb/src/libusb/libusb.h @@ -1371,6 +1371,8 @@ int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint); int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **handle); +int LIBUSB_CALL libusb_open_fd(libusb_device *dev, int fd, + libusb_device_handle **handle); void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); diff --git a/third_party/libusb/src/libusb/libusbi.h b/third_party/libusb/src/libusb/libusbi.h index bc608b92..eb2f0e6 100644 --- a/third_party/libusb/src/libusb/libusbi.h +++ b/third_party/libusb/src/libusb/libusbi.h @@ -615,6 +615,11 @@ struct usbi_os_backend { */ int (*open)(struct libusb_device_handle *handle); + /* Like open() above but uses the file descriptor provided instead of opening + * one on its own. + */ + int (*open_fd)(struct libusb_device_handle *handle, int fd); + /* Close a device such that the handle cannot be used again. Your backend * should destroy any resources that were allocated in the open path. * This may also be a good place to call usbi_remove_pollfd() to inform diff --git a/third_party/libusb/src/libusb/os/darwin_usb.c b/third_party/libusb/src/libusb/os/darwin_usb.c index f95706a..f6b397e 100644 --- a/third_party/libusb/src/libusb/os/darwin_usb.c +++ b/third_party/libusb/src/libusb/os/darwin_usb.c @@ -1877,6 +1877,7 @@ const struct usbi_os_backend darwin_backend = { .get_config_descriptor = darwin_get_config_descriptor, .open = darwin_open, + .open_fd = NULL, /* not implemented */ .close = darwin_close, .get_configuration = darwin_get_configuration, .set_configuration = darwin_set_configuration, diff --git a/third_party/libusb/src/libusb/os/linux_usbfs.c b/third_party/libusb/src/libusb/os/linux_usbfs.c index 142fa2b..e965856 100644 --- a/third_party/libusb/src/libusb/os/linux_usbfs.c +++ b/third_party/libusb/src/libusb/os/linux_usbfs.c @@ -1259,26 +1259,12 @@ static int linux_default_scan_devices (struct libusb_context *ctx) } #endif -static int op_open(struct libusb_device_handle *handle) +static int op_open_fd(struct libusb_device_handle *handle, int fd) { struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); int r; - hpriv->fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); - if (hpriv->fd < 0) { - if (hpriv->fd == LIBUSB_ERROR_NO_DEVICE) { - /* device will still be marked as attached if hotplug monitor thread - * hasn't processed remove event yet */ - usbi_mutex_static_lock(&linux_hotplug_lock); - if (handle->dev->attached) { - usbi_dbg("open failed with no device, but device still attached"); - linux_device_disconnected(handle->dev->bus_number, - handle->dev->device_address, NULL); - } - usbi_mutex_static_unlock(&linux_hotplug_lock); - } - return hpriv->fd; - } + hpriv->fd = fd; r = ioctl(hpriv->fd, IOCTL_USBFS_GET_CAPABILITIES, &hpriv->caps); if (r < 0) { @@ -1296,6 +1282,29 @@ static int op_open(struct libusb_device_handle *handle) return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT); } +static int op_open(struct libusb_device_handle *handle) +{ + struct linux_device_handle_priv *hpriv = _device_handle_priv(handle); + int fd = _get_usbfs_fd(handle->dev, O_RDWR, 0); + + if (fd < 0) { + if (fd == LIBUSB_ERROR_NO_DEVICE) { + /* device will still be marked as attached if hotplug monitor thread + * hasn't processed remove event yet */ + usbi_mutex_static_lock(&linux_hotplug_lock); + if (handle->dev->attached) { + usbi_dbg("open failed with no device, but device still attached"); + linux_device_disconnected(handle->dev->bus_number, + handle->dev->device_address, NULL); + } + usbi_mutex_static_unlock(&linux_hotplug_lock); + } + return fd; + } + + return op_open_fd(handle, fd); +} + static void op_close(struct libusb_device_handle *dev_handle) { int fd = _device_handle_priv(dev_handle)->fd; @@ -2570,6 +2579,7 @@ const struct usbi_os_backend linux_usbfs_backend = { .get_config_descriptor_by_value = op_get_config_descriptor_by_value, .open = op_open, + .open_fd = op_open_fd, .close = op_close, .get_configuration = op_get_configuration, .set_configuration = op_set_configuration, diff --git a/third_party/libusb/src/libusb/os/openbsd_usb.c b/third_party/libusb/src/libusb/os/openbsd_usb.c index 2997e53..2d24f2c 100644 --- a/third_party/libusb/src/libusb/os/openbsd_usb.c +++ b/third_party/libusb/src/libusb/os/openbsd_usb.c @@ -98,6 +98,7 @@ const struct usbi_os_backend openbsd_backend = { obsd_get_device_list, NULL, /* hotplug_poll */ obsd_open, + NULL, /* open_fd */ obsd_close, obsd_get_device_descriptor, diff --git a/third_party/libusb/src/libusb/os/wince_usb.c b/third_party/libusb/src/libusb/os/wince_usb.c index 90c129b..c069c56 100644 --- a/third_party/libusb/src/libusb/os/wince_usb.c +++ b/third_party/libusb/src/libusb/os/wince_usb.c @@ -990,6 +990,7 @@ const struct usbi_os_backend wince_backend = { wince_get_device_list, NULL, /* hotplug_poll */ wince_open, + NULL, /* open_fd */ wince_close, wince_get_device_descriptor, diff --git a/third_party/libusb/src/libusb/os/windows_usb.c b/third_party/libusb/src/libusb/os/windows_usb.c index 4469992..bc4def6 100644 --- a/third_party/libusb/src/libusb/os/windows_usb.c +++ b/third_party/libusb/src/libusb/os/windows_usb.c @@ -2290,6 +2290,7 @@ const struct usbi_os_backend windows_backend = { windows_get_device_list, NULL, /* hotplug_poll */ windows_open, + NULL, /* open_fd */ windows_close, windows_get_device_descriptor,