new sysfs descriptors format
-contexts
-event handling/threads issue
for 1.1 or future
==================
static int find_dpfp_device(void)
{
- devh = libusb_open_device_with_vid_pid(0x05ba, 0x000a);
+ devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
return devh ? 0 : -EIO;
}
if (r < 0) {
libusb_cancel_transfer(irq_transfer);
while (irq_transfer)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(NULL) < 0)
break;
return r;
}
struct sigaction sigact;
int r = 1;
- r = libusb_init();
+ r = libusb_init(NULL);
if (r < 0) {
fprintf(stderr, "failed to initialise libusb\n");
exit(1);
sigaction(SIGQUIT, &sigact, NULL);
while (!do_exit) {
- r = libusb_handle_events();
+ r = libusb_handle_events(NULL);
if (r < 0)
goto out_deinit;
}
}
while (irq_transfer || img_transfer)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(NULL) < 0)
break;
if (do_exit == 1)
libusb_release_interface(devh, 0);
out:
libusb_close(devh);
- libusb_exit();
+ libusb_exit(NULL);
return r >= 0 ? r : -r;
}
while (!do_exit) {
struct timeval tv = { 1, 0 };
- r = libusb_handle_events_timeout(&tv);
+ r = libusb_handle_events_timeout(NULL, &tv);
if (r < 0) {
request_exit(2);
break;
static int find_dpfp_device(void)
{
- devh = libusb_open_device_with_vid_pid(0x05ba, 0x000a);
+ devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);
return devh ? 0 : -EIO;
}
if (r < 0) {
libusb_cancel_transfer(irq_transfer);
while (irq_transfer)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(NULL) < 0)
break;
return r;
}
struct sigaction sigact;
int r = 1;
- r = libusb_init();
+ r = libusb_init(NULL);
if (r < 0) {
fprintf(stderr, "failed to initialise libusb\n");
exit(1);
}
while (img_transfer || irq_transfer)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(NULL) < 0)
break;
if (do_exit == 1)
libusb_release_interface(devh, 0);
out:
libusb_close(devh);
- libusb_exit();
+ libusb_exit(NULL);
return r >= 0 ? r : -r;
}
int r;
ssize_t cnt;
- r = libusb_init();
+ r = libusb_init(NULL);
if (r < 0)
return r;
- cnt = libusb_get_device_list(&devs);
+ cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
return (int) cnt;
print_devs(devs);
libusb_free_device_list(devs, 1);
- libusb_exit();
+ libusb_exit(NULL);
return 0;
}
#include "libusb.h"
#include "libusbi.h"
-static int usbi_debug = 0;
-static int debug_fixed = 0;
-
#ifdef OS_LINUX
const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
#else
#error "Unsupported OS"
#endif
-static struct list_head usb_devs;
-static pthread_mutex_t usb_devs_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* A list of open handles. Backends are free to traverse this if required. */
-struct list_head usbi_open_devs;
-pthread_mutex_t usbi_open_devs_lock = PTHREAD_MUTEX_INITIALIZER;
+struct libusb_context *usbi_default_context = NULL;
+static pthread_mutex_t default_context_lock = PTHREAD_MUTEX_INITIALIZER;
/**
* \mainpage libusb-1.0 API Reference
*/
/**
+ * \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
// discover devices
libusb_device **list;
libusb_device *found = NULL;
-size_t cnt = libusb_get_device_list(&list);
+size_t cnt = libusb_get_device_list(NULL, &list);
size_t i = 0;
int err = 0;
if (cnt < 0)
/* Allocate a new device with a specific session ID. The returned device has
* a reference count of 1. */
-struct libusb_device *usbi_alloc_device(unsigned long session_id)
+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);
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(&usb_devs_lock);
- list_add(&dev->list, &usb_devs);
- pthread_mutex_unlock(&usb_devs_lock);
+ pthread_mutex_lock(&ctx->usb_devs_lock);
+ list_add(&dev->list, &ctx->usb_devs);
+ pthread_mutex_unlock(&ctx->usb_devs_lock);
return dev;
}
num_configurations = raw_desc[DEVICE_DESC_LENGTH - 1];
if (num_configurations > USB_MAXCONFIG) {
- usbi_err("too many configurations");
+ usbi_err(DEVICE_CTX(dev), "too many configurations");
return LIBUSB_ERROR_IO;
} else if (num_configurations < 1) {
usbi_dbg("no configurations?");
/* 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(unsigned long session_id)
+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(&usb_devs_lock);
- list_for_each_entry(dev, &usb_devs, list)
+ 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(&usb_devs_lock);
+ pthread_mutex_unlock(&ctx->usb_devs_lock);
return ret;
}
* 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_device ***list)
+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(&discdevs);
+ r = usbi_backend->get_device_list(ctx, &discdevs);
if (r < 0) {
len = r;
goto out;
r = libusb_get_active_config_descriptor(dev, &config);
if (r < 0) {
- usbi_err("could not retrieve active config descriptor");
+ usbi_err(DEVICE_CTX(dev),
+ "could not retrieve active config descriptor");
return LIBUSB_ERROR_OTHER;
}
if (usbi_backend->destroy_device)
usbi_backend->destroy_device(dev);
- pthread_mutex_lock(&usb_devs_lock);
+ pthread_mutex_lock(&dev->ctx->usb_devs_lock);
list_del(&dev->list);
- pthread_mutex_unlock(&usb_devs_lock);
+ pthread_mutex_unlock(&dev->ctx->usb_devs_lock);
free(dev);
}
return r;
}
- pthread_mutex_lock(&usbi_open_devs_lock);
- list_add(&_handle->list, &usbi_open_devs);
- pthread_mutex_unlock(&usbi_open_devs_lock);
+ pthread_mutex_lock(&dev->ctx->open_devs_lock);
+ list_add(&_handle->list, &dev->ctx->open_devs);
+ pthread_mutex_unlock(&dev->ctx->open_devs_lock);
*handle = _handle;
return 0;
}
* 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(
- uint16_t vendor_id, uint16_t product_id)
+ libusb_context *ctx, uint16_t vendor_id, uint16_t product_id)
{
struct libusb_device **devs;
struct libusb_device *found = NULL;
size_t i = 0;
int r;
- if (libusb_get_device_list(&devs) < 0)
+ if (libusb_get_device_list(ctx, &devs) < 0)
return NULL;
while ((dev = devs[i++]) != NULL) {
return;
usbi_dbg("");
- pthread_mutex_lock(&usbi_open_devs_lock);
+ pthread_mutex_lock(&HANDLE_CTX(dev_handle)->open_devs_lock);
list_del(&dev_handle->list);
- pthread_mutex_unlock(&usbi_open_devs_lock);
+ pthread_mutex_unlock(&HANDLE_CTX(dev_handle)->open_devs_lock);
do_close(dev_handle);
free(dev_handle);
r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
if (r == 0) {
- usbi_err("zero bytes returned in ctrl transfer?");
+ usbi_err(HANDLE_CTX(dev), "zero bytes returned in ctrl transfer?");
r = LIBUSB_ERROR_IO;
} else if (r == 1) {
r = 0;
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);
+ usbi_dbg("interface %d altsetting %d",
+ interface_number, alternate_setting);
if (interface_number >= sizeof(dev->claimed_interfaces) * 8)
return LIBUSB_ERROR_INVALID_PARAM;
* 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(int level)
+API_EXPORTED void libusb_set_debug(libusb_context *ctx, int level)
{
- if (!debug_fixed)
- usbi_debug = 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(void)
+API_EXPORTED int libusb_init(libusb_context **context)
{
char *dbg = getenv("LIBUSB_DEBUG");
+ struct libusb_context *ctx = malloc(sizeof(*ctx));
+
+ if (!ctx)
+ return LIBUSB_ERROR_NO_MEM;
+ memset(ctx, 0, sizeof(*ctx));
+
if (dbg) {
- usbi_debug = atoi(dbg);
- if (usbi_debug)
- debug_fixed = 1;
+ ctx->debug = atoi(dbg);
+ if (ctx->debug)
+ ctx->debug_fixed = 1;
}
usbi_dbg("");
if (usbi_backend->init) {
- int r = usbi_backend->init();
- if (r)
+ int r = usbi_backend->init(ctx);
+ if (r) {
+ free(ctx);
return r;
+ }
+ }
+
+ 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);
+ usbi_io_init(ctx);
+
+ 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);
- list_init(&usb_devs);
- list_init(&usbi_open_devs);
- usbi_io_init();
+ if (context)
+ *context = ctx;
return 0;
}
/** \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(void)
+API_EXPORTED void libusb_exit(struct libusb_context *ctx)
{
+ USBI_GET_CONTEXT(ctx);
usbi_dbg("");
- pthread_mutex_lock(&usbi_open_devs_lock);
- if (!list_empty(&usbi_open_devs)) {
+ pthread_mutex_lock(&ctx->open_devs_lock);
+ if (!list_empty(&ctx->open_devs)) {
struct libusb_device_handle *devh;
struct libusb_device_handle *tmp;
usbi_dbg("naughty app left some devices open!");
- list_for_each_entry_safe(devh, tmp, &usbi_open_devs, list) {
+ list_for_each_entry_safe(devh, tmp, &ctx->open_devs, list) {
list_del(&devh->list);
do_close(devh);
free(devh);
}
}
- pthread_mutex_unlock(&usbi_open_devs_lock);
+ pthread_mutex_unlock(&ctx->open_devs_lock);
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(enum usbi_log_level level, const char *function,
- const char *format, ...)
+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
- if (!usbi_debug)
+ USBI_GET_CONTEXT(ctx);
+ if (!ctx->debug)
return;
- if (level == LOG_LEVEL_WARNING && usbi_debug < 2)
+ if (level == LOG_LEVEL_WARNING && ctx->debug < 2)
return;
- if (level == LOG_LEVEL_INFO && usbi_debug < 3)
+ if (level == LOG_LEVEL_INFO && ctx->debug < 3)
return;
#endif
free((unsigned char *) endpoint->extra);
}
-static int parse_endpoint(struct libusb_endpoint_descriptor *endpoint,
- unsigned char *buffer, int size, int host_endian)
+static int parse_endpoint(struct libusb_context *ctx,
+ struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
+ int size, int host_endian)
{
struct usb_descriptor_header header;
unsigned char *extra;
/* Everything should be fine being passed into here, but we sanity */
/* check JIC */
if (header.bLength > size) {
- usbi_err("ran out of descriptors parsing");
+ usbi_err(ctx, "ran out of descriptors parsing");
return -1;
}
if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) {
- usbi_err("unexpected descriptor %x (expected %x)",
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
header.bDescriptorType, LIBUSB_DT_ENDPOINT);
return parsed;
}
usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength < 2) {
- usbi_err("invalid descriptor length %d", header.bLength);
+ usbi_err(ctx, "invalid descriptor length %d", header.bLength);
return -1;
}
}
-static int parse_interface(struct libusb_interface *interface,
- unsigned char *buffer, int size, int host_endian)
+static int parse_interface(libusb_context *ctx,
+ struct libusb_interface *interface, unsigned char *buffer, int size,
+ int host_endian)
{
int i;
int len;
while (size >= DESC_HEADER_LENGTH) {
usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength < 2) {
- usbi_err("invalid descriptor of length %d", header.bLength);
+ usbi_err(ctx, "invalid descriptor of length %d",
+ header.bLength);
r = LIBUSB_ERROR_IO;
goto err;
}
return parsed;
if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
- usbi_err("too many endpoints (%d)", ifp->bNumEndpoints);
+ usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints);
r = LIBUSB_ERROR_IO;
goto err;
}
usbi_parse_descriptor(buffer, "bb", &header, 0);
if (header.bLength > size) {
- usbi_err("ran out of descriptors parsing");
+ usbi_err(ctx, "ran out of descriptors parsing");
r = LIBUSB_ERROR_IO;
goto err;
}
- r = parse_endpoint(endpoint + i, buffer, size, host_endian);
+ r = parse_endpoint(ctx, endpoint + i, buffer, size,
+ host_endian);
if (r < 0)
goto err;
free((void *) config->extra);
}
-static int parse_configuration(struct libusb_config_descriptor *config,
- unsigned char *buffer, int host_endian)
+static int parse_configuration(struct libusb_context *ctx,
+ struct libusb_config_descriptor *config, unsigned char *buffer,
+ int host_endian)
{
int i;
int r;
size = config->wTotalLength;
if (config->bNumInterfaces > USB_MAXINTERFACES) {
- usbi_err("too many interfaces (%d)", config->bNumInterfaces);
+ usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces);
return LIBUSB_ERROR_IO;
}
if ((header.bLength > size) ||
(header.bLength < DESC_HEADER_LENGTH)) {
- usbi_err("invalid descriptor length of %d", header.bLength);
+ usbi_err(ctx, "invalid descriptor length of %d",
+ header.bLength);
r = LIBUSB_ERROR_IO;
goto err;
}
}
}
- r = parse_interface(interface + i, buffer, size, host_endian);
+ r = parse_interface(ctx, interface + i, buffer, size, host_endian);
if (r < 0)
goto err;
if (r < 0)
goto err;
- r = parse_configuration(_config, buf, host_endian);
+ r = parse_configuration(dev->ctx, _config, buf, host_endian);
if (r < 0) {
- usbi_err("parse_configuration failed with error %d", r);
+ usbi_err(dev->ctx, "parse_configuration failed with error %d", r);
goto err;
} else if (r > 0) {
- usbi_warn("descriptor data still left");
+ usbi_warn(dev->ctx, "descriptor data still left");
}
*config = _config;
if (r < 0)
goto err;
- r = parse_configuration(_config, buf, host_endian);
+ r = parse_configuration(dev->ctx, _config, buf, host_endian);
if (r < 0) {
- usbi_err("parse_configuration failed with error %d", r);
+ usbi_err(dev->ctx, "parse_configuration failed with error %d", r);
goto err;
} else if (r > 0) {
- usbi_warn("descriptor data still left");
+ usbi_warn(dev->ctx, "descriptor data still left");
}
*config = _config;
#include "libusbi.h"
-/* this is a list of in-flight transfer handles, sorted by timeout expiration.
- * URBs to timeout the soonest are placed at the beginning of the list, URBs
- * that will time out later are placed after, and urbs with infinite timeout
- * are always placed at the very end. */
-static struct list_head flying_transfers;
-static pthread_mutex_t flying_transfers_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* list of poll fd's */
-static struct list_head pollfds;
-static pthread_mutex_t pollfds_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* user callbacks for pollfd changes */
-static libusb_pollfd_added_cb fd_added_cb = NULL;
-static libusb_pollfd_removed_cb fd_removed_cb = NULL;
-
-/* this lock ensures that only one thread is handling events at any one time */
-static pthread_mutex_t events_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* used to see if there is an active thread doing event handling */
-static int event_handler_active = 0;
-
-/* used to wait for event completion in threads other than the one that is
- * event handling */
-static pthread_mutex_t event_waiters_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t event_waiters_cond = PTHREAD_COND_INITIALIZER;
-
/**
* \page io Synchronous and asynchronous device I/O
*
// maybe fire off some initial async I/O
while (user_has_not_requested_exit)
- libusb_handle_events();
+ libusb_handle_events(ctx);
// clean up and exit
\endcode
\code
// initialise libusb
-libusb_get_pollfds()
+libusb_get_pollfds(ctx)
while (user has not requested application exit) {
- libusb_get_next_timeout();
+ libusb_get_next_timeout(ctx);
select(on libusb file descriptors plus any other event sources of interest,
using a timeout no larger than the value libusb just suggested)
if (select() indicated activity on libusb file descriptors)
- libusb_handle_events_timeout(0);
+ libusb_handle_events_timeout(ctx, 0);
if (time has elapsed to or beyond the libusb timeout)
- libusb_handle_events_timeout(0);
+ libusb_handle_events_timeout(ctx, 0);
}
// clean up and exit
while (!completed) {
poll(libusb file descriptors, 120*1000);
if (poll indicates activity)
- libusb_handle_events_timeout(0);
+ libusb_handle_events_timeout(ctx, 0);
}
printf("completed!");
// other code here
* Although the events lock is a critical part of the solution, it is not
* enough on it's own. You might wonder if the following is sufficient...
\code
- libusb_lock_events();
+ libusb_lock_events(ctx);
while (!completed) {
poll(libusb file descriptors, 120*1000);
if (poll indicates activity)
- libusb_handle_events_timeout(0);
+ libusb_handle_events_timeout(ctx, 0);
}
- libusb_lock_events();
+ libusb_lock_events(ctx);
\endcode
* ...and the answer is that it is not. This is because the transfer in the
* code shown above may take a long time (say 30 seconds) to complete, and
* This looks like the following, as pseudo-code:
\code
retry:
-if (libusb_try_lock_events() == 0) {
+if (libusb_try_lock_events(ctx) == 0) {
// we obtained the event lock: do our own event handling
- libusb_lock_events();
+ libusb_lock_events(ctx);
while (!completed) {
poll(libusb file descriptors, 120*1000);
if (poll indicates activity)
- libusb_handle_events_locked(0);
+ libusb_handle_events_locked(ctx, 0);
}
- libusb_unlock_events();
+ libusb_unlock_events(ctx);
} else {
// another thread is doing event handling. wait for it to signal us that
// an event has completed
- libusb_lock_event_waiters();
+ libusb_lock_event_waiters(ctx);
while (!completed) {
// now that we have the event waiters lock, double check that another
// thread is still handling events for us. (it may have ceased handling
// events in the time it took us to reach this point)
- if (!libusb_event_handler_active()) {
+ if (!libusb_event_handler_active(ctx)) {
// whoever was handling events is no longer doing so, try again
- libusb_unlock_event_waiters();
+ libusb_unlock_event_waiters(ctx);
goto retry;
}
- libusb_wait_for_event();
+ libusb_wait_for_event(ctx);
}
- libusb_unlock_event_waiters();
+ libusb_unlock_event_waiters(ctx);
}
printf("completed!\n");
\endcode
* fall back to the "event waiters" mechanism detailed above.
*/
-void usbi_io_init()
+void usbi_io_init(struct libusb_context *ctx)
{
- list_init(&flying_transfers);
- list_init(&pollfds);
- fd_added_cb = NULL;
- fd_removed_cb = NULL;
+ pthread_mutex_init(&ctx->flying_transfers_lock, NULL);
+ pthread_mutex_init(&ctx->pollfds_lock, NULL);
+ pthread_mutex_init(&ctx->events_lock, NULL);
+ pthread_mutex_init(&ctx->event_waiters_lock, NULL);
+ pthread_cond_init(&ctx->event_waiters_cond, NULL);
+ list_init(&ctx->flying_transfers);
+ list_init(&ctx->pollfds);
}
static int calculate_timeout(struct usbi_transfer *transfer)
r = clock_gettime(CLOCK_MONOTONIC, ¤t_time);
if (r < 0) {
- usbi_err("failed to read monotonic clock, errno=%d", errno);
+ usbi_err(ITRANSFER_CTX(transfer),
+ "failed to read monotonic clock, errno=%d", errno);
return r;
}
{
struct usbi_transfer *cur;
struct timeval *timeout = &transfer->timeout;
+ struct libusb_context *ctx = ITRANSFER_CTX(transfer);
- pthread_mutex_lock(&flying_transfers_lock);
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
/* if we have no other flying transfers, start the list with this one */
- if (list_empty(&flying_transfers)) {
- list_add(&transfer->list, &flying_transfers);
+ if (list_empty(&ctx->flying_transfers)) {
+ list_add(&transfer->list, &ctx->flying_transfers);
goto out;
}
/* if we have infinite timeout, append to end of list */
if (!timerisset(timeout)) {
- list_add_tail(&transfer->list, &flying_transfers);
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
goto out;
}
/* otherwise, find appropriate place in list */
- list_for_each_entry(cur, &flying_transfers, list) {
+ list_for_each_entry(cur, &ctx->flying_transfers, list) {
/* find first timeout that occurs after the transfer in question */
struct timeval *cur_tv = &cur->timeout;
}
/* otherwise we need to be inserted at the end */
- list_add_tail(&transfer->list, &flying_transfers);
+ list_add_tail(&transfer->list, &ctx->flying_transfers);
out:
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
}
/** \ingroup asyncio
add_to_flying_list(itransfer);
r = usbi_backend->submit_transfer(itransfer);
if (r) {
- pthread_mutex_lock(&flying_transfers_lock);
+ pthread_mutex_lock(&TRANSFER_CTX(transfer)->flying_transfers_lock);
list_del(&itransfer->list);
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&TRANSFER_CTX(transfer)->flying_transfers_lock);
}
return r;
usbi_dbg("");
r = usbi_backend->cancel_transfer(itransfer);
if (r < 0)
- usbi_err("cancel transfer failed error %d", r);
+ usbi_err(TRANSFER_CTX(transfer),
+ "cancel transfer failed error %d", r);
return r;
}
{
struct libusb_transfer *transfer =
__USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ struct libusb_context *ctx = TRANSFER_CTX(transfer);
uint8_t flags;
- pthread_mutex_lock(&flying_transfers_lock);
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
list_del(&itransfer->list);
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
if (status == LIBUSB_TRANSFER_COMPLETED
&& transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
* this point. */
if (flags & LIBUSB_TRANSFER_FREE_TRANSFER)
libusb_free_transfer(transfer);
- pthread_mutex_lock(&event_waiters_lock);
- pthread_cond_broadcast(&event_waiters_cond);
- pthread_mutex_unlock(&event_waiters_lock);
+ pthread_mutex_lock(&ctx->event_waiters_lock);
+ pthread_cond_broadcast(&ctx->event_waiters_cond);
+ pthread_mutex_unlock(&ctx->event_waiters_lock);
}
/* Similar to usbi_handle_transfer_completion() but exclusively for transfers
* If you are no longer handling events, you must call libusb_unlock_events()
* as soon as possible.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \returns 0 if the lock was obtained successfully
* \returns 1 if the lock was not obtained (i.e. another thread holds the lock)
* \see \ref mtasync
*/
-API_EXPORTED int libusb_try_lock_events(void)
+API_EXPORTED int libusb_try_lock_events(libusb_context *ctx)
{
- int r = pthread_mutex_trylock(&events_lock);
+ int r;
+ USBI_GET_CONTEXT(ctx);
+
+ r = pthread_mutex_trylock(&ctx->events_lock);
if (r)
return 1;
- event_handler_active = 1;
+ ctx->event_handler_active = 1;
return 0;
}
* If you are no longer handling events, you must call libusb_unlock_events()
* as soon as possible.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \see \ref mtasync
*/
-API_EXPORTED void libusb_lock_events(void)
+API_EXPORTED void libusb_lock_events(libusb_context *ctx)
{
- pthread_mutex_lock(&events_lock);
- event_handler_active = 1;
+ USBI_GET_CONTEXT(ctx);
+ pthread_mutex_lock(&ctx->events_lock);
+ ctx->event_handler_active = 1;
}
/** \ingroup poll
* libusb_lock_events(). Releasing this lock will wake up any threads blocked
* on libusb_wait_for_event().
*
+ * \param ctx the context to operate on, or NULL for the default context
* \see \ref mtasync
*/
-API_EXPORTED void libusb_unlock_events(void)
+API_EXPORTED void libusb_unlock_events(libusb_context *ctx)
{
- event_handler_active = 0;
- pthread_mutex_unlock(&events_lock);
+ USBI_GET_CONTEXT(ctx);
+ ctx->event_handler_active = 0;
+ pthread_mutex_unlock(&ctx->events_lock);
- pthread_mutex_lock(&event_waiters_lock);
- pthread_cond_broadcast(&event_waiters_cond);
- pthread_mutex_unlock(&event_waiters_lock);
+ pthread_mutex_lock(&ctx->event_waiters_lock);
+ pthread_cond_broadcast(&ctx->event_waiters_cond);
+ pthread_mutex_unlock(&ctx->event_waiters_lock);
}
/** \ingroup poll
* Determine if an active thread is handling events (i.e. if anyone is holding
* the event handling lock).
*
+ * \param ctx the context to operate on, or NULL for the default context
* \returns 1 if a thread is handling events
* \returns 0 if there are no threads currently handling events
* \see \ref mtasync
*/
-API_EXPORTED int libusb_event_handler_active(void)
+API_EXPORTED int libusb_event_handler_active(libusb_context *ctx)
{
- return event_handler_active;
+ USBI_GET_CONTEXT(ctx);
+ return ctx->event_handler_active;
}
/** \ingroup poll
* libusb_handle_events()) then you do not need to be concerned with this
* locking.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \see \ref mtasync
*/
-API_EXPORTED void libusb_lock_event_waiters(void)
+API_EXPORTED void libusb_lock_event_waiters(libusb_context *ctx)
{
- pthread_mutex_lock(&event_waiters_lock);
+ USBI_GET_CONTEXT(ctx);
+ pthread_mutex_lock(&ctx->event_waiters_lock);
}
/** \ingroup poll
* Release the event waiters lock.
+ * \param ctx the context to operate on, or NULL for the default context
* \see \ref mtasync
*/
-API_EXPORTED void libusb_unlock_event_waiters(void)
+API_EXPORTED void libusb_unlock_event_waiters(libusb_context *ctx)
{
- pthread_mutex_unlock(&event_waiters_lock);
+ USBI_GET_CONTEXT(ctx);
+ pthread_mutex_unlock(&ctx->event_waiters_lock);
}
/** \ingroup poll
* This function releases the event waiters lock before putting your thread
* to sleep, and reacquires the lock as it is being woken up.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \param tv maximum timeout for this blocking function. A NULL value
* indicates unlimited timeout.
* \returns 0 after a transfer completes or another thread stops event handling
* \returns 1 if the timeout expired
* \see \ref mtasync
*/
-API_EXPORTED int libusb_wait_for_event(struct timeval *tv)
+API_EXPORTED int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
{
struct timespec timeout;
int r;
+ USBI_GET_CONTEXT(ctx);
if (tv == NULL) {
- pthread_cond_wait(&event_waiters_cond, &event_waiters_lock);
+ pthread_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
return 0;
}
r = clock_gettime(CLOCK_REALTIME, &timeout);
if (r < 0) {
- usbi_err("failed to read realtime clock, error %d", errno);
+ usbi_err(ctx, "failed to read realtime clock, error %d", errno);
return LIBUSB_ERROR_OTHER;
}
timeout.tv_sec++;
}
- r = pthread_cond_timedwait(&event_waiters_cond, &event_waiters_lock,
- &timeout);
+ r = pthread_cond_timedwait(&ctx->event_waiters_cond,
+ &ctx->event_waiters_lock, &timeout);
return (r == ETIMEDOUT);
}
itransfer->flags |= USBI_TRANSFER_TIMED_OUT;
r = libusb_cancel_transfer(transfer);
if (r < 0)
- usbi_warn("async cancel failed %d errno=%d", r, errno);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "async cancel failed %d errno=%d", r, errno);
}
-static int handle_timeouts(void)
+static int handle_timeouts(struct libusb_context *ctx)
{
struct timespec systime_ts;
struct timeval systime;
struct usbi_transfer *transfer;
int r = 0;
- pthread_mutex_lock(&flying_transfers_lock);
- if (list_empty(&flying_transfers))
+ USBI_GET_CONTEXT(ctx);
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
+ if (list_empty(&ctx->flying_transfers))
goto out;
/* get current time */
/* iterate through flying transfers list, finding all transfers that
* have expired timeouts */
- list_for_each_entry(transfer, &flying_transfers, list) {
+ list_for_each_entry(transfer, &ctx->flying_transfers, list) {
struct timeval *cur_tv = &transfer->timeout;
/* if we've reached transfers of infinite timeout, we're all done */
}
out:
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
return r;
}
/* do the actual event handling. assumes that no other thread is concurrently
* doing the same thing. */
-static int handle_events(struct timeval *tv)
+static int handle_events(struct libusb_context *ctx, struct timeval *tv)
{
int r;
struct usbi_pollfd *ipollfd;
int i = -1;
int timeout_ms;
- pthread_mutex_lock(&pollfds_lock);
- list_for_each_entry(ipollfd, &pollfds, list)
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list)
nfds++;
/* TODO: malloc when number of fd's changes, not on every poll */
if (!fds)
return LIBUSB_ERROR_NO_MEM;
- list_for_each_entry(ipollfd, &pollfds, list) {
+ list_for_each_entry(ipollfd, &ctx->pollfds, list) {
struct libusb_pollfd *pollfd = &ipollfd->pollfd;
int fd = pollfd->fd;
i++;
fds[i].events = pollfd->events;
fds[i].revents = 0;
}
- pthread_mutex_unlock(&pollfds_lock);
+ pthread_mutex_unlock(&ctx->pollfds_lock);
timeout_ms = (tv->tv_sec * 1000) + (tv->tv_usec / 1000);
usbi_dbg("poll() returned %d", r);
if (r == 0) {
free(fds);
- return handle_timeouts();
+ return handle_timeouts(ctx);
} else if (r == -1 && errno == EINTR) {
free(fds);
return LIBUSB_ERROR_INTERRUPTED;
} else if (r < 0) {
free(fds);
- usbi_err("poll failed %d err=%d\n", r, errno);
+ usbi_err(ctx, "poll failed %d err=%d\n", r, errno);
return LIBUSB_ERROR_IO;
}
- r = usbi_backend->handle_events(fds, nfds, r);
+ r = usbi_backend->handle_events(ctx, fds, nfds, r);
if (r)
- usbi_err("backend handle_events failed with error %d", r);
+ usbi_err(ctx, "backend handle_events failed with error %d", r);
free(fds);
return r;
* returns 1 if there is an already-expired timeout, otherwise returns 0
* and populates out
*/
-static int get_next_timeout(struct timeval *tv, struct timeval *out)
+static int get_next_timeout(libusb_context *ctx, struct timeval *tv,
+ struct timeval *out)
{
struct timeval timeout;
- int r = libusb_get_next_timeout(&timeout);
+ int r = libusb_get_next_timeout(ctx, &timeout);
if (r) {
/* timeout already expired? */
if (!timerisset(&timeout))
* timeout. If an event arrives or a signal is raised, this function will
* return early.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \param tv the maximum time to block waiting for events, or zero for
* non-blocking mode
* \returns 0 on success, or a LIBUSB_ERROR code on failure
*/
-API_EXPORTED int libusb_handle_events_timeout(struct timeval *tv)
+API_EXPORTED int libusb_handle_events_timeout(libusb_context *ctx,
+ struct timeval *tv)
{
int r;
struct timeval poll_timeout;
- r = get_next_timeout(tv, &poll_timeout);
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts();
+ return handle_timeouts(ctx);
}
retry:
- if (libusb_try_lock_events() == 0) {
+ if (libusb_try_lock_events(ctx) == 0) {
/* we obtained the event lock: do our own event handling */
- r = handle_events(&poll_timeout);
- libusb_unlock_events();
+ r = handle_events(ctx, &poll_timeout);
+ libusb_unlock_events(ctx);
return r;
}
/* another thread is doing event handling. wait for pthread events that
* notify event completion. */
- libusb_lock_event_waiters();
+ libusb_lock_event_waiters(ctx);
- if (!libusb_event_handler_active()) {
+ if (!libusb_event_handler_active(ctx)) {
/* we hit a race: whoever was event handling earlier finished in the
* time it took us to reach this point. try the cycle again. */
- libusb_unlock_event_waiters();
+ libusb_unlock_event_waiters(ctx);
usbi_dbg("event handler was active but went away, retrying");
goto retry;
}
usbi_dbg("another thread is doing event handling");
- r = libusb_wait_for_event(&poll_timeout);
- libusb_unlock_event_waiters();
+ r = libusb_wait_for_event(ctx, &poll_timeout);
+ libusb_unlock_event_waiters(ctx);
if (r < 0)
return r;
else if (r == 1)
- return handle_timeouts();
+ return handle_timeouts(ctx);
else
return 0;
}
* function is blocking or non-blocking, or the maximum timeout, use
* libusb_handle_events_timeout() instead.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \returns 0 on success, or a LIBUSB_ERROR code on failure
*/
-API_EXPORTED int libusb_handle_events(void)
+API_EXPORTED int libusb_handle_events(libusb_context *ctx)
{
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
- return libusb_handle_events_timeout(&tv);
+ return libusb_handle_events_timeout(ctx, &tv);
}
/** \ingroup poll
* You detect events on libusb's descriptors, so you then call this function
* with a zero timeout value (while still holding the event lock).
*
+ * \param ctx the context to operate on, or NULL for the default context
* \param tv the maximum time to block waiting for events, or zero for
* non-blocking mode
* \returns 0 on success, or a LIBUSB_ERROR code on failure
* \see \ref mtasync
*/
-API_EXPORTED int libusb_handle_events_locked(struct timeval *tv)
+API_EXPORTED int libusb_handle_events_locked(libusb_context *ctx,
+ struct timeval *tv)
{
int r;
struct timeval poll_timeout;
- r = get_next_timeout(tv, &poll_timeout);
+ USBI_GET_CONTEXT(ctx);
+ r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts();
+ return handle_timeouts(ctx);
}
- return handle_events(&poll_timeout);
+ return handle_events(ctx, &poll_timeout);
}
/** \ingroup poll
* so you should call libusb_handle_events_timeout() or similar immediately.
* A return code of 0 indicates that there are no pending timeouts.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \param tv output location for a relative time against the current
* clock in which libusb must be called into in order to process timeout events
* \returns 0 if there are no pending timeouts, 1 if a timeout was returned,
* or LIBUSB_ERROR_OTHER on failure
*/
-API_EXPORTED int libusb_get_next_timeout(struct timeval *tv)
+API_EXPORTED int libusb_get_next_timeout(libusb_context *ctx,
+ struct timeval *tv)
{
struct usbi_transfer *transfer;
struct timespec cur_ts;
int r;
int found = 0;
- pthread_mutex_lock(&flying_transfers_lock);
- if (list_empty(&flying_transfers)) {
- pthread_mutex_unlock(&flying_transfers_lock);
+ USBI_GET_CONTEXT(ctx);
+ pthread_mutex_lock(&ctx->flying_transfers_lock);
+ if (list_empty(&ctx->flying_transfers)) {
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
usbi_dbg("no URBs, no timeout!");
return 0;
}
/* find next transfer which hasn't already been processed as timed out */
- list_for_each_entry(transfer, &flying_transfers, list) {
+ list_for_each_entry(transfer, &ctx->flying_transfers, list) {
if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) {
found = 1;
break;
}
}
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&ctx->flying_transfers_lock);
if (!found) {
usbi_dbg("all URBs have already been processed for timeouts");
r = clock_gettime(CLOCK_MONOTONIC, &cur_ts);
if (r < 0) {
- usbi_err("failed to read monotonic clock, errno=%d", errno);
+ usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
return LIBUSB_ERROR_OTHER;
}
TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);
*
* To remove notifiers, pass NULL values for the function pointers.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \param added_cb pointer to function for addition notifications
* \param removed_cb pointer to function for removal notifications
*/
-API_EXPORTED void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb,
- libusb_pollfd_removed_cb removed_cb)
+API_EXPORTED void libusb_set_pollfd_notifiers(libusb_context *ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb)
{
- fd_added_cb = added_cb;
- fd_removed_cb = removed_cb;
+ USBI_GET_CONTEXT(ctx);
+ ctx->fd_added_cb = added_cb;
+ ctx->fd_removed_cb = removed_cb;
}
/* Add a file descriptor to the list of file descriptors to be monitored.
* events should be specified as a bitmask of events passed to poll(), e.g.
* POLLIN and/or POLLOUT. */
-int usbi_add_pollfd(int fd, short events)
+int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
{
struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
if (!ipollfd)
usbi_dbg("add fd %d events %d", fd, events);
ipollfd->pollfd.fd = fd;
ipollfd->pollfd.events = events;
- pthread_mutex_lock(&pollfds_lock);
- list_add(&ipollfd->list, &pollfds);
- pthread_mutex_unlock(&pollfds_lock);
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ list_add(&ipollfd->list, &ctx->pollfds);
+ pthread_mutex_unlock(&ctx->pollfds_lock);
- if (fd_added_cb)
- fd_added_cb(fd, events);
+ if (ctx->fd_added_cb)
+ ctx->fd_added_cb(fd, events);
return 0;
}
/* Remove a file descriptor from the list of file descriptors to be polled. */
-void usbi_remove_pollfd(int fd)
+void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
{
struct usbi_pollfd *ipollfd;
int found = 0;
usbi_dbg("remove fd %d", fd);
- pthread_mutex_lock(&pollfds_lock);
- list_for_each_entry(ipollfd, &pollfds, list)
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list)
if (ipollfd->pollfd.fd == fd) {
found = 1;
break;
if (!found) {
usbi_dbg("couldn't find fd %d to remove", fd);
- pthread_mutex_unlock(&pollfds_lock);
+ pthread_mutex_unlock(&ctx->pollfds_lock);
return;
}
list_del(&ipollfd->list);
- pthread_mutex_unlock(&pollfds_lock);
+ pthread_mutex_unlock(&ctx->pollfds_lock);
free(ipollfd);
- if (fd_removed_cb)
- fd_removed_cb(fd);
+ if (ctx->fd_removed_cb)
+ ctx->fd_removed_cb(fd);
}
/** \ingroup poll
* The returned list is NULL-terminated and should be freed with free() when
* done. The actual list contents must not be touched.
*
+ * \param ctx the context to operate on, or NULL for the default context
* \returns a NULL-terminated list of libusb_pollfd structures, or NULL on
* error
*/
-API_EXPORTED const struct libusb_pollfd **libusb_get_pollfds(void)
+API_EXPORTED const struct libusb_pollfd **libusb_get_pollfds(
+ libusb_context *ctx)
{
struct libusb_pollfd **ret = NULL;
struct usbi_pollfd *ipollfd;
size_t i = 0;
size_t cnt = 0;
- pthread_mutex_lock(&pollfds_lock);
- list_for_each_entry(ipollfd, &pollfds, list)
+ pthread_mutex_lock(&ctx->pollfds_lock);
+ list_for_each_entry(ipollfd, &ctx->pollfds, list)
cnt++;
ret = calloc(cnt + 1, sizeof(struct libusb_pollfd *));
if (!ret)
goto out;
- list_for_each_entry(ipollfd, &pollfds, list)
+ list_for_each_entry(ipollfd, &ctx->pollfds, list)
ret[i++] = (struct libusb_pollfd *) ipollfd;
ret[cnt] = NULL;
out:
- pthread_mutex_unlock(&pollfds_lock);
+ pthread_mutex_unlock(&ctx->pollfds_lock);
return (const struct libusb_pollfd **) ret;
}
*/
while (1) {
- pthread_mutex_lock(&flying_transfers_lock);
+ pthread_mutex_lock(&HANDLE_CTX(handle)->flying_transfers_lock);
to_cancel = NULL;
- list_for_each_entry(cur, &flying_transfers, list)
+ list_for_each_entry(cur, &HANDLE_CTX(handle)->flying_transfers, list)
if (__USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == handle) {
to_cancel = cur;
break;
}
- pthread_mutex_unlock(&flying_transfers_lock);
+ pthread_mutex_unlock(&HANDLE_CTX(handle)->flying_transfers_lock);
if (!to_cancel)
break;
/* libusb */
+struct libusb_context;
struct libusb_device;
struct libusb_device_handle;
+/** \ingroup lib
+ * Structure representing a libusb session. The concept of individual libusb
+ * sessions allows for your program to use two libraries (or dynamically
+ * load two modules) which both independently use libusb. This will prevent
+ * interference between the individual libusb users - for example
+ * libusb_set_debug() will not affect the other user of the library, and
+ * libusb_exit() will not destroy resources that the other user is still
+ * using.
+ *
+ * Sessions are created by libusb_init() and destroyed through libusb_exit().
+ * If your application is guaranteed to only ever include a single libusb
+ * user (i.e. you), you do not have to worry about contexts: pass NULL in
+ * every function call where a context is required. The default context
+ * will be used.
+ *
+ * For more information, see \ref contexts.
+ */
+typedef struct libusb_context libusb_context;
+
/** \ingroup dev
* Structure representing a USB device detected on the system. This is an
* opaque type for which you are only ever provided with a pointer, usually
struct libusb_iso_packet_descriptor iso_packet_desc[0];
};
-int libusb_init(void);
-void libusb_exit(void);
-void libusb_set_debug(int level);
+int libusb_init(libusb_context **ctx);
+void libusb_exit(libusb_context *ctx);
+void libusb_set_debug(libusb_context *ctx, int level);
-ssize_t libusb_get_device_list(libusb_device ***list);
+ssize_t libusb_get_device_list(libusb_context *ctx,
+ libusb_device ***list);
void libusb_free_device_list(libusb_device **list, int unref_devices);
libusb_device *libusb_ref_device(libusb_device *dev);
void libusb_unref_device(libusb_device *dev);
int libusb_claim_interface(libusb_device_handle *dev, int iface);
int libusb_release_interface(libusb_device_handle *dev, int iface);
-libusb_device_handle *libusb_open_device_with_vid_pid(uint16_t vendor_id,
- uint16_t product_id);
+libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context *ctx,
+ uint16_t vendor_id, uint16_t product_id);
int libusb_set_interface_alt_setting(libusb_device_handle *dev,
int interface_number, int alternate_setting);
/* polling and timeouts */
-int libusb_try_lock_events(void);
-void libusb_lock_events(void);
-void libusb_unlock_events(void);
-int libusb_event_handler_active(void);
-void libusb_lock_event_waiters(void);
-void libusb_unlock_event_waiters(void);
-int libusb_wait_for_event(struct timeval *tv);
+int libusb_try_lock_events(libusb_context *ctx);
+void libusb_lock_events(libusb_context *ctx);
+void libusb_unlock_events(libusb_context *ctx);
+int libusb_event_handler_active(libusb_context *ctx);
+void libusb_lock_event_waiters(libusb_context *ctx);
+void libusb_unlock_event_waiters(libusb_context *ctx);
+int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv);
-int libusb_handle_events_timeout(struct timeval *tv);
-int libusb_handle_events(void);
-int libusb_handle_events_locked(struct timeval *tv);
-int libusb_get_next_timeout(struct timeval *tv);
+int libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv);
+int libusb_handle_events(libusb_context *ctx);
+int libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv);
+int libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv);
/** \ingroup poll
* File descriptor for polling
*/
typedef void (*libusb_pollfd_removed_cb)(int fd);
-const struct libusb_pollfd **libusb_get_pollfds(void);
-void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb,
- libusb_pollfd_removed_cb removed_cb);
+const struct libusb_pollfd **libusb_get_pollfds(libusb_context *ctx);
+void libusb_set_pollfd_notifiers(libusb_context *ctx,
+ libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb);
#ifdef __cplusplus
}
LOG_LEVEL_ERROR,
};
-void usbi_log(enum usbi_log_level, const char *function, const char *format, ...);
+void usbi_log(struct libusb_context *ctx, enum usbi_log_level,
+ const char *function, const char *format, ...);
#ifdef ENABLE_LOGGING
-#define _usbi_log(level, fmt...) usbi_log(level, __FUNCTION__, fmt)
+#define _usbi_log(ctx, level, fmt...) usbi_log(ctx, level, __FUNCTION__, fmt)
#else
-#define _usbi_log(level, fmt...)
+#define _usbi_log(ctx, level, fmt...)
#endif
#ifdef ENABLE_DEBUG_LOGGING
-#define usbi_dbg(fmt...) _usbi_log(LOG_LEVEL_DEBUG, fmt)
+#define usbi_dbg(fmt...) _usbi_log(NULL, LOG_LEVEL_DEBUG, fmt)
#else
#define usbi_dbg(fmt...)
#endif
-#define usbi_info(fmt...) _usbi_log(LOG_LEVEL_INFO, fmt)
-#define usbi_warn(fmt...) _usbi_log(LOG_LEVEL_WARNING, fmt)
-#define usbi_err(fmt...) _usbi_log(LOG_LEVEL_ERROR, fmt)
+#define usbi_info(ctx, fmt...) _usbi_log(ctx, LOG_LEVEL_INFO, fmt)
+#define usbi_warn(ctx, fmt...) _usbi_log(ctx, LOG_LEVEL_WARNING, fmt)
+#define usbi_err(ctx, fmt...) _usbi_log(ctx, LOG_LEVEL_ERROR, fmt)
+
+#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context
+#define DEVICE_CTX(dev) ((dev)->ctx)
+#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
+#define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle))
+#define ITRANSFER_CTX(transfer) \
+ (TRANSFER_CTX(__USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)))
+
+extern struct libusb_context *usbi_default_context;
+
+struct libusb_context {
+ int debug;
+ int debug_fixed;
+
+ struct list_head usb_devs;
+ pthread_mutex_t usb_devs_lock;
+
+ /* A list of open handles. Backends are free to traverse this if required.
+ */
+ struct list_head open_devs;
+ pthread_mutex_t open_devs_lock;
+
+ /* this is a list of in-flight transfer handles, sorted by timeout
+ * expiration. URBs to timeout the soonest are placed at the beginning of
+ * the list, URBs that will time out later are placed after, and urbs with
+ * infinite timeout are always placed at the very end. */
+ struct list_head flying_transfers;
+ pthread_mutex_t flying_transfers_lock;
+
+ /* list of poll fd's */
+ struct list_head pollfds;
+ pthread_mutex_t pollfds_lock;
+
+ /* user callbacks for pollfd changes */
+ libusb_pollfd_added_cb fd_added_cb;
+ libusb_pollfd_removed_cb fd_removed_cb;
+
+ /* ensures that only one thread is handling events at any one time */
+ pthread_mutex_t events_lock;
+
+ /* used to see if there is an active thread doing event handling */
+ int event_handler_active;
+
+ /* used to wait for event completion in threads other than the one that is
+ * event handling */
+ pthread_mutex_t event_waiters_lock;
+ pthread_cond_t event_waiters_cond;
+};
struct libusb_device {
/* lock protects refcnt, everything else is finalized at initialization
pthread_mutex_t lock;
int refcnt;
+ struct libusb_context *ctx;
+
uint8_t bus_number;
uint8_t device_address;
uint8_t num_configurations;
/* shared data and functions */
-extern struct list_head usbi_open_devs;
-extern pthread_mutex_t usbi_open_devs_lock;
-
-void usbi_io_init(void);
+void usbi_io_init(struct libusb_context *ctx);
-struct libusb_device *usbi_alloc_device(unsigned long session_id);
-struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id);
+struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
+ unsigned long session_id);
+struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
+ unsigned long session_id);
int usbi_sanitize_device(struct libusb_device *dev);
void usbi_handle_disconnect(struct libusb_device_handle *handle);
struct list_head list;
};
-int usbi_add_pollfd(int fd, short events);
-void usbi_remove_pollfd(int fd);
+int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events);
+void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
/* device discovery */
*
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
- int (*init)(void);
+ int (*init)(struct libusb_context *ctx);
/* Deinitialization. Optional. This function should destroy anything
* that was set up by init.
*
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
- int (*get_device_list)(struct discovered_devs **discdevs);
+ int (*get_device_list)(struct libusb_context *ctx,
+ struct discovered_devs **discdevs);
/* Open a device for I/O and other USB operations. The device handle
* is preallocated for you, you can retrieve the device in question
*
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
- int (*handle_events)(struct pollfd *fds, nfds_t nfds, int num_ready);
+ int (*handle_events)(struct libusb_context *ctx,
+ struct pollfd *fds, nfds_t nfds, int num_ready);
/* Number of bytes to reserve for per-device private backend data.
* This private data area is accessible through the "os_priv" field of
return ret;
}
-static int op_init(void)
+static int op_init(struct libusb_context *ctx)
{
struct stat statbuf;
int r;
usbfs_path = find_usbfs_path();
if (!usbfs_path) {
- usbi_err("could not find usbfs");
+ usbi_err(ctx, "could not find usbfs");
return LIBUSB_ERROR_OTHER;
}
SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
fd = open(filename, O_RDONLY);
if (fd < 0) {
- usbi_err("open %s failed ret=%d errno=%d", filename, fd, errno);
+ usbi_err(DEVICE_CTX(dev),
+ "open %s failed ret=%d errno=%d", filename, fd, errno);
return LIBUSB_ERROR_IO;
}
r = read(fd, buffer, DEVICE_DESC_LENGTH);;
close(fd);
if (r < 0) {
- usbi_err("read failed, ret=%d errno=%d", fd, errno);
+ usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno);
return LIBUSB_ERROR_IO;
} else if (r < DEVICE_DESC_LENGTH) {
- usbi_err("short read %d/%d", r, DEVICE_DESC_LENGTH);
+ usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, DEVICE_DESC_LENGTH);
return LIBUSB_ERROR_IO;
}
off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
if (off < 0) {
- usbi_err("seek failed, ret=%d errno=%d", off, errno);
+ usbi_err(DEVICE_CTX(dev), "seek failed, ret=%d errno=%d", off, errno);
close(fd);
return LIBUSB_ERROR_IO;
}
r = read(fd, buffer, len);
close(fd);
if (r < 0) {
- usbi_err("read failed, ret=%d errno=%d", fd, errno);
+ usbi_err(DEVICE_CTX(dev), "read failed, ret=%d errno=%d", fd, errno);
return LIBUSB_ERROR_IO;
} else if (r == 0) {
usbi_dbg("device is unconfigured");
return LIBUSB_ERROR_NOT_FOUND;
} else if (r < len) {
- usbi_err("short read %d/%d", r, len);
+ usbi_err(DEVICE_CTX(dev), "short read %d/%d", r, len);
return LIBUSB_ERROR_IO;
}
/* takes a usbfs fd, attempts to find the requested config and copy a certain
* amount of it into an output buffer. a bConfigurationValue of -1 indicates
* that the first config should be retreived. */
-static int get_config_descriptor(int fd, uint8_t config_index,
- unsigned char *buffer, size_t len)
+static int get_config_descriptor(struct libusb_context *ctx, int fd,
+ uint8_t config_index, unsigned char *buffer, size_t len)
{
unsigned char tmp[8];
off_t off;
off = lseek(fd, DEVICE_DESC_LENGTH, SEEK_SET);
if (off < 0) {
- usbi_err("seek failed ret=%d errno=%d", off, errno);
+ usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
return LIBUSB_ERROR_IO;
}
/* read first 8 bytes of descriptor */
r = read(fd, tmp, sizeof(tmp));
if (r < 0) {
- usbi_err("read failed ret=%d errno=%d", r, errno);
+ usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
} else if (r < sizeof(tmp)) {
- usbi_err("short descriptor read %d/%d", r, sizeof(tmp));
+ usbi_err(ctx, "short descriptor read %d/%d", r, sizeof(tmp));
return LIBUSB_ERROR_IO;
}
/* seek forward to end of config */
off = lseek(fd, config.wTotalLength - sizeof(tmp), SEEK_CUR);
if (off < 0) {
- usbi_err("seek failed ret=%d errno=%d", off, errno);
+ usbi_err(ctx, "seek failed ret=%d errno=%d", off, errno);
return LIBUSB_ERROR_IO;
}
/* read the rest of the descriptor */
r = read(fd, buffer, len);
if (r < 0) {
- usbi_err("read failed ret=%d errno=%d", r, errno);
+ usbi_err(ctx, "read failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
} else if (r < len) {
- usbi_err("short output read %d/%d", r, len);
+ usbi_err(ctx, "short output read %d/%d", r, len);
return LIBUSB_ERROR_IO;
}
__get_usbfs_path(dev, filename);
fd = open(filename, O_RDONLY);
if (fd < 0) {
- usbi_err("open '%s' failed, ret=%d errno=%d", filename, fd, errno);
+ usbi_err(DEVICE_CTX(dev),
+ "open '%s' failed, ret=%d errno=%d", filename, fd, errno);
return LIBUSB_ERROR_IO;
}
- r = get_config_descriptor(fd, config_index, buffer, len);
+ r = get_config_descriptor(DEVICE_CTX(dev), fd, config_index, buffer, len);
close(fd);
*host_endian = 1;
return r;
return LIBUSB_ERROR_NOT_FOUND;
}
- r = get_config_descriptor(fd, idx, tmp, sizeof(tmp));
+ r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, tmp, sizeof(tmp));
if (r < 0) {
- usbi_err("first read error %d", r);
+ usbi_err(DEVICE_CTX(dev), "first read error %d", r);
return r;
}
if (!buf)
return LIBUSB_ERROR_NO_MEM;
- r = get_config_descriptor(fd, idx, buf, config.wTotalLength);
+ r = get_config_descriptor(DEVICE_CTX(dev), fd, idx, buf,
+ config.wTotalLength);
if (r < 0) {
free(buf);
return r;
r = read(fd, tmp, sizeof(tmp));
close(fd);
if (r < 0) {
- usbi_err("read bConfigurationValue failed ret=%d errno=%d",
- r, errno);
+ usbi_err(DEVICE_CTX(dev),
+ "read bConfigurationValue failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
} else if (r == 0) {
- usbi_err("device unconfigured");
+ usbi_err(DEVICE_CTX(dev), "device unconfigured");
*config = -1;
return 0;
}
if (tmp[sizeof(tmp) - 1] != 0) {
- usbi_err("not null-terminated?");
+ usbi_err(DEVICE_CTX(dev), "not null-terminated?");
return LIBUSB_ERROR_IO;
} else if (tmp[0] == 0) {
- usbi_err("no configuration value?");
+ usbi_err(DEVICE_CTX(dev), "no configuration value?");
return LIBUSB_ERROR_IO;
}
num = strtol(tmp, &endptr, 10);
if (endptr == tmp) {
- usbi_err("error converting '%s' to integer", tmp);
+ usbi_err(DEVICE_CTX(dev), "error converting '%s' to integer", tmp);
return LIBUSB_ERROR_IO;
}
if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("get_configuration failed ret=%d errno=%d", r, errno);
+ usbi_err(DEVICE_CTX(dev),
+ "get_configuration failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
}
if (fd < 0) {
- usbi_err("open failed, ret=%d errno=%d", fd, errno);
+ usbi_err(DEVICE_CTX(dev), "open failed, ret=%d errno=%d", fd, errno);
return LIBUSB_ERROR_IO;
}
/* if we only have read-only access to the device, we cannot
* send a control message to determine the active config. just
* assume the first one is active. */
- usbi_warn("access to %s is read-only; cannot determine "
- "active configuration descriptor", path);
+ usbi_warn(DEVICE_CTX(dev), "access to %s is read-only; cannot "
+ "determine active configuration descriptor", path);
} else {
active_config = usbfs_get_active_config(dev, fd);
if (active_config < 0) {
r = read(fd, dev_buf, DEVICE_DESC_LENGTH);
if (r < 0) {
- usbi_err("read descriptor failed ret=%d errno=%d", fd, errno);
+ usbi_err(DEVICE_CTX(dev),
+ "read descriptor failed ret=%d errno=%d", fd, errno);
free(dev_buf);
close(fd);
return LIBUSB_ERROR_IO;
} else if (r < DEVICE_DESC_LENGTH) {
- usbi_err("short descriptor read (%d)", r);
+ usbi_err(DEVICE_CTX(dev), "short descriptor read (%d)", r);
free(dev_buf);
close(fd);
return LIBUSB_ERROR_IO;
return 0;
}
-static int enumerate_device(struct discovered_devs **_discdevs,
- uint8_t busnum, uint8_t devaddr, const char *sysfs_dir)
+static int enumerate_device(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr,
+ const char *sysfs_dir)
{
struct discovered_devs *discdevs;
unsigned long session_id;
usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
session_id);
- dev = usbi_get_device_by_session_id(session_id);
+ dev = usbi_get_device_by_session_id(ctx, session_id);
if (dev) {
usbi_dbg("using existing device for %d/%d (session %ld)",
busnum, devaddr, session_id);
} else {
usbi_dbg("allocating new device for %d/%d (session %ld)",
busnum, devaddr, session_id);
- dev = usbi_alloc_device(session_id);
+ dev = usbi_alloc_device(ctx, session_id);
if (!dev)
return LIBUSB_ERROR_NO_MEM;
need_unref = 1;
* failure (non-zero return) the pre-existing discdevs should be destroyed
* (and devices freed). on success, the new discdevs pointer should be used
* as it may have been moved. */
-static int usbfs_scan_busdir(struct discovered_devs **_discdevs, uint8_t busnum)
+static int usbfs_scan_busdir(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs, uint8_t busnum)
{
DIR *dir;
char dirpath[PATH_MAX + 1];
usbi_dbg("%s", dirpath);
dir = opendir(dirpath);
if (!dir) {
- usbi_err("opendir '%s' failed, errno=%d", dirpath, errno);
+ usbi_err(ctx, "opendir '%s' failed, errno=%d", dirpath, errno);
/* FIXME: should handle valid race conditions like hub unplugged
* during directory iteration - this is not an error */
return LIBUSB_ERROR_IO;
continue;
}
- r = enumerate_device(&discdevs, busnum, (uint8_t) devaddr, NULL);
+ r = enumerate_device(ctx, &discdevs, busnum, (uint8_t) devaddr, NULL);
if (r < 0)
goto out;
}
return r;
}
-static int usbfs_get_device_list(struct discovered_devs **_discdevs)
+static int usbfs_get_device_list(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs)
{
struct dirent *entry;
DIR *buses = opendir(usbfs_path);
int r = 0;
if (!buses) {
- usbi_err("opendir buses failed errno=%d", errno);
+ usbi_err(ctx, "opendir buses failed errno=%d", errno);
return LIBUSB_ERROR_IO;
}
continue;
}
- r = usbfs_scan_busdir(&discdevs_new, busnum);
+ r = usbfs_scan_busdir(ctx, &discdevs_new, busnum);
if (r < 0)
goto out;
discdevs = discdevs_new;
}
-static int sysfs_scan_device(struct discovered_devs **_discdevs,
- const char *devname, int *usbfs_fallback)
+static int sysfs_scan_device(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs, const char *devname,
+ int *usbfs_fallback)
{
int r;
FILE *fd;
*usbfs_fallback = 1;
return LIBUSB_ERROR_OTHER;
}
- usbi_err("open busnum failed, errno=%d", errno);
+ usbi_err(ctx, "open busnum failed, errno=%d", errno);
return LIBUSB_ERROR_IO;
}
r = fscanf(fd, "%d", &busnum);
fclose(fd);
if (r != 1) {
- usbi_err("fscanf busnum returned %d, errno=%d", r, errno);
+ usbi_err(ctx, "fscanf busnum returned %d, errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
snprintf(filename, PATH_MAX, "%s/%s/devnum", SYSFS_DEVICE_PATH, devname);
fd = fopen(filename, "r");
if (!fd) {
- usbi_err("open devnum failed, errno=%d", errno);
+ usbi_err(ctx, "open devnum failed, errno=%d", errno);
return LIBUSB_ERROR_IO;
}
r = fscanf(fd, "%d", &devaddr);
fclose(fd);
if (r != 1) {
- usbi_err("fscanf devnum returned %d, errno=%d", r, errno);
+ usbi_err(ctx, "fscanf devnum returned %d, errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
if (busnum > 255 || devaddr > 255)
return LIBUSB_ERROR_INVALID_PARAM;
- return enumerate_device(_discdevs, busnum & 0xff, devaddr & 0xff, devname);
+ return enumerate_device(ctx, _discdevs, busnum & 0xff, devaddr & 0xff,
+ devname);
}
-static int sysfs_get_device_list(struct discovered_devs **_discdevs,
- int *usbfs_fallback)
+static int sysfs_get_device_list(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs, int *usbfs_fallback)
{
struct discovered_devs *discdevs = *_discdevs;
DIR *devices = opendir(SYSFS_DEVICE_PATH);
int r = 0;
if (!devices) {
- usbi_err("opendir devices failed errno=%d", errno);
+ usbi_err(ctx, "opendir devices failed errno=%d", errno);
return LIBUSB_ERROR_IO;
}
|| strchr(entry->d_name, ':'))
continue;
- r = sysfs_scan_device(&discdevs_new, entry->d_name, usbfs_fallback);
+ r = sysfs_scan_device(ctx, &discdevs_new, entry->d_name,
+ usbfs_fallback);
if (r < 0)
goto out;
discdevs = discdevs_new;
return r;
}
-static int op_get_device_list(struct discovered_devs **_discdevs)
+static int op_get_device_list(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs)
{
/* we can retrieve device list and descriptors from sysfs or usbfs.
* sysfs is preferable, because if we use usbfs we end up resuming
*/
if (sysfs_can_relate_devices != 0) {
int usbfs_fallback = 0;
- int r = sysfs_get_device_list(_discdevs, &usbfs_fallback);
+ int r = sysfs_get_device_list(ctx, _discdevs, &usbfs_fallback);
if (!usbfs_fallback)
return r;
}
- return usbfs_get_device_list(_discdevs);
+ return usbfs_get_device_list(ctx, _discdevs);
}
static int op_open(struct libusb_device_handle *handle)
} else if (errno == ENOENT) {
return LIBUSB_ERROR_NO_DEVICE;
} else {
- usbi_err("open failed, code %d errno %d", hpriv->fd, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "open failed, code %d errno %d", hpriv->fd, errno);
return LIBUSB_ERROR_IO;
}
}
- return usbi_add_pollfd(hpriv->fd, POLLOUT);
+ return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
}
static void op_close(struct libusb_device_handle *dev_handle)
{
int fd = __device_handle_priv(dev_handle)->fd;
- usbi_remove_pollfd(fd);
+ usbi_remove_pollfd(HANDLE_CTX(dev_handle), fd);
close(fd);
}
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("failed, error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle), "failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
} else {
r = cache_active_config(handle->dev, fd, config);
if (r < 0)
- usbi_warn("failed to update cached config descriptor, "
- "error %d", r);
+ usbi_warn(HANDLE_CTX(handle),
+ "failed to update cached config descriptor, error %d", r);
}
}
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("claim interface failed, error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "claim interface failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
return 0;
if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("release interface failed, error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "release interface failed, error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
return 0;
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("setintf failed error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "setintf failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("clear_halt failed error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "clear_halt failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
if (errno == ENODEV)
return LIBUSB_ERROR_NOT_FOUND;
- usbi_err("reset failed error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "reset failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("get driver failed error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "get driver failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
else if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("detach failed error %d errno %d", r, errno);
+ usbi_err(HANDLE_CTX(handle),
+ "detach failed error %d errno %d", r, errno);
return LIBUSB_ERROR_OTHER;
}
if (errno == ENODEV) {
r = LIBUSB_ERROR_NO_DEVICE;
} else {
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
r = LIBUSB_ERROR_IO;
}
else if (errno == EINVAL)
tpriv->awaiting_reap++;
else
- usbi_warn("unrecognised discard return %d", tmp);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard return %d", tmp);
}
usbi_dbg("reporting successful submission but waiting for %d "
if (errno == ENODEV) {
r = LIBUSB_ERROR_NO_DEVICE;
} else {
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
r = LIBUSB_ERROR_IO;
}
else if (errno == EINVAL)
tpriv->awaiting_reap++;
else
- usbi_warn("unrecognised discard return %d", tmp);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard return %d", tmp);
}
usbi_dbg("reporting successful submission but waiting for %d "
if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("submiturb failed error %d errno=%d", r, errno);
+ usbi_err(TRANSFER_CTX(transfer),
+ "submiturb failed error %d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
}
return 0;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
return submit_iso_transfer(itransfer);
default:
- usbi_err("unknown endpoint type %d", transfer->type);
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
}
usbi_dbg("URB not found --> assuming ready to be reaped");
return 0;
} else {
- usbi_err("unrecognised DISCARD code %d", errno);
+ usbi_err(TRANSFER_CTX(transfer),
+ "unrecognised DISCARD code %d", errno);
return LIBUSB_ERROR_OTHER;
}
}
else if (errno == EINVAL)
tpriv->awaiting_reap++;
else
- usbi_warn("unrecognised discard return %d", errno);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard return %d", errno);
}
}
else if (errno == EINVAL)
tpriv->awaiting_reap++;
else
- usbi_warn("unrecognised discard return %d", errno);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised discard return %d", errno);
}
}
cancel_iso_transfer(itransfer);
return 0;
default:
- usbi_err("unknown endpoint type %d", transfer->type);
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM;
}
}
free_iso_urbs(tpriv);
break;
default:
- usbi_err("unknown endpoint type %d", transfer->type);
+ usbi_err(TRANSFER_CTX(transfer),
+ "unknown endpoint type %d", transfer->type);
}
}
if (urb->status == -ENOENT) {
usbi_dbg("CANCEL: detected a cancelled URB");
if (tpriv->awaiting_discard == 0)
- usbi_err("CANCEL: cancelled URB but not awaiting discards?");
+ usbi_err(ITRANSFER_CTX(itransfer),
+ "CANCEL: cancelled URB but not awaiting discards?");
else
tpriv->awaiting_discard--;
} else if (urb->status == 0) {
/* FIXME we could solve this extreme corner case with a memmove
* or something */
if (tpriv->reap_action == COMPLETED_EARLY)
- usbi_warn("SOME DATA LOST! (completed early but remaining "
- "urb completed)");
+ usbi_warn(ITRANSFER_CTX(itransfer), "SOME DATA LOST! "
+ "(completed early but remaining urb completed)");
if (tpriv->awaiting_reap == 0)
- usbi_err("CANCEL: completed URB not awaiting reap?");
+ usbi_err(ITRANSFER_CTX(itransfer),
+ "CANCEL: completed URB not awaiting reap?");
else
tpriv->awaiting_reap--;
} else if (urb->status == -EPIPE || urb->status == -EOVERFLOW) {
if (tpriv->awaiting_reap == 0)
- usbi_err("CANCEL: completed URB not awaiting reap?");
+ usbi_err(ITRANSFER_CTX(itransfer),
+ "CANCEL: completed URB not awaiting reap?");
else
tpriv->awaiting_reap--;
} else {
- usbi_warn("unhandled CANCEL urb status %d", urb->status);
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unhandled CANCEL urb status %d", urb->status);
}
if (tpriv->awaiting_reap == 0 && tpriv->awaiting_discard == 0) {
status = LIBUSB_TRANSFER_OVERFLOW;
goto out;
} else if (urb->status != 0) {
- usbi_warn("unrecognised urb status %d", urb->status);
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
}
/* if we're the last urb or we got less data than requested then we're
__device_handle_priv(transfer->dev_handle);
int i;
- usbi_dbg("short transfer %d/%d --> complete!",
- urb->actual_length, urb->buffer_length);
+ usbi_dbg("short transfer %d/%d --> complete!", urb->actual_length,
+ urb->buffer_length);
/* we have to cancel the remaining urbs and wait for their completion
* before reporting results */
else if (errno == EINVAL)
tpriv->awaiting_reap++;
else
- usbi_warn("unrecognised discard return %d", errno);
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised discard return %d", errno);
}
return 0;
} else {
}
}
if (urb_idx == 0) {
- usbi_err("could not locate urb!");
+ usbi_err(TRANSFER_CTX(transfer), "could not locate urb!");
return LIBUSB_ERROR_NOT_FOUND;
}
if (urb->status == -ENOENT) {
usbi_dbg("CANCEL: detected a cancelled URB");
if (tpriv->awaiting_discard == 0)
- usbi_err("CANCEL: cancelled URB but not awaiting discards?");
+ usbi_err(TRANSFER_CTX(transfer),
+ "CANCEL: cancelled URB but not awaiting discards?");
else
tpriv->awaiting_discard--;
} else if (urb->status == 0) {
usbi_dbg("CANCEL: detected a completed URB");
if (tpriv->awaiting_reap == 0)
- usbi_err("CANCEL: completed URB not awaiting reap?");
+ usbi_err(TRANSFER_CTX(transfer),
+ "CANCEL: completed URB not awaiting reap?");
else
tpriv->awaiting_reap--;
} else {
- usbi_warn("unhandled CANCEL urb status %d", urb->status);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unhandled CANCEL urb status %d", urb->status);
}
if (tpriv->awaiting_reap == 0 && tpriv->awaiting_discard == 0) {
}
if (urb->status != 0)
- usbi_warn("unrecognised urb status %d", urb->status);
+ usbi_warn(TRANSFER_CTX(transfer),
+ "unrecognised urb status %d", urb->status);
/* if we're the last urb or we got less data than requested then we're
* done */
if (tpriv->reap_action == CANCELLED) {
if (urb->status != 0 && urb->status != -ENOENT)
- usbi_warn("cancel: unrecognised urb status %d", urb->status);
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "cancel: unrecognised urb status %d", urb->status);
free(tpriv->urbs);
usbi_handle_transfer_cancellation(itransfer);
return 0;
status = LIBUSB_TRANSFER_STALL;
goto out;
} else if (urb->status != 0) {
- usbi_warn("unrecognised urb status %d", urb->status);
+ usbi_warn(ITRANSFER_CTX(itransfer),
+ "unrecognised urb status %d", urb->status);
status = LIBUSB_TRANSFER_ERROR;
goto out;
}
if (errno == ENODEV)
return LIBUSB_ERROR_NO_DEVICE;
- usbi_err("reap failed error %d errno=%d", r, errno);
+ usbi_err(HANDLE_CTX(handle), "reap failed error %d errno=%d",
+ r, errno);
return LIBUSB_ERROR_IO;
}
case LIBUSB_TRANSFER_TYPE_CONTROL:
return handle_control_completion(itransfer, urb);
default:
- usbi_err("unrecognised endpoint type %x", transfer->type);
+ usbi_err(HANDLE_CTX(handle), "unrecognised endpoint type %x",
+ transfer->type);
return LIBUSB_ERROR_OTHER;
}
}
-static int op_handle_events(struct pollfd *fds, nfds_t nfds, int num_ready)
+static int op_handle_events(struct libusb_context *ctx,
+ struct pollfd *fds, nfds_t nfds, int num_ready)
{
int r;
int i = 0;
- pthread_mutex_lock(&usbi_open_devs_lock);
+ pthread_mutex_lock(&ctx->open_devs_lock);
for (i = 0; i < nfds && num_ready > 0; i++) {
struct pollfd *pollfd = &fds[i];
struct libusb_device_handle *handle;
continue;
num_ready--;
- list_for_each_entry(handle, &usbi_open_devs, list) {
+ list_for_each_entry(handle, &ctx->open_devs, list) {
hpriv = __device_handle_priv(handle);
if (hpriv->fd == pollfd->fd)
break;
}
if (pollfd->revents & POLLERR) {
- usbi_remove_pollfd(hpriv->fd);
+ usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd);
usbi_handle_disconnect(handle);
continue;
}
r = 0;
out:
- pthread_mutex_unlock(&usbi_open_devs_lock);
+ pthread_mutex_unlock(&ctx->open_devs_lock);
return r;
}
}
while (!completed) {
- r = libusb_handle_events();
+ r = libusb_handle_events(HANDLE_CTX(dev_handle));
if (r < 0) {
libusb_cancel_transfer(transfer);
while (!completed)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(HANDLE_CTX(dev_handle)) < 0)
break;
libusb_free_transfer(transfer);
return r;
r = LIBUSB_ERROR_NO_DEVICE;
break;
default:
- usbi_warn("unrecognised status code %d", transfer->status);
+ usbi_warn(HANDLE_CTX(dev_handle),
+ "unrecognised status code %d", transfer->status);
r = LIBUSB_ERROR_OTHER;
}
}
while (!completed) {
- r = libusb_handle_events();
+ r = libusb_handle_events(HANDLE_CTX(dev_handle));
if (r < 0) {
libusb_cancel_transfer(transfer);
while (!completed)
- if (libusb_handle_events() < 0)
+ if (libusb_handle_events(HANDLE_CTX(dev_handle)) < 0)
break;
libusb_free_transfer(transfer);
return r;
r = LIBUSB_ERROR_NO_DEVICE;
break;
default:
- usbi_warn("unrecognised status code %d", transfer->status);
+ usbi_warn(HANDLE_CTX(dev_handle),
+ "unrecognised status code %d", transfer->status);
r = LIBUSB_ERROR_OTHER;
}