/* if we have no other flying transfers, start the list with this one */
if (list_empty(&ctx->flying_transfers)) {
list_add(&transfer->list, &ctx->flying_transfers);
- if (timerisset(timeout))
- r = 1;
goto out;
}
/* if we have infinite timeout, append to end of list */
if (!timerisset(timeout)) {
list_add_tail(&transfer->list, &ctx->flying_transfers);
+ /* first is irrelevant in this case */
goto out;
}
(cur_tv->tv_sec == timeout->tv_sec &&
cur_tv->tv_usec > timeout->tv_usec)) {
list_add_tail(&transfer->list, &cur->list);
- r = first;
goto out;
}
first = 0;
}
+ /* first is 0 at this stage (list not empty) */
/* otherwise we need to be inserted at the end */
list_add_tail(&transfer->list, &ctx->flying_transfers);
out:
+#ifdef USBI_TIMERFD_AVAILABLE
+ if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) {
+ /* if this transfer has the lowest timeout of all active transfers,
+ * rearm the timerfd with this transfer's timeout */
+ const struct itimerspec it = { {0, 0},
+ { transfer->timeout.tv_sec, transfer->timeout.tv_usec * 1000 } };
+ usbi_dbg("arm timerfd for timeout in %dms (first in line)",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0) {
+ usbi_warn(ctx, "failed to arm first timerfd (error %d)", r);
+ r = LIBUSB_ERROR_OTHER;
+ }
+ }
+#else
+ UNUSED(first);
+#endif
+
usbi_mutex_unlock(&ctx->flying_transfers_lock);
return r;
}
+ sizeof(struct libusb_transfer)
+ (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
+ os_alloc_size;
- struct usbi_transfer *itransfer = malloc(alloc_size);
+ struct usbi_transfer *itransfer = calloc(1, alloc_size);
if (!itransfer)
return NULL;
- memset(itransfer, 0, alloc_size);
itransfer->num_iso_packets = iso_packets;
usbi_mutex_init(&itransfer->lock, NULL);
return USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
free(itransfer);
}
+#ifdef USBI_TIMERFD_AVAILABLE
+static int disarm_timerfd(struct libusb_context *ctx)
+{
+ const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
+ int r;
+
+ usbi_dbg("");
+ r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ else
+ return 0;
+}
+
+/* iterates through the flying transfers, and rearms the timerfd based on the
+ * next upcoming timeout.
+ * must be called with flying_list locked.
+ * returns 0 if there was no timeout to arm, 1 if the next timeout was armed,
+ * or a LIBUSB_ERROR code on failure.
+ */
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ struct usbi_transfer *transfer;
+
+ list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+ struct timeval *cur_tv = &transfer->timeout;
+
+ /* if we've reached transfers of infinite timeout, then we have no
+ * arming to do */
+ if (!timerisset(cur_tv))
+ goto disarm;
+
+ /* act on first transfer that is not already cancelled */
+ if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) {
+ int r;
+ const struct itimerspec it = { {0, 0},
+ { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
+ usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
+ r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
+ if (r < 0)
+ return LIBUSB_ERROR_OTHER;
+ return 1;
+ }
+ }
+
+disarm:
+ return disarm_timerfd(ctx);
+}
+#else
+static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+{
+ (void)ctx;
+ return 0;
+}
+#endif
+
/** \ingroup asyncio
* Submit a transfer. This function will fire off the USB transfer and then
* return immediately.
struct usbi_transfer *itransfer =
LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer);
int r;
- int first;
int updated_fds;
usbi_mutex_lock(&itransfer->lock);
goto out;
}
- first = add_to_flying_list(itransfer);
+ r = add_to_flying_list(itransfer);
+ if (r)
+ goto out;
r = usbi_backend->submit_transfer(itransfer);
if (r) {
usbi_mutex_lock(&ctx->flying_transfers_lock);
list_del(&itransfer->list);
+ arm_timerfd_for_next_timeout(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
}
-#ifdef USBI_TIMERFD_AVAILABLE
- else if (first && usbi_using_timerfd(ctx)) {
- /* if this transfer has the lowest timeout of all active transfers,
- * rearm the timerfd with this transfer's timeout */
- const struct itimerspec it = { {0, 0},
- { itransfer->timeout.tv_sec, itransfer->timeout.tv_usec * 1000 } };
- usbi_dbg("arm timerfd for timeout in %dms (first in line)", transfer->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0)
- r = LIBUSB_ERROR_OTHER;
- }
-#else
- (void)first;
-#endif
out:
updated_fds = (itransfer->flags & USBI_TRANSFER_UPDATED_FDS);
return r;
}
-#ifdef USBI_TIMERFD_AVAILABLE
-static int disarm_timerfd(struct libusb_context *ctx)
-{
- const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } };
- int r;
-
- usbi_dbg("");
- r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL);
- if (r < 0)
- return LIBUSB_ERROR_OTHER;
- else
- return 0;
-}
-
-/* iterates through the flying transfers, and rearms the timerfd based on the
- * next upcoming timeout.
- * must be called with flying_list locked.
- * returns 0 if there was no timeout to arm, 1 if the next timeout was armed,
- * or a LIBUSB_ERROR code on failure.
- */
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
-{
- struct usbi_transfer *transfer;
-
- list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- struct timeval *cur_tv = &transfer->timeout;
-
- /* if we've reached transfers of infinite timeout, then we have no
- * arming to do */
- if (!timerisset(cur_tv))
- return 0;
-
- /* act on first transfer that is not already cancelled */
- if (!(transfer->flags & USBI_TRANSFER_TIMED_OUT)) {
- int r;
- const struct itimerspec it = { {0, 0},
- { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } };
- usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0)
- return LIBUSB_ERROR_OTHER;
- return 1;
- }
- }
-
- return 0;
-}
-#else
-static int disarm_timerfd(struct libusb_context *ctx)
-{
- (void)ctx;
- return 0;
-}
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
-{
- (void)ctx;
- return 0;
-}
-#endif
-
/* Handle completion of a transfer (completion might be an error condition).
* This will invoke the user-supplied callback function, which may end up
* freeing the transfer. Therefore you cannot use the transfer structure
if (usbi_using_timerfd(ctx))
r = arm_timerfd_for_next_timeout(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
-
- if (usbi_using_timerfd(ctx)) {
- if (r < 0)
- return r;
- r = disarm_timerfd(ctx);
- if (r < 0)
- return r;
- }
+ if (usbi_using_timerfd(ctx) && (r < 0))
+ return r;
if (status == LIBUSB_TRANSFER_COMPLETED
&& transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) {
{
int r;
- r = disarm_timerfd(ctx);
- if (r < 0)
- return r;
-
usbi_mutex_lock(&ctx->flying_transfers_lock);
/* process the timeout that just happened */
int r;
struct usbi_pollfd *ipollfd;
POLL_NFDS_TYPE nfds = 0;
- struct pollfd *fds;
+ struct pollfd *fds = NULL;
int i = -1;
int timeout_ms;
nfds++;
/* TODO: malloc when number of fd's changes, not on every poll */
- fds = malloc(sizeof(*fds) * nfds);
+ if (nfds != 0)
+ fds = malloc(sizeof(*fds) * nfds);
if (!fds) {
usbi_mutex_unlock(&ctx->pollfds_lock);
return LIBUSB_ERROR_NO_MEM;
r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
if (r < 0) {
usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
- return LIBUSB_ERROR_OTHER;
+ return 0;
}
TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts);