/* Define to 1 to enable message logging. */
#define ENABLE_LOGGING 1
+/* Define to 1 if using the POSIX events abstraction. */
+#define EVENTS_POSIX 1
+
/* On 10.12 and later, use newly available clock_*() functions */
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
/* Define to 1 if you have the `clock_gettime' function. */
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
-/* Define to 1 if using the POSIX poll() implementation. */
-#define POLL_POSIX 1
-
/* Define to 1 if using POSIX threads. */
#define THREADS_POSIX 1
008FBF901628B7E800BC5BE2 /* libusbi.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF671628B7E800BC5BE2 /* libusbi.h */; };
008FBF921628B7E800BC5BE2 /* darwin_usb.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBF6C1628B7E800BC5BE2 /* darwin_usb.c */; };
008FBF931628B7E800BC5BE2 /* darwin_usb.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF6D1628B7E800BC5BE2 /* darwin_usb.h */; };
- 008FBF971628B7E800BC5BE2 /* poll_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF711628B7E800BC5BE2 /* poll_posix.h */; };
008FBF9A1628B7E800BC5BE2 /* threads_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBF741628B7E800BC5BE2 /* threads_posix.c */; };
008FBF9B1628B7E800BC5BE2 /* threads_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = 008FBF751628B7E800BC5BE2 /* threads_posix.h */; };
008FBFA01628B7E800BC5BE2 /* sync.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBF7A1628B7E800BC5BE2 /* sync.c */; };
008FC0311628BC7800BC5BE2 /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
1438D77A17A2ED9F00166101 /* hotplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77817A2ED9F00166101 /* hotplug.c */; };
1438D77B17A2ED9F00166101 /* hotplug.h in Headers */ = {isa = PBXBuildFile; fileRef = 1438D77917A2ED9F00166101 /* hotplug.h */; };
- 1438D77D17A2EDCD00166101 /* poll_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77C17A2EDCD00166101 /* poll_posix.c */; };
1438D77F17A2F0EA00166101 /* strerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77E17A2F0EA00166101 /* strerror.c */; };
+ 2018D95F24E453BA001589B2 /* events_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = 2018D95E24E453BA001589B2 /* events_posix.c */; };
+ 2018D96124E453D0001589B2 /* events_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = 2018D96024E453D0001589B2 /* events_posix.h */; };
20468D70243298C100650534 /* sam3u_benchmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 20468D6E243298C100650534 /* sam3u_benchmark.c */; };
20468D7E2432990100650534 /* testlibusb.c in Sources */ = {isa = PBXBuildFile; fileRef = 20468D7C2432990000650534 /* testlibusb.c */; };
20468D7F2432993300650534 /* libusb-1.0.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 008FBF311628B79300BC5BE2 /* libusb-1.0.0.dylib */; };
008FBF671628B7E800BC5BE2 /* libusbi.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = libusbi.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
008FBF6C1628B7E800BC5BE2 /* darwin_usb.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.c; path = darwin_usb.c; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
008FBF6D1628B7E800BC5BE2 /* darwin_usb.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = darwin_usb.h; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
- 008FBF711628B7E800BC5BE2 /* poll_posix.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = poll_posix.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
008FBF741628B7E800BC5BE2 /* threads_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = threads_posix.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
008FBF751628B7E800BC5BE2 /* threads_posix.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = threads_posix.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
008FBF7A1628B7E800BC5BE2 /* sync.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = sync.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
008FC0261628BC6B00BC5BE2 /* listdevs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = listdevs; sourceTree = BUILT_PRODUCTS_DIR; };
1438D77817A2ED9F00166101 /* hotplug.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = hotplug.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
1438D77917A2ED9F00166101 /* hotplug.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = hotplug.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
- 1438D77C17A2EDCD00166101 /* poll_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = poll_posix.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
1438D77E17A2F0EA00166101 /* strerror.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = strerror.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
1443EE8416417E63007E0579 /* common.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = common.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
1443EE8516417E63007E0579 /* debug.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = debug.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
1443EE8716417E63007E0579 /* libusb.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = libusb.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
1443EE8816417E63007E0579 /* release.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = release.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
1443EE8916417EA6007E0579 /* libusb_release.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = libusb_release.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
+ 2018D95E24E453BA001589B2 /* events_posix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = events_posix.c; sourceTree = "<group>"; };
+ 2018D96024E453D0001589B2 /* events_posix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = events_posix.h; sourceTree = "<group>"; };
20468D67243298AE00650534 /* sam3u_benchmark */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sam3u_benchmark; sourceTree = BUILT_PRODUCTS_DIR; };
20468D6E243298C100650534 /* sam3u_benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sam3u_benchmark.c; sourceTree = "<group>"; usesTabs = 1; };
20468D75243298D300650534 /* testlibusb */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testlibusb; sourceTree = BUILT_PRODUCTS_DIR; };
children = (
008FBF6C1628B7E800BC5BE2 /* darwin_usb.c */,
008FBF6D1628B7E800BC5BE2 /* darwin_usb.h */,
- 1438D77C17A2EDCD00166101 /* poll_posix.c */,
- 008FBF711628B7E800BC5BE2 /* poll_posix.h */,
+ 2018D95E24E453BA001589B2 /* events_posix.c */,
+ 2018D96024E453D0001589B2 /* events_posix.h */,
008FBF741628B7E800BC5BE2 /* threads_posix.c */,
008FBF751628B7E800BC5BE2 /* threads_posix.h */,
);
files = (
008FBFA51628B84200BC5BE2 /* config.h in Headers */,
008FBF931628B7E800BC5BE2 /* darwin_usb.h in Headers */,
+ 2018D96124E453D0001589B2 /* events_posix.h in Headers */,
1438D77B17A2ED9F00166101 /* hotplug.h in Headers */,
008FBF891628B7E800BC5BE2 /* libusb.h in Headers */,
008FBF901628B7E800BC5BE2 /* libusbi.h in Headers */,
- 008FBF971628B7E800BC5BE2 /* poll_posix.h in Headers */,
008FBF9B1628B7E800BC5BE2 /* threads_posix.h in Headers */,
008FBFA11628B7E800BC5BE2 /* version.h in Headers */,
008FBFA21628B7E800BC5BE2 /* version_nano.h in Headers */,
008FBF861628B7E800BC5BE2 /* core.c in Sources */,
008FBF921628B7E800BC5BE2 /* darwin_usb.c in Sources */,
008FBF871628B7E800BC5BE2 /* descriptor.c in Sources */,
+ 2018D95F24E453BA001589B2 /* events_posix.c in Sources */,
1438D77A17A2ED9F00166101 /* hotplug.c in Sources */,
008FBF881628B7E800BC5BE2 /* io.c in Sources */,
- 1438D77D17A2EDCD00166101 /* poll_posix.c in Sources */,
1438D77F17A2F0EA00166101 /* strerror.c in Sources */,
008FBFA01628B7E800BC5BE2 /* sync.c in Sources */,
008FBF9A1628B7E800BC5BE2 /* threads_posix.c in Sources */,
/* Define to 1 to enable message logging. */
#define ENABLE_LOGGING 1
+/* Define to 1 if using the POSIX events abstraction. */
+#define EVENTS_POSIX 1
+
/* Define to 1 if you have the <asm/types.h> header file. */
#define HAVE_ASM_TYPES_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
-/* Define to 1 if using the POSIX poll() implementation. */
-#define POLL_POSIX 1
-
/* Define to 1 if using POSIX threads. */
#define THREADS_POSIX 1
$(LIBUSB_ROOT_REL)/libusb/sync.c \
$(LIBUSB_ROOT_REL)/libusb/strerror.c \
$(LIBUSB_ROOT_REL)/libusb/os/linux_usbfs.c \
- $(LIBUSB_ROOT_REL)/libusb/os/poll_posix.c \
+ $(LIBUSB_ROOT_REL)/libusb/os/events_posix.c \
$(LIBUSB_ROOT_REL)/libusb/os/threads_posix.c \
$(LIBUSB_ROOT_REL)/libusb/os/linux_netlink.c
*-darwin*)
AC_MSG_RESULT([Darwin/Mac OS X])
backend=darwin
- poll=posix
+ events=posix
threads=posix
;;
*-haiku*)
AC_MSG_RESULT([Haiku])
backend=haiku
- poll=posix
+ events=posix
threads=posix
;;
*-linux* | *-uclinux*)
;;
esac
backend=linux
- poll=posix
+ events=posix
threads=posix
;;
*-netbsd*)
AC_MSG_RESULT([NetBSD])
backend=netbsd
- poll=posix
+ events=posix
threads=posix
;;
*-openbsd*)
AC_MSG_RESULT([OpenBSD])
backend=openbsd
- poll=posix
+ events=posix
threads=posix
;;
*-solaris*)
AC_MSG_RESULT([SunOS])
backend=sunos
- poll=posix
+ events=posix
threads=posix
;;
*-cygwin*)
AC_MSG_RESULT([Windows (using Cygwin)])
backend=windows
- poll=windows
+ events=windows
threads=posix
;;
*-mingw* | *msys*)
AC_MSG_RESULT([Windows])
backend=windows
- poll=windows
+ events=windows
threads=windows
test "x$enable_shared" = xyes && create_import_lib=yes
EXTRA_CFLAGS="-fno-omit-frame-pointer"
AC_MSG_WARN([The host being compiled for is not supported.])
AC_MSG_WARN([The library may compile but will not function in any useful manner.])
backend="null"
- poll=posix
+ events=posix
threads="posix"
;;
esac
-if test "x$poll" = xposix; then
- AC_DEFINE([POLL_POSIX], [1], [Define to 1 if using the POSIX poll() implementation.])
+if test "x$events" = xposix; then
+ AC_DEFINE([EVENTS_POSIX], [1], [Define to 1 if using the POSIX events abstraction.])
AC_CHECK_TYPES([nfds_t], [], [], [[#include <poll.h>]])
AC_CHECK_FUNCS([pipe2])
-elif test "x$poll" = xwindows; then
- AC_DEFINE([POLL_WINDOWS], [1], [Define to 1 if using the Windows poll() implementation.])
+elif test "x$events" = xwindows; then
+ AC_DEFINE([EVENTS_WINDOWS], [1], [Define to 1 if using the Windows events abstraction.])
else
- AC_MSG_ERROR([Unknown poll implementation])
+ AC_MSG_ERROR([Unknown events abstraction])
fi
if test "x$threads" = xposix; then
AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$build_examples" != xno])
AM_CONDITIONAL([BUILD_TESTS], [test "x$build_tests" != xno])
AM_CONDITIONAL([CREATE_IMPORT_LIB], [test "x$create_import_lib" = xyes])
+AM_CONDITIONAL([EVENTS_POSIX], [test "x$events" = xposix])
+AM_CONDITIONAL([EVENTS_WINDOWS], [test "x$events" = xwindows])
AM_CONDITIONAL([HAVE_SIGACTION], [test "x$have_sigaction" = xyes])
AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = xdarwin])
AM_CONDITIONAL([OS_HAIKU], [test "x$backend" = xhaiku])
AM_CONDITIONAL([OS_OPENBSD], [test "x$backend" = xopenbsd])
AM_CONDITIONAL([OS_SUNOS], [test "x$backend" = xsunos])
AM_CONDITIONAL([OS_WINDOWS], [test "x$backend" = xwindows])
-AM_CONDITIONAL([POLL_POSIX], [test "x$poll" = xposix])
-AM_CONDITIONAL([POLL_WINDOWS], [test "x$poll" = xwindows])
AM_CONDITIONAL([THREADS_POSIX], [test "x$threads" = xposix])
AM_CONDITIONAL([THREADS_WINDOWS], [test "x$threads" = xwindows])
AM_CONDITIONAL([USE_UDEV], [test "x$use_udev" = xyes])
lib_LTLIBRARIES = libusb-1.0.la
-POSIX_POLL_SRC = os/poll_posix.h os/poll_posix.c
-WINDOWS_POLL_SRC = os/poll_windows.h os/poll_windows.c
+POSIX_EVENTS_SRC = os/events_posix.h os/events_posix.c
+WINDOWS_EVENTS_SRC = os/events_windows.h os/events_windows.c
-if POLL_POSIX
-POLL_SRC = $(POSIX_POLL_SRC)
+if EVENTS_POSIX
+EVENTS_SRC = $(POSIX_EVENTS_SRC)
else
-POLL_SRC = $(WINDOWS_POLL_SRC)
+EVENTS_SRC = $(WINDOWS_EVENTS_SRC)
endif
POSIX_THREADS_SRC = os/threads_posix.h os/threads_posix.c
libusb_1_0_la_LDFLAGS = $(LT_LDFLAGS)
libusb_1_0_la_SOURCES = libusbi.h version.h version_nano.h \
core.c descriptor.c hotplug.h hotplug.c io.c strerror.c sync.c \
- $(POLL_SRC) $(THREADS_SRC) $(OS_SRC)
+ $(EVENTS_SRC) $(THREADS_SRC) $(OS_SRC)
pkginclude_HEADERS = libusb.h
}
}
-/*
- * Signal the event pipe so that the event handling thread will be
- * interrupted to process an internal event.
- */
-int usbi_signal_event(struct libusb_context *ctx)
-{
- unsigned char dummy = 1;
- ssize_t r;
-
- /* write some data on event pipe to interrupt event handlers */
- r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy));
- if (r != sizeof(dummy)) {
- usbi_warn(ctx, "internal signalling write failed");
- return LIBUSB_ERROR_IO;
- }
-
- return 0;
-}
-
-/*
- * Clear the event pipe so that the event handling will no longer be
- * interrupted.
- */
-int usbi_clear_event(struct libusb_context *ctx)
-{
- unsigned char dummy;
- ssize_t r;
-
- /* read some data on event pipe to clear it */
- r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy));
- if (r != sizeof(dummy)) {
- usbi_warn(ctx, "internal signalling read failed");
- return LIBUSB_ERROR_IO;
- }
-
- return 0;
-}
-
/** \ingroup libusb_dev
* Wrap a platform-specific system device handle and obtain a libusb device
* handle for the underlying device. The handle allows you to use libusb to
pending_events = usbi_pending_events(ctx);
ctx->device_close++;
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
/* take event handling lock */
ctx->device_close--;
pending_events = usbi_pending_events(ctx);
if (!pending_events)
- usbi_clear_event(ctx);
+ usbi_clear_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
/* Release event handling lock and wake up event waiters */
pending_events = usbi_pending_events(ctx);
list_add_tail(&message->list, &ctx->hotplug_msgs);
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
pending_events = usbi_pending_events(ctx);
ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
}
#include "hotplug.h"
#include <errno.h>
-#ifdef HAVE_TIMERFD
-#include <sys/timerfd.h>
-#include <unistd.h>
-#endif
/**
* \page libusb_io Synchronous and asynchronous device I/O
usbi_mutex_init(&ctx->event_data_lock);
usbi_tls_key_create(&ctx->event_handling_key);
list_init(&ctx->flying_transfers);
- list_init(&ctx->ipollfds);
- list_init(&ctx->removed_ipollfds);
+ list_init(&ctx->event_sources);
+ list_init(&ctx->removed_event_sources);
list_init(&ctx->hotplug_msgs);
list_init(&ctx->completed_transfers);
- /* FIXME should use an eventfd on kernels that support it */
- r = usbi_pipe(ctx->event_pipe);
- if (r < 0) {
- r = LIBUSB_ERROR_OTHER;
+ r = usbi_create_event(&ctx->event);
+ if (r < 0)
goto err;
- }
- r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN);
+ r = usbi_add_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event), USBI_EVENT_POLL_EVENTS);
if (r < 0)
- goto err_close_pipe;
+ goto err_destroy_event;
-#ifdef HAVE_TIMERFD
- ctx->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
- if (ctx->timerfd >= 0) {
- usbi_dbg("using timerfd for timeouts");
- r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
+#ifdef HAVE_OS_TIMER
+ r = usbi_create_timer(&ctx->timer);
+ if (r == 0) {
+ usbi_dbg("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_close_timerfd;
+ goto err_destroy_timer;
} else {
- usbi_dbg("timerfd not available, errno=%d", errno);
+ usbi_dbg("timer not available for timeouts");
}
#endif
return 0;
-#ifdef HAVE_TIMERFD
-err_close_timerfd:
- close(ctx->timerfd);
- usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
+#ifdef HAVE_OS_TIMER
+err_destroy_timer:
+ usbi_destroy_timer(&ctx->timer);
+ usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
#endif
-err_close_pipe:
- usbi_close(ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[1]);
+err_destroy_event:
+ usbi_destroy_event(&ctx->event);
err:
usbi_mutex_destroy(&ctx->flying_transfers_lock);
usbi_mutex_destroy(&ctx->events_lock);
return r;
}
-static void cleanup_removed_pollfds(struct libusb_context *ctx)
+static void cleanup_removed_event_sources(struct libusb_context *ctx)
{
- struct usbi_pollfd *ipollfd, *tmp;
+ struct usbi_event_source *ievent_source, *tmp;
- for_each_removed_pollfd_safe(ctx, ipollfd, tmp) {
- list_del(&ipollfd->list);
- free(ipollfd);
+ for_each_removed_event_source_safe(ctx, ievent_source, tmp) {
+ list_del(&ievent_source->list);
+ free(ievent_source);
}
}
void usbi_io_exit(struct libusb_context *ctx)
{
- usbi_remove_pollfd(ctx, ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[0]);
- usbi_close(ctx->event_pipe[1]);
-#ifdef HAVE_TIMERFD
- if (usbi_using_timerfd(ctx)) {
- usbi_remove_pollfd(ctx, ctx->timerfd);
- close(ctx->timerfd);
+#ifdef HAVE_OS_TIMER
+ if (usbi_using_timer(ctx)) {
+ usbi_remove_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer));
+ usbi_destroy_timer(&ctx->timer);
}
#endif
+ usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event));
+ usbi_destroy_event(&ctx->event);
usbi_mutex_destroy(&ctx->flying_transfers_lock);
usbi_mutex_destroy(&ctx->events_lock);
usbi_mutex_destroy(&ctx->event_waiters_lock);
usbi_cond_destroy(&ctx->event_waiters_cond);
usbi_mutex_destroy(&ctx->event_data_lock);
usbi_tls_key_delete(ctx->event_handling_key);
- free(ctx->pollfds);
- cleanup_removed_pollfds(ctx);
+ cleanup_removed_event_sources(ctx);
+ free(ctx->event_data);
}
static int calculate_timeout(struct usbi_transfer *itransfer)
free(ptr);
}
-#ifdef HAVE_TIMERFD
-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
+/* iterates through the flying transfers, and rearms the timer based on the
* next upcoming timeout.
* must be called with flying_list locked.
* returns 0 on success or a LIBUSB_ERROR code on failure.
*/
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+#ifdef HAVE_OS_TIMER
+static int arm_timer_for_next_timeout(struct libusb_context *ctx)
{
struct usbi_transfer *itransfer;
+ if (!usbi_using_timer(ctx))
+ return 0;
+
for_each_transfer(ctx, itransfer) {
struct timespec *cur_ts = &itransfer->timeout;
/* if we've reached transfers of infinite timeout, then we have no
* arming to do */
if (!TIMESPEC_IS_SET(cur_ts))
- goto disarm;
+ break;
/* act on first transfer that has not already been handled */
if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) {
- int r;
- const struct itimerspec it = { {0, 0},
- { cur_ts->tv_sec, cur_ts->tv_nsec } };
usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0)
- return LIBUSB_ERROR_OTHER;
- return 0;
+ return usbi_arm_timer(&ctx->timer, cur_ts);
}
}
-disarm:
- return disarm_timerfd(ctx);
+ usbi_dbg("no timeouts, disarming timer");
+ return usbi_disarm_timer(&ctx->timer);
}
#else
-static int arm_timerfd_for_next_timeout(struct libusb_context *ctx)
+static inline int arm_timer_for_next_timeout(struct libusb_context *ctx)
{
UNUSED(ctx);
return 0;
/* otherwise we need to be inserted at the end */
list_add_tail(&itransfer->list, &ctx->flying_transfers);
out:
-#ifdef HAVE_TIMERFD
- if (first && usbi_using_timerfd(ctx) && TIMESPEC_IS_SET(timeout)) {
+#ifdef HAVE_OS_TIMER
+ if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(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},
- { timeout->tv_sec, timeout->tv_nsec } };
- usbi_dbg("arm timerfd for timeout in %ums (first in line)",
+ * rearm the timer with this transfer's timeout */
+ usbi_dbg("arm timer for timeout in %ums (first in line)",
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout);
- r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL);
- if (r < 0) {
- usbi_warn(ctx, "failed to arm first timerfd, errno=%d", errno);
- r = LIBUSB_ERROR_OTHER;
- }
+ r = usbi_arm_timer(&ctx->timer, timeout);
}
#else
UNUSED(first);
static int remove_from_flying_list(struct usbi_transfer *itransfer)
{
struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
- int rearm_timerfd;
+ int rearm_timer;
int r = 0;
usbi_mutex_lock(&ctx->flying_transfers_lock);
- rearm_timerfd = (TIMESPEC_IS_SET(&itransfer->timeout) &&
+ rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) &&
list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer);
list_del(&itransfer->list);
- if (usbi_using_timerfd(ctx) && rearm_timerfd)
- r = arm_timerfd_for_next_timeout(ctx);
+ if (rearm_timer)
+ r = arm_timer_for_next_timeout(ctx);
usbi_mutex_unlock(&ctx->flying_transfers_lock);
return r;
r = remove_from_flying_list(itransfer);
if (r < 0)
- usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno);
+ usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout");
usbi_mutex_lock(&itransfer->lock);
itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT;
pending_events = usbi_pending_events(ctx);
list_add_tail(&itransfer->completed_list, &ctx->completed_transfers);
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
}
pending_events = usbi_pending_events(ctx);
ctx->event_flags |= USBI_EVENT_USER_INTERRUPT;
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
usbi_mutex_unlock(&ctx->event_data_lock);
}
return r;
}
-#ifdef HAVE_TIMERFD
-static int handle_timerfd_trigger(struct libusb_context *ctx)
+static int handle_event_trigger(struct libusb_context *ctx)
+{
+ struct list_head hotplug_msgs;
+ int r = 0;
+
+ usbi_dbg("event triggered");
+
+ list_init(&hotplug_msgs);
+
+ /* take the the event data lock while processing events */
+ usbi_mutex_lock(&ctx->event_data_lock);
+
+ /* check if someone modified the event sources */
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED)
+ usbi_dbg("someone updated the event sources");
+
+ if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
+ usbi_dbg("someone purposefully interrupted");
+ ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
+ }
+
+ /* check if someone is closing a device */
+ if (ctx->device_close)
+ usbi_dbg("someone is closing a device");
+
+ /* check for any pending hotplug messages */
+ if (!list_empty(&ctx->hotplug_msgs)) {
+ usbi_dbg("hotplug message received");
+ list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
+ }
+
+ /* complete any pending transfers */
+ while (r == 0 && !list_empty(&ctx->completed_transfers)) {
+ struct usbi_transfer *itransfer =
+ list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
+
+ list_del(&itransfer->completed_list);
+ usbi_mutex_unlock(&ctx->event_data_lock);
+ r = usbi_backend.handle_transfer_completion(itransfer);
+ if (r)
+ usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r);
+ usbi_mutex_lock(&ctx->event_data_lock);
+ }
+
+ /* if no further pending events, clear the event */
+ if (!usbi_pending_events(ctx))
+ usbi_clear_event(&ctx->event);
+
+ 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);
+ }
+
+ return r;
+}
+
+#ifdef HAVE_OS_TIMER
+static int handle_timer_trigger(struct libusb_context *ctx)
{
int r;
goto out;
/* arm for next timeout */
- r = arm_timerfd_for_next_timeout(ctx);
+ r = arm_timer_for_next_timeout(ctx);
out:
usbi_mutex_unlock(&ctx->flying_transfers_lock);
* doing the same thing. */
static int handle_events(struct libusb_context *ctx, struct timeval *tv)
{
- int r;
- struct usbi_pollfd *ipollfd;
- usbi_nfds_t nfds = 0;
- usbi_nfds_t internal_nfds;
- struct pollfd *fds = NULL;
- int timeout_ms;
+ struct usbi_reported_events reported_events;
+ int r, timeout_ms;
/* prevent attempts to recursively handle events (e.g. calling into
* libusb_handle_events() from within a hotplug or transfer callback) */
- usbi_mutex_lock(&ctx->event_data_lock);
- r = 0;
if (usbi_handling_events(ctx))
- r = LIBUSB_ERROR_BUSY;
- else
- usbi_start_event_handling(ctx);
- usbi_mutex_unlock(&ctx->event_data_lock);
-
- if (r)
- return r;
-
- /* there are certain fds that libusb uses internally, currently:
- *
- * 1) event pipe
- * 2) timerfd
- *
- * the backend will never need to attempt to handle events on these fds, so
- * we determine how many fds are in use internally for this context and when
- * handle_events() is called in the backend, the pollfd list and count will
- * be adjusted to skip over these internal fds */
- if (usbi_using_timerfd(ctx))
- internal_nfds = 2;
- else
- internal_nfds = 1;
+ return LIBUSB_ERROR_BUSY;
- /* only reallocate the poll fds when the list of poll fds has been modified
- * since the last poll, otherwise reuse them to save the additional overhead */
+ /* only reallocate the event source data when the list of event sources has
+ * been modified since the last handle_events(), otherwise reuse them to
+ * save the additional overhead */
usbi_mutex_lock(&ctx->event_data_lock);
- /* clean up removed poll fds */
- cleanup_removed_pollfds(ctx);
- if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) {
- int i = 0;
-
- usbi_dbg("poll fds modified, reallocating");
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+ usbi_dbg("event sources modified, reallocating event data");
- free(ctx->pollfds);
- ctx->pollfds = NULL;
+ /* free anything removed since we last ran */
+ cleanup_removed_event_sources(ctx);
- /* sanity check - it is invalid for a context to have fewer than the
- * required internal fds (memory corruption?) */
- assert(ctx->pollfds_cnt >= internal_nfds);
-
- ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds));
- if (!ctx->pollfds) {
+ r = usbi_alloc_event_data(ctx);
+ if (r) {
usbi_mutex_unlock(&ctx->event_data_lock);
- r = LIBUSB_ERROR_NO_MEM;
- goto done;
- }
-
- for_each_pollfd(ctx, ipollfd) {
- struct libusb_pollfd *pollfd = &ipollfd->pollfd;
- ctx->pollfds[i].fd = pollfd->fd;
- ctx->pollfds[i].events = pollfd->events;
- i++;
+ return r;
}
/* reset the flag now that we have the updated list */
- ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED;
+ ctx->event_flags &= ~USBI_EVENT_EVENT_SOURCES_MODIFIED;
- /* if no further pending events, clear the event pipe so that we do
- * not immediately return from poll */
+ /* if no further pending events, clear the event so that we do
+ * not immediately return from the wait function */
if (!usbi_pending_events(ctx))
- usbi_clear_event(ctx);
+ usbi_clear_event(&ctx->event);
}
- fds = ctx->pollfds;
- nfds = ctx->pollfds_cnt;
usbi_mutex_unlock(&ctx->event_data_lock);
timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
if (tv->tv_usec % 1000)
timeout_ms++;
- usbi_dbg("poll() %d fds with timeout in %dms", (int)nfds, timeout_ms);
- r = usbi_poll(fds, nfds, timeout_ms);
- usbi_dbg("poll() returned %d", r);
- if (r == 0) {
- r = handle_timeouts(ctx);
- goto done;
- } else if (r == -1 && errno == EINTR) {
- r = LIBUSB_ERROR_INTERRUPTED;
- goto done;
- } else if (r < 0) {
- usbi_err(ctx, "poll failed, errno=%d", errno);
- r = LIBUSB_ERROR_IO;
+ usbi_start_event_handling(ctx);
+
+ r = usbi_wait_for_events(ctx, &reported_events, timeout_ms);
+ if (r != LIBUSB_SUCCESS) {
+ if (r == LIBUSB_ERROR_TIMEOUT)
+ r = handle_timeouts(ctx);
goto done;
}
- /* fds[0] is always the event pipe */
- if (fds[0].revents) {
- struct list_head hotplug_msgs;
- struct usbi_transfer *itransfer;
- int hotplug_cb_deregistered = 0;
- int ret = 0;
-
- list_init(&hotplug_msgs);
-
- usbi_dbg("caught a fish on the event pipe");
-
- /* take the the event data lock while processing events */
- usbi_mutex_lock(&ctx->event_data_lock);
-
- /* check if someone added a new poll fd */
- if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED)
- usbi_dbg("someone updated the poll fds");
-
- if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) {
- usbi_dbg("someone purposely interrupted");
- ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
- }
-
- if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
- usbi_dbg("someone unregistered a hotplug cb");
- ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
- hotplug_cb_deregistered = 1;
- }
-
- /* check if someone is closing a device */
- if (ctx->device_close)
- usbi_dbg("someone is closing a device");
-
- /* check for any pending hotplug messages */
- if (!list_empty(&ctx->hotplug_msgs)) {
- usbi_dbg("hotplug message received");
- list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
- }
-
- /* complete any pending transfers */
- while (ret == 0 && !list_empty(&ctx->completed_transfers)) {
- itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
- list_del(&itransfer->completed_list);
- usbi_mutex_unlock(&ctx->event_data_lock);
- ret = usbi_backend.handle_transfer_completion(itransfer);
- if (ret)
- usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
- usbi_mutex_lock(&ctx->event_data_lock);
- }
-
- /* if no further pending events, clear the event pipe */
- if (!usbi_pending_events(ctx))
- usbi_clear_event(ctx);
-
- usbi_mutex_unlock(&ctx->event_data_lock);
-
- if (hotplug_cb_deregistered)
- usbi_hotplug_deregister(ctx, 0);
-
- /* 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 (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event)
- libusb_unref_device(message->device);
-
- list_del(&message->list);
- free(message);
- }
-
- if (ret) {
+ if (reported_events.event_triggered) {
+ r = handle_event_trigger(ctx);
+ if (r) {
/* return error code */
- r = ret;
goto done;
}
-
- if (0 == --r)
- goto done;
}
-#ifdef HAVE_TIMERFD
- /* on timerfd configurations, fds[1] is the timerfd */
- if (usbi_using_timerfd(ctx) && fds[1].revents) {
- /* timerfd indicates that a timeout has expired */
- int ret;
- usbi_dbg("timerfd triggered");
-
- ret = handle_timerfd_trigger(ctx);
- if (ret < 0) {
+#ifdef HAVE_OS_TIMER
+ if (reported_events.timer_triggered) {
+ r = handle_timer_trigger(ctx);
+ if (r) {
/* return error code */
- r = ret;
goto done;
}
-
- if (0 == --r)
- goto done;
}
#endif
- for_each_removed_pollfd(ctx, ipollfd) {
- usbi_nfds_t n;
-
- for (n = internal_nfds ; n < nfds ; n++) {
- if (ipollfd->pollfd.fd == fds[n].fd) {
- /* pollfd was removed between the creation of the fd
- * array and here. remove any triggered revent as
- * it is no longer relevant */
- usbi_dbg("pollfd %d was removed. ignoring raised events",
- fds[n].fd);
- fds[n].revents = 0;
- break;
- }
- }
- }
+ if (!reported_events.num_ready)
+ goto done;
- r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
+ r = usbi_backend.handle_events(ctx, reported_events.event_data,
+ reported_events.event_data_count, reported_events.num_ready);
if (r)
usbi_err(ctx, "backend handle_events failed with error %d", r);
*/
int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx)
{
-#ifdef HAVE_TIMERFD
ctx = usbi_get_context(ctx);
- return usbi_using_timerfd(ctx);
-#else
- UNUSED(ctx);
- return 0;
-#endif
+ return usbi_using_timer(ctx);
}
/** \ingroup libusb_poll
int r;
ctx = usbi_get_context(ctx);
- if (usbi_using_timerfd(ctx))
+ if (usbi_using_timer(ctx))
return 0;
usbi_mutex_lock(&ctx->flying_transfers_lock);
libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
void *user_data)
{
+#if !defined(_WIN32) && !defined(__CYGWIN__)
ctx = usbi_get_context(ctx);
ctx->fd_added_cb = added_cb;
ctx->fd_removed_cb = removed_cb;
ctx->fd_cb_user_data = user_data;
+#else
+ usbi_err(ctx, "external polling of libusb's internal event sources " \
+ "is not yet supported on Windows");
+ UNUSED(added_cb);
+ UNUSED(removed_cb);
+ UNUSED(user_data);
+#endif
}
/*
* Interrupt the iteration of the event handling thread, so that it picks
- * up the fd change. Callers of this function must hold the event_data_lock.
+ * up the event source change. Callers of this function must hold the event_data_lock.
*/
-static void usbi_fd_notification(struct libusb_context *ctx)
+static void usbi_event_source_notification(struct libusb_context *ctx)
{
int pending_events;
/* Record that there is a new poll fd.
* Only signal an event if there are no prior pending events. */
pending_events = usbi_pending_events(ctx);
- ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED;
+ ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED;
if (!pending_events)
- usbi_signal_event(ctx);
+ usbi_signal_event(&ctx->event);
}
-/* Add a file descriptor to the list of file descriptors to be monitored.
- * events should be specified as a bitmask of events passed to poll(), e.g.
+/* Add an event source to the list of event sources to be monitored.
+ * poll_events should be specified as a bitmask of events passed to poll(), e.g.
* POLLIN and/or POLLOUT. */
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events)
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, short poll_events)
{
- struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd));
- if (!ipollfd)
+ struct usbi_event_source *ievent_source = malloc(sizeof(*ievent_source));
+
+ if (!ievent_source)
return LIBUSB_ERROR_NO_MEM;
- usbi_dbg("add fd %d events %d", fd, events);
- ipollfd->pollfd.fd = fd;
- ipollfd->pollfd.events = events;
+ usbi_dbg("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);
- list_add_tail(&ipollfd->list, &ctx->ipollfds);
- ctx->pollfds_cnt++;
- usbi_fd_notification(ctx);
+ list_add_tail(&ievent_source->list, &ctx->event_sources);
+ usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
+#if !defined(_WIN32) && !defined(__CYGWIN__)
if (ctx->fd_added_cb)
- ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data);
+ ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data);
+#endif
+
return 0;
}
-/* Remove a file descriptor from the list of file descriptors to be polled. */
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd)
+/* Remove an event source from the list of event sources to be monitored. */
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle)
{
- struct usbi_pollfd *ipollfd;
+ struct usbi_event_source *ievent_source;
int found = 0;
- usbi_dbg("remove fd %d", fd);
+ usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle);
usbi_mutex_lock(&ctx->event_data_lock);
- for_each_pollfd(ctx, ipollfd) {
- if (ipollfd->pollfd.fd == fd) {
+ for_each_event_source(ctx, ievent_source) {
+ if (ievent_source->data.os_handle == os_handle) {
found = 1;
break;
}
}
if (!found) {
- usbi_dbg("couldn't find fd %d to remove", fd);
+ usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle);
usbi_mutex_unlock(&ctx->event_data_lock);
return;
}
- list_del(&ipollfd->list);
- list_add_tail(&ipollfd->list, &ctx->removed_ipollfds);
- ctx->pollfds_cnt--;
- usbi_fd_notification(ctx);
+ list_del(&ievent_source->list);
+ list_add_tail(&ievent_source->list, &ctx->removed_event_sources);
+ usbi_event_source_notification(ctx);
usbi_mutex_unlock(&ctx->event_data_lock);
+#if !defined(_WIN32) && !defined(__CYGWIN__)
if (ctx->fd_removed_cb)
- ctx->fd_removed_cb(fd, ctx->fd_cb_user_data);
+ ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data);
+#endif
}
/** \ingroup libusb_poll
const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
libusb_context *ctx)
{
-#ifndef _WIN32
+#if !defined(_WIN32) && !defined(__CYGWIN__)
struct libusb_pollfd **ret = NULL;
- struct usbi_pollfd *ipollfd;
- size_t i = 0;
+ struct usbi_event_source *ievent_source;
+ size_t i;
+
+ static_assert(sizeof(struct usbi_event_source_data) == sizeof(struct libusb_pollfd),
+ "mismatch between usbi_event_source_data and libusb_pollfd sizes");
ctx = usbi_get_context(ctx);
usbi_mutex_lock(&ctx->event_data_lock);
- ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *));
+ i = 0;
+ for_each_event_source(ctx, ievent_source)
+ i++;
+
+ ret = calloc(i + 1, sizeof(struct libusb_pollfd *));
if (!ret)
goto out;
- for_each_pollfd(ctx, ipollfd)
- ret[i++] = (struct libusb_pollfd *)ipollfd;
- ret[ctx->pollfds_cnt] = NULL;
+ i = 0;
+ for_each_event_source(ctx, ievent_source)
+ ret[i++] = (struct libusb_pollfd *)ievent_source;
out:
usbi_mutex_unlock(&ctx->event_data_lock);
return (const struct libusb_pollfd **)ret;
#else
- usbi_err(ctx, "external polling of libusb's internal descriptors "\
- "is not yet supported on Windows platforms");
+ usbi_err(ctx, "external polling of libusb's internal event sources " \
+ "is not yet supported on Windows");
return NULL;
#endif
}
*/
void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds)
{
+#if !defined(_WIN32) && !defined(__CYGWIN__)
free((void *)pollfds);
+#else
+ UNUSED(pollfds);
+#endif
}
/* Backends may call this from handle_events to report disconnection of a
#define PTR_ALIGN(v) \
(((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1))
-/* Internal abstraction for poll */
-#if defined(POLL_POSIX)
-#include "os/poll_posix.h"
-#elif defined(POLL_WINDOWS)
-#include "os/poll_windows.h"
+/* Internal abstraction for event handling */
+#if defined(EVENTS_POSIX)
+#include "os/events_posix.h"
+#elif defined(EVENTS_WINDOWS)
+#include "os/events_windows.h"
#endif
/* Internal abstraction for thread synchronization */
libusb_log_cb log_handler;
#endif
- /* internal event pipe, used for signalling occurrence of an internal event. */
- int event_pipe[2];
+ /* used for signalling occurrence of an internal event. */
+ usbi_event_t event;
+
+#ifdef HAVE_OS_TIMER
+ /* used for timeout handling, if supported by OS.
+ * this timer is maintained to trigger on the next pending timeout */
+ usbi_timer_t timer;
+#endif
struct list_head usb_devs;
usbi_mutex_t usb_devs_lock;
* take this lock first */
usbi_mutex_t flying_transfers_lock;
+#if !defined(_WIN32) && !defined(__CYGWIN__)
/* user callbacks for pollfd changes */
libusb_pollfd_added_cb fd_added_cb;
libusb_pollfd_removed_cb fd_removed_cb;
void *fd_cb_user_data;
+#endif
/* ensures that only one thread is handling events at any one time */
usbi_mutex_t events_lock;
* in order to safely close a device. Protected by event_data_lock. */
unsigned int device_close;
- /* list and count of poll fds and an array of poll fd structures that is
- * (re)allocated as necessary prior to polling. Protected by event_data_lock. */
- struct list_head ipollfds;
- /* list of pollfds that have been removed. keeps track of pollfd changes
- * between the poll call and */
- struct list_head removed_ipollfds;
- struct pollfd *pollfds;
- usbi_nfds_t pollfds_cnt;
+ /* A list of currently active event sources. Protected by event_data_lock. */
+ struct list_head event_sources;
+
+ /* A list of event sources that have been removed since the last time
+ * event sources were waited on. Protected by event_data_lock. */
+ struct list_head removed_event_sources;
+
+ /* A pointer and count to platform-specific data used for monitoring event
+ * sources. Only accessed during event handling. */
+ void *event_data;
+ unsigned int event_data_cnt;
/* A list of pending hotplug messages. Protected by event_data_lock. */
struct list_head hotplug_msgs;
/* A list of pending completed transfers. Protected by event_data_lock. */
struct list_head completed_transfers;
-#ifdef HAVE_TIMERFD
- /* used for timeout handling, if supported by OS.
- * this timerfd is maintained to trigger on the next pending timeout */
- int timerfd;
-#endif
-
struct list_head list;
};
}
enum usbi_event_flags {
- /* The list of pollfds has been modified */
- USBI_EVENT_POLLFDS_MODIFIED = 1U << 0,
+ /* The list of event sources has been modified */
+ USBI_EVENT_EVENT_SOURCES_MODIFIED = 1U << 0,
/* The user has interrupted the event handler */
USBI_EVENT_USER_INTERRUPT = 1U << 1,
!list_empty(&ctx->completed_transfers);
}
-static inline int usbi_using_timerfd(struct libusb_context *ctx)
-{
-#ifdef HAVE_TIMERFD
- return ctx->timerfd >= 0;
-#else
- UNUSED(ctx);
- return 0;
-#endif
-}
-
struct libusb_device {
/* lock protects refcnt, everything else is finalized at initialization
* time */
void usbi_connect_device(struct libusb_device *dev);
void usbi_disconnect_device(struct libusb_device *dev);
-int usbi_signal_event(struct libusb_context *ctx);
-int usbi_clear_event(struct libusb_context *ctx);
+struct usbi_event_source {
+ struct usbi_event_source_data {
+ usbi_os_handle_t os_handle;
+ short poll_events;
+ } data;
+ struct list_head list;
+};
-struct usbi_pollfd {
- /* must come first */
- struct libusb_pollfd pollfd;
+int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle,
+ short poll_events);
+void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
- struct list_head list;
+/* OS event abstraction */
+
+int usbi_create_event(usbi_event_t *event);
+void usbi_destroy_event(usbi_event_t *event);
+void usbi_signal_event(usbi_event_t *event);
+void usbi_clear_event(usbi_event_t *event);
+
+#ifdef HAVE_OS_TIMER
+int usbi_create_timer(usbi_timer_t *timer);
+void usbi_destroy_timer(usbi_timer_t *timer);
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout);
+int usbi_disarm_timer(usbi_timer_t *timer);
+#endif
+
+static inline int usbi_using_timer(struct libusb_context *ctx)
+{
+#ifdef HAVE_OS_TIMER
+ return usbi_timer_valid(&ctx->timer);
+#else
+ UNUSED(ctx);
+ return 0;
+#endif
+}
+
+struct usbi_reported_events {
+ unsigned int event_triggered:1;
+#ifdef HAVE_OS_TIMER
+ unsigned int timer_triggered:1;
+#endif
+ void *event_data;
+ unsigned int event_data_count;
+ unsigned int num_ready;
};
-int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events);
-void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
+int usbi_alloc_event_data(struct libusb_context *ctx);
+int usbi_wait_for_events(struct libusb_context *ctx,
+ struct usbi_reported_events *reported_events, int timeout_ms);
/* accessor functions for structure private data */
* and other operations so that those operations can happen (hopefully)
* without hiccup. This is also a good place to inform libusb that it
* should monitor certain file descriptors related to this device -
- * see the usbi_add_pollfd() function.
+ * see the usbi_add_event_source() function.
*
* Your backend should also initialize the device structure
* (dev_handle->dev), which is NULL at the beginning of the call.
* and other operations so that those operations can happen (hopefully)
* without hiccup. This is also a good place to inform libusb that it
* should monitor certain file descriptors related to this device -
- * see the usbi_add_pollfd() function.
+ * see the usbi_add_event_source() function.
*
* This function should not generate any bus I/O and should not block.
*
/* Close a device such that the handle cannot be used again. Your backend
* should destroy any resources that were allocated in the open path.
- * This may also be a good place to call usbi_remove_pollfd() to inform
- * libusb of any file descriptors associated with this device that should
- * no longer be monitored.
+ * This may also be a good place to call usbi_remove_event_source() to
+ * inform libusb of any event sources associated with this device that
+ * should no longer be monitored.
*
* This function is called when the user closes a device handle.
*/
*/
void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
- /* Handle any pending events on file descriptors. Optional.
+ /* Handle any pending events on event sources. Optional.
*
- * Provide this function when file descriptors directly indicate device
- * or transfer activity. If your backend does not have such file descriptors,
+ * Provide this function when event sources directly indicate device
+ * or transfer activity. If your backend does not have such event sources,
* implement the handle_transfer_completion function below.
*
* This involves monitoring any active transfers and processing their
* completion or cancellation.
*
- * The function is passed an array of pollfd structures (size nfds)
- * as a result of the poll() system call. The num_ready parameter
- * indicates the number of file descriptors that have reported events
- * (i.e. the poll() return value). This should be enough information
- * for you to determine which actions need to be taken on the currently
- * active transfers.
+ * The function is passed a pointer that represents platform-specific
+ * data for monitoring event sources (size count). This data is to be
+ * (re)allocated as necessary when event sources are modified.
+ * The num_ready parameter indicates the number of event sources that
+ * have reported events. This should be enough information for you to
+ * determine which actions need to be taken on the currently active
+ * transfers.
*
* For any cancelled transfers, call usbi_handle_transfer_cancellation().
* For completed transfers, call usbi_handle_transfer_completion().
* Return 0 on success, or a LIBUSB_ERROR code on failure.
*/
int (*handle_events)(struct libusb_context *ctx,
- struct pollfd *fds, usbi_nfds_t nfds, int num_ready);
+ void *event_data, unsigned int count, unsigned int num_ready);
/* Handle transfer completion. Optional.
*
- * Provide this function when there are no file descriptors available
- * that directly indicate device or transfer activity. If your backend does
- * have such file descriptors, implement the handle_events function above.
+ * Provide this function when there are no event sources available that
+ * directly indicate device or transfer activity. If your backend does
+ * have such event sources, implement the handle_events function above.
*
* Your backend must tell the library when a transfer has completed by
* calling usbi_signal_transfer_completion(). You should store any private
#define for_each_transfer_safe(ctx, t, n) \
for_each_safe_helper(t, n, &(ctx)->flying_transfers, struct usbi_transfer)
-#define for_each_pollfd(ctx, p) \
- for_each_helper(p, &(ctx)->ipollfds, struct usbi_pollfd)
+#define for_each_event_source(ctx, e) \
+ for_each_helper(e, &(ctx)->event_sources, struct usbi_event_source)
-#define for_each_removed_pollfd(ctx, p) \
- for_each_helper(p, &(ctx)->removed_ipollfds, struct usbi_pollfd)
+#define for_each_removed_event_source(ctx, e) \
+ for_each_helper(e, &(ctx)->removed_event_sources, struct usbi_event_source)
-#define for_each_removed_pollfd_safe(ctx, p, n) \
- for_each_safe_helper(p, n, &(ctx)->removed_ipollfds, struct usbi_pollfd)
+#define for_each_removed_event_source_safe(ctx, e, n) \
+ for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
#ifdef __cplusplus
}
--- /dev/null
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * 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
+ */
+
+#include "libusbi.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_TIMERFD
+#include <sys/timerfd.h>
+#endif
+#include <unistd.h>
+
+#ifdef HAVE_NFDS_T
+typedef nfds_t usbi_nfds_t;
+#else
+typedef unsigned int usbi_nfds_t;
+#endif
+
+int usbi_create_event(usbi_event_t *event)
+{
+#if defined(HAVE_PIPE2)
+ int ret = pipe2(event->pipefd, O_CLOEXEC);
+#else
+ int ret = pipe(event->pipefd);
+#endif
+
+ if (ret != 0) {
+ usbi_err(NULL, "failed to create pipe, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
+ ret = fcntl(event->pipefd[0], F_GETFD);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+
+ ret = fcntl(event->pipefd[1], F_GETFD);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+#endif
+
+ ret = fcntl(event->pipefd[1], F_GETFL);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+ ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK);
+ if (ret == -1) {
+ usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno);
+ goto err_close_pipe;
+ }
+
+ return 0;
+
+err_close_pipe:
+ close(event->pipefd[1]);
+ close(event->pipefd[0]);
+ return LIBUSB_ERROR_OTHER;
+}
+
+void usbi_destroy_event(usbi_event_t *event)
+{
+ if (close(event->pipefd[1]) == -1)
+ usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno);
+ if (close(event->pipefd[0]) == -1)
+ usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno);
+}
+
+void usbi_signal_event(usbi_event_t *event)
+{
+ unsigned char dummy = 1;
+ ssize_t r;
+
+ r = write(event->pipefd[1], &dummy, sizeof(dummy));
+ if (r != sizeof(dummy))
+ usbi_warn(NULL, "pipe write failed");
+}
+
+void usbi_clear_event(usbi_event_t *event)
+{
+ unsigned char dummy;
+ ssize_t r;
+
+ r = read(event->pipefd[0], &dummy, sizeof(dummy));
+ if (r != sizeof(dummy))
+ usbi_warn(NULL, "pipe read failed");
+}
+
+#ifdef HAVE_TIMERFD
+int usbi_create_timer(usbi_timer_t *timer)
+{
+ timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
+ if (timer->timerfd == -1) {
+ usbi_warn(NULL, "failed to create timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+void usbi_destroy_timer(usbi_timer_t *timer)
+{
+ if (close(timer->timerfd) == -1)
+ usbi_warn(NULL, "failed to close timerfd, errno=%d", errno);
+}
+
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
+{
+ const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } };
+
+ if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) {
+ usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+int usbi_disarm_timer(usbi_timer_t *timer)
+{
+ const struct itimerspec it = { { 0, 0 }, { 0, 0 } };
+
+ if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) {
+ usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno);
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+#endif
+
+int usbi_alloc_event_data(struct libusb_context *ctx)
+{
+ struct usbi_event_source *ievent_source;
+ struct pollfd *fds;
+ size_t i = 0;
+
+ if (ctx->event_data) {
+ free(ctx->event_data);
+ ctx->event_data = NULL;
+ }
+
+ ctx->event_data_cnt = 0;
+ for_each_event_source(ctx, ievent_source)
+ ctx->event_data_cnt++;
+
+ fds = calloc(ctx->event_data_cnt, sizeof(*fds));
+ if (!fds)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for_each_event_source(ctx, ievent_source) {
+ fds[i].fd = ievent_source->data.os_handle;
+ fds[i].events = ievent_source->data.poll_events;
+ i++;
+ }
+
+ ctx->event_data = fds;
+ return 0;
+}
+
+int usbi_wait_for_events(struct libusb_context *ctx,
+ struct usbi_reported_events *reported_events, int timeout_ms)
+{
+ struct pollfd *fds = ctx->event_data;
+ usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt;
+ int internal_fds, num_ready;
+
+ usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
+ num_ready = poll(fds, nfds, timeout_ms);
+ usbi_dbg("poll() returned %d", num_ready);
+ if (num_ready == 0) {
+ if (usbi_using_timer(ctx))
+ goto done;
+ return LIBUSB_ERROR_TIMEOUT;
+ } else if (num_ready == -1) {
+ if (errno == EINTR)
+ return LIBUSB_ERROR_INTERRUPTED;
+ usbi_err(ctx, "poll() failed, errno=%d", errno);
+ return LIBUSB_ERROR_IO;
+ }
+
+ /* fds[0] is always the internal signalling event */
+ if (fds[0].revents) {
+ reported_events->event_triggered = 1;
+ num_ready--;
+ } else {
+ reported_events->event_triggered = 0;
+ }
+
+#ifdef HAVE_OS_TIMER
+ /* on timer configurations, fds[1] is the timer */
+ if (usbi_using_timer(ctx) && fds[1].revents) {
+ reported_events->timer_triggered = 1;
+ num_ready--;
+ } else {
+ reported_events->timer_triggered = 0;
+ }
+#endif
+
+ assert(num_ready > 0);
+ if (!num_ready)
+ goto done;
+
+ /* the backend will never need to attempt to handle events on the
+ * library's internal file descriptors, so we determine how many are
+ * in use internally for this context and skip these when passing any
+ * remaining pollfds to the backend. */
+ internal_fds = usbi_using_timer(ctx) ? 2 : 1;
+ fds += internal_fds;
+ nfds -= internal_fds;
+
+ usbi_mutex_lock(&ctx->event_data_lock);
+ if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) {
+ struct usbi_event_source *ievent_source;
+
+ for_each_removed_event_source(ctx, ievent_source) {
+ usbi_nfds_t n;
+
+ for (n = 0; n < nfds; n++) {
+ if (ievent_source->data.os_handle != fds[n].fd)
+ continue;
+ if (!fds[n].revents)
+ continue;
+ /* pollfd was removed between the creation of the fds array and
+ * here. remove triggered revent as it is no longer relevant. */
+ usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd);
+ fds[n].revents = 0;
+ num_ready--;
+ break;
+ }
+ }
+ }
+ usbi_mutex_unlock(&ctx->event_data_lock);
+
+ if (num_ready) {
+ assert(num_ready > 0);
+ reported_events->event_data = fds;
+ reported_events->event_data_count = (unsigned int)nfds;
+ }
+
+done:
+ reported_events->num_ready = num_ready;
+ return LIBUSB_SUCCESS;
+}
--- /dev/null
+/*
+ * libusb event abstraction on POSIX platforms
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * 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_EVENTS_POSIX_H
+#define LIBUSB_EVENTS_POSIX_H
+
+#include <poll.h>
+
+typedef int usbi_os_handle_t;
+#define USBI_OS_HANDLE_FORMAT_STRING "fd %d"
+
+typedef struct usbi_event {
+ int pipefd[2];
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0])
+#define USBI_EVENT_POLL_EVENTS POLLIN
+#define USBI_INVALID_EVENT { { -1, -1 } }
+
+#ifdef HAVE_TIMERFD
+#define HAVE_OS_TIMER 1
+typedef struct usbi_timer {
+ int timerfd;
+} usbi_timer_t;
+#define USBI_TIMER_OS_HANDLE(t) ((t)->timerfd)
+#define USBI_TIMER_POLL_EVENTS POLLIN
+
+static inline int usbi_timer_valid(usbi_timer_t *timer)
+{
+ return timer->timerfd >= 0;
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * libusb event abstraction on Microsoft Windows
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * 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
+ */
+
+#include <config.h>
+
+#include "libusbi.h"
+#include "windows_common.h"
+
+int usbi_create_event(usbi_event_t *event)
+{
+ event->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (event->hEvent == NULL) {
+ usbi_err(NULL, "CreateEvent failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+void usbi_destroy_event(usbi_event_t *event)
+{
+ if (!CloseHandle(event->hEvent))
+ usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0));
+}
+
+void usbi_signal_event(usbi_event_t *event)
+{
+ if (!SetEvent(event->hEvent))
+ usbi_warn(NULL, "SetEvent failed: %s", windows_error_str(0));
+}
+
+void usbi_clear_event(usbi_event_t *event)
+{
+ if (!ResetEvent(event->hEvent))
+ usbi_warn(NULL, "ResetEvent failed: %s", windows_error_str(0));
+}
+
+#ifdef HAVE_OS_TIMER
+int usbi_create_timer(usbi_timer_t *timer)
+{
+ timer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
+ if (timer->hTimer == NULL) {
+ usbi_warn(NULL, "CreateWaitableTimer failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+void usbi_destroy_timer(usbi_timer_t *timer)
+{
+ if (!CloseHandle(timer->hTimer))
+ usbi_warn(NULL, "CloseHandle failed: %s", windows_error_str(0));
+}
+
+int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout)
+{
+ struct timespec systime, remaining;
+ FILETIME filetime;
+ LARGE_INTEGER dueTime;
+ int r;
+
+ /* Transfer timeouts are based on the monotonic clock and the waitable
+ * timers on the system clock. This requires a conversion between the
+ * two, so we calculate the remaining time relative to the monotonic
+ * clock and calculate an absolute system time for the timer expiration.
+ * Note that if the timeout has already passed, the remaining time will
+ * be negative and thus an absolute system time in the past will be set.
+ * This works just as intended because the timer becomes signalled
+ * immediately. */
+ r = usbi_clock_gettime(USBI_CLOCK_MONOTONIC, &systime);
+ if (r < 0) {
+ usbi_err(NULL, "failed to read monotonic clock");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ TIMESPEC_SUB(timeout, &systime, &remaining);
+
+ GetSystemTimeAsFileTime(&filetime);
+ dueTime.LowPart = filetime.dwLowDateTime;
+ dueTime.HighPart = filetime.dwHighDateTime;
+ dueTime.QuadPart += (remaining.tv_sec * 10000000LL) + (remaining.tv_nsec / 100LL);
+
+ if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) {
+ usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+
+int usbi_disarm_timer(usbi_timer_t *timer)
+{
+ LARGE_INTEGER dueTime;
+
+ /* A manual-reset waitable timer will stay in the signalled state until
+ * another call to SetWaitableTimer() is made. It is possible that the
+ * timer has already expired by the time we come in to disarm it, so to
+ * be entirely sure the timer is disarmed and not in the signalled state,
+ * we will set it with an impossibly large expiration and immediately
+ * cancel. */
+ dueTime.QuadPart = LLONG_MAX;
+ if (!SetWaitableTimer(timer->hTimer, &dueTime, 0, NULL, NULL, FALSE)) {
+ usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ if (!CancelWaitableTimer(timer->hTimer)) {
+ usbi_warn(NULL, "SetWaitableTimer failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ return 0;
+}
+#endif
+
+int usbi_alloc_event_data(struct libusb_context *ctx)
+{
+ struct usbi_event_source *ievent_source;
+ HANDLE *handles;
+ size_t i = 0;
+
+ /* Event sources are only added during usbi_io_init(). We should not
+ * be running this function again if the event data has already been
+ * allocated. */
+ if (ctx->event_data) {
+ usbi_warn(ctx, "program assertion failed - event data already allocated");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ctx->event_data_cnt = 0;
+ for_each_event_source(ctx, ievent_source)
+ ctx->event_data_cnt++;
+
+ /* We only expect up to two HANDLEs to wait on, one for the internal
+ * signalling event and the other for the timer. */
+ if (ctx->event_data_cnt != 1 && ctx->event_data_cnt != 2) {
+ usbi_err(ctx, "program assertion failed - expected exactly 1 or 2 HANDLEs");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ handles = calloc(ctx->event_data_cnt, sizeof(HANDLE));
+ if (!handles)
+ return LIBUSB_ERROR_NO_MEM;
+
+ for_each_event_source(ctx, ievent_source) {
+ handles[i] = ievent_source->data.os_handle;
+ i++;
+ }
+
+ ctx->event_data = handles;
+ return 0;
+}
+
+int usbi_wait_for_events(struct libusb_context *ctx,
+ struct usbi_reported_events *reported_events, int timeout_ms)
+{
+ HANDLE *handles = ctx->event_data;
+ DWORD num_handles = (DWORD)ctx->event_data_cnt;
+ DWORD result;
+
+ usbi_dbg("WaitForMultipleObjects() for %lu HANDLEs with timeout in %dms", ULONG_CAST(num_handles), timeout_ms);
+ result = WaitForMultipleObjects(num_handles, handles, FALSE, (DWORD)timeout_ms);
+ usbi_dbg("WaitForMultipleObjects() returned %lu", ULONG_CAST(result));
+ if (result == WAIT_TIMEOUT) {
+ if (usbi_using_timer(ctx))
+ goto done;
+ return LIBUSB_ERROR_TIMEOUT;
+ } else if (result == WAIT_FAILED) {
+ usbi_err(ctx, "WaitForMultipleObjects() failed: %s", windows_error_str(0));
+ return LIBUSB_ERROR_IO;
+ }
+
+ result -= WAIT_OBJECT_0;
+
+ /* handles[0] is always the internal signalling event */
+ if (result == 0)
+ reported_events->event_triggered = 1;
+ else
+ reported_events->event_triggered = 0;
+
+#ifdef HAVE_OS_TIMER
+ /* on timer configurations, handles[1] is the timer */
+ if (usbi_using_timer(ctx)) {
+ /* The WaitForMultipleObjects() function reports the index of
+ * the first object that became signalled. If the internal
+ * signalling event was reported, we need to also check and
+ * report whether the timer is in the signalled state. */
+ if (result == 1 || WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0)
+ reported_events->timer_triggered = 1;
+ else
+ reported_events->timer_triggered = 0;
+ } else {
+ reported_events->timer_triggered = 0;
+ }
+#endif
+
+done:
+ /* no events are ever reported to the backend */
+ reported_events->num_ready = 0;
+ return LIBUSB_SUCCESS;
+}
--- /dev/null
+/*
+ * libusb event abstraction on Microsoft Windows
+ *
+ * Copyright © 2020 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * 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_EVENTS_WINDOWS_H
+#define LIBUSB_EVENTS_WINDOWS_H
+
+typedef HANDLE usbi_os_handle_t;
+#define USBI_OS_HANDLE_FORMAT_STRING "HANDLE %p"
+
+typedef struct usbi_event {
+ HANDLE hEvent;
+} usbi_event_t;
+#define USBI_EVENT_OS_HANDLE(e) ((e)->hEvent)
+#define USBI_EVENT_POLL_EVENTS 0
+#define USBI_INVALID_EVENT { INVALID_HANDLE_VALUE }
+
+#define HAVE_OS_TIMER 1
+typedef struct usbi_timer {
+ HANDLE hTimer;
+} usbi_timer_t;
+#define USBI_TIMER_OS_HANDLE(t) ((t)->hTimer)
+#define USBI_TIMER_POLL_EVENTS 0
+
+static inline int usbi_timer_valid(usbi_timer_t *timer)
+{
+ return timer->hTimer != NULL;
+}
+
+#endif
#endif
static int linux_netlink_socket = -1;
-static int netlink_control_pipe[2] = { -1, -1 };
+static usbi_event_t netlink_control_event = USBI_INVALID_EVENT;
static pthread_t libusb_linux_event_thread;
static void *linux_netlink_event_thread_main(void *arg);
goto err_close_socket;
}
- ret = usbi_pipe(netlink_control_pipe);
+ ret = usbi_create_event(&netlink_control_event);
if (ret) {
- usbi_err(NULL, "failed to create netlink control pipe");
+ usbi_err(NULL, "failed to create netlink control event");
goto err_close_socket;
}
ret = pthread_create(&libusb_linux_event_thread, NULL, linux_netlink_event_thread_main, NULL);
if (ret != 0) {
usbi_err(NULL, "failed to create netlink event thread (%d)", ret);
- goto err_close_pipe;
+ goto err_destroy_event;
}
return LIBUSB_SUCCESS;
-err_close_pipe:
- close(netlink_control_pipe[0]);
- close(netlink_control_pipe[1]);
- netlink_control_pipe[0] = -1;
- netlink_control_pipe[1] = -1;
+err_destroy_event:
+ usbi_destroy_event(&netlink_control_event);
+ netlink_control_event = (usbi_event_t)USBI_INVALID_EVENT;
err_close_socket:
close(linux_netlink_socket);
linux_netlink_socket = -1;
int linux_netlink_stop_event_monitor(void)
{
- char dummy = 1;
- ssize_t r;
+ int ret;
assert(linux_netlink_socket != -1);
- /* Write some dummy data to the control pipe and
- * wait for the thread to exit */
- r = write(netlink_control_pipe[1], &dummy, sizeof(dummy));
- if (r <= 0)
- usbi_warn(NULL, "netlink control pipe signal failed");
+ /* Signal the control event and wait for the thread to exit */
+ usbi_signal_event(&netlink_control_event);
+
+ ret = pthread_join(libusb_linux_event_thread, NULL);
+ if (ret)
+ usbi_warn(NULL, "failed to join netlink event thread (%d)", ret);
- pthread_join(libusb_linux_event_thread, NULL);
+ usbi_destroy_event(&netlink_control_event);
+ netlink_control_event = (usbi_event_t)USBI_INVALID_EVENT;
close(linux_netlink_socket);
linux_netlink_socket = -1;
- /* close and reset control pipe */
- close(netlink_control_pipe[0]);
- close(netlink_control_pipe[1]);
- netlink_control_pipe[0] = -1;
- netlink_control_pipe[1] = -1;
-
return LIBUSB_SUCCESS;
}
static void *linux_netlink_event_thread_main(void *arg)
{
- char dummy;
- int r;
- ssize_t nb;
struct pollfd fds[] = {
- { .fd = netlink_control_pipe[0],
- .events = POLLIN },
+ { .fd = USBI_EVENT_OS_HANDLE(&netlink_control_event),
+ .events = USBI_EVENT_POLL_EVENTS },
{ .fd = linux_netlink_socket,
.events = POLLIN },
};
+ int r;
UNUSED(arg);
usbi_dbg("netlink event thread entering");
- while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) {
- if (r < 0) {
- /* temporary failure */
- continue;
+ while (1) {
+ r = poll(fds, 2, -1);
+ if (r == -1) {
+ /* check for temporary failure */
+ if (errno == EINTR)
+ continue;
+ usbi_err(NULL, "poll() failed, errno=%d", errno);
+ break;
}
- if (fds[0].revents & POLLIN) {
- /* activity on control pipe, read the byte and exit */
- nb = read(netlink_control_pipe[0], &dummy, sizeof(dummy));
- if (nb <= 0)
- usbi_warn(NULL, "netlink control pipe read failed");
+ if (fds[0].revents) {
+ /* activity on control event, exit */
break;
}
- if (fds[1].revents & POLLIN) {
+ if (fds[1].revents) {
usbi_mutex_static_lock(&linux_hotplug_lock);
linux_netlink_read_message();
usbi_mutex_static_unlock(&linux_hotplug_lock);
/* udev context */
static struct udev *udev_ctx = NULL;
static int udev_monitor_fd = -1;
-static int udev_control_pipe[2] = {-1, -1};
+static usbi_event_t udev_control_event = USBI_INVALID_EVENT;
static struct udev_monitor *udev_monitor = NULL;
static pthread_t linux_event_thread;
}
}
- r = usbi_pipe(udev_control_pipe);
+ r = usbi_create_event(&udev_control_event);
if (r) {
- usbi_err(NULL, "could not create udev control pipe");
+ usbi_err(NULL, "failed to create udev control event");
goto err_free_monitor;
}
r = pthread_create(&linux_event_thread, NULL, linux_udev_event_thread_main, NULL);
if (r) {
- usbi_err(NULL, "creating hotplug event thread (%d)", r);
- goto err_close_pipe;
+ usbi_err(NULL, "failed to create hotplug event thread (%d)", r);
+ goto err_destroy_event;
}
return LIBUSB_SUCCESS;
-err_close_pipe:
- close(udev_control_pipe[0]);
- close(udev_control_pipe[1]);
+err_destroy_event:
+ usbi_destroy_event(&udev_control_event);
+ udev_control_event = (usbi_event_t)USBI_INVALID_EVENT;
err_free_monitor:
udev_monitor_unref(udev_monitor);
udev_monitor = NULL;
int linux_udev_stop_event_monitor(void)
{
- char dummy = 1;
- ssize_t r;
+ int r;
assert(udev_ctx != NULL);
assert(udev_monitor != NULL);
assert(udev_monitor_fd != -1);
- /* Write some dummy data to the control pipe and
- * wait for the thread to exit */
- r = write(udev_control_pipe[1], &dummy, sizeof(dummy));
- if (r <= 0) {
- usbi_warn(NULL, "udev control pipe signal failed");
- }
- pthread_join(linux_event_thread, NULL);
+ /* Signal the control event and wait for the thread to exit */
+ usbi_signal_event(&udev_control_event);
+
+ r = pthread_join(linux_event_thread, NULL);
+ if (r)
+ usbi_warn(NULL, "failed to join hotplug event thread (%d)", r);
+
+ usbi_destroy_event(&udev_control_event);
+ udev_control_event = (usbi_event_t)USBI_INVALID_EVENT;
/* Release the udev monitor */
udev_monitor_unref(udev_monitor);
udev_unref(udev_ctx);
udev_ctx = NULL;
- /* close and reset control pipe */
- close(udev_control_pipe[0]);
- close(udev_control_pipe[1]);
- udev_control_pipe[0] = -1;
- udev_control_pipe[1] = -1;
-
return LIBUSB_SUCCESS;
}
static void *linux_udev_event_thread_main(void *arg)
{
- char dummy;
- int r;
- ssize_t nb;
- struct udev_device *udev_dev;
struct pollfd fds[] = {
- {.fd = udev_control_pipe[0],
- .events = POLLIN},
- {.fd = udev_monitor_fd,
- .events = POLLIN},
+ { .fd = USBI_EVENT_OS_HANDLE(&udev_control_event),
+ .events = USBI_EVENT_POLL_EVENTS },
+ { .fd = udev_monitor_fd,
+ .events = POLLIN },
};
+ struct udev_device *udev_dev;
+ int r;
UNUSED(arg);
usbi_dbg("udev event thread entering");
- while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) {
- if (r < 0) {
- /* temporary failure */
- continue;
+ while (1) {
+ r = poll(fds, 2, -1);
+ if (r == -1) {
+ /* check for temporary failure */
+ if (errno == EINTR)
+ continue;
+ usbi_err(NULL, "poll() failed, errno=%d", errno);
+ break;
}
- if (fds[0].revents & POLLIN) {
- /* activity on control pipe, read the byte and exit */
- nb = read(udev_control_pipe[0], &dummy, sizeof(dummy));
- if (nb <= 0) {
- usbi_warn(NULL, "udev control pipe read failed");
- }
+ if (fds[0].revents) {
+ /* activity on control event, exit */
break;
}
- if (fds[1].revents & POLLIN) {
+ if (fds[1].revents) {
usbi_mutex_static_lock(&linux_hotplug_lock);
udev_dev = udev_monitor_receive_device(udev_monitor);
if (udev_dev)
#include <sys/mman.h>
#include <sys/utsname.h>
#include <sys/vfs.h>
+#include <unistd.h>
/* sysfs vs usbfs:
* opening a usbfs node causes the device to be resumed, so we attempt to
hpriv->caps = USBFS_CAP_BULK_CONTINUATION;
}
- return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
+ return usbi_add_event_source(HANDLE_CTX(handle), hpriv->fd, POLLOUT);
}
static int op_wrap_sys_device(struct libusb_context *ctx,
/* fd may have already been removed by POLLERR condition in op_handle_events() */
if (!hpriv->fd_removed)
- usbi_remove_pollfd(HANDLE_CTX(dev_handle), hpriv->fd);
+ usbi_remove_event_source(HANDLE_CTX(dev_handle), hpriv->fd);
if (!hpriv->fd_keep)
close(hpriv->fd);
}
}
static int op_handle_events(struct libusb_context *ctx,
- struct pollfd *fds, usbi_nfds_t nfds, int num_ready)
+ void *event_data, unsigned int count, unsigned int num_ready)
{
- usbi_nfds_t n;
+ struct pollfd *fds = event_data;
+ unsigned int n;
int r;
usbi_mutex_lock(&ctx->open_devs_lock);
- for (n = 0; n < nfds && num_ready > 0; n++) {
+ for (n = 0; n < count && num_ready > 0; n++) {
struct pollfd *pollfd = &fds[n];
struct libusb_device_handle *handle;
struct linux_device_handle_priv *hpriv = NULL;
/* remove the fd from the pollfd set so that it doesn't continuously
* trigger an event, and flag that it has been removed so op_close()
* doesn't try to remove it a second time */
- usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->fd);
+ usbi_remove_event_source(HANDLE_CTX(handle), hpriv->fd);
hpriv->fd_removed = 1;
/* device will still be marked as attached if hotplug monitor thread
+++ /dev/null
-/*
- * poll_posix: poll compatibility wrapper for POSIX systems
- * Copyright © 2013 RealVNC Ltd.
- *
- * 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
- *
- */
-
-#include "libusbi.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-int usbi_pipe(int pipefd[2])
-{
-#if defined(HAVE_PIPE2)
- int ret = pipe2(pipefd, O_CLOEXEC);
-#else
- int ret = pipe(pipefd);
-#endif
-
- if (ret != 0) {
- usbi_err(NULL, "failed to create pipe, errno=%d", errno);
- return ret;
- }
-
-#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
- ret = fcntl(pipefd[0], F_GETFD);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
- goto err_close_pipe;
- }
-
- ret = fcntl(pipefd[1], F_GETFD);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno);
- goto err_close_pipe;
- }
-#endif
-
- ret = fcntl(pipefd[1], F_GETFL);
- if (ret == -1) {
- usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno);
- goto err_close_pipe;
- }
- ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
- if (ret == -1) {
- usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno);
- goto err_close_pipe;
- }
-
- return 0;
-
-err_close_pipe:
- close(pipefd[0]);
- close(pipefd[1]);
- return ret;
-}
+++ /dev/null
-#ifndef LIBUSB_POLL_POSIX_H
-#define LIBUSB_POLL_POSIX_H
-
-#include <poll.h>
-#include <unistd.h>
-
-#ifdef HAVE_NFDS_T
-typedef nfds_t usbi_nfds_t;
-#else
-typedef unsigned int usbi_nfds_t;
-#endif
-
-#define usbi_write write
-#define usbi_read read
-#define usbi_close close
-#define usbi_poll poll
-
-int usbi_pipe(int pipefd[2]);
-
-#endif /* LIBUSB_POLL_POSIX_H */
+++ /dev/null
-/*
- * poll_windows: poll compatibility wrapper for Windows
- * Copyright © 2017 Chris Dickens <christopher.a.dickens@gmail.com>
- *
- * 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
- *
- */
-
-/*
- * poll() and pipe() Windows compatibility layer for libusb 1.0
- *
- * The pipe pollable synchronous I/O works using the overlapped event associated
- * with a fake pipe. The read/write functions are only meant to be used in that
- * context.
- */
-
-#include "libusbi.h"
-#include "windows_common.h"
-
-#include <errno.h>
-#include <intrin.h>
-#include <malloc.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-
-// private data
-struct file_descriptor {
- LONG refcount;
- OVERLAPPED overlapped;
-};
-
-static usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER;
-
-#define BITS_PER_BYTE 8
-#define BITMAP_BITS_PER_WORD (sizeof(unsigned long) * BITS_PER_BYTE)
-#define FD_TABLE_INCR_SIZE 64
-
-static struct file_descriptor **fd_table;
-static unsigned long *fd_table_bitmap;
-static unsigned int fd_table_size;
-static unsigned int fd_count;
-
-#define return_with_errno(err) \
- do { \
- errno = (err); \
- return -1; \
- } while (0)
-
-static struct file_descriptor *alloc_fd(LONG refcount)
-{
- struct file_descriptor *fd = calloc(1, sizeof(*fd));
-
- if (fd == NULL)
- return NULL;
- fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- if (fd->overlapped.hEvent == NULL) {
- free(fd);
- return NULL;
- }
- fd->refcount = refcount;
- return fd;
-}
-
-static struct file_descriptor *get_fd(unsigned int _fd, bool ref)
-{
- struct file_descriptor *fd = NULL;
-
- if (_fd < fd_table_size)
- fd = fd_table[_fd];
- if (fd != NULL && ref)
- InterlockedIncrement(&fd->refcount);
-
- return fd;
-}
-
-static void put_fd(struct file_descriptor *fd)
-{
- if (InterlockedDecrement(&fd->refcount) == 0L) {
- CloseHandle(fd->overlapped.hEvent);
- free(fd);
- }
-}
-
-static int install_fd(struct file_descriptor *fd)
-{
- unsigned int n;
-
- if (fd_count == fd_table_size) {
- struct file_descriptor **new_table;
- unsigned long *new_bitmap;
-
- // Need to expand the fd table and bitmap
- new_table = realloc(fd_table, (fd_table_size + FD_TABLE_INCR_SIZE) * sizeof(*new_table));
- if (new_table == NULL)
- return -ENOMEM;
- memset(new_table + fd_table_size, 0, FD_TABLE_INCR_SIZE * sizeof(*new_table));
- fd_table = new_table;
-
- new_bitmap = realloc(fd_table_bitmap, (fd_table_size + FD_TABLE_INCR_SIZE) / BITS_PER_BYTE);
- if (new_bitmap == NULL)
- return -ENOMEM;
- memset(new_bitmap + (fd_table_size / BITMAP_BITS_PER_WORD), 0, FD_TABLE_INCR_SIZE / BITS_PER_BYTE);
- fd_table_bitmap = new_bitmap;
-
- fd_table_size += FD_TABLE_INCR_SIZE;
- assert(fd_table_size < (unsigned int)INT_MAX);
- }
-
- for (n = 0; n < fd_table_size; n += BITMAP_BITS_PER_WORD) {
- unsigned int idx = n / BITMAP_BITS_PER_WORD;
- ULONG mask, pos = 0U;
- unsigned char nonzero;
-
- mask = ~fd_table_bitmap[idx];
- if (mask == 0U)
- continue;
-
- nonzero = _BitScanForward(&pos, mask);
- assert(nonzero);
- fd_table_bitmap[idx] |= 1U << pos;
- n += pos;
- break;
- }
-
- assert(n < fd_table_size);
- assert(fd_table[n] == NULL);
- fd_table[n] = fd;
- fd_count++;
-
- return n;
-}
-
-static void remove_fd(unsigned int pos)
-{
- assert(fd_table[pos] != NULL);
- fd_table[pos] = NULL;
- fd_table_bitmap[pos / BITMAP_BITS_PER_WORD] &= ~(1U << (pos % BITMAP_BITS_PER_WORD));
- fd_count--;
- if (fd_count == 0) {
- free(fd_table);
- free(fd_table_bitmap);
- fd_table = NULL;
- fd_table_bitmap = NULL;
- fd_table_size = 0;
- }
-}
-
-struct wait_thread_data {
- HANDLE thread;
- HANDLE handles[MAXIMUM_WAIT_OBJECTS];
- DWORD num_handles;
- DWORD error;
-};
-
-static DWORD WINAPI WaitThread(LPVOID lpParam)
-{
- struct wait_thread_data *thread_data = lpParam;
- HANDLE notify_event = thread_data->handles[0];
- DWORD status;
-
- status = WaitForMultipleObjects(thread_data->num_handles, thread_data->handles, FALSE, INFINITE);
- if (status < (WAIT_OBJECT_0 + thread_data->num_handles)) {
- if (status > WAIT_OBJECT_0) {
- // This will wake up all the other waiting threads
- SetEvent(notify_event);
- }
- thread_data->error = 0;
- } else {
- assert(status == WAIT_FAILED);
- thread_data->error = (status == WAIT_FAILED) ? GetLastError() : ERROR_CAN_NOT_COMPLETE;
- }
-
- return 0;
-}
-
-static DWORD poll_wait(const HANDLE *wait_handles, DWORD num_wait_handles, DWORD timeout)
-{
- struct wait_thread_data *thread_data;
- HANDLE notify_event;
- HANDLE *handles;
- int n, num_threads;
- DWORD error, status;
-
- if (num_wait_handles <= MAXIMUM_WAIT_OBJECTS)
- return WaitForMultipleObjects(num_wait_handles, wait_handles, FALSE, timeout);
-
- // To wait on more than MAXIMUM_WAIT_OBJECTS, each thread (including the
- // current thread) will wait on an event and (MAXIMUM_WAIT_OBJECTS - 1)
- // HANDLEs. The event is shared amongst all threads so that any thread
- // that returns from a WaitForMultipleObjects() call will set the event
- // and wake up all the other threads.
- notify_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (notify_event == NULL)
- return WAIT_FAILED;
-
- num_threads = 1 + (num_wait_handles - MAXIMUM_WAIT_OBJECTS - 1) / (MAXIMUM_WAIT_OBJECTS - 1);
- thread_data = malloc(num_threads * sizeof(*thread_data));
- if (thread_data == NULL) {
- CloseHandle(notify_event);
- SetLastError(ERROR_OUTOFMEMORY);
- return WAIT_FAILED;
- }
-
- handles = _alloca(MAXIMUM_WAIT_OBJECTS * sizeof(HANDLE));
- handles[0] = notify_event;
- memcpy(handles + 1, wait_handles, (MAXIMUM_WAIT_OBJECTS - 1) * sizeof(HANDLE));
- wait_handles += MAXIMUM_WAIT_OBJECTS - 1;
- num_wait_handles -= MAXIMUM_WAIT_OBJECTS - 1;
-
- for (n = 0; n < num_threads; n++) {
- DWORD copy_size = MIN(num_wait_handles, MAXIMUM_WAIT_OBJECTS - 1);
-
- thread_data[n].handles[0] = notify_event;
- memcpy(thread_data[n].handles + 1, wait_handles, copy_size * sizeof(HANDLE));
- thread_data[n].num_handles = copy_size + 1;
-
- // Create the thread that will wait on these HANDLEs
- thread_data[n].thread = CreateThread(NULL, 0, WaitThread, &thread_data[n], 0, NULL);
- if (thread_data[n].thread == NULL) {
- thread_data[n].error = GetLastError();
- SetEvent(notify_event);
- num_threads = n + 1;
- break;
- }
-
- wait_handles += copy_size;
- num_wait_handles -= copy_size;
- }
-
- status = WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS, handles, FALSE, timeout);
- if (status < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)) {
- if (status > WAIT_OBJECT_0) {
- // Wake up all the waiting threads
- SetEvent(notify_event);
- status = WAIT_OBJECT_0;
- }
- error = 0;
- } else if (status == WAIT_TIMEOUT) {
- // Wake up all the waiting threads
- SetEvent(notify_event);
- error = 0;
- } else {
- assert(status == WAIT_FAILED);
- error = (status == WAIT_FAILED) ? GetLastError() : ERROR_CAN_NOT_COMPLETE;
- }
-
- for (n = 0; n < num_threads; n++) {
- if (thread_data[n].thread != NULL) {
- if (WaitForSingleObject(thread_data[n].thread, INFINITE) != WAIT_OBJECT_0)
- usbi_err(NULL, "WaitForSingleObject() failed: %lu", ULONG_CAST(GetLastError()));
- CloseHandle(thread_data[n].thread);
- }
- if (thread_data[n].error) {
- usbi_err(NULL, "wait thread %d had error %lu\n", n, ULONG_CAST(thread_data[n].error));
- error = thread_data[n].error;
- status = WAIT_FAILED;
- }
- }
-
- free(thread_data);
-
- CloseHandle(notify_event);
-
- if (status == WAIT_FAILED)
- SetLastError(error);
-
- return status;
-}
-
-/*
- * POSIX poll equivalent, using Windows OVERLAPPED
- * Currently, this function only accepts one of POLLIN or POLLOUT per fd
- * (but you can create multiple fds from the same handle for read and write)
- */
-int usbi_poll(struct pollfd *fds, usbi_nfds_t nfds, int timeout)
-{
- struct file_descriptor **fds_array;
- HANDLE *handles_array;
- struct file_descriptor *fd;
- usbi_nfds_t n;
- int nready;
-
- if (nfds <= MAXIMUM_WAIT_OBJECTS) {
- fds_array = _alloca(nfds * sizeof(*fds_array));
- handles_array = _alloca(nfds * sizeof(*handles_array));
- } else {
- fds_array = malloc(nfds * sizeof(*fds_array));
- if (fds_array == NULL)
- return_with_errno(ENOMEM);
- handles_array = malloc(nfds * sizeof(*handles_array));
- if (handles_array == NULL) {
- free(fds_array);
- return_with_errno(ENOMEM);
- }
- }
-
- usbi_mutex_static_lock(&fd_table_lock);
- for (n = 0; n < nfds; n++) {
- struct pollfd *pfd = &fds[n];
-
- // Keep it simple - only allow either POLLIN *or* POLLOUT
- assert((pfd->events == POLLIN) || (pfd->events == POLLOUT));
- if ((pfd->events != POLLIN) && (pfd->events != POLLOUT)) {
- fds_array[n] = NULL;
- continue;
- }
-
- // All file descriptors must be valid
- fd = get_fd(pfd->fd, true);
- assert(fd != NULL);
- if (fd == NULL) {
- fds_array[n] = NULL;
- continue;
- }
-
- // We hold a reference to fd for the duration of usbi_poll()
- fds_array[n] = fd;
- handles_array[n] = fd->overlapped.hEvent;
- }
- usbi_mutex_static_unlock(&fd_table_lock);
-
- nready = 0;
- while (nready == 0) {
- DWORD ret;
-
- // Check all fds for events
- for (n = 0; n < nfds; n++) {
- fd = fds_array[n];
- if (fd == NULL) {
- fds[n].revents = POLLNVAL;
- nready++;
- } else if (HasOverlappedIoCompleted(&fd->overlapped)) {
- fds[n].revents = fds[n].events;
- nready++;
- } else {
- fds[n].revents = 0;
- }
- }
-
- if ((nready != 0) || (timeout == 0))
- break;
-
- // Wait for any of the events to trigger
- ret = poll_wait(handles_array, nfds, (timeout < 0) ? INFINITE : (DWORD)timeout);
- if (ret == WAIT_TIMEOUT) {
- assert(timeout > 0);
- timeout = 0;
- } else if (ret == WAIT_FAILED) {
- usbi_err(NULL, "WaitForMultipleObjects failed: %lu", ULONG_CAST(GetLastError()));
- errno = EIO;
- nready = -1;
- }
- }
-
- for (n = 0; n < nfds; n++) {
- if (fds_array[n] != NULL)
- put_fd(fds_array[n]);
- }
-
- if (nfds > MAXIMUM_WAIT_OBJECTS) {
- free(handles_array);
- free(fds_array);
- }
-
- return nready;
-}
-
-/*
- * close a fake file descriptor
- */
-int usbi_close(int _fd)
-{
- struct file_descriptor *fd;
-
- usbi_mutex_static_lock(&fd_table_lock);
- fd = get_fd(_fd, false);
- if (fd != NULL)
- remove_fd(_fd);
- usbi_mutex_static_unlock(&fd_table_lock);
-
- if (fd == NULL)
- return_with_errno(EBADF);
-
- put_fd(fd);
-
- return 0;
-}
-
-/*
-* Create a fake pipe.
-* As libusb only uses pipes for signaling, all we need from a pipe is an
-* event. To that extent, we create a single wfd and overlapped as a means
-* to access that event.
-*/
-int usbi_pipe(int filedes[2])
-{
- struct file_descriptor *fd;
- int r_fd, w_fd;
- int error = 0;
-
- fd = alloc_fd(2);
- if (fd == NULL)
- return_with_errno(ENOMEM);
-
- fd->overlapped.Internal = STATUS_PENDING;
-
- usbi_mutex_static_lock(&fd_table_lock);
- r_fd = install_fd(fd);
- if (r_fd >= 0) {
- w_fd = install_fd(fd);
- if (w_fd < 0) {
- remove_fd(r_fd);
- error = w_fd;
- }
- } else {
- error = r_fd;
- w_fd = -1; // Keep compiler happy
- }
- usbi_mutex_static_unlock(&fd_table_lock);
-
- if (error) {
- CloseHandle(fd->overlapped.hEvent);
- free(fd);
- return_with_errno(error);
- }
-
- filedes[0] = r_fd;
- filedes[1] = w_fd;
-
- return 0;
-}
-
-/*
- * synchronous write for fake "pipe" signaling
- */
-ssize_t usbi_write(int _fd, const void *buf, size_t count)
-{
- struct file_descriptor *fd;
-
- UNUSED(buf);
-
- if (count != sizeof(unsigned char)) {
- usbi_err(NULL, "this function should only used for signaling");
- return_with_errno(EINVAL);
- }
-
- usbi_mutex_static_lock(&fd_table_lock);
- fd = get_fd(_fd, false);
- if (fd != NULL) {
- assert(fd->overlapped.Internal == STATUS_PENDING);
- fd->overlapped.Internal = STATUS_WAIT_0;
- SetEvent(fd->overlapped.hEvent);
- }
- usbi_mutex_static_unlock(&fd_table_lock);
-
- if (fd == NULL)
- return_with_errno(EBADF);
-
- return sizeof(unsigned char);
-}
-
-/*
- * synchronous read for fake "pipe" signaling
- */
-ssize_t usbi_read(int _fd, void *buf, size_t count)
-{
- struct file_descriptor *fd;
-
- UNUSED(buf);
-
- if (count != sizeof(unsigned char)) {
- usbi_err(NULL, "this function should only used for signaling");
- return_with_errno(EINVAL);
- }
-
- usbi_mutex_static_lock(&fd_table_lock);
- fd = get_fd(_fd, false);
- if (fd != NULL) {
- assert(fd->overlapped.Internal == STATUS_WAIT_0);
- fd->overlapped.Internal = STATUS_PENDING;
- ResetEvent(fd->overlapped.hEvent);
- }
- usbi_mutex_static_unlock(&fd_table_lock);
-
- if (fd == NULL)
- return_with_errno(EBADF);
-
- return sizeof(unsigned char);
-}
+++ /dev/null
-/*
- * Windows compat: POSIX compatibility wrapper
- * Copyright © 2012-2013 RealVNC Ltd.
- * Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
- * Copyright © 2016-2018 Chris Dickens <christopher.a.dickens@gmail.com>
- * With contributions from Michael Plante, Orin Eman et al.
- * Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
- *
- * 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_POLL_WINDOWS_H
-#define LIBUSB_POLL_WINDOWS_H
-
-#define POLLIN 0x0001 /* There is data to read */
-#define POLLPRI 0x0002 /* There is urgent data to read */
-#define POLLOUT 0x0004 /* Writing now will not block */
-#define POLLERR 0x0008 /* Error condition */
-#define POLLHUP 0x0010 /* Hung up */
-#define POLLNVAL 0x0020 /* Invalid request: fd not open */
-
-typedef unsigned int usbi_nfds_t;
-
-struct pollfd {
- int fd; /* file descriptor */
- short events; /* requested events */
- short revents; /* returned events */
-};
-
-int usbi_pipe(int pipefd[2]);
-int usbi_poll(struct pollfd *fds, usbi_nfds_t nfds, int timeout);
-ssize_t usbi_write(int fd, const void *buf, size_t count);
-ssize_t usbi_read(int fd, void *buf, size_t count);
-int usbi_close(int fd);
-
-#endif
/*
* Open a device and associate the HANDLE with the context's I/O completion port
*/
-HANDLE windows_open(struct libusb_device *dev, const char *path, DWORD access)
+static HANDLE windows_open(struct libusb_device *dev, const char *path, DWORD access)
{
struct libusb_context *ctx = DEVICE_CTX(dev);
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
-#define LIBUSB_NANO 11531
+#define LIBUSB_NANO 11532
/* Define to 1 to enable message logging. */
#define ENABLE_LOGGING 1
-/* Define to 1 if using the Windows poll() implementation. */
-#define POLL_WINDOWS 1
+/* Define to 1 if using the Windows events abstraction. */
+#define EVENTS_WINDOWS 1
/* Define to 1 if using Windows threads. */
#define THREADS_WINDOWS 1
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">
<ItemGroup>
<ClCompile Include="..\libusb\core.c" />
<ClCompile Include="..\libusb\descriptor.c" />
+ <ClCompile Include="..\libusb\os\events_windows.c" />
<ClCompile Include="..\libusb\hotplug.c" />
<ClCompile Include="..\libusb\io.c" />
- <ClCompile Include="..\libusb\os\poll_windows.c" />
<ClCompile Include="..\libusb\strerror.c" />
<ClCompile Include="..\libusb\sync.c" />
<ClCompile Include="..\libusb\os\threads_windows.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include=".\config.h" />
+ <ClInclude Include="..\libusb\os\events_windows.h" />
<ClInclude Include="..\libusb\hotplug.h" />
<ClInclude Include="..\libusb\libusb.h" />
<ClInclude Include="..\libusb\libusbi.h" />
- <ClInclude Include="..\libusb\os\poll_windows.h" />
<ClInclude Include="..\libusb\os\threads_windows.h" />
<ClInclude Include="..\libusb\version.h" />
<ClInclude Include="..\libusb\version_nano.h" />
<ClInclude Include=".\config.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\libusb\os\events_windows.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\libusb\hotplug.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\libusb\libusbi.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="..\libusb\os\poll_windows.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="..\libusb\os\threads_windows.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClCompile Include="..\libusb\descriptor.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\hotplug.c">
+ <ClCompile Include="..\libusb\os\events_windows.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\io.c">
+ <ClCompile Include="..\libusb\hotplug.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\libusb\os\poll_windows.c">
+ <ClCompile Include="..\libusb\io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\libusb\strerror.c">