123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- /*
- * 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_EVENTFD
- #include <sys/eventfd.h>
- #endif
- #ifdef HAVE_TIMERFD
- #include <sys/timerfd.h>
- #endif
- #include <unistd.h>
- #ifdef HAVE_EVENTFD
- #define EVENT_READ_FD(e) ((e)->eventfd)
- #define EVENT_WRITE_FD(e) ((e)->eventfd)
- #else
- #define EVENT_READ_FD(e) ((e)->pipefd[0])
- #define EVENT_WRITE_FD(e) ((e)->pipefd[1])
- #endif
- #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)
- {
- #ifdef HAVE_EVENTFD
- event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
- if (event->eventfd == -1) {
- usbi_err(NULL, "failed to create eventfd, errno=%d", errno);
- return LIBUSB_ERROR_OTHER;
- }
- return 0;
- #else
- #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;
- #endif
- }
- void usbi_destroy_event(usbi_event_t *event)
- {
- #ifdef HAVE_EVENTFD
- if (close(event->eventfd) == -1)
- usbi_warn(NULL, "failed to close eventfd, errno=%d", errno);
- #else
- 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);
- #endif
- }
- void usbi_signal_event(usbi_event_t *event)
- {
- uint64_t dummy = 1;
- ssize_t r;
- r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy));
- if (r != sizeof(dummy))
- usbi_warn(NULL, "event write failed");
- }
- void usbi_clear_event(usbi_event_t *event)
- {
- uint64_t dummy;
- ssize_t r;
- r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy));
- if (r != sizeof(dummy))
- usbi_warn(NULL, "event 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(ctx, "poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms);
- num_ready = poll(fds, nfds, timeout_ms);
- usbi_dbg(ctx, "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
- 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(ctx, "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;
- }
|