#define LOG_PREFIX "usb"
+#if !HAVE_LIBUSB_OS_HANDLE
+typedef int libusb_os_handle;
+#endif
+
/**
* Find USB devices according to a connection string.
*
sr_dbg("Closed USB device %d.%d.", usb->bus, usb->address);
}
-#ifdef G_OS_WIN32
-/*
- * USB callback wrapper run when the main loop is idle.
- */
-static int usb_callback(int fd, int revents, void *cb_data)
+static LIBUSB_CALL void usb_pollfd_added(libusb_os_handle fd,
+ short events, void *user_data)
{
- int64_t start_time, stop_time, due, timeout;
- struct timeval tv;
- struct sr_context *ctx;
- int ret;
+ struct sr_session *session;
+ gintptr tag;
- (void)fd;
- (void)revents;
- ctx = cb_data;
-
- start_time = g_get_monotonic_time();
- due = ctx->usb_due;
-
- if (due > start_time) {
- timeout = due - start_time;
- tv.tv_sec = timeout / G_USEC_PER_SEC;
- tv.tv_usec = timeout % G_USEC_PER_SEC;
-
- sr_spew("libusb_handle_events enter: %g ms timeout",
- 1e-3 * timeout);
-
- ret = libusb_handle_events_timeout_completed(ctx->libusb_ctx,
- (ctx->usb_timeout < 0) ? NULL : &tv, NULL);
- if (ret != 0) {
- /* Warn but still invoke the callback, to give
- * the driver a chance to deal with the problem.
- */
- sr_warn("Error handling libusb event (%s)",
- libusb_error_name(ret));
- }
- stop_time = g_get_monotonic_time();
-
- sr_spew("libusb_handle_events leave: %g ms elapsed",
- 1e-3 * (stop_time - start_time));
- /*
- * The event source may have been removed by the driver's
- * libusb transfer callback. Skip the callback in that case.
- */
- if (!ctx->usb_source_present)
- return TRUE;
- } else {
- /* Timeout already expired on entry.
- */
- stop_time = start_time;
+ session = user_data;
+ tag = (gintptr)session->ctx->libusb_ctx;
+#ifdef G_OS_WIN32
+ events = G_IO_IN;
+#endif
+ sr_session_source_poll_add(session, tag, (gintptr)fd, events);
+}
- sr_spew("libusb_handle_events skipped");
- }
+static LIBUSB_CALL void usb_pollfd_removed(libusb_os_handle fd, void *user_data)
+{
+ struct sr_session *session;
+ gintptr tag;
+
+ session = user_data;
+ tag = (gintptr)session->ctx->libusb_ctx;
- if (ctx->usb_timeout >= 0)
- ctx->usb_due = stop_time + ctx->usb_timeout;
- /*
- * Run the registered callback to execute any follow-up activity
- * to libusb's event handling.
- */
- return ctx->usb_cb(-1, (stop_time < due) ? G_IO_IN : 0,
- ctx->usb_cb_data);
+ sr_session_source_poll_remove(session, tag, (gintptr)fd);
}
-#endif
SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
int timeout, sr_receive_data_callback cb, void *cb_data)
{
+ const struct libusb_pollfd **pollfds;
+ gintptr tag;
+ int i;
int ret;
+ int events;
if (ctx->usb_source_present) {
sr_err("A USB event source is already present.");
return SR_ERR;
}
-
-#ifdef G_OS_WIN32
- if (timeout >= 0) {
- ctx->usb_timeout = INT64_C(1000) * timeout;
- ctx->usb_due = g_get_monotonic_time() + ctx->usb_timeout;
- } else {
- ctx->usb_timeout = -1;
- ctx->usb_due = INT64_MAX;
- }
- ctx->usb_cb = cb;
- ctx->usb_cb_data = cb_data;
- /*
- * TODO: Install an idle source which will fire permanently, and block
- * in a wrapper callback until any libusb events have been processed.
- * This will have to do for now, until we implement a proper way to
- * deal with libusb events on Windows.
- */
- ret = sr_session_source_add_internal(session, NULL, 0,
- 0, &usb_callback, ctx, (gintptr)ctx->libusb_ctx);
-#else
- const struct libusb_pollfd **lupfd;
- GPollFD *pollfds;
- int i;
- int num_fds = 0;
-
- lupfd = libusb_get_pollfds(ctx->libusb_ctx);
- if (!lupfd || !lupfd[0]) {
- free(lupfd);
+ pollfds = libusb_get_pollfds(ctx->libusb_ctx);
+ if (!pollfds) {
sr_err("Failed to get libusb file descriptors.");
return SR_ERR;
}
- while (lupfd[num_fds])
- ++num_fds;
- pollfds = g_new(GPollFD, num_fds);
-
- for (i = 0; i < num_fds; ++i) {
- pollfds[i].fd = lupfd[i]->fd;
- pollfds[i].events = lupfd[i]->events;
- pollfds[i].revents = 0;
+ tag = (gintptr)ctx->libusb_ctx;
+ ret = sr_session_source_add_internal(session,
+ timeout, cb, cb_data, tag);
+
+ ctx->usb_source_present = (ret == SR_OK);
+
+ for (i = 0; ret == SR_OK && pollfds[i]; ++i) {
+#ifdef G_OS_WIN32
+ events = G_IO_IN;
+#else
+ events = pollfds[i]->events;
+#endif
+ ret = sr_session_source_poll_add(session, tag,
+ (gintptr)pollfds[i]->fd, events);
}
- free(lupfd);
- ret = sr_session_source_add_internal(session, pollfds, num_fds,
- timeout, cb, cb_data, (gintptr)ctx->libusb_ctx);
- g_free(pollfds);
+#if (LIBUSB_API_VERSION >= 0x01000104)
+ libusb_free_pollfds(pollfds);
+#else
+ free(pollfds);
#endif
- ctx->usb_source_present = (ret == SR_OK);
+ if (ret != SR_OK)
+ return ret;
- return ret;
+ libusb_set_pollfd_notifiers(ctx->libusb_ctx,
+ &usb_pollfd_added, &usb_pollfd_removed, session);
+
+ return SR_OK;
}
SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx)
{
+ ctx->usb_source_present = FALSE;
+
+ libusb_set_pollfd_notifiers(ctx->libusb_ctx, NULL, NULL, NULL);
+
return sr_session_source_remove_internal(session,
(gintptr)ctx->libusb_ctx);
}