static void LIBUSB_CALL usb_remove_pollfd(int fd, void *opaque) { (void)opaque; log_event_debug("Got told to remove libusb pollfd (handle: %d)", fd); event_remove_source(fd, EVENT_SOURCE_TYPE_USB); }
static void LIBUSB_CALL usb_add_pollfd(int fd, short events, void *opaque) { libusb_context *context = opaque; log_event_debug("Got told to add libusb pollfd (handle: %d, events: %d)", fd, events); // FIXME: need to handle libusb timeouts event_add_source(fd, EVENT_SOURCE_TYPE_USB, events, usb_handle_events, context); // FIXME: handle error? }
int event_run_platform(Array *event_sources, bool *running, EventCleanupFunction cleanup) { int result = -1; int i; EventSource *event_source; fd_set *fd_read_set; fd_set *fd_write_set; fd_set *fd_error_set; int ready; int handled; uint8_t byte = 1; int rc; int event_source_count; uint32_t received_events; if (event_add_source(_usb_poll_ready_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC, EVENT_READ, event_forward_usb_events, event_sources) < 0) { return -1; } *running = true; _usb_poll_running = true; thread_create(&_usb_poll_thread, event_poll_usb_events, event_sources); cleanup(); event_cleanup_sources(); while (*running) { // update SocketSet arrays if (event_reserve_socket_set(&_socket_read_set, // FIXME: this over-allocates event_sources->count) < 0) { log_error("Could not resize socket read set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } if (event_reserve_socket_set(&_socket_write_set, // FIXME: this over-allocates event_sources->count) < 0) { log_error("Could not resize socket write set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } if (event_reserve_socket_set(&_socket_error_set, // FIXME: this over-allocates event_sources->count) < 0) { log_error("Could not resize socket error set: %s (%d)", get_errno_name(errno), errno); goto cleanup; } _socket_read_set->count = 0; _socket_write_set->count = 0; _socket_error_set->count = 0; for (i = 0; i < event_sources->count; ++i) { event_source = array_get(event_sources, i); if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) { continue; } if ((event_source->events & EVENT_READ) != 0) { _socket_read_set->sockets[_socket_read_set->count++] = event_source->handle; } if ((event_source->events & EVENT_WRITE) != 0) { _socket_write_set->sockets[_socket_write_set->count++] = event_source->handle; } if ((event_source->events & EVENT_PRIO) != 0) { log_error("Event prio is not supported"); } if ((event_source->events & EVENT_ERROR) != 0) { _socket_error_set->sockets[_socket_error_set->count++] = event_source->handle; } } // start to select log_event_debug("Starting to select on %d + %d + %d %s event source(s)", _socket_read_set->count, _socket_write_set->count, _socket_error_set->count, event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false)); semaphore_release(&_usb_poll_resume); fd_read_set = event_get_socket_set_as_fd_set(_socket_read_set); fd_write_set = event_get_socket_set_as_fd_set(_socket_write_set); fd_error_set = event_get_socket_set_as_fd_set(_socket_error_set); ready = select(0, fd_read_set, fd_write_set, fd_error_set, NULL); if (_usb_poll_running) { log_event_debug("Sending suspend signal to USB poll thread"); if (usbi_write(_usb_poll_suspend_pipe[1], &byte, 1) < 0) { log_error("Could not write to USB suspend pipe"); _usb_poll_stuck = true; goto cleanup; } semaphore_acquire(&_usb_poll_suspend); if (usbi_read(_usb_poll_suspend_pipe[0], &byte, 1) < 0) { log_error("Could not read from USB suspend pipe"); _usb_poll_stuck = true; goto cleanup; } } if (ready == SOCKET_ERROR) { rc = ERRNO_WINAPI_OFFSET + WSAGetLastError(); if (rc == ERRNO_WINAPI_OFFSET + WSAEINTR) { continue; } log_error("Could not select on %s event sources: %s (%d)", event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false), get_errno_name(rc), rc); goto cleanup; } // handle select result log_event_debug("Select returned %d %s event source(s) as ready", ready, event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false)); handled = 0; // cache event source count here to avoid looking at new event // sources that got added during the event handling event_source_count = event_sources->count; for (i = 0; *running && i < event_source_count && ready > handled; ++i) { event_source = array_get(event_sources, i); received_events = 0; if (event_source->type != EVENT_SOURCE_TYPE_GENERIC) { continue; } if (FD_ISSET(event_source->handle, fd_read_set)) { received_events |= EVENT_READ; } if (FD_ISSET(event_source->handle, fd_write_set)) { received_events |= EVENT_WRITE; } if (FD_ISSET(event_source->handle, fd_error_set)) { received_events |= EVENT_ERROR; } if (received_events == 0) { continue; } event_handle_source(event_source, received_events); ++handled; } if (ready == handled) { log_event_debug("Handled all ready %s event sources", event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false)); } else if (*running) { log_warn("Handled only %d of %d ready %s event source(s)", handled, ready, event_get_source_type_name(EVENT_SOURCE_TYPE_GENERIC, false)); } // now cleanup event sources that got marked as disconnected/removed // during the event handling cleanup(); event_cleanup_sources(); } result = 0; cleanup: *running = false; if (_usb_poll_running && !_usb_poll_stuck) { _usb_poll_running = false; log_debug("Stopping USB poll thread"); if (usbi_write(_usb_poll_suspend_pipe[1], &byte, 1) < 0) { log_error("Could not write to USB suspend pipe"); } else { semaphore_release(&_usb_poll_resume); thread_join(&_usb_poll_thread); } } thread_destroy(&_usb_poll_thread); event_remove_source(_usb_poll_ready_pipe.read_end, EVENT_SOURCE_TYPE_GENERIC); return result; }
static void event_forward_usb_events(void *opaque) { Array *event_sources = opaque; uint8_t byte; int handled = 0; int event_source_count; int i; int k; EventSource *event_source; struct usbi_pollfd *pollfd; (void)opaque; if (pipe_read(&_usb_poll_ready_pipe, &byte, sizeof(byte)) < 0) { log_error("Could not read from USB ready pipe: %s (%d)", get_errno_name(errno), errno); return; } if (_usb_poll_pollfds.count == 0 || _usb_poll_pollfds_ready == 0) { return; } // cache event source count here to avoid looking at new event // sources that got added during the event handling event_source_count = event_sources->count; // this loop assumes that the USB subset of the event source array and the // pollfd array can be matched by index. this means that the first N USB // items of the event source array (with N = items in pollfd array - 1) are // not removed or replaced during the iteration over the pollfd array. // because of this event_remove_source only marks event sources as removed, // the actual removal is done later by event_cleanup_sources for (i = 0, k = 1; i < event_source_count && k < _usb_poll_pollfds.count && _usb_poll_pollfds_ready > handled; ++i) { event_source = array_get(event_sources, i); if (event_source->type != EVENT_SOURCE_TYPE_USB) { continue; } pollfd = array_get(&_usb_poll_pollfds, k); if ((int)event_source->handle != pollfd->fd) { continue; } ++k; if (pollfd->revents == 0) { continue; } event_handle_source(event_source, pollfd->revents); ++handled; } if (_usb_poll_pollfds_ready == handled) { log_event_debug("Handled all ready %s event sources", event_get_source_type_name(EVENT_SOURCE_TYPE_USB, false)); } else { log_warn("Handled only %d of %d ready %s event source(s)", handled, _usb_poll_pollfds_ready, event_get_source_type_name(EVENT_SOURCE_TYPE_USB, false)); } }
static void event_poll_usb_events(void *opaque) { Array *event_sources = opaque; int count; struct usbi_pollfd *pollfd; EventSource *event_source; int i; int k; int ready; uint8_t byte = 0; log_debug("Started USB poll thread"); for (;;) { semaphore_acquire(&_usb_poll_resume); log_event_debug("Resumed USB poll thread"); if (!_usb_poll_running) { goto cleanup; } _usb_poll_pollfds_ready = 0; // update pollfd array count = 0; for (i = 0; i < event_sources->count; ++i) { event_source = array_get(event_sources, i); if (event_source->type == EVENT_SOURCE_TYPE_USB) { ++count; } } if (count == 0) { goto suspend; } ++count; // add the suspend pipe if (array_resize(&_usb_poll_pollfds, count, NULL) < 0) { log_error("Could not resize USB pollfd array: %s (%d)", get_errno_name(errno), errno); goto cleanup; } pollfd = array_get(&_usb_poll_pollfds, 0); pollfd->fd = _usb_poll_suspend_pipe[0]; pollfd->events = USBI_POLLIN; pollfd->revents = 0; for (i = 0, k = 1; i < event_sources->count; ++i) { event_source = array_get(event_sources, i); if (event_source->type != EVENT_SOURCE_TYPE_USB) { continue; } pollfd = array_get(&_usb_poll_pollfds, k); pollfd->fd = event_source->handle; pollfd->events = (short)event_source->events; pollfd->revents = 0; ++k; } // start to poll log_event_debug("Starting to poll on %d %s event source(s)", _usb_poll_pollfds.count - 1, event_get_source_type_name(EVENT_SOURCE_TYPE_USB, false)); retry: ready = usbi_poll((struct usbi_pollfd *)_usb_poll_pollfds.bytes, _usb_poll_pollfds.count, -1); if (ready < 0) { if (errno_interrupted()) { log_debug("Poll got interrupted, retrying"); goto retry; } log_error("Could not poll on %s event source(s): %s (%d)", event_get_source_type_name(EVENT_SOURCE_TYPE_USB, false), get_errno_name(errno), errno); goto suspend; } if (ready == 0) { goto suspend; } // handle poll result pollfd = array_get(&_usb_poll_pollfds, 0); if (pollfd->revents != 0) { log_event_debug("Received suspend signal"); --ready; // remove the suspend pipe } if (ready == 0) { goto suspend; } log_event_debug("Poll returned %d %s event source(s) as ready", ready, event_get_source_type_name(EVENT_SOURCE_TYPE_USB, false)); _usb_poll_pollfds_ready = ready; if (pipe_write(&_usb_poll_ready_pipe, &byte, sizeof(byte)) < 0) { log_error("Could not write to USB ready pipe: %s (%d)", get_errno_name(errno), errno); goto cleanup; } suspend: log_event_debug("Suspending USB poll thread"); semaphore_release(&_usb_poll_suspend); } cleanup: log_debug("Stopped USB poll thread"); semaphore_release(&_usb_poll_suspend); _usb_poll_running = false; }