Commits

Anonymous committed 21333b3

Added libusb resources / code for reference

Comments (0)

Files changed (14)

+/*
+ * Core functions for libusb
+ * Copyright (C) 2007-2008 Daniel Drake <dsd@gentoo.org>
+ * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libusb.h"
+#include "libusbi.h"
+
+#ifdef OS_LINUX
+const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
+#else
+#error "Unsupported OS"
+#endif
+
+struct libusb_context *usbi_default_context = NULL;
+static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * \mainpage libusb-1.0 API Reference
+ *
+ * \section intro Introduction
+ *
+ * libusb is an open source library that allows you to communicate with USB
+ * devices from userspace. For more info, see the
+ * <a href="http://libusb.sourceforge.net">libusb homepage</a>.
+ *
+ * This documentation is aimed at application developers wishing to
+ * communicate with USB peripherals from their own software. After reviewing
+ * this documentation, feedback and questions can be sent to the
+ * <a href="http://sourceforge.net/mail/?group_id=1674">libusb-devel mailing
+ * list</a>.
+ *
+ * This documentation assumes knowledge of how to operate USB devices from
+ * a software standpoint (descriptors, configurations, interfaces, endpoints,
+ * control/bulk/interrupt/isochronous transfers, etc). Full information
+ * can be found in the <a href="http://www.usb.org/developers/docs/">USB 2.0
+ * Specification</a> which is available for free download. You can probably
+ * find less verbose introductions by searching the web.
+ *
+ * \section features Library features
+ *
+ * - All transfer types supported (control/bulk/interrupt/isochronous)
+ * - 2 transfer interfaces:
+ *    -# Synchronous (simple)
+ *    -# Asynchronous (more complicated, but more powerful)
+ * - Thread safe (although the asynchronous interface means that you
+ *   usually won't need to thread)
+ * - Lightweight with lean API
+ * - Compatible with libusb-0.1 through the libusb-compat-0.1 translation layer
+ *
+ * \section gettingstarted Getting Started
+ *
+ * To begin reading the API documentation, start with the Modules page which
+ * links to the different categories of libusb's functionality.
+ *
+ * One decision you will have to make is whether to use the synchronous
+ * or the asynchronous data transfer interface. The \ref io documentation
+ * provides some insight into this topic.
+ *
+ * Some example programs can be found in the libusb source distribution under
+ * the "examples" subdirectory. The libusb homepage includes a list of
+ * real-life project examples which use libusb.
+ *
+ * \section errorhandling Error handling
+ *
+ * libusb functions typically return 0 on success or a negative error code
+ * on failure. These negative error codes relate to LIBUSB_ERROR constants
+ * which are listed on the \ref misc "miscellaneous" documentation page.
+ *
+ * \section msglog Debug message logging
+ *
+ * libusb does not log any messages by default. Your application is therefore
+ * free to close stdout/stderr and those descriptors may be reused without
+ * worry.
+ *
+ * The libusb_set_debug() function can be used to enable stdout/stderr logging
+ * of certain messages. Under standard configuration, libusb doesn't really
+ * log much at all, so you are advised to use this function to enable all
+ * error/warning/informational messages. It will help you debug problems with
+ * your software.
+ *
+ * The logged messages are unstructured. There is no one-to-one correspondence
+ * between messages being logged and success or failure return codes from
+ * libusb functions. There is no format to the messages, so you should not
+ * try to capture or parse them. They are not and will not be localized.
+ * These messages are not suitable for being passed to your application user;
+ * instead, you should interpret the error codes returned from libusb functions
+ * and provide appropriate notification to the user. The messages are simply
+ * there to aid you as a programmer, and if you're confused because you're
+ * getting a strange error code from a libusb function, enabling message
+ * logging may give you a suitable explanation.
+ *
+ * The LIBUSB_DEBUG environment variable can be used to enable message logging
+ * at run-time. This environment variable should be set to a number, which is
+ * interpreted the same as the libusb_set_debug() parameter. When this
+ * environment variable is set, the message logging verbosity level is fixed
+ * and libusb_set_debug() effectively does nothing.
+ *
+ * libusb can be compiled without any logging functions, useful for embedded
+ * systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment
+ * variable have no effects.
+ *
+ * libusb can also be compiled with verbose debugging messages. When the
+ * library is compiled in this way, all messages of all verbosities are always
+ * logged.  libusb_set_debug() and the LIBUSB_DEBUG environment variable have
+ * no effects.
+ *
+ * \section remarks Other remarks
+ *
+ * libusb does have imperfections. The \ref caveats "caveats" page attempts
+ * to document these.
+ */
+
+/**
+ * \page caveats Caveats
+ *
+ * \section devresets Device resets
+ *
+ * The libusb_reset_device() function allows you to reset a device. If your
+ * program has to call such a function, it should obviously be aware that
+ * the reset will cause device state to change (e.g. register values may be
+ * reset).
+ *
+ * The problem is that any other program could reset the device your program
+ * is working with, at any time. libusb does not offer a mechanism to inform
+ * you when this has happened, so if someone else resets your device it will
+ * not be clear to your own program why the device state has changed.
+ *
+ * Ultimately, this is a limitation of writing drivers in userspace.
+ * Separation from the USB stack in the underlying kernel makes it difficult
+ * for the operating system to deliver such notifications to your program.
+ * The Linux kernel USB stack allows such reset notifications to be delivered
+ * to in-kernel USB drivers, but it is not clear how such notifications could
+ * be delivered to second-class drivers that live in userspace.
+ *
+ * \section blockonly Blocking-only functionality
+ *
+ * The functionality listed below is only available through synchronous,
+ * blocking functions. There are no asynchronous/non-blocking alternatives,
+ * and no clear ways of implementing these.
+ *
+ * - Configuration activation (libusb_set_configuration())
+ * - Interface/alternate setting activation (libusb_set_interface_alt_setting())
+ * - Releasing of interfaces (libusb_release_interface())
+ * - Clearing of halt/stall condition (libusb_clear_halt())
+ * - Device resets (libusb_reset_device())
+ *
+ * \section nohotplug No hotplugging
+ *
+ * libusb-1.0 lacks functionality for providing notifications of when devices
+ * are added or removed. This functionality is planned to be implemented
+ * for libusb-1.1.
+ *
+ * That said, there is basic disconnection handling for open device handles:
+ *  - If there are ongoing transfers, libusb's handle_events loop will detect
+ *    disconnections and complete ongoing transfers with the
+ *    LIBUSB_TRANSFER_NO_DEVICE status code.
+ *  - Many functions such as libusb_set_configuration() return the special
+ *    LIBUSB_ERROR_NO_DEVICE error code when the device has been disconnected.
+ *
+ * \section configsel Configuration selection and handling
+ *
+ * When libusb presents a device handle to an application, there is a chance
+ * that the corresponding device may be in unconfigured state. For devices
+ * with multiple configurations, there is also a chance that the configuration
+ * currently selected is not the one that the application wants to use.
+ *
+ * The obvious solution is to add a call to libusb_set_configuration() early
+ * on during your device initialization routines, but there are caveats to
+ * be aware of:
+ * -# If the device is already in the desired configuration, calling
+ *    libusb_set_configuration() using the same configuration value will cause
+ *    a lightweight device reset. This may not be desirable behaviour.
+ * -# libusb will be unable to change configuration if the device is in
+ *    another configuration and other programs or drivers have claimed
+ *    interfaces under that configuration.
+ * -# In the case where the desired configuration is already active, libusb
+ *    may not even be able to perform a lightweight device reset. For example,
+ *    take my USB keyboard with fingerprint reader: I'm interested in driving
+ *    the fingerprint reader interface through libusb, but the kernel's
+ *    USB-HID driver will almost always have claimed the keyboard interface.
+ *    Because the kernel has claimed an interface, it is not even possible to
+ *    perform the lightweight device reset, so libusb_set_configuration() will
+ *    fail. (Luckily the device in question only has a single configuration.)
+ *
+ * One solution to some of the above problems is to consider the currently
+ * active configuration. If the configuration we want is already active, then
+ * we don't have to select any configuration:
+\code
+cfg = libusb_get_configuration(dev);
+if (cfg != desired)
+	libusb_set_configuration(dev, desired);
+\endcode
+ *
+ * This is probably suitable for most scenarios, but is inherently racy:
+ * another application or driver may change the selected configuration
+ * <em>after</em> the libusb_get_configuration() call.
+ *
+ * Even in cases where libusb_set_configuration() succeeds, consider that other
+ * applications or drivers may change configuration after your application
+ * calls libusb_set_configuration().
+ *
+ * One possible way to lock your device into a specific configuration is as
+ * follows:
+ * -# Set the desired configuration (or use the logic above to realise that
+ *    it is already in the desired configuration)
+ * -# Claim the interface that you wish to use
+ * -# Check that the currently active configuration is the one that you want
+ *    to use.
+ *
+ * The above method works because once an interface is claimed, no application
+ * or driver is able to select another configuration.
+ */
+
+/**
+ * \page contexts Contexts
+ *
+ * It is possible that libusb may be used simultaneously from two independent
+ * libraries linked into the same executable. For example, if your application
+ * has a plugin-like system which allows the user to dynamically load a range
+ * of modules into your program, it is feasible that two independently
+ * developed modules may both use libusb.
+ *
+ * libusb is written to allow for these multiple user scenarios. The two
+ * "instances" of libusb will not interfere: libusb_set_debug() calls
+ * from one user will not affect the same settings for other users, other
+ * users can continue using libusb after one of them calls libusb_exit(), etc.
+ *
+ * This is made possible through libusb's <em>context</em> concept. When you
+ * call libusb_init(), you are (optionally) given a context. You can then pass
+ * this context pointer back into future libusb functions.
+ *
+ * In order to keep things simple for more simplistic applications, it is
+ * legal to pass NULL to all functions requiring a context pointer (as long as
+ * you're sure no other code will attempt to use libusb from the same process).
+ * When you pass NULL, the default context will be used. The default context
+ * is created the first time a process calls libusb_init() when no other
+ * context is alive. Contexts are destroyed during libusb_exit().
+ *
+ * You may be wondering why only a subset of libusb functions require a
+ * context pointer in their function definition. Internally, libusb stores
+ * context pointers in other objects (e.g. libusb_device instances) and hence
+ * can infer the context from those objects.
+ */
+
+/**
+ * @defgroup lib Library initialization/deinitialization
+ * This page details how to initialize and deinitialize libusb. Initialization
+ * must be performed before using any libusb functionality, and similarly you
+ * must not call any libusb functions after deinitialization.
+ */
+
+/**
+ * @defgroup dev Device handling and enumeration
+ * The functionality documented below is designed to help with the following
+ * operations:
+ * - Enumerating the USB devices currently attached to the system
+ * - Choosing a device to operate from your software
+ * - Opening and closing the chosen device
+ *
+ * \section nutshell In a nutshell...
+ *
+ * The description below really makes things sound more complicated than they
+ * actually are. The following sequence of function calls will be suitable
+ * for almost all scenarios and does not require you to have such a deep
+ * understanding of the resource management issues:
+ * \code
+// discover devices
+libusb_device **list;
+libusb_device *found = NULL;
+size_t cnt = libusb_get_device_list(NULL, &list);
+size_t i = 0;
+int err = 0;
+if (cnt < 0)
+	error();
+
+for (i = 0; i < cnt; i++) {
+	libusb_device *device = list[i];
+	if (is_interesting(device)) {
+		found = device;
+		break;
+	}
+}
+
+if (found) {
+	libusb_device_handle *handle;
+
+	err = libusb_open(found, &handle);
+	if (err)
+		error();
+	// etc
+}
+
+libusb_free_device_list(list, 1);
+\endcode
+ *
+ * The two important points:
+ * - You asked libusb_free_device_list() to unreference the devices (2nd
+ *   parameter)
+ * - You opened the device before freeing the list and unreferencing the
+ *   devices
+ *
+ * If you ended up with a handle, you can now proceed to perform I/O on the
+ * device.
+ *
+ * \section devshandles Devices and device handles
+ * libusb has a concept of a USB device, represented by the
+ * \ref libusb_device opaque type. A device represents a USB device that
+ * is currently or was previously connected to the system. Using a reference
+ * to a device, you can determine certain information about the device (e.g.
+ * you can read the descriptor data).
+ *
+ * The libusb_get_device_list() function can be used to obtain a list of
+ * devices currently connected to the system. This is known as device
+ * discovery.
+ *
+ * Just because you have a reference to a device does not mean it is
+ * necessarily usable. The device may have been unplugged, you may not have
+ * permission to operate such device, or another program or driver may be
+ * using the device.
+ *
+ * When you've found a device that you'd like to operate, you must ask
+ * libusb to open the device using the libusb_open() function. Assuming
+ * success, libusb then returns you a <em>device handle</em>
+ * (a \ref libusb_device_handle pointer). All "real" I/O operations then
+ * operate on the handle rather than the original device pointer.
+ *
+ * \section devref Device discovery and reference counting
+ *
+ * Device discovery (i.e. calling libusb_get_device_list()) returns a
+ * freshly-allocated list of devices. The list itself must be freed when
+ * you are done with it. libusb also needs to know when it is OK to free
+ * the contents of the list - the devices themselves.
+ *
+ * To handle these issues, libusb provides you with two separate items:
+ * - A function to free the list itself
+ * - A reference counting system for the devices inside
+ *
+ * New devices presented by the libusb_get_device_list() function all have a
+ * reference count of 1. You can increase and decrease reference count using
+ * libusb_ref_device() and libusb_unref_device(). A device is destroyed when
+ * its reference count reaches 0.
+ *
+ * With the above information in mind, the process of opening a device can
+ * be viewed as follows:
+ * -# Discover devices using libusb_get_device_list().
+ * -# Choose the device that you want to operate, and call libusb_open().
+ * -# Unref all devices in the discovered device list.
+ * -# Free the discovered device list.
+ *
+ * The order is important - you must not unreference the device before
+ * attempting to open it, because unreferencing it may destroy the device.
+ *
+ * For convenience, the libusb_free_device_list() function includes a
+ * parameter to optionally unreference all the devices in the list before
+ * freeing the list itself. This combines steps 3 and 4 above.
+ *
+ * As an implementation detail, libusb_open() actually adds a reference to
+ * the device in question. This is because the device remains available
+ * through the handle via libusb_get_device(). The reference is deleted during
+ * libusb_close().
+ */
+
+/** @defgroup misc Miscellaneous */
+
+/* we traverse usbfs without knowing how many devices we are going to find.
+ * so we create this discovered_devs model which is similar to a linked-list
+ * which grows when required. it can be freed once discovery has completed,
+ * eliminating the need for a list node in the libusb_device structure
+ * itself. */
+#define DISCOVERED_DEVICES_SIZE_STEP 8
+
+static struct discovered_devs *discovered_devs_alloc(void)
+{
+	struct discovered_devs *ret =
+		malloc(sizeof(*ret) + (sizeof(void *) * DISCOVERED_DEVICES_SIZE_STEP));
+
+	if (ret) {
+		ret->len = 0;
+		ret->capacity = DISCOVERED_DEVICES_SIZE_STEP;
+	}
+	return ret;
+}
+
+/* append a device to the discovered devices collection. may realloc itself,
+ * returning new discdevs. returns NULL on realloc failure. */
+struct discovered_devs *discovered_devs_append(
+	struct discovered_devs *discdevs, struct libusb_device *dev)
+{
+	size_t len = discdevs->len;
+	size_t capacity;
+
+	/* if there is space, just append the device */
+	if (len < discdevs->capacity) {
+		discdevs->devices[len] = libusb_ref_device(dev);
+		discdevs->len++;
+		return discdevs;
+	}
+
+	/* exceeded capacity, need to grow */
+	usbi_dbg("need to increase capacity");
+	capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
+	discdevs = realloc(discdevs,
+		sizeof(*discdevs) + (sizeof(void *) * capacity));
+	if (discdevs) {
+		discdevs->capacity = capacity;
+		discdevs->devices[len] = libusb_ref_device(dev);
+		discdevs->len++;
+	}
+
+	return discdevs;
+}
+
+static void discovered_devs_free(struct discovered_devs *discdevs)
+{
+	size_t i;
+
+	for (i = 0; i < discdevs->len; i++)
+		libusb_unref_device(discdevs->devices[i]);
+
+	free(discdevs);
+}
+
+/* Allocate a new device with a specific session ID. The returned device has
+ * a reference count of 1. */
+struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
+	unsigned long session_id)
+{
+	size_t priv_size = usbi_backend->device_priv_size;
+	struct libusb_device *dev = malloc(sizeof(*dev) + priv_size);
+	int r;
+
+	if (!dev)
+		return NULL;
+
+	r = pthread_mutex_init(&dev->lock, NULL);
+	if (r)
+		return NULL;
+
+	dev->ctx = ctx;
+	dev->refcnt = 1;
+	dev->session_data = session_id;
+	memset(&dev->os_priv, 0, priv_size);
+
+	pthread_mutex_lock(&ctx->usb_devs_lock);
+	list_add(&dev->list, &ctx->usb_devs);
+	pthread_mutex_unlock(&ctx->usb_devs_lock);
+	return dev;
+}
+
+/* Perform some final sanity checks on a newly discovered device. If this
+ * function fails (negative return code), the device should not be added
+ * to the discovered device list. */
+int usbi_sanitize_device(struct libusb_device *dev)
+{
+	int r;
+	unsigned char raw_desc[DEVICE_DESC_LENGTH];
+	uint8_t num_configurations;
+	int host_endian;
+
+	r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian);
+	if (r < 0)
+		return r;
+
+	num_configurations = raw_desc[DEVICE_DESC_LENGTH - 1];
+	if (num_configurations > USB_MAXCONFIG) {
+		usbi_err(DEVICE_CTX(dev), "too many configurations");
+		return LIBUSB_ERROR_IO;
+	} else if (num_configurations < 1) {
+		usbi_dbg("no configurations?");
+		return LIBUSB_ERROR_IO;
+	}
+
+	dev->num_configurations = num_configurations;
+	return 0;
+}
+
+/* Examine libusb's internal list of known devices, looking for one with
+ * a specific session ID. Returns the matching device if it was found, and
+ * NULL otherwise. */
+struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
+	unsigned long session_id)
+{
+	struct libusb_device *dev;
+	struct libusb_device *ret = NULL;
+
+	pthread_mutex_lock(&ctx->usb_devs_lock);
+	list_for_each_entry(dev, &ctx->usb_devs, list)
+		if (dev->session_data == session_id) {
+			ret = dev;
+			break;
+		}
+	pthread_mutex_unlock(&ctx->usb_devs_lock);
+
+	return ret;
+}
+
+/** @ingroup dev
+ * Returns a list of USB devices currently attached to the system. This is
+ * your entry point into finding a USB device to operate.
+ *
+ * You are expected to unreference all the devices when you are done with
+ * them, and then free the list with libusb_free_device_list(). Note that
+ * libusb_free_device_list() can unref all the devices for you. Be careful
+ * not to unreference a device you are about to open until after you have
+ * opened it.
+ *
+ * This return value of this function indicates the number of devices in
+ * the resultant list. The list is actually one element larger, as it is
+ * NULL-terminated.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param list output location for a list of devices. Must be later freed with
+ * libusb_free_device_list().
+ * \returns the number of devices in the outputted list, or LIBUSB_ERROR_NO_MEM
+ * on memory allocation failure.
+ */
+API_EXPORTED ssize_t libusb_get_device_list(libusb_context *ctx,
+	libusb_device ***list)
+{
+	struct discovered_devs *discdevs = discovered_devs_alloc();
+	struct libusb_device **ret;
+	int r = 0;
+	size_t i;
+	ssize_t len;
+	USBI_GET_CONTEXT(ctx);
+	usbi_dbg("");
+
+	if (!discdevs)
+		return LIBUSB_ERROR_NO_MEM;
+
+	r = usbi_backend->get_device_list(ctx, &discdevs);
+	if (r < 0) {
+		len = r;
+		goto out;
+	}
+
+	/* convert discovered_devs into a list */
+	len = discdevs->len;
+	ret = malloc(sizeof(void *) * (len + 1));
+	if (!ret) {
+		len = LIBUSB_ERROR_NO_MEM;
+		goto out;
+	}
+
+	ret[len] = NULL;
+	for (i = 0; i < len; i++) {
+		struct libusb_device *dev = discdevs->devices[i];
+		ret[i] = libusb_ref_device(dev);
+	}
+	*list = ret;
+
+out:
+	discovered_devs_free(discdevs);
+	return len;
+}
+
+/** \ingroup dev
+ * Frees a list of devices previously discovered using
+ * libusb_get_device_list(). If the unref_devices parameter is set, the
+ * reference count of each device in the list is decremented by 1.
+ * \param list the list to free
+ * \param unref_devices whether to unref the devices in the list
+ */
+API_EXPORTED void libusb_free_device_list(libusb_device **list,
+	int unref_devices)
+{
+	if (!list)
+		return;
+
+	if (unref_devices) {
+		int i = 0;
+		struct libusb_device *dev;
+
+		while ((dev = list[i++]) != NULL)
+			libusb_unref_device(dev);
+	}
+	free(list);
+}
+
+/** \ingroup dev
+ * Get the number of the bus that a device is connected to.
+ * \param dev a device
+ * \returns the bus number
+ */
+API_EXPORTED uint8_t libusb_get_bus_number(libusb_device *dev)
+{
+	return dev->bus_number;
+}
+
+/** \ingroup dev
+ * Get the address of the device on the bus it is connected to.
+ * \param dev a device
+ * \returns the device address
+ */
+API_EXPORTED uint8_t libusb_get_device_address(libusb_device *dev)
+{
+	return dev->device_address;
+}
+
+/** \ingroup dev
+ * Convenience function to retrieve the wMaxPacketSize value for a particular
+ * endpoint in the active device configuration. This is useful for setting up
+ * isochronous transfers.
+ *
+ * \param dev a device
+ * \param endpoint address of the endpoint in question
+ * \returns the wMaxPacketSize value
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_OTHER on other failure
+ */
+API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev,
+	unsigned char endpoint)
+{
+	int iface_idx;
+	struct libusb_config_descriptor *config;
+	int r;
+
+	r = libusb_get_active_config_descriptor(dev, &config);
+	if (r < 0) {
+		usbi_err(DEVICE_CTX(dev),
+			"could not retrieve active config descriptor");
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	r = LIBUSB_ERROR_NOT_FOUND;
+	for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) {
+		const struct libusb_interface *iface = &config->interface[iface_idx];
+		int altsetting_idx;
+
+		for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting;
+				altsetting_idx++) {
+			const struct libusb_interface_descriptor *altsetting
+				= &iface->altsetting[altsetting_idx];
+			int ep_idx;
+
+			for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) {
+				const struct libusb_endpoint_descriptor *ep =
+					&altsetting->endpoint[ep_idx];
+				if (ep->bEndpointAddress == endpoint) {
+					r = ep->wMaxPacketSize;
+					goto out;
+				}
+			}
+		}
+	}
+
+out:
+	libusb_free_config_descriptor(config);
+	return r;
+}
+
+/** \ingroup dev
+ * Increment the reference count of a device.
+ * \param dev the device to reference
+ * \returns the same device
+ */
+API_EXPORTED libusb_device *libusb_ref_device(libusb_device *dev)
+{
+	pthread_mutex_lock(&dev->lock);
+	dev->refcnt++;
+	pthread_mutex_unlock(&dev->lock);
+	return dev;
+}
+
+/** \ingroup dev
+ * Decrement the reference count of a device. If the decrement operation
+ * causes the reference count to reach zero, the device shall be destroyed.
+ * \param dev the device to unreference
+ */
+API_EXPORTED void libusb_unref_device(libusb_device *dev)
+{
+	int refcnt;
+
+	if (!dev)
+		return;
+
+	pthread_mutex_lock(&dev->lock);
+	refcnt = --dev->refcnt;
+	pthread_mutex_unlock(&dev->lock);
+
+	if (refcnt == 0) {
+		usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address);
+
+		if (usbi_backend->destroy_device)
+			usbi_backend->destroy_device(dev);
+
+		pthread_mutex_lock(&dev->ctx->usb_devs_lock);
+		list_del(&dev->list);
+		pthread_mutex_unlock(&dev->ctx->usb_devs_lock);
+
+		free(dev);
+	}
+}
+
+/** \ingroup dev
+ * Open a device and obtain a device handle. A handle allows you to perform
+ * I/O on the device in question.
+ *
+ * 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 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
+ */
+API_EXPORTED int libusb_open(libusb_device *dev, 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;
+	unsigned char dummy = 1;
+	int r;
+	usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
+
+	_handle = malloc(sizeof(*_handle) + priv_size);
+	if (!_handle)
+		return LIBUSB_ERROR_NO_MEM;
+
+	r = pthread_mutex_init(&_handle->lock, NULL);
+	if (r)
+		return LIBUSB_ERROR_OTHER;
+
+	_handle->dev = libusb_ref_device(dev);
+	_handle->claimed_interfaces = 0;
+	memset(&_handle->os_priv, 0, priv_size);
+
+	r = usbi_backend->open(_handle);
+	if (r < 0) {
+		libusb_unref_device(dev);
+		free(_handle);
+		return r;
+	}
+
+	pthread_mutex_lock(&ctx->open_devs_lock);
+	list_add(&_handle->list, &ctx->open_devs);
+	pthread_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 libusb 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. */
+
+	/* record that we are messing with poll fds */
+	pthread_mutex_lock(&ctx->pollfd_modify_lock);
+	ctx->pollfd_modify++;
+	pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+	/* write some data on control pipe to interrupt event handlers */
+	r = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
+	if (r <= 0) {
+		usbi_warn(ctx, "internal signalling write failed");
+		pthread_mutex_lock(&ctx->pollfd_modify_lock);
+		ctx->pollfd_modify--;
+		pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+		return 0;
+	}
+
+	/* take event handling lock */
+	libusb_lock_events(ctx);
+
+	/* read the dummy data */
+	r = read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy));
+	if (r <= 0)
+		usbi_warn(ctx, "internal signalling read failed");
+
+	/* we're done with modifying poll fds */
+	pthread_mutex_lock(&ctx->pollfd_modify_lock);
+	ctx->pollfd_modify--;
+	pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+	/* Release event handling lock and wake up event waiters */
+	libusb_unlock_events(ctx);
+
+	return 0;
+}
+
+/** \ingroup dev
+ * Convenience function for finding a device with a particular
+ * <tt>idVendor</tt>/<tt>idProduct</tt> combination. This function is intended
+ * for those scenarios where you are using libusb to knock up a quick test
+ * application - it allows you to avoid calling libusb_get_device_list() and
+ * worrying about traversing/freeing the list.
+ *
+ * This function has limitations and is hence not intended for use in real
+ * applications: if multiple devices have the same IDs it will only
+ * give you the first one, etc.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param vendor_id the idVendor value to search for
+ * \param product_id the idProduct value to search for
+ * \returns a handle for the first found device, or NULL on error or if the
+ * device could not be found. */
+API_EXPORTED libusb_device_handle *libusb_open_device_with_vid_pid(
+	libusb_context *ctx, uint16_t vendor_id, uint16_t product_id)
+{
+	struct libusb_device **devs;
+	struct libusb_device *found = NULL;
+	struct libusb_device *dev;
+	struct libusb_device_handle *handle = NULL;
+	size_t i = 0;
+	int r;
+
+	if (libusb_get_device_list(ctx, &devs) < 0)
+		return NULL;
+
+	while ((dev = devs[i++]) != NULL) {
+		struct libusb_device_descriptor desc;
+		r = libusb_get_device_descriptor(dev, &desc);
+		if (r < 0)
+			goto out;
+		if (desc.idVendor == vendor_id && desc.idProduct == product_id) {
+			found = dev;
+			break;
+		}
+	}
+
+	if (found) {
+		r = libusb_open(found, &handle);
+		if (r < 0)
+			handle = NULL;
+	}
+
+out:
+	libusb_free_device_list(devs, 1);
+	return handle;
+}
+
+static void do_close(struct libusb_context *ctx,
+	struct libusb_device_handle *dev_handle)
+{
+	pthread_mutex_lock(&ctx->open_devs_lock);
+	list_del(&dev_handle->list);
+	pthread_mutex_unlock(&ctx->open_devs_lock);
+
+	usbi_backend->close(dev_handle);
+	libusb_unref_device(dev_handle->dev);
+	free(dev_handle);
+}
+
+/** \ingroup dev
+ * Close a device handle. Should be called on all open handles before your
+ * application exits.
+ *
+ * Internally, this function destroys the reference that was added by
+ * libusb_open() on the given device.
+ *
+ * This is a non-blocking function; no requests are sent over the bus.
+ *
+ * \param dev_handle the handle to close
+ */
+API_EXPORTED void libusb_close(libusb_device_handle *dev_handle)
+{
+	struct libusb_context *ctx;
+	unsigned char dummy = 1;
+	ssize_t r;
+
+	if (!dev_handle)
+		return;
+	usbi_dbg("");
+
+	ctx = HANDLE_CTX(dev_handle);
+
+	/* Similarly to libusb_open(), we want to interrupt all event handlers
+	 * at this point. More importantly, we want to perform the actual close of
+	 * the device while holding the event handling lock (preventing any other
+	 * thread from doing event handling) because we will be removing a file
+	 * descriptor from the polling loop. */
+
+	/* record that we are messing with poll fds */
+	pthread_mutex_lock(&ctx->pollfd_modify_lock);
+	ctx->pollfd_modify++;
+	pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+	/* write some data on control pipe to interrupt event handlers */
+	r = write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy));
+	if (r <= 0) {
+		usbi_warn(ctx, "internal signalling write failed, closing anyway");
+		do_close(ctx, dev_handle);
+		pthread_mutex_lock(&ctx->pollfd_modify_lock);
+		ctx->pollfd_modify--;
+		pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+		return;
+	}
+
+	/* take event handling lock */
+	libusb_lock_events(ctx);
+
+	/* read the dummy data */
+	r = read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy));
+	if (r <= 0)
+		usbi_warn(ctx, "internal signalling read failed, closing anyway");
+
+	/* Close the device */
+	do_close(ctx, dev_handle);
+
+	/* we're done with modifying poll fds */
+	pthread_mutex_lock(&ctx->pollfd_modify_lock);
+	ctx->pollfd_modify--;
+	pthread_mutex_unlock(&ctx->pollfd_modify_lock);
+
+	/* Release event handling lock and wake up event waiters */
+	libusb_unlock_events(ctx);
+}
+
+/** \ingroup dev
+ * Get the underlying device for a handle. This function does not modify
+ * the reference count of the returned device, so do not feel compelled to
+ * unreference it when you are done.
+ * \param dev_handle a device handle
+ * \returns the underlying device
+ */
+API_EXPORTED libusb_device *libusb_get_device(libusb_device_handle *dev_handle)
+{
+	return dev_handle->dev;
+}
+
+/** \ingroup dev
+ * Determine the bConfigurationValue of the currently active configuration.
+ *
+ * You could formulate your own control request to obtain this information,
+ * but this function has the advantage that it may be able to retrieve the
+ * information from operating system caches (no I/O involved).
+ *
+ * If the OS does not cache this information, then this function will block
+ * while a control transfer is submitted to retrieve the information.
+ *
+ * This function will return a value of 0 in the <tt>config</tt> output
+ * parameter if the device is in unconfigured state.
+ *
+ * \param dev a device handle
+ * \param config output location for the bConfigurationValue of the active
+ * configuration (only valid for return code 0)
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_get_configuration(libusb_device_handle *dev,
+	int *config)
+{
+	int r = LIBUSB_ERROR_NOT_SUPPORTED;
+
+	usbi_dbg("");
+	if (usbi_backend->get_configuration)
+		r = usbi_backend->get_configuration(dev, config);
+
+	if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
+		uint8_t tmp = 0;
+		usbi_dbg("falling back to control message");
+		r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
+			LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
+		if (r == 0) {
+			usbi_err(HANDLE_CTX(dev), "zero bytes returned in ctrl transfer?");
+			r = LIBUSB_ERROR_IO;
+		} else if (r == 1) {
+			r = 0;
+			*config = tmp;
+		} else {
+			usbi_dbg("control failed, error %d", r);
+		}
+	}
+
+	if (r == 0)
+		usbi_dbg("active config %d", *config);
+
+	return r;
+}
+
+/** \ingroup dev
+ * Set the active configuration for a device.
+ *
+ * The operating system may or may not have already set an active
+ * configuration on the device. It is up to your application to ensure the
+ * correct configuration is selected before you attempt to claim interfaces
+ * and perform other operations.
+ *
+ * If you call this function on a device already configured with the selected
+ * configuration, then this function will act as a lightweight device reset:
+ * it will issue a SET_CONFIGURATION request using the current configuration,
+ * causing most USB-related device state to be reset (altsetting reset to zero,
+ * endpoint halts cleared, toggles reset).
+ *
+ * You cannot change/reset configuration if your application has claimed
+ * interfaces - you should free them with libusb_release_interface() first.
+ * You cannot change/reset configuration if other applications or drivers have
+ * claimed interfaces.
+ *
+ * A configuration value of -1 will put the device in unconfigured state.
+ * The USB specifications state that a configuration value of 0 does this,
+ * however buggy devices exist which actually have a configuration 0.
+ *
+ * You should always use this function rather than formulating your own
+ * SET_CONFIGURATION control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param configuration the bConfigurationValue of the configuration you
+ * wish to activate, or -1 if you wish to put the device in unconfigured state
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist
+ * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_set_configuration(libusb_device_handle *dev,
+	int configuration)
+{
+	usbi_dbg("configuration %d", configuration);
+	return usbi_backend->set_configuration(dev, configuration);
+}
+
+/** \ingroup dev
+ * Claim an interface on a given device handle. You must claim the interface
+ * you wish to use before you can perform I/O on any of its endpoints.
+ *
+ * It is legal to attempt to claim an already-claimed interface, in which
+ * case libusb just returns 0 without doing anything.
+ *
+ * Claiming of interfaces is a purely logical operation; it does not cause
+ * any requests to be sent over the bus. Interface claiming is used to
+ * instruct the underlying operating system that your application wishes
+ * to take ownership of the interface.
+ *
+ * This is a non-blocking function.
+ *
+ * \param dev a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the interface you
+ * wish to claim
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the requested interface does not exist
+ * \returns LIBUSB_ERROR_BUSY if another program or driver has claimed the
+ * interface
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns a LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_claim_interface(libusb_device_handle *dev,
+	int interface_number)
+{
+	int r = 0;
+
+	usbi_dbg("interface %d", interface_number);
+	if (interface_number >= sizeof(dev->claimed_interfaces) * 8)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	pthread_mutex_lock(&dev->lock);
+	if (dev->claimed_interfaces & (1 << interface_number))
+		goto out;
+
+	r = usbi_backend->claim_interface(dev, interface_number);
+	if (r == 0)
+		dev->claimed_interfaces |= 1 << interface_number;
+
+out:
+	pthread_mutex_unlock(&dev->lock);
+	return r;
+}
+
+/** \ingroup dev
+ * Release an interface previously claimed with libusb_claim_interface(). You
+ * should release all claimed interfaces before closing a device handle.
+ *
+ * This is a blocking function. A SET_INTERFACE control request will be sent
+ * to the device, resetting interface state to the first alternate setting.
+ *
+ * \param dev a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the
+ * previously-claimed interface
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_release_interface(libusb_device_handle *dev,
+	int interface_number)
+{
+	int r;
+
+	usbi_dbg("interface %d", interface_number);
+	if (interface_number >= sizeof(dev->claimed_interfaces) * 8)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	pthread_mutex_lock(&dev->lock);
+	if (!(dev->claimed_interfaces & (1 << interface_number))) {
+		r = LIBUSB_ERROR_NOT_FOUND;
+		goto out;
+	}
+
+	r = usbi_backend->release_interface(dev, interface_number);
+	if (r == 0)
+		dev->claimed_interfaces &= ~(1 << interface_number);
+
+out:
+	pthread_mutex_unlock(&dev->lock);
+	return r;
+}
+
+/** \ingroup dev
+ * Activate an alternate setting for an interface. The interface must have
+ * been previously claimed with libusb_claim_interface().
+ *
+ * You should always use this function rather than formulating your own
+ * SET_INTERFACE control request. This is because the underlying operating
+ * system needs to know when such changes happen.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param interface_number the <tt>bInterfaceNumber</tt> of the
+ * previously-claimed interface
+ * \param alternate_setting the <tt>bAlternateSetting</tt> of the alternate
+ * setting to activate
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the interface was not claimed, or the
+ * requested alternate setting does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_set_interface_alt_setting(libusb_device_handle *dev,
+	int interface_number, int alternate_setting)
+{
+	usbi_dbg("interface %d altsetting %d",
+		interface_number, alternate_setting);
+	if (interface_number >= sizeof(dev->claimed_interfaces) * 8)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	pthread_mutex_lock(&dev->lock);
+	if (!(dev->claimed_interfaces & (1 << interface_number))) {
+		pthread_mutex_unlock(&dev->lock);
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+	pthread_mutex_unlock(&dev->lock);
+
+	return usbi_backend->set_interface_altsetting(dev, interface_number,
+		alternate_setting);
+}
+
+/** \ingroup dev
+ * Clear the halt/stall condition for an endpoint. Endpoints with halt status
+ * are unable to receive or transmit data until the halt condition is stalled.
+ *
+ * You should cancel all pending transfers before attempting to clear the halt
+ * condition.
+ *
+ * This is a blocking function.
+ *
+ * \param dev a device handle
+ * \param endpoint the endpoint to clear halt status
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist
+ * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_clear_halt(libusb_device_handle *dev,
+	unsigned char endpoint)
+{
+	usbi_dbg("endpoint %x", endpoint);
+	return usbi_backend->clear_halt(dev, endpoint);
+}
+
+/** \ingroup dev
+ * Perform a USB port reset to reinitialize a device. The system will attempt
+ * to restore the previous configuration and alternate settings after the
+ * reset has completed.
+ *
+ * If the reset fails, the descriptors change, or the previous state cannot be
+ * restored, the device will appear to be disconnected and reconnected. This
+ * means that the device handle is no longer valid (you should close it) and
+ * rediscover the device. A return code of LIBUSB_ERROR_NOT_FOUND indicates
+ * when this is the case.
+ *
+ * This is a blocking function which usually incurs a noticeable delay.
+ *
+ * \param dev a handle of the device to reset
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if re-enumeration is required, or if the
+ * device has been disconnected
+ * \returns another LIBUSB_ERROR code on other failure
+ */
+API_EXPORTED int libusb_reset_device(libusb_device_handle *dev)
+{
+	usbi_dbg("");
+	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_NO_DEVICE if the device has been disconnected
+ * \returns another LIBUSB_ERROR code on other 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 LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \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 dev
+ * Re-attach an interface's kernel driver, which was previously detached
+ * using libusb_detach_kernel_driver().
+ *
+ * \param dev a device handle
+ * \param interface the interface to attach 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 LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
+ * \returns LIBUSB_ERROR_BUSY if the driver cannot be attached because the
+ * interface is claimed by a program or driver
+ * \returns another LIBUSB_ERROR code on other failure
+ * \see libusb_kernel_driver_active()
+ */
+API_EXPORTED int libusb_attach_kernel_driver(libusb_device_handle *dev,
+	int interface)
+{
+	usbi_dbg("interface %d", interface);
+	if (usbi_backend->attach_kernel_driver)
+		return usbi_backend->attach_kernel_driver(dev, interface);
+	else
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+/** \ingroup lib
+ * Set message verbosity.
+ *  - Level 0: no messages ever printed by the library (default)
+ *  - Level 1: error messages are printed to stderr
+ *  - Level 2: warning and error messages are printed to stderr
+ *  - Level 3: informational messages are printed to stdout, warning and error
+ *    messages are printed to stderr
+ *
+ * The default level is 0, which means no messages are ever printed. If you
+ * choose to increase the message verbosity level, ensure that your
+ * application does not close the stdout/stderr file descriptors.
+ *
+ * You are advised to set level 3. libusb is conservative with its message
+ * logging and most of the time, will only log messages that explain error
+ * conditions and other oddities. This will help you debug your software.
+ *
+ * If the LIBUSB_DEBUG environment variable was set when libusb was
+ * initialized, this function does nothing: the message verbosity is fixed
+ * to the value in the environment variable.
+ *
+ * If libusb was compiled without any message logging, this function does
+ * nothing: you'll never get any messages.
+ *
+ * If libusb was compiled with verbose debug message logging, this function
+ * does nothing: you'll always get messages from all levels.
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param level debug level to set
+ */
+API_EXPORTED void libusb_set_debug(libusb_context *ctx, int level)
+{
+	USBI_GET_CONTEXT(ctx);
+	if (!ctx->debug_fixed)
+		ctx->debug = level;
+}
+
+/** \ingroup lib
+ * Initialize libusb. This function must be called before calling any other
+ * libusb function.
+ * \param context Optional output location for context pointer.
+ * Only valid on return code 0.
+ * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ */
+API_EXPORTED int libusb_init(libusb_context **context)
+{
+	char *dbg = getenv("LIBUSB_DEBUG");
+	struct libusb_context *ctx = malloc(sizeof(*ctx));
+	int r;
+
+	if (!ctx)
+		return LIBUSB_ERROR_NO_MEM;
+	memset(ctx, 0, sizeof(*ctx));
+
+	if (dbg) {
+		ctx->debug = atoi(dbg);
+		if (ctx->debug)
+			ctx->debug_fixed = 1;
+	}
+
+	usbi_dbg("");
+
+	if (usbi_backend->init) {
+		r = usbi_backend->init(ctx);
+		if (r)
+			goto err;
+	}
+
+	pthread_mutex_init(&ctx->usb_devs_lock, NULL);
+	pthread_mutex_init(&ctx->open_devs_lock, NULL);
+	list_init(&ctx->usb_devs);
+	list_init(&ctx->open_devs);
+
+	r = usbi_io_init(ctx);
+	if (r < 0) {
+		if (usbi_backend->exit)
+			usbi_backend->exit();
+		goto err;
+	}
+
+	pthread_mutex_lock(&default_context_lock);
+	if (!usbi_default_context) {
+		usbi_dbg("created default context");
+		usbi_default_context = ctx;
+	}
+	pthread_mutex_unlock(&default_context_lock);
+
+	if (context)
+		*context = ctx;
+	return 0;
+
+err:
+	free(ctx);
+	return r;
+}
+
+/** \ingroup lib
+ * Deinitialize libusb. Should be called after closing all open devices and
+ * before your application terminates.
+ * \param ctx the context to deinitialize, or NULL for the default context
+ */
+API_EXPORTED void libusb_exit(struct libusb_context *ctx)
+{
+	USBI_GET_CONTEXT(ctx);
+	usbi_dbg("");
+
+	/* a little sanity check. doesn't bother with open_devs locking because
+	 * unless there is an application bug, nobody will be accessing this. */
+	if (!list_empty(&ctx->open_devs))
+		usbi_warn(ctx, "application left some devices open");
+
+	usbi_io_exit(ctx);
+	if (usbi_backend->exit)
+		usbi_backend->exit();
+
+	pthread_mutex_lock(&default_context_lock);
+	if (ctx == usbi_default_context) {
+		usbi_dbg("freeing default context");
+		usbi_default_context = NULL;
+	}
+	pthread_mutex_unlock(&default_context_lock);
+
+	free(ctx);
+}
+
+void usbi_log(struct libusb_context *ctx, enum usbi_log_level level,
+	const char *function, const char *format, ...)
+{
+	va_list args;
+	FILE *stream = stdout;
+	const char *prefix;
+
+#ifndef ENABLE_DEBUG_LOGGING
+	USBI_GET_CONTEXT(ctx);
+	if (!ctx->debug)
+		return;
+	if (level == LOG_LEVEL_WARNING && ctx->debug < 2)
+		return;
+	if (level == LOG_LEVEL_INFO && ctx->debug < 3)
+		return;
+#endif
+
+	switch (level) {
+	case LOG_LEVEL_INFO:
+		prefix = "info";
+		break;
+	case LOG_LEVEL_WARNING:
+		stream = stderr;
+		prefix = "warning";
+		break;
+	case LOG_LEVEL_ERROR:
+		stream = stderr;
+		prefix = "error";
+		break;
+	case LOG_LEVEL_DEBUG:
+		stream = stderr;
+		prefix = "debug";
+		break;
+	default:
+		stream = stderr;
+		prefix = "unknown";
+		break;
+	}
+
+	fprintf(stream, "libusb:%s [%s] ", prefix, function);
+
+	va_start (args, format);
+	vfprintf(stream, format, args);
+	va_end (args);
+
+	fprintf(stream, "\n");
+}
+

resources/darwin.c

+/*
+ * Darwin/MacOS X Support
+ *
+ * (c) 2002-2005 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ *
+ * (04/17/2005):
+ *   - Lots of minor fixes.
+ *   - Endpoint table now made by claim_interface to fix a bug.
+ *   - Merged Read/Write to make modifications easier.
+ * (03/25/2005):
+ *   - Fixed a bug when using asynchronous callbacks within a multi-threaded application.
+ * (03/14/2005):
+ *   - Added an endpoint table to speed up bulk transfers.
+ * 0.1.11 (02/22/2005):
+ *   - Updated error checking in read/write routines to check completion codes.
+ *   - Updated set_configuration so that the open interface is reclaimed before completion.
+ *   - Fixed several typos.
+ * 0.1.8 (01/12/2004):
+ *   - Fixed several memory leaks.
+ *   - Readded 10.0 support
+ *   - Added support for USB fuctions defined in 10.3 and above
+ * (01/02/2003):
+ *   - Applied a patch by Philip Edelbrock <phil@edgedesign.us> that fixes a bug in usb_control_msg.
+ * (12/16/2003):
+ *   - Even better error printing.
+ *   - Devices that cannot be opened can have their interfaces opened.
+ * 0.1.6 (05/12/2002):
+ *   - Fixed problem where libusb holds resources after program completion.
+ *   - Mouse should no longer freeze up now.
+ * 0.1.2 (02/13/2002):
+ *   - Bulk functions should work properly now.
+ * 0.1.1 (02/11/2002):
+ *   - Fixed major bug (device and interface need to be released after use)
+ * 0.1.0 (01/06/2002):
+ *   - Tested driver with gphoto (works great as long as Image Capture isn't running)
+ * 0.1d  (01/04/2002):
+ *   - Implimented clear_halt and resetep
+ *   - Uploaded to CVS.
+ * 0.1b  (01/04/2002):
+ *   - Added usb_debug line to bulk read and write function.
+ * 0.1a  (01/03/2002):
+ *   - Driver mostly completed using the macosx driver I wrote for my rioutil software.
+ *
+ * Derived from Linux version by Richard Tobin.
+ * Also partly derived from BSD version.
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/* standard includes for darwin/os10 (IOKit) */
+#include <mach/mach_port.h>
+#include <IOKit/IOCFBundle.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOCFPlugIn.h>
+
+#include "usbi.h"
+
+/* some defines */
+/* IOUSBInterfaceInferface */
+#if defined (kIOUSBInterfaceInterfaceID220)
+
+// #warning "libusb being compiled for 10.4 or later"
+#define usb_interface_t IOUSBInterfaceInterface220
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
+#define InterfaceVersion 220
+
+#elif defined (kIOUSBInterfaceInterfaceID197)
+
+// #warning "libusb being compiled for 10.3 or later"
+#define usb_interface_t IOUSBInterfaceInterface197
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID197
+#define InterfaceVersion 197
+
+#elif defined (kIOUSBInterfaceInterfaceID190)
+
+// #warning "libusb being compiled for 10.2 or later"
+#define usb_interface_t IOUSBInterfaceInterface190
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID190
+#define InterfaceVersion 190
+
+#elif defined (kIOUSBInterfaceInterfaceID182)
+
+// #warning "libusb being compiled for 10.1 or later"
+#define usb_interface_t IOUSBInterfaceInterface182
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID182
+#define InterfaceVersion 182
+
+#else
+
+/* No timeout functions available! Time to upgrade your os. */
+#warning "libusb being compiled without support for timeout bulk functions! 10.0 and up"
+#define usb_interface_t IOUSBInterfaceInterface
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID
+#define LIBUSB_NO_TIMEOUT_INTERFACE
+#define InterfaceVersion 180
+
+#endif
+
+/* IOUSBDeviceInterface */
+#if defined (kIOUSBDeviceInterfaceID197)
+
+#define usb_device_t    IOUSBDeviceInterface197
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
+#define DeviceVersion 197
+
+#elif defined (kIOUSBDeviceInterfaceID187)
+
+#define usb_device_t    IOUSBDeviceInterface187
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID187
+#define DeviceVersion 187
+
+#elif defined (kIOUSBDeviceInterfaceID182)
+
+#define usb_device_t    IOUSBDeviceInterface182
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID182
+#define DeviceVersion 182
+
+#else
+
+#define usb_device_t    IOUSBDeviceInterface
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID
+#define LIBUSB_NO_TIMEOUT_DEVICE
+#define LIBUSB_NO_SEIZE_DEVICE
+#define DeviceVersion 180
+
+#endif
+
+typedef IOReturn io_return_t;
+typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
+typedef SInt32 s_int32_t;
+typedef IOReturn (*rw_async_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size,
+				    IOAsyncCallback1 callback, void *refcon);
+typedef IOReturn (*rw_async_to_func_t)(void *self, UInt8 pipeRef, void *buf, UInt32 size,
+				       UInt32 noDataTimeout, UInt32 completionTimeout,
+				       IOAsyncCallback1 callback, void *refcon);
+
+#if !defined(IO_OBJECT_NULL)
+#define IO_OBJECT_NULL ((io_object_t)0)
+#endif
+
+/* Darwin/OS X impl does not use fd field, instead it uses this */
+struct darwin_dev_handle {
+  usb_device_t **device;
+  usb_interface_t **interface;
+  int open;
+
+  /* stored translation table for pipes to endpoints */
+  int num_endpoints;
+  unsigned char *endpoint_addrs;
+};
+
+static CFMutableDictionaryRef matchingDict;
+static IONotificationPortRef gNotifyPort;
+static mach_port_t masterPort = MACH_PORT_NULL;
+
+static void darwin_cleanup (void)
+{
+  IONotificationPortDestroy(gNotifyPort);
+  mach_port_deallocate(mach_task_self(), masterPort);
+}
+
+static char *darwin_error_str (int result) {
+  switch (result) {
+  case kIOReturnSuccess:
+    return "no error";
+  case kIOReturnNotOpen:
+    return "device not opened for exclusive access";
+  case kIOReturnNoDevice:
+    return "no connection to an IOService";
+  case kIOUSBNoAsyncPortErr:
+    return "no async port has been opened for interface";
+  case kIOReturnExclusiveAccess:
+    return "another process has device opened for exclusive access";
+  case kIOUSBPipeStalled:
+    return "pipe is stalled";
+  case kIOReturnError:
+    return "could not establish a connection to the Darwin kernel";
+  case kIOUSBTransactionTimeout:
+    return "transaction timed out";
+  case kIOReturnBadArgument:
+    return "invalid argument";
+  case kIOReturnAborted:
+    return "transaction aborted";
+  default:
+    return "unknown error";
+  }
+}
+
+/* not a valid errorno outside darwin.c */
+#define LUSBDARWINSTALL (ELAST+1)
+
+static int darwin_to_errno (int result) {
+  switch (result) {
+  case kIOReturnSuccess:
+    return 0;
+  case kIOReturnNotOpen:
+    return EBADF;
+  case kIOReturnNoDevice:
+  case kIOUSBNoAsyncPortErr:
+    return ENXIO;
+  case kIOReturnExclusiveAccess:
+    return EBUSY;
+  case kIOUSBPipeStalled:
+    return LUSBDARWINSTALL;
+  case kIOReturnBadArgument:
+    return EINVAL;
+  case kIOUSBTransactionTimeout:
+    return ETIMEDOUT;
+  case kIOReturnError:
+  default:
+    return 1;
+  }
+}
+
+static int usb_setup_iterator (io_iterator_t *deviceIterator)
+{
+  int result;
+
+  /* set up the matching dictionary for class IOUSBDevice and its subclasses.
+     It will be consumed by the IOServiceGetMatchingServices call */
+  if ((matchingDict = IOServiceMatching(kIOUSBDeviceClassName)) == NULL) {
+    darwin_cleanup ();
+    
+    USB_ERROR_STR(-1, "libusb/darwin.c usb_setup_iterator: Could not create a matching dictionary.\n");
+  }
+
+  result = IOServiceGetMatchingServices(masterPort, matchingDict, deviceIterator);
+  matchingDict = NULL;
+
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR (-darwin_to_errno (result), "libusb/darwin.c usb_setup_iterator: IOServiceGetMatchingServices: %s\n",
+		   darwin_error_str(result));
+
+  return 0;
+}
+
+static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp)
+{
+  io_cf_plugin_ref_t *plugInInterface = NULL;
+  usb_device_t **device;
+  io_service_t usbDevice;
+  long result, score;
+
+  if (!IOIteratorIsValid (deviceIterator) || !(usbDevice = IOIteratorNext(deviceIterator)))
+    return NULL;
+  
+  result = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID,
+					     kIOCFPlugInInterfaceID, &plugInInterface,
+					     &score);
+  
+  result = IOObjectRelease(usbDevice);
+  if (result || !plugInInterface)
+    return NULL;
+  
+  (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(DeviceInterfaceID),
+				     (LPVOID)&device);
+  
+  (*plugInInterface)->Stop(plugInInterface);
+  IODestroyPlugInInterface (plugInInterface);
+  plugInInterface = NULL;
+  
+  (*(device))->GetLocationID(device, locationp);
+
+  return device;
+}
+
+int usb_os_open(usb_dev_handle *dev)
+{
+  struct darwin_dev_handle *device;
+
+  io_return_t result;
+  io_iterator_t deviceIterator;
+
+  usb_device_t **darwin_device;
+
+  UInt32 location = *((UInt32 *)dev->device->dev);
+  UInt32 dlocation;
+
+  if (!dev)
+    USB_ERROR(-ENXIO);
+
+  if (masterPort == MACH_PORT_NULL)
+    USB_ERROR(-EINVAL);
+
+  device = calloc(1, sizeof(struct darwin_dev_handle));
+  if (!device)
+    USB_ERROR(-ENOMEM);
+
+  if (usb_debug > 3)
+    fprintf(stderr, "usb_os_open: %04x:%04x\n",
+	    dev->device->descriptor.idVendor,
+	    dev->device->descriptor.idProduct);
+
+  if ((result = usb_setup_iterator (&deviceIterator)) < 0)
+    return result;
+
+  /* This port of libusb uses locations to keep track of devices. */
+  while ((darwin_device = usb_get_next_device (deviceIterator, &dlocation)) != NULL) {
+    if (dlocation == location)
+      break;
+
+    (*darwin_device)->Release(darwin_device);
+  }
+
+  IOObjectRelease(deviceIterator);
+  device->device = darwin_device;
+
+  if (device->device == NULL)
+    USB_ERROR_STR (-ENOENT, "usb_os_open: %s\n", "Device not found!");
+
+#if !defined (LIBUSB_NO_SEIZE_DEVICE)
+  result = (*(device->device))->USBDeviceOpenSeize (device->device);
+#else
+  /* No Seize in OS X 10.0 (Darwin 1.4) */
+  result = (*(device->device))->USBDeviceOpen (device->device);
+#endif
+
+  if (result != kIOReturnSuccess) {
+    switch (result) {
+    case kIOReturnExclusiveAccess:
+      if (usb_debug > 0)
+	fprintf (stderr, "usb_os_open(USBDeviceOpenSeize): %s\n", darwin_error_str(result));
+      break;
+    default:
+      (*(device->device))->Release (device->device);
+      USB_ERROR_STR(-darwin_to_errno (result), "usb_os_open(USBDeviceOpenSeize): %s",
+		    darwin_error_str(result));
+    }
+    
+    device->open = 0;
+  } else
+    device->open = 1;
+    
+  dev->impl_info = device;
+  dev->interface = -1;
+  dev->altsetting = -1;
+
+  device->num_endpoints  = 0;
+  device->endpoint_addrs = NULL;
+
+  return 0;
+}
+
+int usb_os_close(usb_dev_handle *dev)
+{
+  struct darwin_dev_handle *device;
+  io_return_t result;
+
+  if (!dev)
+    USB_ERROR(-ENXIO);
+
+  if ((device = dev->impl_info) == NULL)
+    USB_ERROR(-ENOENT);
+
+  usb_release_interface(dev, dev->interface);
+
+  if (usb_debug > 3)
+    fprintf(stderr, "usb_os_close: %04x:%04x\n",
+	    dev->device->descriptor.idVendor,
+	    dev->device->descriptor.idProduct);
+
+  if (device->open == 1)
+    result = (*(device->device))->USBDeviceClose(device->device);
+  else
+    result = kIOReturnSuccess;
+
+  /* device may not need to be released, but if it has to... */
+  (*(device->device))->Release(device->device);
+
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR(-darwin_to_errno(result), "usb_os_close(USBDeviceClose): %s", darwin_error_str(result));
+
+  free (device);
+
+  return 0;
+}
+
+static int get_endpoints (struct darwin_dev_handle *device)
+{
+  io_return_t ret;
+
+  u_int8_t numep, direction, number;
+  u_int8_t dont_care1, dont_care3;
+  u_int16_t dont_care2;
+
+  int i;
+
+  if (device == NULL || device->interface == NULL)
+    return -EINVAL;
+
+  if (usb_debug > 1)
+    fprintf(stderr, "libusb/darwin.c get_endpoints: building table of endpoints.\n");
+
+  /* retrieve the total number of endpoints on this interface */
+  ret = (*(device->interface))->GetNumEndpoints(device->interface, &numep);
+  if ( ret ) {
+    if ( usb_debug > 1 )
+      fprintf ( stderr, "get_endpoints: interface is %p\n", device->interface );
+
+    USB_ERROR_STR ( -ret, "get_endpoints: can't get number of endpoints for interface" );
+  }
+
+  free (device->endpoint_addrs);
+  device->endpoint_addrs = calloc (sizeof (unsigned char), numep);
+
+  /* iterate through pipe references */
+  for (i = 1 ; i <= numep ; i++) {
+    ret = (*(device->interface))->GetPipeProperties(device->interface, i, &direction, &number,
+						    &dont_care1, &dont_care2, &dont_care3);
+
+    if (ret != kIOReturnSuccess) {
+      fprintf (stderr, "get_endpoints: an error occurred getting pipe information on pipe %d\n",
+	       i );
+      USB_ERROR_STR(-darwin_to_errno(ret), "get_endpoints(GetPipeProperties): %s", darwin_error_str(ret));
+    }
+
+    if (usb_debug > 1)
+      fprintf (stderr, "get_endpoints: Pipe %i: DIR: %i number: %i\n", i, direction, number);
+
+    device->endpoint_addrs[i - 1] = ((direction << 7 & USB_ENDPOINT_DIR_MASK) |
+				     (number & USB_ENDPOINT_ADDRESS_MASK));
+  }
+
+  device->num_endpoints = numep;
+
+  if (usb_debug > 1)
+    fprintf(stderr, "libusb/darwin.c get_endpoints: complete.\n");
+  
+  return 0;
+}
+
+static int claim_interface (usb_dev_handle *dev, int interface)
+{
+  io_iterator_t interface_iterator;
+  io_service_t  usbInterface = IO_OBJECT_NULL;
+  io_return_t result;
+  io_cf_plugin_ref_t *plugInInterface = NULL;
+
+  IOUSBFindInterfaceRequest request;
+
+  struct darwin_dev_handle *device;
+  long score;
+  int current_interface;
+
+  device = dev->impl_info;
+
+  request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+  request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+  request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+  request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+  result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator);
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s",
+		   darwin_error_str(result));
+
+  for ( current_interface=0 ; current_interface <= interface ; current_interface++ ) {
+    usbInterface = IOIteratorNext(interface_iterator);
+    if ( usb_debug > 3 )
+      fprintf ( stderr, "Interface %d of device is 0x%08x\n",
+		current_interface, usbInterface );
+  }
+
+  current_interface--;
+
+  /* the interface iterator is no longer needed, release it */
+  IOObjectRelease(interface_iterator);
+
+  if (!usbInterface) {
+    u_int8_t nConfig;			     /* Index of configuration to use */
+    IOUSBConfigurationDescriptorPtr configDesc; /* to describe which configuration to select */
+    /* Only a composite class device with no vendor-specific driver will
+       be configured. Otherwise, we need to do it ourselves, or there
+       will be no interfaces for the device. */
+
+    if ( usb_debug > 3 )
+      fprintf ( stderr,"claim_interface: No interface found; selecting configuration\n" );
+
+    result = (*(device->device))->GetNumberOfConfigurations ( device->device, &nConfig );
+    if (result != kIOReturnSuccess)
+      USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetNumberOfConfigurations): %s",
+		    darwin_error_str(result));
+    
+    if (nConfig < 1)
+      USB_ERROR_STR(-ENXIO ,"claim_interface(GetNumberOfConfigurations): no configurations");
+    else if ( nConfig > 1 && usb_debug > 0 )
+      fprintf ( stderr, "claim_interface: device has more than one"
+		" configuration, using the first (warning)\n" );
+
+    if ( usb_debug > 3 )
+      fprintf ( stderr, "claim_interface: device has %d configuration%s\n",
+		(int)nConfig, (nConfig>1?"s":"") );
+
+    /* Always use the first configuration */
+    result = (*(device->device))->GetConfigurationDescriptorPtr ( (device->device), 0, &configDesc );
+    if (result != kIOReturnSuccess) {
+      if (device->open == 1) {
+        (*(device->device))->USBDeviceClose ( (device->device) );
+        (*(device->device))->Release ( (device->device) );
+      }
+
+      USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(GetConfigurationDescriptorPtr): %s",
+		    darwin_error_str(result));
+    } else if ( usb_debug > 3 )
+      fprintf ( stderr, "claim_interface: configuration value is %d\n",
+		configDesc->bConfigurationValue );
+
+    if (device->open == 1) {
+      result = (*(device->device))->SetConfiguration ( (device->device), configDesc->bConfigurationValue );
+
+      if (result != kIOReturnSuccess) {
+	(*(device->device))->USBDeviceClose ( (device->device) );
+	(*(device->device))->Release ( (device->device) );
+
+	USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(SetConfiguration): %s",
+		      darwin_error_str(result));
+      }
+
+      dev->config = configDesc->bConfigurationValue;
+    }
+    
+    request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
+    request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
+    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
+
+    /* Now go back and get the chosen interface */
+    result = (*(device->device))->CreateInterfaceIterator(device->device, &request, &interface_iterator);
+    if (result != kIOReturnSuccess)
+      USB_ERROR_STR (-darwin_to_errno(result), "claim_interface(CreateInterfaceIterator): %s",
+		     darwin_error_str(result));
+
+    for (current_interface = 0 ; current_interface <= interface ; current_interface++) {
+      usbInterface = IOIteratorNext(interface_iterator);
+
+      if ( usb_debug > 3 )
+	fprintf ( stderr, "claim_interface: Interface %d of device is 0x%08x\n",
+		  current_interface, usbInterface );
+    }
+    current_interface--;
+
+    /* the interface iterator is no longer needed, release it */
+    IOObjectRelease(interface_iterator);
+
+    if (!usbInterface)
+      USB_ERROR_STR (-ENOENT, "claim_interface: interface iterator returned NULL");
+  }
+
+  result = IOCreatePlugInInterfaceForService(usbInterface,
+					     kIOUSBInterfaceUserClientTypeID,
+					     kIOCFPlugInInterfaceID,
+					     &plugInInterface, &score);
+  /* No longer need the usbInterface object after getting the plug-in */
+  result = IOObjectRelease(usbInterface);
+  if (result || !plugInInterface)
+    USB_ERROR(-ENOENT);
+
+  /* Now create the device interface for the interface */
+  result = (*plugInInterface)->QueryInterface(plugInInterface,
+					      CFUUIDGetUUIDBytes(InterfaceInterfaceID),
+					      (LPVOID) &device->interface);
+
+  /* No longer need the intermediate plug-in */
+  (*plugInInterface)->Stop(plugInInterface);
+  IODestroyPlugInInterface (plugInInterface);
+
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(QueryInterface): %s",
+		  darwin_error_str(result));
+
+  if (!device->interface)
+    USB_ERROR(-EACCES);
+
+  if ( usb_debug > 3 )
+    fprintf ( stderr, "claim_interface: Interface %d of device from QueryInterface is %p\n",
+	      current_interface, device->interface);
+
+  /* claim the interface */
+  result = (*(device->interface))->USBInterfaceOpen(device->interface);
+  if (result)
+    USB_ERROR_STR(-darwin_to_errno(result), "claim_interface(USBInterfaceOpen): %s",
+		  darwin_error_str(result));
+
+  result = get_endpoints (device);
+
+  if (result) {
+    /* this should not happen */
+    usb_release_interface (dev, interface);
+    USB_ERROR_STR ( result, "claim_interface: could not build endpoint table");
+  }
+
+  return 0;
+}
+
+int usb_set_configuration (usb_dev_handle *dev, int configuration)
+{
+  struct darwin_dev_handle *device;
+  io_return_t result;
+  int interface;
+
+  if ( usb_debug > 3 )
+    fprintf ( stderr, "usb_set_configuration: called for config %x\n", configuration );
+
+  if (!dev)
+    USB_ERROR_STR ( -ENXIO, "usb_set_configuration: called with null device\n" );
+
+  if ((device = dev->impl_info) == NULL)
+    USB_ERROR_STR ( -ENOENT, "usb_set_configuration: device not properly initialized" );
+
+  /* Setting configuration will invalidate the interface, so we need
+     to reclaim it. First, dispose of existing interface, if any. */
+  interface = dev->interface;
+
+  if ( device->interface )
+    usb_release_interface(dev, dev->interface);
+
+  result = (*(device->device))->SetConfiguration(device->device, configuration);
+
+  if (result)
+    USB_ERROR_STR(-darwin_to_errno(result), "usb_set_configuration(SetConfiguration): %s",
+		  darwin_error_str(result));
+
+  /* Reclaim interface */
+  if (interface != -1)
+    result = usb_claim_interface (dev, interface);
+
+  dev->config = configuration;
+
+  return result;
+}
+
+int usb_claim_interface(usb_dev_handle *dev, int interface)
+{
+  struct darwin_dev_handle *device = dev->impl_info;
+
+  io_return_t result;
+
+  if ( usb_debug > 3 )
+    fprintf ( stderr, "usb_claim_interface: called for interface %d\n", interface );
+
+  if (!device)
+    USB_ERROR_STR ( -ENOENT, "usb_claim_interface: device is NULL" );
+
+  if (!(device->device))
+    USB_ERROR_STR ( -EINVAL, "usb_claim_interface: device->device is NULL" );
+
+  /* If we have already claimed an interface, release it */
+  if ( device->interface )
+    usb_release_interface(dev, dev->interface);
+
+  result = claim_interface ( dev, interface );
+  if ( result )
+    USB_ERROR_STR ( result, "usb_claim_interface: couldn't claim interface" );
+
+  dev->interface = interface;
+
+  /* interface is claimed and async IO is set up: return 0 */
+  return 0;
+}
+
+int usb_release_interface(usb_dev_handle *dev, int interface)
+{
+  struct darwin_dev_handle *device;
+  io_return_t result;
+
+  if (!dev)
+    USB_ERROR(-ENXIO);
+
+  if ((device = dev->impl_info) == NULL)
+    USB_ERROR(-ENOENT);
+
+  /* interface is not open */
+  if (!device->interface)
+    return 0;
+
+  result = (*(device->interface))->USBInterfaceClose(device->interface);
+
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(USBInterfaceClose): %s",
+		  darwin_error_str(result));
+
+  result = (*(device->interface))->Release(device->interface);
+
+  if (result != kIOReturnSuccess)
+    USB_ERROR_STR(-darwin_to_errno(result), "usb_release_interface(Release): %s",
+		  darwin_error_str(result));
+
+  device->interface = NULL;
+
+  free (device->endpoint_addrs);
+
+  device->num_endpoints  = 0;
+  device->endpoint_addrs = NULL;
+
+  dev->interface = -1;
+  dev->altsetting = -1;
+
+  return 0;
+}
+
+int usb_set_altinterface(usb_dev_handle *dev, int alternate)
+{
+  struct darwin_dev_handle *device;
+  io_return_t result;
+
+  if (!dev)
+    USB_ERROR(-ENXIO);
+
+  if ((device = dev->impl_info) == NULL)
+    USB_ERROR(-ENOENT);
+
+  /* interface is not open */
+  if (!device->interface)
+    USB_ERROR_STR(-EACCES, "usb_set_altinterface: interface used without being claimed");
+
+  result = (*(device->interface))->SetAlternateInterface(device->interface, alternate);
+
+  if (result)
+    USB_ERROR_STR(result, "usb_set_altinterface: could not set alternate interface");
+
+  dev->altsetting = alternate;
+
+  result = get_endpoints (device);
+  if (result) {
+    /* this should not happen */
+    USB_ERROR_STR ( result, "usb_set_altinterface: could not build endpoint table");
+  }
+
+  return 0;
+}
+
+/* simple function that figures out what pipeRef is associated with an endpoint */
+static int ep_to_pipeRef (struct darwin_dev_handle *device, int ep)
+{
+  int i;