summaryrefslogtreecommitdiff
path: root/libusb/os
diff options
context:
space:
mode:
authorChris Dickens <christopher.a.dickens@gmail.com>2020-08-12 16:06:38 -0700
committerChris Dickens <christopher.a.dickens@gmail.com>2020-08-12 16:06:38 -0700
commitd67eb5beaa44c17c09f089a522ff483f4978a978 (patch)
tree8a5dffe3e6779dc95fa1adb32a1dc6d7be69c7af /libusb/os
parentba6b8bcb7ea204e65a3deec3be81aacc9f4b6d5a (diff)
downloadlibusb-d67eb5beaa44c17c09f089a522ff483f4978a978.tar.gz
core: Introduce platform events abstraction
The way in which system handles or resources are represented differs greatly between Unix-like operating systems and Windows. Ever since Windows support was added to libusb, Windows been emulating principles of Unix-like operating systems such as file descriptors and poll(). This commit introduces an abstraction layer that completely removes the need to perform any emulation. Fundamentally there are three things that each platform provides to libusb: 1) A signallable event 2) A timer (not required, but useful) 3) A means to wait for event sources such as the above to be triggered The POSIX abstraction for Unix-like operating systems uses file descriptors as the "handles" to the underlying system resources. The signallable event is implemented using a pipe, the timer as a timerfd (where supported) and the poll() system call is used to wait for events. The Windows abstraction uses native HANDLEs as the "handles" to the underlying system resources. The signallable event is implemented using a manual-reset event, the timer as a manual-reset waitable timer, and the WaitForMultipleObjects() system call is used to wait for events. Closes #252 Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
Diffstat (limited to 'libusb/os')
-rw-r--r--libusb/os/events_posix.c275
-rw-r--r--libusb/os/events_posix.h50
-rw-r--r--libusb/os/events_windows.c219
-rw-r--r--libusb/os/events_windows.h46
-rw-r--r--libusb/os/linux_netlink.c68
-rw-r--r--libusb/os/linux_udev.c77
-rw-r--r--libusb/os/linux_usbfs.c14
-rw-r--r--libusb/os/poll_posix.c81
-rw-r--r--libusb/os/poll_posix.h20
-rw-r--r--libusb/os/poll_windows.c502
-rw-r--r--libusb/os/poll_windows.h49
-rw-r--r--libusb/os/windows_winusb.c2
12 files changed, 664 insertions, 739 deletions
diff --git a/libusb/os/events_posix.c b/libusb/os/events_posix.c
new file mode 100644
index 0000000..01064af
--- /dev/null
+++ b/libusb/os/events_posix.c
@@ -0,0 +1,275 @@
+/*
+ * 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;
+}
diff --git a/libusb/os/events_posix.h b/libusb/os/events_posix.h
new file mode 100644
index 0000000..401de04
--- /dev/null
+++ b/libusb/os/events_posix.h
@@ -0,0 +1,50 @@
+/*
+ * 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
diff --git a/libusb/os/events_windows.c b/libusb/os/events_windows.c
new file mode 100644
index 0000000..9f120bb
--- /dev/null
+++ b/libusb/os/events_windows.c
@@ -0,0 +1,219 @@
+/*
+ * 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;
+}
diff --git a/libusb/os/events_windows.h b/libusb/os/events_windows.h
new file mode 100644
index 0000000..0c5e0b0
--- /dev/null
+++ b/libusb/os/events_windows.h
@@ -0,0 +1,46 @@
+/*
+ * 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
diff --git a/libusb/os/linux_netlink.c b/libusb/os/linux_netlink.c
index 9917df2..77c83c5 100644
--- a/libusb/os/linux_netlink.c
+++ b/libusb/os/linux_netlink.c
@@ -48,7 +48,7 @@
#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);
@@ -125,25 +125,23 @@ int linux_netlink_start_event_monitor(void)
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;
@@ -153,28 +151,23 @@ err:
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;
}
@@ -353,15 +346,13 @@ static int linux_netlink_read_message(void)
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);
@@ -373,19 +364,20 @@ static void *linux_netlink_event_thread_main(void *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);
diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c
index e8fb198..beb2f05 100644
--- a/libusb/os/linux_udev.c
+++ b/libusb/os/linux_udev.c
@@ -34,7 +34,7 @@
/* 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;
@@ -100,23 +100,23 @@ int linux_udev_start_event_monitor(void)
}
}
- 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;
@@ -130,20 +130,21 @@ err:
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);
@@ -154,27 +155,19 @@ int linux_udev_stop_event_monitor(void)
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);
@@ -186,20 +179,20 @@ static void *linux_udev_event_thread_main(void *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)
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 1e727ef..c2980d0 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -36,6 +36,7 @@
#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
@@ -1266,7 +1267,7 @@ static int initialize_handle(struct libusb_device_handle *handle, int fd)
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,
@@ -1352,7 +1353,7 @@ static void op_close(struct libusb_device_handle *dev_handle)
/* 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);
}
@@ -2614,13 +2615,14 @@ static int reap_for_handle(struct libusb_device_handle *handle)
}
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;
@@ -2645,7 +2647,7 @@ static int op_handle_events(struct libusb_context *ctx,
/* 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
diff --git a/libusb/os/poll_posix.c b/libusb/os/poll_posix.c
deleted file mode 100644
index 93e9bee..0000000
--- a/libusb/os/poll_posix.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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;
-}
diff --git a/libusb/os/poll_posix.h b/libusb/os/poll_posix.h
deleted file mode 100644
index 48c4904..0000000
--- a/libusb/os/poll_posix.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#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 */
diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c
deleted file mode 100644
index c2bc105..0000000
--- a/libusb/os/poll_windows.c
+++ /dev/null
@@ -1,502 +0,0 @@
-/*
- * 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);
-}
diff --git a/libusb/os/poll_windows.h b/libusb/os/poll_windows.h
deleted file mode 100644
index 14a5b27..0000000
--- a/libusb/os/poll_windows.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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
diff --git a/libusb/os/windows_winusb.c b/libusb/os/windows_winusb.c
index 04dda4a..fa6721e 100644
--- a/libusb/os/windows_winusb.c
+++ b/libusb/os/windows_winusb.c
@@ -467,7 +467,7 @@ static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc,
/*
* 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);