]> sigrok.org Git - libsigrok.git/blobdiff - src/usb.c
Build: Set local include directories in Makefile.am
[libsigrok.git] / src / usb.c
index ec502c97501704f38bed9b2d535374166ff7601e..7fe1eff6e5b0bdba7608127ecbd065d39dcd6bc5 100644 (file)
--- a/src/usb.c
+++ b/src/usb.c
@@ -23,7 +23,7 @@
 #include <memory.h>
 #include <glib.h>
 #include <libusb.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
 /* SR_CONF_CONN takes one of these: */
@@ -178,32 +178,48 @@ SR_PRIV int sr_usb_open(libusb_context *usb_ctx, struct sr_usb_dev_inst *usb)
 }
 
 #ifdef _WIN32
+/* Thread used to run libusb_wait_for_event() and set a pollable event. */
 static gpointer usb_thread(gpointer data)
 {
        struct sr_context *ctx = data;
 
        while (ctx->usb_thread_running) {
-               g_mutex_lock(&ctx->usb_mutex);
+               /* Acquire event waiters lock, needed for libusb_wait_for_event(). */
+               libusb_lock_event_waiters(ctx->libusb_ctx);
+               /* Wait for any libusb event. The main thread can interrupt this wait
+                * by calling libusb_unlock_events(). */
                libusb_wait_for_event(ctx->libusb_ctx, NULL);
-               SetEvent(ctx->usb_event);
-               g_mutex_unlock(&ctx->usb_mutex);
-               g_thread_yield();
+               /* Release event waiters lock. */
+               libusb_unlock_event_waiters(ctx->libusb_ctx);
+               /* Set event that the main loop will be polling on. */
+               SetEvent(ctx->usb_wait_complete_event);
+               /* Wait for the main thread to signal us to run again. */
+               WaitForSingleObject(ctx->usb_wait_request_event, INFINITE);
+               ResetEvent(ctx->usb_wait_request_event);
        }
 
        return NULL;
 }
 
+/* Callback wrapper run when main g_poll() gets a USB event or timeout. */
 static int usb_callback(int fd, int revents, void *cb_data)
 {
        struct sr_context *ctx = cb_data;
        int ret;
 
-       g_mutex_lock(&ctx->usb_mutex);
+       /* Run registered callback to handle libusb events. */
        ret = ctx->usb_cb(fd, revents, ctx->usb_cb_data);
 
-       if (ctx->usb_thread_running) {
-               ResetEvent(ctx->usb_event);
-               g_mutex_unlock(&ctx->usb_mutex);
+       /* Were we triggered by an event from the wait thread, rather than by a
+        * timeout? */
+       int triggered_by_event = (revents & G_IO_IN);
+
+       /* If so, and if the USB event source has not been removed from the
+        * session, reset the event that woke us and tell the wait thread to start
+        * waiting for events again. */
+       if (triggered_by_event && ctx->usb_thread_running) {
+               ResetEvent(ctx->usb_wait_complete_event);
+               SetEvent(ctx->usb_wait_request_event);
        }
 
        return ret;
@@ -219,11 +235,14 @@ SR_PRIV int usb_source_add(struct sr_session *session, struct sr_context *ctx,
        }
 
 #ifdef _WIN32
-       ctx->usb_event = CreateEvent(NULL, TRUE, FALSE, NULL);
-       g_mutex_init(&ctx->usb_mutex);
+       /* Create events used to signal between main and USB wait threads. */
+       ctx->usb_wait_request_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+       ctx->usb_wait_complete_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+       /* Start USB wait thread. */
        ctx->usb_thread_running = TRUE;
        ctx->usb_thread = g_thread_new("usb", usb_thread, ctx);
-       ctx->usb_pollfd.fd = ctx->usb_event;
+       /* Add event, set by USB wait thread, to session poll set. */
+       ctx->usb_pollfd.fd = ctx->usb_wait_complete_event;
        ctx->usb_pollfd.events = G_IO_IN;
        ctx->usb_cb = cb;
        ctx->usb_cb_data = cb_data;
@@ -250,13 +269,17 @@ SR_PRIV int usb_source_remove(struct sr_session *session, struct sr_context *ctx
                return SR_OK;
 
 #ifdef _WIN32
+       /* Signal the USB wait thread to stop, then unblock it so it does. */
        ctx->usb_thread_running = FALSE;
-       g_mutex_unlock(&ctx->usb_mutex);
+       SetEvent(ctx->usb_wait_request_event);
        libusb_unlock_events(ctx->libusb_ctx);
+       /* Wait for USB wait thread to terminate. */
        g_thread_join(ctx->usb_thread);
-       g_mutex_clear(&ctx->usb_mutex);
+       /* Remove USB event from session poll set. */
        sr_session_source_remove_pollfd(session, &ctx->usb_pollfd);
-       CloseHandle(ctx->usb_event);
+       /* Close event handles that were used between threads. */
+       CloseHandle(ctx->usb_wait_request_event);
+       CloseHandle(ctx->usb_wait_complete_event);
 #else
        const struct libusb_pollfd **lupfd;
        unsigned int i;
@@ -276,7 +299,29 @@ SR_PRIV int usb_get_port_path(libusb_device *dev, char *path, int path_len)
        uint8_t port_numbers[8];
        int i, n, len;
 
+/*
+ * FreeBSD requires that devices prior to calling libusb_get_port_numbers()
+ * have been opened with libusb_open().
+ */
+#ifdef __FreeBSD__
+       struct libusb_device_handle *devh;
+       if (libusb_open(dev, &devh) != 0)
+               return SR_ERR;
+#endif
        n = libusb_get_port_numbers(dev, port_numbers, sizeof(port_numbers));
+#ifdef __FreeBSD__
+       libusb_close(devh);
+#endif
+
+/* Workaround FreeBSD libusb_get_port_numbers() returning 0. */
+#ifdef __FreeBSD__
+       if (n == 0) {
+               port_numbers[0] = libusb_get_device_address(dev);
+               n = 1;
+       }
+#endif
+       if (n < 1)
+               return SR_ERR;
 
        len = snprintf(path, path_len, "usb/%d-%d",
                       libusb_get_bus_number(dev), port_numbers[0]);