Remove timers and signalfd
Instead of timers, add a mechanism for informing the parent app when the
next timeout is due to happen, so that it can call us at that time.
As we no longer use signals, signalfd has also been removed.
diff --git a/TODO b/TODO
index cc3597b..5f62598 100644
--- a/TODO
+++ b/TODO
@@ -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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
return r;
}
- r = timer_create(CLOCK_MONOTONIC, &sigevt, &urbh->timer);
- if (r < 0) {
- usbi_err("failed to create monotonic timer");
- return r;
+ current_time.tv_sec += timeout / 1000;
+ current_time.tv_nsec += (timeout % 1000) * 1000000;
+
+ if (current_time.tv_nsec > 1000000000) {
+ current_time.tv_nsec -= 1000000000;
+ current_time.tv_sec++;
}
- 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++;
- }
-
- r = timer_settime(&urbh->timer, TIMER_ABSTIME, &itspec, NULL);
- if (r < 0) {
- usbi_err("failed to arm monotonic timer");
- return r;
- }
-
- 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 @@
}
/* 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 @@
/* 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 @@
{
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 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 @@
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 @@
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 @@
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 @@
}
/* 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(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 @@
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 @@
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 @@
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_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 @@
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
-