*/
#include "libusbi.h"
-#include "hotplug.h"
-
-#include <errno.h>
/**
* \page libusb_io Synchronous and asynchronous device I/O
#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);
}
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);
* 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);
}
list_cut(&completed_transfers, &ctx->completed_transfers);
usbi_mutex_unlock(&ctx->event_data_lock);
- __for_each_transfer_safe(&completed_transfers, itransfer, tmp) {
+ __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) {
list_del(&itransfer->completed_list);
r = usbi_backend.handle_transfer_completion(itransfer);
if (r) {
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);
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
* \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;
}
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);