summaryrefslogtreecommitdiff
path: root/gusb/gusb-context.c
diff options
context:
space:
mode:
Diffstat (limited to 'gusb/gusb-context.c')
-rw-r--r--gusb/gusb-context.c76
1 files changed, 69 insertions, 7 deletions
diff --git a/gusb/gusb-context.c b/gusb/gusb-context.c
index f19fd52..edfc8f6 100644
--- a/gusb/gusb-context.c
+++ b/gusb/gusb-context.c
@@ -19,9 +19,10 @@
#include "gusb-context-private.h"
#include "gusb-device-private.h"
+#include "gusb-source-private.h"
#include "gusb-util.h"
-enum { PROP_0, PROP_LIBUSB_CONTEXT, PROP_DEBUG_LEVEL, N_PROPERTIES };
+enum { PROP_0, PROP_LIBUSB_CONTEXT, PROP_DEBUG_LEVEL, PROP_FLAGS, N_PROPERTIES };
enum { DEVICE_ADDED_SIGNAL, DEVICE_REMOVED_SIGNAL, LAST_SIGNAL };
@@ -43,6 +44,8 @@ typedef struct {
GThread *thread_event;
gboolean done_enumerate;
volatile gint thread_event_run;
+ GMutex source_mutex;
+ GUsbSource *source;
guint hotplug_poll_id;
guint hotplug_poll_interval;
int debug_level;
@@ -119,7 +122,7 @@ g_usb_context_dispose(GObject *object)
GUsbContextPrivate *priv = GET_PRIVATE(self);
/* this is safe to call even when priv->hotplug_id is unset */
- if (g_atomic_int_dec_and_test(&priv->thread_event_run)) {
+ if (priv->thread_event != NULL && g_atomic_int_dec_and_test(&priv->thread_event_run)) {
libusb_hotplug_deregister_callback(priv->ctx, priv->hotplug_id);
g_thread_join(priv->thread_event);
}
@@ -132,6 +135,10 @@ g_usb_context_dispose(GObject *object)
g_source_remove(priv->idle_events_id);
priv->idle_events_id = 0;
}
+ if (priv->source != NULL) {
+ _g_usb_source_destroy(priv->source);
+ priv->source = NULL;
+ }
g_clear_pointer(&priv->main_ctx, g_main_context_unref);
g_clear_pointer(&priv->devices, g_ptr_array_unref);
@@ -166,6 +173,9 @@ g_usb_context_get_property(GObject *object, guint prop_id, GValue *value, GParam
case PROP_DEBUG_LEVEL:
g_value_set_int(value, priv->debug_level);
break;
+ case PROP_FLAGS:
+ g_value_set_uint64(value, priv->flags);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -187,6 +197,9 @@ g_usb_context_set_property(GObject *object, guint prop_id, const GValue *value,
libusb_set_debug(priv->ctx, priv->debug_level);
#endif
break;
+ case PROP_FLAGS:
+ g_usb_context_set_flags(self, g_value_get_uint64(value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -215,6 +228,12 @@ g_usb_context_class_init(GUsbContextClass *klass)
pspecs[PROP_DEBUG_LEVEL] =
g_param_spec_int("debug_level", NULL, NULL, 0, 3, 0, G_PARAM_READWRITE);
+ /**
+ * GUsbContext:flags:
+ */
+ pspecs[PROP_FLAGS] =
+ g_param_spec_uint64("flags", NULL, NULL, 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
+
g_object_class_install_properties(object_class, N_PROPERTIES, pspecs);
/**
@@ -767,6 +786,9 @@ g_usb_context_enumerate(GUsbContext *self)
g_ptr_array_index(priv->devices, i));
}
+ /* setup with the default mainloop if not already done */
+ g_usb_context_get_source(self, NULL);
+
/* any queued up hotplug events are queued as idle handlers */
}
@@ -784,7 +806,10 @@ void
g_usb_context_set_flags(GUsbContext *self, GUsbContextFlags flags)
{
GUsbContextPrivate *priv = GET_PRIVATE(self);
+ if (priv->flags == flags)
+ return;
priv->flags = flags;
+ g_object_notify_by_pspec(G_OBJECT(self), pspecs[PROP_FLAGS]);
}
/**
@@ -831,6 +856,7 @@ g_usb_context_init(GUsbContext *self)
priv->devices_removed = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
priv->dict_usb_ids = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
priv->dict_replug = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ g_mutex_init(&priv->source_mutex);
/* to escape the thread into the mainloop */
g_rec_mutex_init(&priv->idle_events_mutex);
@@ -859,8 +885,13 @@ g_usb_context_initable_init(GInitable *initable, GCancellable *cancellable, GErr
priv->main_ctx = g_main_context_ref(g_main_context_default());
priv->ctx = ctx;
- priv->thread_event_run = 1;
- priv->thread_event = g_thread_new("GUsbEventThread", g_usb_context_event_thread_cb, self);
+
+ /* FreeBSD cannot use libusb_set_pollfd_notifiers(), so use a thread instead */
+ if (priv->flags & G_USB_CONTEXT_FLAGS_USE_HOTPLUG_THREAD) {
+ priv->thread_event_run = 1;
+ priv->thread_event =
+ g_thread_new("GUsbEventThread", g_usb_context_event_thread_cb, self);
+ }
/* watch for add/remove */
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
@@ -910,7 +941,11 @@ _g_usb_context_get_context(GUsbContext *self)
* @self: a #GUsbContext
* @main_ctx: a #GMainContext, or %NULL
*
- * This function does nothing.
+ * Returns a source for this context. The first call actually creates the source and the result
+ * is returned in all future calls, unless threading is being used.
+ *
+ * If the platform does not support libusb_set_pollfd_notifiers() then a thread is being used,
+ * and this function returns %NULL.
*
* Return value: (transfer none): the #GUsbSource.
*
@@ -919,7 +954,16 @@ _g_usb_context_get_context(GUsbContext *self)
GUsbSource *
g_usb_context_get_source(GUsbContext *self, GMainContext *main_ctx)
{
- return NULL;
+ GUsbContextPrivate *priv = GET_PRIVATE(self);
+ g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&priv->source_mutex);
+
+ g_assert(locker != NULL);
+
+ if (priv->thread_event != NULL)
+ return NULL;
+ if (priv->source == NULL)
+ priv->source = _g_usb_source_new(main_ctx, self);
+ return priv->source;
}
/**
@@ -1314,5 +1358,23 @@ g_usb_context_wait_for_replug(GUsbContext *self,
GUsbContext *
g_usb_context_new(GError **error)
{
- return g_initable_new(G_USB_TYPE_CONTEXT, NULL, error, NULL);
+ return g_usb_context_new_full(G_USB_CONTEXT_FLAGS_USE_HOTPLUG_THREAD, NULL, error);
+}
+
+/**
+ * g_usb_context_new_full:
+ * @flags: a #GUsbContextFlags, e.g. %G_USB_CONTEXT_FLAGS_SAVE_EVENTS
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Creates a new context for accessing USB devices.
+ *
+ * Return value: a new %GUsbContext object or %NULL on error.
+ *
+ * Since: 0.4.2
+ **/
+GUsbContext *
+g_usb_context_new_full(GUsbContextFlags flags, GCancellable *cancellable, GError **error)
+{
+ return g_initable_new(G_USB_TYPE_CONTEXT, cancellable, error, "flags", flags, NULL);
}