blob: 56765f7f892176cf6701af9ffcbea745e58b70c9 [file] [log] [blame]
From bcda70ea60b19e0226ebd8d2368de17185eb63b2 Mon Sep 17 00:00:00 2001
From: Jingkui Wang <jkwang@google.com>
Date: Tue, 30 Oct 2018 15:29:02 -0700
Subject: [PATCH] add libusb_open_fd
libusb_open_fd will take an external fd to construct device handle. This will
all sandboxing libusb.
This patch is adapted from chromium repo:
src/third_party/libusb/open-fd.patch
---
libusb/core.c | 69 +++++++++++++++++++++++++++++++++++++++++
libusb/libusb.h | 2 ++
libusb/libusbi.h | 5 +++
libusb/os/linux_usbfs.c | 62 +++++++++++++++++++++++-------------
4 files changed, 117 insertions(+), 21 deletions(-)
diff --git a/libusb/core.c b/libusb/core.c
index 99aab7b..8c9b8e9 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -1282,6 +1282,75 @@ int API_EXPORTED libusb_open(libusb_device *dev,
return 0;
}
+/** \ingroup libusb_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);
+ 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;
+
+ return 0;
+}
+
/** \ingroup libusb_dev
* Convenience function for finding a device with a particular
* <tt>idVendor</tt>/<tt>idProduct</tt> combination. This function is intended
diff --git a/libusb/libusb.h b/libusb/libusb.h
index f73e31c..34d0784 100644
--- a/libusb/libusb.h
+++ b/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 **dev_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/libusb/libusbi.h b/libusb/libusbi.h
index cc0906c..ca5e86a 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -712,6 +712,11 @@ struct usbi_os_backend {
*/
int (*open)(struct libusb_device_handle *dev_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/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 9cbeb80..89102ef 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -187,6 +187,11 @@ static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
int fd;
int delay = 10000;
+ if (!usbfs_path) {
+ usbi_err(ctx, "could not find usbfs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
if (usbdev_names)
snprintf(path, PATH_MAX, "%s/usbdev%d.%d",
usbfs_path, dev->bus_number, dev->device_address);
@@ -379,10 +384,6 @@ static int op_init(struct libusb_context *ctx)
int r;
usbfs_path = find_usbfs_path();
- if (!usbfs_path) {
- usbi_err(ctx, "could not find usbfs");
- return LIBUSB_ERROR_OTHER;
- }
if (monotonic_clkid == -1)
monotonic_clkid = find_monotonic_clock();
@@ -1138,6 +1139,11 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
struct dirent *entry;
int r = LIBUSB_ERROR_IO;
+ if (!usbfs_path) {
+ usbi_err(ctx, "could not find usbfs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
usbi_dbg("%s", dirpath);
dir = opendir(dirpath);
@@ -1175,6 +1181,12 @@ static int usbfs_scan_busdir(struct libusb_context *ctx, uint8_t busnum)
static int usbfs_get_device_list(struct libusb_context *ctx)
{
struct dirent *entry;
+
+ if (!usbfs_path) {
+ usbi_err(ctx, "could not find usbfs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
DIR *buses = opendir(usbfs_path);
int r = 0;
@@ -1280,26 +1292,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);
- }
- 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) {
@@ -1317,10 +1315,31 @@ static int op_open(struct libusb_device_handle *handle)
r = usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
if (r < 0)
close(hpriv->fd);
-
return r;
}
+static int op_open(struct libusb_device_handle *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);
+ }
+ 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)
{
struct linux_device_handle_priv *hpriv = _device_handle_priv(dev_handle);
@@ -2696,6 +2715,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,
--
2.20.0.rc0.387.gc7a69e6b6c-goog