diff options
Diffstat (limited to 'libusb/io.c')
-rw-r--r-- | libusb/io.c | 227 |
1 files changed, 109 insertions, 118 deletions
diff --git a/libusb/io.c b/libusb/io.c index 65edede..ae572ab 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -30,57 +30,17 @@ #include <time.h> #include <unistd.h> -/* signalfd() support is present in glibc-2.7 onwards, but glibc-2.7 contains - * a bug where the header is neither installed or compilable. This will be - * fixed for glibc-2.8. */ -#if __GLIBC_PREREQ(2, 8) -#include <sys/signalfd.h> -#else -#include "signalfd.h" -#endif - #include "libusbi.h" -static int sigfd; -static int signum; - /* this is a list of in-flight rb_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_urbs; -static int setup_signalfd(int _signum) -{ - sigset_t sigset; - if (_signum == 0) - _signum = SIGRTMIN; - usbi_dbg("signal %d", _signum); - - sigemptyset(&sigset); - sigaddset(&sigset, _signum); - sigfd = signalfd(-1, &sigset, 0); - if (sigfd < 0) { - usbi_err("signalfd failed, code=%d errno=%d", sigfd, errno); - return sigfd; - } - usbi_dbg("got signalfd %d", sigfd); - signum = _signum; - - sigemptyset(&sigset); - sigaddset(&sigset, _signum); - return sigprocmask(SIG_BLOCK, &sigset, NULL); -} - -int usbi_io_init(int _signum) +void usbi_io_init() { list_init(&flying_urbs); - return setup_signalfd(signum); -} - -void usbi_io_exit(void) -{ - close(sigfd); } static int calculate_timeout(struct libusb_urb_handle *urbh, @@ -88,12 +48,6 @@ static int calculate_timeout(struct libusb_urb_handle *urbh, { int r; struct timespec current_time; - struct sigevent sigevt = { - .sigev_notify = SIGEV_SIGNAL, - .sigev_signo = signum, - }; - struct itimerspec itspec; - struct timespec *it_value = &itspec.it_value; if (!timeout) return 0; @@ -104,37 +58,22 @@ static int calculate_timeout(struct libusb_urb_handle *urbh, return r; } - r = timer_create(CLOCK_MONOTONIC, &sigevt, &urbh->timer); - if (r < 0) { - usbi_err("failed to create monotonic timer"); - return r; - } - - memset(&itspec, 0, sizeof(itspec)); - it_value->tv_sec = current_time.tv_sec + (timeout / 1000); - it_value->tv_nsec = current_time.tv_nsec + - ((timeout % 1000) * 1000000); - - if (it_value->tv_nsec > 1000000000) { - it_value->tv_nsec -= 1000000000; - it_value->tv_sec++; - } + current_time.tv_sec += timeout / 1000; + current_time.tv_nsec += (timeout % 1000) * 1000000; - r = timer_settime(&urbh->timer, TIMER_ABSTIME, &itspec, NULL); - if (r < 0) { - usbi_err("failed to arm monotonic timer"); - return r; + if (current_time.tv_nsec > 1000000000) { + current_time.tv_nsec -= 1000000000; + current_time.tv_sec++; } - urbh->timeout = itspec.it_value; - + TIMESPEC_TO_TIMEVAL(&urbh->timeout, ¤t_time); return 0; } static void add_to_flying_list(struct libusb_urb_handle *urbh) { struct libusb_urb_handle *cur; - struct timespec *timeout = &urbh->timeout; + struct timeval *timeout = &urbh->timeout; /* if we have no other flying urbs, start the list with this one */ if (list_empty(&flying_urbs)) { @@ -143,7 +82,7 @@ static void add_to_flying_list(struct libusb_urb_handle *urbh) } /* if we have infinite timeout, append to end of list */ - if (!TIMESPEC_IS_SET(timeout)) { + if (!timerisset(timeout)) { list_add_tail(&urbh->list, &flying_urbs); return; } @@ -151,11 +90,11 @@ static void add_to_flying_list(struct libusb_urb_handle *urbh) /* otherwise, find appropriate place in list */ list_for_each_entry(cur, &flying_urbs, list) { /* find first timeout that occurs after the urbh in question */ - struct timespec *cur_ts = &cur->timeout; + struct timeval *cur_tv = &cur->timeout; - if (!TIMESPEC_IS_SET(cur_ts) || (cur_ts->tv_sec > timeout->tv_sec) || - (cur_ts->tv_sec == timeout->tv_sec && - cur_ts->tv_nsec > timeout->tv_nsec)) { + if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) || + (cur_tv->tv_sec == timeout->tv_sec && + cur_tv->tv_usec > timeout->tv_usec)) { list_add_tail(&urbh->list, &cur->list); return; } @@ -341,9 +280,6 @@ int handle_transfer_completion(struct libusb_dev_handle *devh, { struct usb_urb *urb = &urbh->urb; - if (TIMESPEC_IS_SET(&urbh->timeout)) - timer_delete(urbh->timer); - if (status == FP_URB_SILENT_COMPLETION) return 0; @@ -457,7 +393,8 @@ static void handle_timeout(struct libusb_urb_handle *urbh) static int handle_timeouts(void) { - struct timespec systime; + struct timespec systime_ts; + struct timeval systime; struct libusb_urb_handle *urbh; int r; @@ -465,23 +402,29 @@ static int handle_timeouts(void) return 0; /* get current time */ - r = clock_gettime(CLOCK_MONOTONIC, &systime); + r = clock_gettime(CLOCK_MONOTONIC, &systime_ts); if (r < 0) return r; + TIMESPEC_TO_TIMEVAL(&systime, &systime_ts); + /* iterate through flying urbs list, finding all urbs that have expired * timeouts */ list_for_each_entry(urbh, &flying_urbs, list) { - struct timespec *cur_ts = &urbh->timeout; + struct timeval *cur_tv = &urbh->timeout; /* if we've reached urbs of infinite timeout, we're all done */ - if (!TIMESPEC_IS_SET(cur_ts)) + if (!timerisset(cur_tv)) return 0; + /* ignore timeouts we've already handled */ + if (urbh->flags & LIBUSB_URBH_TIMED_OUT) + continue; + /* if urb has non-expired timeout, nothing more to do */ - if ((cur_ts->tv_sec > systime.tv_sec) || - (cur_ts->tv_sec == systime.tv_sec && - cur_ts->tv_nsec > systime.tv_nsec)) + if ((cur_tv->tv_sec > systime.tv_sec) || + (cur_tv->tv_sec == systime.tv_sec && + cur_tv->tv_usec > systime.tv_usec)) return 0; /* otherwise, we've got an expired timeout to handle */ @@ -491,32 +434,29 @@ static int handle_timeouts(void) return 0; } -static int flush_sigfd(void) -{ - int r; - struct signalfd_siginfo siginfo; - r = read(sigfd, &siginfo, sizeof(siginfo)); - if (r < 0) { - usbi_err("sigfd read failed %d %d", r, errno); - return r; - } - if ((unsigned int) r < sizeof(siginfo)) { - usbi_err("sigfd short read (%d/%d)", r, sizeof(siginfo)); - return -1; - } - return 0; -} - static int poll_io(struct timeval *tv) { struct libusb_dev_handle *devh; int r; - int maxfd = sigfd; - fd_set readfds; + int maxfd = 0; fd_set writefds; + struct timeval select_timeout; + struct timeval timeout; - FD_ZERO(&readfds); - FD_SET(sigfd, &readfds); + r = libusb_get_next_timeout(&timeout); + if (r) { + /* timeout already expired? */ + if (!timerisset(&timeout)) + return handle_timeouts(); + + /* choose the smallest of next URB timeout or user specified timeout */ + if (timercmp(&timeout, tv, <)) + select_timeout = timeout; + else + select_timeout = *tv; + } else { + select_timeout = *tv; + } FD_ZERO(&writefds); list_for_each_entry(devh, &open_devs, list) { @@ -526,17 +466,21 @@ static int poll_io(struct timeval *tv) maxfd = fd; } - r = select(maxfd + 1, &readfds, &writefds, NULL, tv); - if (r == 0 || (r == -1 && errno == EINTR)) { + usbi_dbg("select() with timeout in %d.%06ds", select_timeout.tv_sec, + select_timeout.tv_usec); + r = select(maxfd + 1, NULL, &writefds, NULL, &select_timeout); + usbi_dbg("select() returned %d with %d.%06ds remaining", r, select_timeout.tv_sec, + select_timeout.tv_usec); + if (r == 0) { + *tv = select_timeout; + return handle_timeouts(); + } else if (r == -1 && errno == EINTR) { return 0; } else if (r < 0) { usbi_err("select failed %d err=%d\n", r, errno); return r; } - if (FD_ISSET(sigfd, &readfds)) - flush_sigfd(); - list_for_each_entry(devh, &open_devs, list) { if (!FD_ISSET(devh->fd, &writefds)) continue; @@ -548,8 +492,7 @@ static int poll_io(struct timeval *tv) } /* FIXME check return value? */ - handle_timeouts(); - return 0; + return handle_timeouts(); } API_EXPORTED int libusb_poll_timeout(struct timeval *tv) @@ -560,11 +503,64 @@ API_EXPORTED int libusb_poll_timeout(struct timeval *tv) API_EXPORTED int libusb_poll(void) { struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 500000; + tv.tv_sec = 2; + tv.tv_usec = 0; return poll_io(&tv); } +API_EXPORTED int libusb_get_next_timeout(struct timeval *tv) +{ + struct libusb_urb_handle *urbh; + struct timespec cur_ts; + struct timeval cur_tv; + struct timeval *next_timeout; + int r; + int found = 0; + + if (list_empty(&flying_urbs)) { + usbi_dbg("no URBs, no timeout!"); + return 0; + } + + /* find next urb which hasn't already been processed as timed out */ + list_for_each_entry(urbh, &flying_urbs, list) { + if (!(urbh->flags & LIBUSB_URBH_TIMED_OUT)) { + found = 1; + break; + } + } + + if (!found) { + usbi_dbg("all URBs have already been processed for timeouts"); + return 0; + } + + next_timeout = &urbh->timeout; + + /* no timeout for next urb */ + if (!timerisset(next_timeout)) { + usbi_dbg("no URBs with timeouts, no timeout!"); + return 0; + } + + r = clock_gettime(CLOCK_MONOTONIC, &cur_ts); + if (r < 0) { + usbi_err("failed to read monotonic clock, errno=%d", errno); + return r; + } + TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); + + if (timercmp(&cur_tv, next_timeout, >=)) { + usbi_dbg("first timeout already expired"); + timerclear(tv); + } else { + timersub(next_timeout, &cur_tv, tv); + usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec); + } + + return 1; +} + struct sync_ctrl_handle { enum libusb_urb_cb_status status; unsigned char *data; @@ -705,8 +701,3 @@ API_EXPORTED void libusb_urb_handle_free(struct libusb_urb_handle *urbh) free(urbh); } -int usbi_get_signalfd(void) -{ - return sigfd; -} - |