/** Create an event source for libusb I/O. * * TODO: The combination of the USB I/O source with a user timeout is * conceptually broken. The user timeout supplied here is completely * unrelated to I/O -- the actual I/O timeout is set when submitting * a USB transfer. * The sigrok drivers generally use the timeout to poll device state. * Usually, this polling can be sensibly done only when there is no * active USB transfer -- i.e. it's actually mutually exclusive with * waiting for transfer completion. * Thus, the user timeout should be removed from the USB event source * API at some point. Instead, drivers should install separate timer * event sources for their polling needs. * * @param session The session the event source belongs to. * @param usb_ctx The libusb context for which to handle events. * @param timeout_ms The timeout interval in ms, or -1 to wait indefinitely. * @return A new event source object, or NULL on failure. */ static GSource *usb_source_new(struct sr_session *session, struct libusb_context *usb_ctx, int timeout_ms) { static GSourceFuncs usb_source_funcs = { .prepare = &usb_source_prepare, .check = &usb_source_check, .dispatch = &usb_source_dispatch, .finalize = &usb_source_finalize }; GSource *source; struct usb_source *usource; const struct libusb_pollfd **upollfds, **upfd; upollfds = libusb_get_pollfds(usb_ctx); if (!upollfds) { sr_err("Failed to get libusb file descriptors."); return NULL; } source = g_source_new(&usb_source_funcs, sizeof(struct usb_source)); usource = (struct usb_source *)source; g_source_set_name(source, "usb"); if (timeout_ms >= 0) { usource->timeout_us = 1000 * (int64_t)timeout_ms; usource->due_us = 0; } else { usource->timeout_us = -1; usource->due_us = INT64_MAX; } usource->session = session; usource->usb_ctx = usb_ctx; usource->pollfds = g_ptr_array_new_full(8, &usb_source_free_pollfd); for (upfd = upollfds; *upfd != NULL; upfd++) usb_pollfd_added((*upfd)->fd, (*upfd)->events, usource); #if (LIBUSB_API_VERSION >= 0x01000104) libusb_free_pollfds(upollfds); #else free(upollfds); #endif libusb_set_pollfd_notifiers(usb_ctx, &usb_pollfd_added, &usb_pollfd_removed, usource); return source; }
void usb_destroy_context(libusb_context *context) { const struct libusb_pollfd **pollfds = NULL; const struct libusb_pollfd **pollfd; libusb_set_pollfd_notifiers(context, NULL, NULL, NULL); pollfds = libusb_get_pollfds(context); if (pollfds == NULL) { log_error("Could not get pollfds from main libusb context"); } else { for (pollfd = pollfds; *pollfd != NULL; ++pollfd) { event_remove_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB); } #if defined(_WIN32) || (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000104) // libusb 1.0.20 libusb_free_pollfds(pollfds); // avoids possible heap-mismatch on Windows #else free(pollfds); #endif } libusb_exit(context); }
int usb_create_context(libusb_context **context) { int phase = 0; int rc; const struct libusb_pollfd **pollfds = NULL; const struct libusb_pollfd **pollfd; const struct libusb_pollfd **last_added_pollfd = NULL; rc = libusb_init(context); if (rc < 0) { log_error("Could not initialize libusb context: %s (%d)", usb_get_error_name(rc), rc); goto cleanup; } switch (log_get_effective_level()) { case LOG_LEVEL_ERROR: libusb_set_debug(*context, 1); break; case LOG_LEVEL_WARN: libusb_set_debug(*context, 2); break; case LOG_LEVEL_INFO: libusb_set_debug(*context, 3); break; case LOG_LEVEL_DEBUG: if (log_is_included(LOG_LEVEL_DEBUG, &_libusb_log_source, LOG_DEBUG_GROUP_LIBUSB)) { libusb_set_debug(*context, 4); } else { libusb_set_debug(*context, 3); } break; default: break; } phase = 1; // get pollfds from main libusb context pollfds = libusb_get_pollfds(*context); if (pollfds == NULL) { log_error("Could not get pollfds from libusb context"); goto cleanup; } for (pollfd = pollfds; *pollfd != NULL; ++pollfd) { if (event_add_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB, (*pollfd)->events, usb_handle_events, *context) < 0) { goto cleanup; } last_added_pollfd = pollfd; phase = 2; } phase = 3; // register pollfd notifiers libusb_set_pollfd_notifiers(*context, usb_add_pollfd, usb_remove_pollfd, *context); cleanup: switch (phase) { // no breaks, all cases fall through intentionally case 2: for (pollfd = pollfds; pollfd != last_added_pollfd; ++pollfd) { event_remove_source((*pollfd)->fd, EVENT_SOURCE_TYPE_USB); } case 1: libusb_exit(*context); default: break; } #if defined(_WIN32) || (defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000104) // libusb 1.0.20 libusb_free_pollfds(pollfds); // avoids possible heap-mismatch on Windows #else free(pollfds); #endif return phase == 3 ? 0 : -1; }