*/
#include "libusbi.h"
-#include "hotplug.h"
-
-#include <errno.h>
/**
* \page libusb_io Synchronous and asynchronous device I/O
* a single function call. When the function call returns, the transfer has
* completed and you can parse the results.
*
- * If you have used the libusb-0.1 before, this I/O style will seem familar to
+ * If you have used the libusb-0.1 before, this I/O style will seem familiar to
* you. libusb-0.1 only offered a synchronous interface.
*
* In our input device example, to read button presses you might write code
* sleeping for that long. Execution will be tied up inside the library -
* the entire thread will be useless for that duration.
*
- * Another issue is that by tieing up the thread with that single transaction
+ * Another issue is that by tying up the thread with that single transaction
* there is no possibility of performing I/O with multiple endpoints and/or
* multiple devices simultaneously, unless you resort to creating one thread
* per transaction.
* Freeing the transfer after it has been cancelled but before cancellation
* has completed will result in undefined behaviour.
*
+ * \attention
* When a transfer is cancelled, some of the data may have been transferred.
- * libusb will communicate this to you in the transfer callback. Do not assume
- * that no data was transferred.
+ * libusb will communicate this to you in the transfer callback.
+ * <b>Do not assume that no data was transferred.</b>
+ *
+ * \section asyncpartial Partial data transfer resulting from cancellation
+ *
+ * As noted above, some of the data may have been transferred at the time a
+ * transfer is cancelled. It is helpful to see how this is possible if you
+ * consider a bulk transfer to an endpoint with a packet size of 64 bytes.
+ * Supposing you submit a 512-byte transfer to this endpoint, the operating
+ * system will divide this transfer up into 8 separate 64-byte frames that the
+ * host controller will schedule for the device to transfer data. If this
+ * transfer is cancelled while the device is transferring data, a subset of
+ * these frames may be descheduled from the host controller before the device
+ * has the opportunity to finish transferring data to the host.
+ *
+ * What your application should do with a partial data transfer is a policy
+ * decision; there is no single answer that satisfies the needs of every
+ * application. The data that was successfully transferred should be
+ * considered entirely valid, but your application must decide what to do with
+ * the remaining data that was not transferred. Some possible actions to take
+ * are:
+ * - Resubmit another transfer for the remaining data, possibly with a shorter
+ * timeout
+ * - Discard the partially transferred data and report an error
+ *
+ * \section asynctimeout Timeouts
+ *
+ * When a transfer times out, libusb internally notes this and attempts to
+ * cancel the transfer. As noted in \ref asyncpartial "above", it is possible
+ * that some of the data may actually have been transferred. Your application
+ * should <b>always</b> check how much data was actually transferred once the
+ * transfer completes and act accordingly.
*
* \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints
*
* wLength of the setup packet, rather than the size of the data buffer. So,
* if your wLength was 4, your transfer's <tt>length</tt> was 12, then you
* should expect an <tt>actual_length</tt> of 4 to indicate that the data was
- * transferred in entirity.
+ * transferred in entirety.
*
* To simplify parsing of setup packets and obtaining the data from the
* correct offset, you may wish to use the libusb_control_transfer_get_data()
* libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple()
* functions may help you here.
*
- * <b>Note</b>: Some operating systems (e.g. Linux) may impose limits on the
- * length of individual isochronous packets and/or the total length of the
- * isochronous transfer. Such limits can be difficult for libusb to detect,
- * so the library will simply try and submit the transfer as set up by you.
- * If the transfer fails to submit because it is too large,
+ * \section asynclimits Transfer length limitations
+ *
+ * Some operating systems may impose limits on the length of the transfer data
+ * buffer or, in the case of isochronous transfers, the length of individual
+ * isochronous packets. Such limits can be difficult for libusb to detect, so
+ * in most cases the library will simply try and submit the transfer as set up
+ * by you. If the transfer fails to submit because it is too large,
* libusb_submit_transfer() will return
* \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM".
*
+ * The following are known limits for control transfer lengths. Note that this
+ * length includes the 8-byte setup packet.
+ * - Linux (4,096 bytes)
+ * - Windows (4,096 bytes)
+ *
* \section asyncmem Memory caveats
*
* In most circumstances, it is not safe to use stack memory for transfer
* application must call into when libusb has work do to. This gives libusb
* the opportunity to reap pending transfers, invoke callbacks, etc.
*
- * There are 2 different approaches to dealing with libusb_handle_events:
+ * \note
+ * All event handling is performed by whichever thread calls the
+ * libusb_handle_events() function. libusb does not invoke any callbacks
+ * outside of this context. Consequently, any callbacks will be run on the
+ * thread that calls the libusb_handle_events() function.
+ *
+ * When to call the libusb_handle_events() function depends on which model
+ * your application decides to use. The 2 different approaches:
*
* -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated
* thread.
*
* Lets begin with stating the obvious: If you're going to use a separate
* thread for libusb event handling, your callback functions MUST be
- * threadsafe.
+ * thread-safe.
*
* Other then that doing event handling from a separate thread, is mostly
* simple. You can use an event thread function as follows:
* system calls. This is directly exposed at the
* \ref libusb_asyncio "asynchronous interface" but it is important to note that the
* \ref libusb_syncio "synchronous interface" is implemented on top of the
- * asynchonrous interface, therefore the same considerations apply.
+ * asynchronous interface, therefore the same considerations apply.
*
* The issue is that if two or more threads are concurrently calling poll()
* or select() on libusb's file descriptors then only one of those threads
* do is submit a single transfer and wait for its completion, then using
* one of the synchronous I/O functions is much easier.
*
+ * \note
+ * The `completed` variable must be modified while holding the event lock,
+ * otherwise a race condition can still exist. It is simplest to do so from
+ * within the transfer callback as shown above.
+ *
* \section eventlock The events lock
*
* The problem is when we consider the fact that libusb exposes file
* event handling), because the event waiter seems to have taken the event
* waiters lock while waiting for an event. However, the system does support
* multiple event waiters, because libusb_wait_for_event() actually drops
- * the lock while waiting, and reaquires it before continuing.
+ * the lock while waiting, and reacquires it before continuing.
*
* We have now implemented code which can dynamically handle situations where
* nobody is handling events (so we should do it ourselves), and it can also
#ifdef HAVE_OS_TIMER
r = usbi_create_timer(&ctx->timer);
if (r == 0) {
- usbi_dbg("using timer for timeouts");
+ usbi_dbg(ctx, "using timer for timeouts");
r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS);
if (r < 0)
goto err_destroy_timer;
} else {
- usbi_dbg("timer not available for timeouts");
+ usbi_dbg(ctx, "timer not available for timeouts");
}
#endif
free(ctx->event_data);
}
-static int calculate_timeout(struct usbi_transfer *itransfer)
+static void calculate_timeout(struct usbi_transfer *itransfer)
{
- int r;
unsigned int timeout =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout;
if (!timeout) {
TIMESPEC_CLEAR(&itransfer->timeout);
- return 0;
+ return;
}
- r = usbi_clock_gettime(USBI_CLOCK_MONOTONIC, &itransfer->timeout);
- if (r < 0) {
- usbi_err(ITRANSFER_CTX(itransfer),
- "failed to read monotonic clock, errno=%d", errno);
- return LIBUSB_ERROR_OTHER;
- }
+ usbi_get_monotonic_time(&itransfer->timeout);
itransfer->timeout.tv_sec += timeout / 1000U;
itransfer->timeout.tv_nsec += (timeout % 1000U) * 1000000L;
- if (itransfer->timeout.tv_nsec >= 1000000000L) {
+ if (itransfer->timeout.tv_nsec >= NSEC_PER_SEC) {
++itransfer->timeout.tv_sec;
- itransfer->timeout.tv_nsec -= 1000000000L;
+ itransfer->timeout.tv_nsec -= NSEC_PER_SEC;
}
-
- return 0;
}
/** \ingroup libusb_asyncio
itransfer->priv = ptr;
usbi_mutex_init(&itransfer->lock);
transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
- usbi_dbg("transfer %p", transfer);
return transfer;
}
if (!transfer)
return;
- usbi_dbg("transfer %p", transfer);
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p", transfer);
if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER)
free(transfer->buffer);
/* act on first transfer that has not already been handled */
if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
+ usbi_dbg(ctx, "next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
- usbi_dbg("no timeouts, disarming timer");
+ usbi_dbg(ctx, "no timeouts, disarming timer");
return usbi_disarm_timer(&ctx->timer);
}
#else
struct usbi_transfer *cur;
struct timespec *timeout = &itransfer->timeout;
struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
- int r;
+ int r = 0;
int first = 1;
- r = calculate_timeout(itransfer);
- if (r)
- return r;
+ calculate_timeout(itransfer);
/* if we have no other flying transfers, start the list with this one */
if (list_empty(&ctx->flying_transfers)) {
if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) {
/* if this transfer has the lowest timeout of all active transfers,
* rearm the timer with this transfer's timeout */
- usbi_dbg("arm timer for timeout in %ums (first in line)",
+ usbi_dbg(ctx, "arm timer for timeout in %ums (first in line)",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
r = usbi_arm_timer(&ctx->timer, timeout);
}
* \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported
* by the operating system.
* \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than
- * the operating system and/or hardware can support
+ * the operating system and/or hardware can support (see \ref asynclimits)
* \returns another LIBUSB_ERROR code on other failure
*/
int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
struct libusb_context *ctx = TRANSFER_CTX(transfer);
int r;
- usbi_dbg("transfer %p", transfer);
+ usbi_dbg(ctx, "transfer %p", transfer);
/*
* Important note on locking, this function takes / releases locks
{
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
int r;
- usbi_dbg("transfer %p", transfer );
+ usbi_dbg(ctx, "transfer %p", transfer );
usbi_mutex_lock(&itransfer->lock);
if (!(itransfer->state_flags & USBI_TRANSFER_IN_FLIGHT)
|| (itransfer->state_flags & USBI_TRANSFER_CANCELLING)) {
if (r < 0) {
if (r != LIBUSB_ERROR_NOT_FOUND &&
r != LIBUSB_ERROR_NO_DEVICE)
- usbi_err(TRANSFER_CTX(transfer),
- "cancel transfer failed error %d", r);
+ usbi_err(ctx, "cancel transfer failed error %d", r);
else
- usbi_dbg("cancel transfer failed error %d", r);
+ usbi_dbg(ctx, "cancel transfer failed error %d", r);
if (r == LIBUSB_ERROR_NO_DEVICE)
itransfer->state_flags |= USBI_TRANSFER_DEVICE_DISAPPEARED;
struct libusb_transfer *transfer =
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct libusb_device_handle *dev_handle = transfer->dev_handle;
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
uint8_t flags;
int r;
r = remove_from_flying_list(itransfer);
if (r < 0)
- usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
+ usbi_err(ctx, "failed to set timer for next timeout");
usbi_mutex_lock(&itransfer->lock);
itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
if (transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL)
rqlen -= LIBUSB_CONTROL_SETUP_SIZE;
if (rqlen != itransfer->transferred) {
- usbi_dbg("interpreting short transfer as error");
+ usbi_dbg(ctx, "interpreting short transfer as error");
status = LIBUSB_TRANSFER_ERROR;
}
}
flags = transfer->flags;
transfer->status = status;
transfer->actual_length = itransfer->transferred;
- usbi_dbg("transfer %p has callback %p", transfer, transfer->callback);
+ usbi_dbg(ctx, "transfer %p has callback %p", transfer, transfer->callback);
if (transfer->callback)
transfer->callback(transfer);
/* transfer might have been freed by the above call, do not use from
/* if the URB was cancelled due to timeout, report timeout to the user */
if (timed_out) {
- usbi_dbg("detected timeout cancellation");
+ usbi_dbg(ctx, "detected timeout cancellation");
return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT);
}
ru = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (ru) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
r = usbi_mutex_trylock(&ctx->events_lock);
- if (r)
+ if (!r)
return 1;
ctx->event_handler_active = 1;
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 0;
}
r = ctx->device_close;
usbi_mutex_unlock(&ctx->event_data_lock);
if (r) {
- usbi_dbg("someone else is closing a device");
+ usbi_dbg(ctx, "someone else is closing a device");
return 1;
}
{
unsigned int event_flags;
- usbi_dbg(" ");
+ usbi_dbg(ctx, " ");
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
*
* You only need to use this lock if you are developing an application
* which calls poll() or select() on libusb's file descriptors directly,
- * <b>and</b> may potentially be handling events from 2 threads simultaenously.
+ * <b>and</b> may potentially be handling events from 2 threads simultaneously.
* If you stick to libusb's event handling loop functions (e.g.
* libusb_handle_events()) then you do not need to be concerned with this
* locking.
* indicates unlimited timeout.
* \returns 0 after a transfer completes or another thread stops event handling
* \returns 1 if the timeout expired
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv)
int r;
ctx = usbi_get_context(ctx);
- if (tv == NULL) {
+ if (!tv) {
usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock);
return 0;
}
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
r = usbi_cond_timedwait(&ctx->event_waiters_cond,
&ctx->event_waiters_lock, tv);
-
if (r < 0)
- return r;
- else
- return (r == ETIMEDOUT);
+ return r == LIBUSB_ERROR_TIMEOUT;
+
+ return 0;
}
static void handle_timeout(struct usbi_transfer *itransfer)
itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT;
else
usbi_warn(TRANSFER_CTX(transfer),
- "async cancel failed %d errno=%d", r, errno);
+ "async cancel failed %d", r);
}
-static int handle_timeouts_locked(struct libusb_context *ctx)
+static void handle_timeouts_locked(struct libusb_context *ctx)
{
- int r;
struct timespec systime;
struct usbi_transfer *itransfer;
if (list_empty(&ctx->flying_transfers))
- return 0;
+ return;
/* get current time */
- r = usbi_clock_gettime(USBI_CLOCK_MONOTONIC, &systime);
- if (r < 0) {
- usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
- return LIBUSB_ERROR_OTHER;
- }
+ usbi_get_monotonic_time(&systime);
/* iterate through flying transfers list, finding all transfers that
* have expired timeouts */
/* if we've reached transfers of infinite timeout, we're all done */
if (!TIMESPEC_IS_SET(cur_ts))
- return 0;
+ return;
/* ignore timeouts we've already handled */
if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
/* if transfer has non-expired timeout, nothing more to do */
if (TIMESPEC_CMP(cur_ts, &systime, >))
- return 0;
+ return;
/* otherwise, we've got an expired timeout to handle */
handle_timeout(itransfer);
}
- return 0;
}
-static int handle_timeouts(struct libusb_context *ctx)
+static void handle_timeouts(struct libusb_context *ctx)
{
- int r;
-
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->flying_transfers_lock);
- r = handle_timeouts_locked(ctx);
+ handle_timeouts_locked(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
- return r;
}
static int handle_event_trigger(struct libusb_context *ctx)
{
struct list_head hotplug_msgs;
+ int hotplug_event = 0;
int r = 0;
- usbi_dbg("event triggered");
+ usbi_dbg(ctx, "event triggered");
list_init(&hotplug_msgs);
/* check if someone modified the event sources */
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
- usbi_dbg("someone updated the event sources");
+ usbi_dbg(ctx, "someone updated the event sources");
if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
- usbi_dbg("someone purposefully interrupted");
+ usbi_dbg(ctx, "someone purposefully interrupted");
ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
}
+ if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
+ usbi_dbg(ctx, "someone unregistered a hotplug cb");
+ ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+ hotplug_event = 1;
+ }
+
/* check if someone is closing a device */
if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
- usbi_dbg("someone is closing a device");
+ usbi_dbg(ctx, "someone is closing a device");
/* check for any pending hotplug messages */
if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
- usbi_dbg("hotplug message received");
+ usbi_dbg(ctx, "hotplug message received");
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
+ hotplug_event = 1;
assert(!list_empty(&ctx->hotplug_msgs));
list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
}
/* complete any pending transfers */
if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) {
+ struct usbi_transfer *itransfer, *tmp;
+ struct list_head completed_transfers;
+
assert(!list_empty(&ctx->completed_transfers));
- while (r == 0 && !list_empty(&ctx->completed_transfers)) {
- struct usbi_transfer *itransfer =
- list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
+ list_cut(&completed_transfers, &ctx->completed_transfers);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) {
list_del(&itransfer->completed_list);
- usbi_mutex_unlock(&ctx->event_data_lock);
r = usbi_backend.handle_transfer_completion(itransfer);
- if (r)
+ if (r) {
usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
- usbi_mutex_lock(&ctx->event_data_lock);
+ break;
+ }
}
- if (list_empty(&ctx->completed_transfers))
+ usbi_mutex_lock(&ctx->event_data_lock);
+ if (!list_empty(&completed_transfers)) {
+ /* an error occurred, put the remaining transfers back on the list */
+ list_splice_front(&completed_transfers, &ctx->completed_transfers);
+ } else if (list_empty(&ctx->completed_transfers)) {
ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED;
+ }
}
/* if no further pending events, clear the event */
usbi_mutex_unlock(&ctx->event_data_lock);
- /* process the hotplug messages, if any */
- while (!list_empty(&hotplug_msgs)) {
- struct libusb_hotplug_message *message =
- list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
-
- usbi_hotplug_match(ctx, message->device, message->event);
-
- /* the device left, dereference the device */
- if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
- libusb_unref_device(message->device);
-
- list_del(&message->list);
- free(message);
- }
+ /* process the hotplug events, if any */
+ if (hotplug_event)
+ usbi_hotplug_process(ctx, &hotplug_msgs);
return r;
}
usbi_mutex_lock(&ctx->flying_transfers_lock);
/* process the timeout that just happened */
- r = handle_timeouts_locked(ctx);
- if (r < 0)
- goto out;
+ handle_timeouts_locked(ctx);
/* arm for next timeout */
r = arm_timer_for_next_timeout(ctx);
-out:
usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
return r;
}
#endif
* save the additional overhead */
usbi_mutex_lock(&ctx->event_data_lock);
if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
- usbi_dbg("event sources modified, reallocating event data");
+ usbi_dbg(ctx, "event sources modified, reallocating event data");
/* free anything removed since we last ran */
cleanup_removed_event_sources(ctx);
if (tv->tv_usec % 1000)
timeout_ms++;
+ reported_events.event_bits = 0;
+
usbi_start_event_handling(ctx);
r = usbi_wait_for_events(ctx, &reported_events, timeout_ms);
if (r != LIBUSB_SUCCESS) {
- if (r == LIBUSB_ERROR_TIMEOUT)
- r = handle_timeouts(ctx);
+ if (r == LIBUSB_ERROR_TIMEOUT) {
+ handle_timeouts(ctx);
+ r = LIBUSB_SUCCESS;
+ }
goto done;
}
* \param tv the maximum time to block waiting for events, or an all zero
* timeval struct for non-blocking mode
* \param completed pointer to completion integer to check, or NULL
- * \returns 0 on success, or a LIBUSB_ERROR code on failure
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx,
int r;
struct timeval poll_timeout;
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
ctx = usbi_get_context(ctx);
r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts(ctx);
+ handle_timeouts(ctx);
+ return 0;
}
retry:
if (libusb_try_lock_events(ctx) == 0) {
if (completed == NULL || !*completed) {
/* we obtained the event lock: do our own event handling */
- usbi_dbg("doing our own event handling");
+ usbi_dbg(ctx, "doing our own event handling");
r = handle_events(ctx, &poll_timeout);
}
libusb_unlock_events(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(ctx);
- usbi_dbg("event handler was active but went away, retrying");
+ usbi_dbg(ctx, "event handler was active but went away, retrying");
goto retry;
}
- usbi_dbg("another thread is doing event handling");
+ usbi_dbg(ctx, "another thread is doing event handling");
r = libusb_wait_for_event(ctx, &poll_timeout);
already_done:
if (r < 0)
return r;
else if (r == 1)
- return handle_timeouts(ctx);
- else
- return 0;
+ handle_timeouts(ctx);
+ return 0;
}
/** \ingroup libusb_poll
/** \ingroup libusb_poll
* Handle any pending events in blocking mode. There is currently a timeout
- * hardcoded at 60 seconds but we plan to make it unlimited in future. For
+ * hard-coded at 60 seconds but we plan to make it unlimited in future. For
* finer control over whether this function is blocking or non-blocking, or
* for control over the timeout, use libusb_handle_events_timeout_completed()
* instead.
* \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
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid
+ * \returns another LIBUSB_ERROR code on other failure
* \ref libusb_mtasync
*/
int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx,
int r;
struct timeval poll_timeout;
+ if (!TIMEVAL_IS_VALID(tv))
+ return LIBUSB_ERROR_INVALID_PARAM;
+
ctx = usbi_get_context(ctx);
r = get_next_timeout(ctx, tv, &poll_timeout);
if (r) {
/* timeout already expired */
- return handle_timeouts(ctx);
+ handle_timeouts(ctx);
+ return 0;
}
return handle_events(ctx, &poll_timeout);
struct usbi_transfer *itransfer;
struct timespec systime;
struct timespec next_timeout = { 0, 0 };
- int r;
ctx = usbi_get_context(ctx);
if (usbi_using_timer(ctx))
usbi_mutex_lock(&ctx->flying_transfers_lock);
if (list_empty(&ctx->flying_transfers)) {
usbi_mutex_unlock(&ctx->flying_transfers_lock);
- usbi_dbg("no URBs, no timeout!");
+ usbi_dbg(ctx, "no URBs, no timeout!");
return 0;
}
if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))
continue;
- /* if we've reached transfers of infinte timeout, we're done looking */
+ /* if we've reached transfers of infinite timeout, we're done looking */
if (!TIMESPEC_IS_SET(&itransfer->timeout))
break;
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (!TIMESPEC_IS_SET(&next_timeout)) {
- usbi_dbg("no URB with timeout or all handled by OS; no timeout!");
+ usbi_dbg(ctx, "no URB with timeout or all handled by OS; no timeout!");
return 0;
}
- r = usbi_clock_gettime(USBI_CLOCK_MONOTONIC, &systime);
- if (r < 0) {
- usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
- return 0;
- }
+ usbi_get_monotonic_time(&systime);
if (!TIMESPEC_CMP(&systime, &next_timeout, <)) {
- usbi_dbg("first timeout already expired");
+ usbi_dbg(ctx, "first timeout already expired");
timerclear(tv);
} else {
TIMESPEC_SUB(&next_timeout, &systime, &next_timeout);
TIMESPEC_TO_TIMEVAL(tv, &next_timeout);
- usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
+ usbi_dbg(ctx, "next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec);
}
return 1;
libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
void *user_data)
{
-#if !defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(PLATFORM_WINDOWS)
ctx = usbi_get_context(ctx);
ctx->fd_added_cb = added_cb;
ctx->fd_removed_cb = removed_cb;
if (!ievent_source)
return LIBUSB_ERROR_NO_MEM;
- usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
+ usbi_dbg(ctx, "add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events);
ievent_source->data.os_handle = os_handle;
ievent_source->data.poll_events = poll_events;
usbi_mutex_lock(&ctx->event_data_lock);
usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
-#if !defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(PLATFORM_WINDOWS)
if (ctx->fd_added_cb)
ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data);
#endif
struct usbi_event_source *ievent_source;
int found = 0;
- usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
+ usbi_dbg(ctx, "remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
usbi_mutex_lock(&ctx->event_data_lock);
for_each_event_source(ctx, ievent_source) {
if (ievent_source->data.os_handle == os_handle) {
}
if (!found) {
- usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
+ usbi_dbg(ctx, "couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
usbi_mutex_unlock(&ctx->event_data_lock);
return;
}
usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
-#if !defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(PLATFORM_WINDOWS)
if (ctx->fd_removed_cb)
ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data);
#endif
const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
libusb_context *ctx)
{
-#if !defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(PLATFORM_WINDOWS)
struct libusb_pollfd **ret = NULL;
struct usbi_event_source *ievent_source;
size_t i;
*/
void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
{
-#if !defined(_WIN32) && !defined(__CYGWIN__)
+#if !defined(PLATFORM_WINDOWS)
free((void *)pollfds);
#else
UNUSED(pollfds);
struct usbi_transfer *cur;
struct usbi_transfer *to_cancel;
- usbi_dbg("device %d.%d",
+ usbi_dbg(ctx, "device %d.%d",
dev_handle->dev->bus_number, dev_handle->dev->device_address);
/* terminate all pending transfers with the LIBUSB_TRANSFER_NO_DEVICE
if (!to_cancel)
break;
- usbi_dbg("cancelling transfer %p from disconnect",
+ usbi_dbg(ctx, "cancelling transfer %p from disconnect",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
usbi_mutex_lock(&to_cancel->lock);