diff options
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | examples/dpfp.c | 2 | ||||
-rw-r--r-- | examples/lsusb.c | 7 | ||||
-rw-r--r-- | libusb/Makefile.am | 2 | ||||
-rw-r--r-- | libusb/core.c | 13 | ||||
-rw-r--r-- | libusb/io.c | 227 | ||||
-rw-r--r-- | libusb/libusb.h | 4 | ||||
-rw-r--r-- | libusb/libusbi.h | 6 | ||||
-rw-r--r-- | libusb/signalfd.h | 76 |
10 files changed, 127 insertions, 212 deletions
@@ -1,6 +1,5 @@ isochronous endpoint I/O optional timerfd support (runtime detection) -timer (call me back in 2 seconds) implementation (then remove signalfd) API docs notifications of hotplugged/unplugged devices thread safety diff --git a/configure.ac b/configure.ac index 660ca2a..58b4aa2 100644 --- a/configure.ac +++ b/configure.ac @@ -8,6 +8,7 @@ AC_PROG_CC AC_PROG_LIBTOOL AC_C_INLINE AM_PROG_CC_C_O +AC_DEFINE([_GNU_SOURCE], [], [Use GNU extensions]) # Library versioning lt_major="0" diff --git a/examples/dpfp.c b/examples/dpfp.c index d5b0c63..d99b3af 100644 --- a/examples/dpfp.c +++ b/examples/dpfp.c @@ -473,7 +473,7 @@ int main(void) struct sigaction sigact; int r = 1; - r = libusb_init(0); + r = libusb_init(); if (r < 0) { fprintf(stderr, "failed to initialise libusb\n"); exit(1); diff --git a/examples/lsusb.c b/examples/lsusb.c index 8145d63..c511b29 100644 --- a/examples/lsusb.c +++ b/examples/lsusb.c @@ -34,7 +34,12 @@ void print_devs(libusb_dev *devs) int main(void) { libusb_dev *devs; - libusb_init(0); + int r; + + r = libusb_init(); + if (r < 0) + return r; + libusb_find_devices(); devs = libusb_get_devices(); diff --git a/libusb/Makefile.am b/libusb/Makefile.am index d921468..702e702 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -1,7 +1,7 @@ lib_LTLIBRARIES = libusb-1.0.la libusb_1_0_la_CFLAGS = -fvisibility=hidden $(AM_CFLAGS) -libusb_1_0_la_SOURCES = signalfd.h libusbi.h usbfs.h core.c descriptor.c io.c +libusb_1_0_la_SOURCES = libusbi.h usbfs.h core.c descriptor.c io.c libusb_1_0_la_LIBADD = -lrt pkginclude_HEADERS = libusb.h diff --git a/libusb/core.c b/libusb/core.c index 8fb8fd7..e9939f4 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -286,13 +286,14 @@ API_EXPORTED int libusb_release_interface(struct libusb_dev_handle *dev, return r; } -API_EXPORTED int libusb_init(int signum) +API_EXPORTED int libusb_init(void) { /* FIXME: find correct usb node path */ usbi_dbg(""); list_init(&usb_devs); list_init(&open_devs); - return usbi_io_init(signum); + usbi_io_init(); + return 0; } API_EXPORTED void libusb_exit(void) @@ -304,15 +305,13 @@ API_EXPORTED void libusb_exit(void) list_for_each_entry(devh, &open_devs, list) do_close(devh); } - usbi_io_exit(); } API_EXPORTED size_t libusb_get_pollfds(struct libusb_pollfd **pollfds) { struct libusb_dev_handle *devh; struct libusb_pollfd *ret; - /* initialise to 1 for signalfd */ - size_t cnt = 1; + size_t cnt = 0; size_t i = 0; /* count number of open devices */ @@ -328,10 +327,6 @@ API_EXPORTED size_t libusb_get_pollfds(struct libusb_pollfd **pollfds) ret[i].events = POLLOUT; } - /* add signalfd */ - ret[i].fd = usbi_get_signalfd(); - ret[i].events = POLLIN; - *pollfds = ret; return cnt; } 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; -} - diff --git a/libusb/libusb.h b/libusb/libusb.h index 1358680..b07314e 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -23,6 +23,7 @@ #include <stdint.h> #include <sys/time.h> +#include <time.h> #ifdef __cplusplus extern "C" { @@ -218,7 +219,7 @@ typedef void (*libusb_bulk_cb_fn)(libusb_dev_handle *devh, libusb_urb_handle *ur enum libusb_urb_cb_status status, unsigned char endpoint, int rqlength, unsigned char *data, int actual_length, void *user_data); -int libusb_init(int signum); +int libusb_init(void); void libusb_exit(void); int libusb_find_devices(void); @@ -257,6 +258,7 @@ void libusb_urb_handle_free(libusb_urb_handle *urbh); int libusb_poll_timeout(struct timeval *tv); int libusb_poll(void); +int libusb_get_next_timeout(struct timeval *tv); size_t libusb_get_pollfds(struct libusb_pollfd **pollfds); /* sync I/O */ diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 0ff4962..127990f 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -169,8 +169,7 @@ struct libusb_urb_handle { struct libusb_dev_handle *devh; struct usb_urb urb; struct list_head list; - struct timespec timeout; - timer_t timer; + struct timeval timeout; unsigned char urb_type; unsigned char endpoint; int transfer_len; @@ -193,8 +192,7 @@ struct usb_descriptor_header { extern struct list_head open_devs; -int usbi_io_init(int _signum); -void usbi_io_exit(void); +void usbi_io_init(void); int usbi_parse_descriptor(unsigned char *source, char *descriptor, void *dest); int usbi_parse_configuration(struct libusb_config_descriptor *config, diff --git a/libusb/signalfd.h b/libusb/signalfd.h deleted file mode 100644 index e96d693..0000000 --- a/libusb/signalfd.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * signalfd header - * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org> - * - * Based on glibc header - * Copyright (C) 2007 Free Software Foundation, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef __LIBUSB_SIGNALFD_H__ -#define __LIBUSB_SIGNALFD_H__ - -/* FIXME: in future, remove this and unconditionally use glibc directly when - * glibc-2.8 is widespread */ - -#include <signal.h> -#include <stdint.h> - -#ifdef __i386__ -#define __NR_signalfd 321 -#elif defined(__x86_64__) -#define __NR_signalfd 282 -#else -#error "signalfd unsupported on this architecture" -#endif - -/* signalfd() implementation was added as of glibc-2.7 */ -#if __GLIBC_PREREQ(2, 7) -int signalfd(int fd, const sigset_t *mask, int flags); -#else -#include <sys/syscall.h> - -#define SIZEOF_SIG (_NSIG / 8) -#define SIZEOF_SIGSET (SIZEOF_SIG > sizeof(sigset_t) ? sizeof(sigset_t): SIZEOF_SIG) - -static inline int signalfd(int fd, const sigset_t *mask, int flags) -{ - return syscall(__NR_signalfd, fd, mask, SIZEOF_SIGSET); -} -#endif - -struct signalfd_siginfo { - uint32_t ssi_signo; - int32_t ssi_errno; - int32_t ssi_code; - uint32_t ssi_pid; - uint32_t ssi_uid; - int32_t ssi_fd; - uint32_t ssi_tid; - uint32_t ssi_band; - uint32_t ssi_overrun; - uint32_t ssi_trapno; - int32_t ssi_status; - int32_t ssi_int; - uint64_t ssi_ptr; - uint64_t ssi_utime; - uint64_t ssi_stime; - uint64_t ssi_addr; - uint8_t __pad[48]; -}; - -#endif - |