mirror of
https://github.com/libusb/libusb
synced 2024-11-21 06:26:10 -07:00
core: Refactor initialization and how the default context is handled
Highlights for this change: - usbi_default_context is only set if libusb_init() is called with NULL. - All hotplug related functionality (e.g. initialization, processing) has been moved to hotplug.c - Backends are simplified by removing initialization mutexes. Mutual exclusion between init()/exit() is provided by default_context_lock. - Make hotplug types and functions part of libusbi.h with the common usbi_ prefixes (removes hotplug.h). Addresses issue highlighted in #855 Closes #856 Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com> Signed-off-by: Nathan Hjelm <hjelmn@google.com>
This commit is contained in:
parent
a2b81aeff1
commit
32a2206942
28 changed files with 339 additions and 457 deletions
|
@ -56,7 +56,6 @@
|
|||
008FC0211628BC5200BC5BE2 /* ezusb.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBFDC1628BA0E00BC5BE2 /* ezusb.c */; };
|
||||
008FC0301628BC7400BC5BE2 /* listdevs.c in Sources */ = {isa = PBXBuildFile; fileRef = 008FBFE71628BA0E00BC5BE2 /* listdevs.c */; };
|
||||
1438D77A17A2ED9F00166101 /* hotplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77817A2ED9F00166101 /* hotplug.c */; };
|
||||
1438D77B17A2ED9F00166101 /* hotplug.h in Headers */ = {isa = PBXBuildFile; fileRef = 1438D77917A2ED9F00166101 /* hotplug.h */; };
|
||||
1438D77F17A2F0EA00166101 /* strerror.c in Sources */ = {isa = PBXBuildFile; fileRef = 1438D77E17A2F0EA00166101 /* strerror.c */; };
|
||||
2018D95F24E453BA001589B2 /* events_posix.c in Sources */ = {isa = PBXBuildFile; fileRef = 2018D95E24E453BA001589B2 /* events_posix.c */; };
|
||||
2018D96124E453D0001589B2 /* events_posix.h in Headers */ = {isa = PBXBuildFile; fileRef = 2018D96024E453D0001589B2 /* events_posix.h */; };
|
||||
|
@ -266,7 +265,6 @@
|
|||
008FC0151628BC0300BC5BE2 /* fxload */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = fxload; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
008FC0261628BC6B00BC5BE2 /* listdevs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = listdevs; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1438D77817A2ED9F00166101 /* hotplug.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = hotplug.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
|
||||
1438D77917A2ED9F00166101 /* hotplug.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = hotplug.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
|
||||
1438D77E17A2F0EA00166101 /* strerror.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = strerror.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; };
|
||||
1443EE8416417E63007E0579 /* common.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = common.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
|
||||
1443EE8516417E63007E0579 /* debug.xcconfig */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = text.xcconfig; path = debug.xcconfig; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 1; };
|
||||
|
@ -417,7 +415,6 @@
|
|||
008FBF541628B7E800BC5BE2 /* core.c */,
|
||||
008FBF551628B7E800BC5BE2 /* descriptor.c */,
|
||||
1438D77817A2ED9F00166101 /* hotplug.c */,
|
||||
1438D77917A2ED9F00166101 /* hotplug.h */,
|
||||
008FBF561628B7E800BC5BE2 /* io.c */,
|
||||
008FBF5A1628B7E800BC5BE2 /* libusb.h */,
|
||||
008FBF671628B7E800BC5BE2 /* libusbi.h */,
|
||||
|
@ -498,7 +495,6 @@
|
|||
008FBFA51628B84200BC5BE2 /* config.h in Headers */,
|
||||
008FBF931628B7E800BC5BE2 /* darwin_usb.h in Headers */,
|
||||
2018D96124E453D0001589B2 /* events_posix.h in Headers */,
|
||||
1438D77B17A2ED9F00166101 /* hotplug.h in Headers */,
|
||||
008FBF901628B7E800BC5BE2 /* libusbi.h in Headers */,
|
||||
008FBF9B1628B7E800BC5BE2 /* threads_posix.h in Headers */,
|
||||
008FBFA11628B7E800BC5BE2 /* version.h in Headers */,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
LIBUSB_SRC_DIR = @top_srcdir@/libusb
|
||||
EXCLUDED_FILES = hotplug.h libusbi.h version.h version_nano.h
|
||||
EXCLUDED_FILES = libusbi.h version.h version_nano.h
|
||||
LIBUSB_SRC = $(wildcard $(LIBUSB_SRC_DIR)/*.c) $(wildcard $(LIBUSB_SRC_DIR)/*.h)
|
||||
LIBUSB_DOC_SRC = $(filter-out $(addprefix $(LIBUSB_SRC_DIR)/,$(EXCLUDED_FILES)),$(LIBUSB_SRC))
|
||||
|
||||
|
|
|
@ -899,8 +899,7 @@ RECURSIVE = NO
|
|||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE = @top_srcdir@/libusb/hotplug.h \
|
||||
@top_srcdir@/libusb/libusbi.h \
|
||||
EXCLUDE = @top_srcdir@/libusb/libusbi.h \
|
||||
@top_srcdir@/libusb/version.h \
|
||||
@top_srcdir@/libusb/version_nano.h \
|
||||
@top_srcdir@/libusb/os
|
||||
|
|
|
@ -82,7 +82,7 @@ endif
|
|||
|
||||
libusb_1_0_la_LDFLAGS = $(LT_LDFLAGS)
|
||||
libusb_1_0_la_SOURCES = libusbi.h version.h version_nano.h \
|
||||
core.c descriptor.c hotplug.h hotplug.c io.c strerror.c sync.c \
|
||||
core.c descriptor.c hotplug.c io.c strerror.c sync.c \
|
||||
$(PLATFORM_SRC) $(OS_SRC)
|
||||
|
||||
pkginclude_HEADERS = libusb.h
|
||||
|
|
242
libusb/core.c
242
libusb/core.c
|
@ -21,7 +21,6 @@
|
|||
*/
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
#include "version.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
@ -33,17 +32,18 @@
|
|||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
struct libusb_context *usbi_default_context;
|
||||
static const struct libusb_version libusb_version_internal =
|
||||
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
|
||||
LIBUSB_RC, "http://libusb.info" };
|
||||
static int default_context_refcnt;
|
||||
static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
|
||||
static struct timespec timestamp_origin;
|
||||
#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY)
|
||||
static libusb_log_cb log_handler;
|
||||
#endif
|
||||
|
||||
struct libusb_context *usbi_default_context;
|
||||
static int default_context_refcnt;
|
||||
static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
|
||||
|
||||
usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
|
||||
struct list_head active_contexts_list;
|
||||
|
||||
|
@ -710,9 +710,8 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
|
|||
dev->session_data = session_id;
|
||||
dev->speed = LIBUSB_SPEED_UNKNOWN;
|
||||
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
usbi_connect_device (dev);
|
||||
}
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
usbi_connect_device(dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -727,12 +726,7 @@ void usbi_connect_device(struct libusb_device *dev)
|
|||
list_add(&dev->list, &dev->ctx->usb_devs);
|
||||
usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
|
||||
|
||||
/* Signal that an event has occurred for this device if we support hotplug AND
|
||||
* the hotplug message list is ready. This prevents an event from getting raised
|
||||
* during initial enumeration. */
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
|
||||
usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
|
||||
}
|
||||
usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
|
||||
}
|
||||
|
||||
void usbi_disconnect_device(struct libusb_device *dev)
|
||||
|
@ -745,13 +739,7 @@ void usbi_disconnect_device(struct libusb_device *dev)
|
|||
list_del(&dev->list);
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
|
||||
/* Signal that an event has occurred for this device if we support hotplug AND
|
||||
* the hotplug message list is ready. This prevents an event from getting raised
|
||||
* during initial enumeration. libusb_handle_events will take care of dereferencing
|
||||
* the device. */
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) {
|
||||
usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
|
||||
}
|
||||
usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
|
||||
}
|
||||
|
||||
/* Perform some final sanity checks on a newly discovered device. If this
|
||||
|
@ -2247,113 +2235,105 @@ static enum libusb_log_level get_env_debug_level(void)
|
|||
* context will be created. If there was already a default context, it will
|
||||
* be reused (and nothing will be initialized/reinitialized).
|
||||
*
|
||||
* \param context Optional output location for context pointer.
|
||||
* \param ctx Optional output location for context pointer.
|
||||
* Only valid on return code 0.
|
||||
* \returns 0 on success, or a LIBUSB_ERROR code on failure
|
||||
* \see libusb_contexts
|
||||
*/
|
||||
int API_EXPORTED libusb_init(libusb_context **context)
|
||||
int API_EXPORTED libusb_init(libusb_context **ctx)
|
||||
{
|
||||
struct libusb_device *dev, *next;
|
||||
size_t priv_size = usbi_backend.context_priv_size;
|
||||
struct libusb_context *ctx;
|
||||
static int first_init = 1;
|
||||
int r = 0;
|
||||
struct libusb_context *_ctx;
|
||||
int r;
|
||||
|
||||
usbi_mutex_static_lock(&default_context_lock);
|
||||
|
||||
if (!timestamp_origin.tv_sec)
|
||||
usbi_get_monotonic_time(×tamp_origin);
|
||||
|
||||
if (!context && usbi_default_context) {
|
||||
if (!ctx && usbi_default_context) {
|
||||
usbi_dbg("reusing default context");
|
||||
default_context_refcnt++;
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size);
|
||||
if (!ctx) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err_unlock;
|
||||
/* check for first init */
|
||||
if (!active_contexts_list.next) {
|
||||
list_init(&active_contexts_list);
|
||||
usbi_get_monotonic_time(×tamp_origin);
|
||||
}
|
||||
|
||||
_ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size);
|
||||
if (!_ctx) {
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
ctx->debug = get_env_debug_level();
|
||||
if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
|
||||
ctx->debug_fixed = 1;
|
||||
_ctx->debug = get_env_debug_level();
|
||||
if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE)
|
||||
_ctx->debug_fixed = 1;
|
||||
#endif
|
||||
|
||||
/* default context should be initialized before calling usbi_dbg */
|
||||
if (!usbi_default_context) {
|
||||
usbi_default_context = ctx;
|
||||
default_context_refcnt++;
|
||||
if (!ctx) {
|
||||
usbi_default_context = _ctx;
|
||||
default_context_refcnt = 1;
|
||||
usbi_dbg("created default context");
|
||||
}
|
||||
|
||||
usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor,
|
||||
libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc);
|
||||
|
||||
usbi_mutex_init(&ctx->usb_devs_lock);
|
||||
usbi_mutex_init(&ctx->open_devs_lock);
|
||||
usbi_mutex_init(&ctx->hotplug_cbs_lock);
|
||||
list_init(&ctx->usb_devs);
|
||||
list_init(&ctx->open_devs);
|
||||
list_init(&ctx->hotplug_cbs);
|
||||
ctx->next_hotplug_cb_handle = 1;
|
||||
usbi_mutex_init(&_ctx->usb_devs_lock);
|
||||
usbi_mutex_init(&_ctx->open_devs_lock);
|
||||
list_init(&_ctx->usb_devs);
|
||||
list_init(&_ctx->open_devs);
|
||||
|
||||
r = usbi_io_init(_ctx);
|
||||
if (r < 0) {
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
goto err_free_ctx;
|
||||
}
|
||||
|
||||
usbi_mutex_static_lock(&active_contexts_lock);
|
||||
if (first_init) {
|
||||
first_init = 0;
|
||||
list_init(&active_contexts_list);
|
||||
}
|
||||
list_add (&ctx->list, &active_contexts_list);
|
||||
list_add(&_ctx->list, &active_contexts_list);
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
if (usbi_backend.init) {
|
||||
r = usbi_backend.init(ctx);
|
||||
r = usbi_backend.init(_ctx);
|
||||
if (r)
|
||||
goto err_free_ctx;
|
||||
goto err_io_exit;
|
||||
}
|
||||
|
||||
r = usbi_io_init(ctx);
|
||||
if (r < 0)
|
||||
goto err_backend_exit;
|
||||
usbi_hotplug_init(_ctx);
|
||||
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
|
||||
if (context)
|
||||
*context = ctx;
|
||||
if (ctx)
|
||||
*ctx = _ctx;
|
||||
|
||||
return 0;
|
||||
|
||||
err_backend_exit:
|
||||
if (usbi_backend.exit)
|
||||
usbi_backend.exit(ctx);
|
||||
err_free_ctx:
|
||||
if (ctx == usbi_default_context) {
|
||||
usbi_default_context = NULL;
|
||||
default_context_refcnt--;
|
||||
}
|
||||
|
||||
err_io_exit:
|
||||
usbi_mutex_static_lock(&active_contexts_lock);
|
||||
list_del(&ctx->list);
|
||||
list_del(&_ctx->list);
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
for_each_device_safe(ctx, dev, next) {
|
||||
list_del(&dev->list);
|
||||
libusb_unref_device(dev);
|
||||
if (!ctx) {
|
||||
usbi_default_context = NULL;
|
||||
default_context_refcnt = 0;
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
|
||||
usbi_mutex_destroy(&ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->usb_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
|
||||
|
||||
free(ctx);
|
||||
err_unlock:
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
|
||||
usbi_hotplug_exit(_ctx);
|
||||
usbi_io_exit(_ctx);
|
||||
|
||||
err_free_ctx:
|
||||
usbi_mutex_destroy(&_ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&_ctx->usb_devs_lock);
|
||||
|
||||
free(_ctx);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -2364,18 +2344,14 @@ err_unlock:
|
|||
*/
|
||||
void API_EXPORTED libusb_exit(libusb_context *ctx)
|
||||
{
|
||||
struct libusb_device *dev, *next;
|
||||
struct timeval tv = { 0, 0 };
|
||||
int destroying_default_context = 0;
|
||||
struct libusb_context *_ctx;
|
||||
struct libusb_device *dev;
|
||||
|
||||
usbi_dbg(" ");
|
||||
|
||||
ctx = usbi_get_context(ctx);
|
||||
usbi_mutex_static_lock(&default_context_lock);
|
||||
|
||||
/* if working with default context, only actually do the deinitialization
|
||||
* if we're the last user */
|
||||
usbi_mutex_static_lock(&default_context_lock);
|
||||
if (ctx == usbi_default_context) {
|
||||
if (!ctx) {
|
||||
if (!usbi_default_context) {
|
||||
usbi_dbg("no default context, not initialized?");
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
|
@ -2387,80 +2363,44 @@ void API_EXPORTED libusb_exit(libusb_context *ctx)
|
|||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
return;
|
||||
}
|
||||
usbi_dbg("destroying default context");
|
||||
|
||||
/*
|
||||
* Setting this flag without unlocking the default context, as
|
||||
* we are actually destroying the default context.
|
||||
* usbi_default_context is not set to NULL yet, as all activities
|
||||
* would only stop after usbi_backend->exit() returns.
|
||||
*/
|
||||
destroying_default_context = 1;
|
||||
usbi_dbg("destroying default context");
|
||||
_ctx = usbi_default_context;
|
||||
} else {
|
||||
/* Unlock default context, as we're not modifying it. */
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
usbi_dbg(" ");
|
||||
_ctx = ctx;
|
||||
}
|
||||
|
||||
usbi_mutex_static_lock(&active_contexts_lock);
|
||||
list_del(&ctx->list);
|
||||
list_del(&_ctx->list);
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
/* Don't bother with locking after this point because unless there is
|
||||
* an application bug, nobody will be accessing these. */
|
||||
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
usbi_hotplug_deregister(ctx, 1);
|
||||
|
||||
/*
|
||||
* Ensure any pending unplug events are read from the hotplug
|
||||
* pipe. The usb_device-s hold in the events are no longer part
|
||||
* of usb_devs, but the events still hold a reference!
|
||||
*
|
||||
* Note we don't do this if the application has left devices
|
||||
* open (which implies a buggy app) to avoid packet completion
|
||||
* handlers running when the app does not expect them to run.
|
||||
*/
|
||||
if (list_empty(&ctx->open_devs))
|
||||
libusb_handle_events_timeout(ctx, &tv);
|
||||
|
||||
for_each_device_safe(ctx, dev, next) {
|
||||
if (usbi_atomic_load(&dev->refcnt) > 1)
|
||||
usbi_warn(ctx, "device %d.%d still referenced",
|
||||
dev->bus_number, dev->device_address);
|
||||
list_del(&dev->list);
|
||||
libusb_unref_device(dev);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Backends without hotplug store enumerated devices on the
|
||||
* usb_devs list when libusb_get_device_list() is called.
|
||||
* These devices are removed from the list when the last
|
||||
* reference is dropped, typically when the device list is
|
||||
* freed. Any device still on the list has a reference held
|
||||
* by the app, which is a bug.
|
||||
*/
|
||||
for_each_device(ctx, dev) {
|
||||
usbi_warn(ctx, "device %d.%d still referenced",
|
||||
dev->bus_number, dev->device_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&ctx->open_devs))
|
||||
usbi_warn(ctx, "application left some devices open");
|
||||
|
||||
usbi_io_exit(ctx);
|
||||
if (usbi_backend.exit)
|
||||
usbi_backend.exit(ctx);
|
||||
usbi_backend.exit(_ctx);
|
||||
|
||||
usbi_mutex_destroy(&ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->usb_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
|
||||
free(ctx);
|
||||
|
||||
if (destroying_default_context) {
|
||||
if (!ctx)
|
||||
usbi_default_context = NULL;
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
|
||||
usbi_mutex_static_unlock(&default_context_lock);
|
||||
|
||||
/* Don't bother with locking after this point because unless there is
|
||||
* an application bug, nobody will be accessing the context. */
|
||||
|
||||
usbi_hotplug_exit(_ctx);
|
||||
usbi_io_exit(_ctx);
|
||||
|
||||
for_each_device(_ctx, dev) {
|
||||
usbi_warn(_ctx, "device %d.%d still referenced",
|
||||
dev->bus_number, dev->device_address);
|
||||
}
|
||||
|
||||
if (!list_empty(&_ctx->open_devs))
|
||||
usbi_warn(_ctx, "application left some devices open");
|
||||
|
||||
usbi_mutex_destroy(&_ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&_ctx->usb_devs_lock);
|
||||
|
||||
free(_ctx);
|
||||
}
|
||||
|
||||
/** \ingroup libusb_misc
|
||||
|
|
247
libusb/hotplug.c
247
libusb/hotplug.c
|
@ -20,7 +20,6 @@
|
|||
*/
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
/**
|
||||
* @defgroup libusb_hotplug Device hotplug event notification
|
||||
|
@ -144,15 +143,68 @@ int main (void) {
|
|||
*/
|
||||
|
||||
#define VALID_HOTPLUG_EVENTS \
|
||||
(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
|
||||
#define VALID_HOTPLUG_FLAGS \
|
||||
(LIBUSB_HOTPLUG_ENUMERATE)
|
||||
(LIBUSB_HOTPLUG_ENUMERATE)
|
||||
|
||||
static int usbi_hotplug_match_cb(struct libusb_context *ctx,
|
||||
struct libusb_device *dev, libusb_hotplug_event event,
|
||||
struct libusb_hotplug_callback *hotplug_cb)
|
||||
void usbi_hotplug_init(struct libusb_context *ctx)
|
||||
{
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
return;
|
||||
|
||||
usbi_mutex_init(&ctx->hotplug_cbs_lock);
|
||||
list_init(&ctx->hotplug_cbs);
|
||||
ctx->next_hotplug_cb_handle = 1;
|
||||
usbi_atomic_store(&ctx->hotplug_ready, 1);
|
||||
}
|
||||
|
||||
void usbi_hotplug_exit(struct libusb_context *ctx)
|
||||
{
|
||||
struct usbi_hotplug_callback *hotplug_cb, *next_cb;
|
||||
struct usbi_hotplug_message *msg;
|
||||
struct libusb_device *dev, *next_dev;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
return;
|
||||
|
||||
/* free all registered hotplug callbacks */
|
||||
for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
|
||||
/* free all pending hotplug messages */
|
||||
while (!list_empty(&ctx->hotplug_msgs)) {
|
||||
msg = list_first_entry(&ctx->hotplug_msgs, struct usbi_hotplug_message, list);
|
||||
|
||||
/* if the device left, the message holds a reference
|
||||
* and we must drop it */
|
||||
if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
libusb_unref_device(msg->device);
|
||||
|
||||
list_del(&msg->list);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* free all discovered devices */
|
||||
for_each_device_safe(ctx, dev, next_dev) {
|
||||
/* remove the device from the usb_devs list only if there are no
|
||||
* references held, otherwise leave it on the list so that a
|
||||
* warning message will be shown */
|
||||
if (usbi_atomic_load(&dev->refcnt) == 1)
|
||||
list_del(&dev->list);
|
||||
libusb_unref_device(dev);
|
||||
}
|
||||
|
||||
usbi_mutex_destroy(&ctx->hotplug_cbs_lock);
|
||||
}
|
||||
|
||||
static int usbi_hotplug_match_cb(struct libusb_device *dev,
|
||||
libusb_hotplug_event event, struct usbi_hotplug_callback *hotplug_cb)
|
||||
{
|
||||
if (!(hotplug_cb->flags & event)) {
|
||||
return 0;
|
||||
|
@ -173,28 +225,82 @@ static int usbi_hotplug_match_cb(struct libusb_context *ctx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data);
|
||||
return hotplug_cb->cb(DEVICE_CTX(dev), dev, event, hotplug_cb->user_data);
|
||||
}
|
||||
|
||||
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb, *next;
|
||||
int ret;
|
||||
struct usbi_hotplug_message *msg;
|
||||
unsigned int event_flags;
|
||||
|
||||
/* Only generate a notification if hotplug is ready. This prevents hotplug
|
||||
* notifications from being generated during initial enumeration or if the
|
||||
* backend does not support hotplug. */
|
||||
if (!usbi_atomic_load(&ctx->hotplug_ready))
|
||||
return;
|
||||
|
||||
msg = calloc(1, sizeof(*msg));
|
||||
if (!msg) {
|
||||
usbi_err(ctx, "error allocating hotplug message");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->event = event;
|
||||
msg->device = dev;
|
||||
|
||||
/* Take the event data lock and add this message to the list.
|
||||
* Only signal an event if there are no prior pending events. */
|
||||
usbi_mutex_lock(&ctx->event_data_lock);
|
||||
event_flags = ctx->event_flags;
|
||||
ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
|
||||
list_add_tail(&msg->list, &ctx->hotplug_msgs);
|
||||
if (!event_flags)
|
||||
usbi_signal_event(&ctx->event);
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
}
|
||||
|
||||
void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs)
|
||||
{
|
||||
struct usbi_hotplug_callback *hotplug_cb, *next_cb;
|
||||
struct usbi_hotplug_message *msg;
|
||||
int r;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
|
||||
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
|
||||
/* process deregistration in usbi_hotplug_deregister() */
|
||||
continue;
|
||||
/* dispatch all pending hotplug messages */
|
||||
while (!list_empty(hotplug_msgs)) {
|
||||
msg = list_first_entry(hotplug_msgs, struct usbi_hotplug_message, list);
|
||||
|
||||
for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
|
||||
/* skip callbacks that have unregistered */
|
||||
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)
|
||||
continue;
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
r = usbi_hotplug_match_cb(msg->device, msg->event, hotplug_cb);
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
if (r) {
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb);
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
/* if the device left, the message holds a reference
|
||||
* and we must drop it */
|
||||
if (msg->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
libusb_unref_device(msg->device);
|
||||
|
||||
if (ret) {
|
||||
list_del(&msg->list);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
/* free any callbacks that have unregistered */
|
||||
for_each_hotplug_cb_safe(ctx, hotplug_cb, next_cb) {
|
||||
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
|
||||
usbi_dbg("freeing hotplug cb %p with handle %d",
|
||||
hotplug_cb, hotplug_cb->handle);
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
|
@ -203,41 +309,16 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
|||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
}
|
||||
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event)
|
||||
{
|
||||
struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
|
||||
unsigned int event_flags;
|
||||
|
||||
if (!message) {
|
||||
usbi_err(ctx, "error allocating hotplug message");
|
||||
return;
|
||||
}
|
||||
|
||||
message->event = event;
|
||||
message->device = dev;
|
||||
|
||||
/* Take the event data lock and add this message to the list.
|
||||
* Only signal an event if there are no prior pending events. */
|
||||
usbi_mutex_lock(&ctx->event_data_lock);
|
||||
event_flags = ctx->event_flags;
|
||||
ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING;
|
||||
list_add_tail(&message->list, &ctx->hotplug_msgs);
|
||||
if (!event_flags)
|
||||
usbi_signal_event(&ctx->event);
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
}
|
||||
|
||||
int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
|
||||
int events, int flags,
|
||||
int vendor_id, int product_id, int dev_class,
|
||||
libusb_hotplug_callback_fn cb_fn, void *user_data,
|
||||
libusb_hotplug_callback_handle *callback_handle)
|
||||
{
|
||||
struct libusb_hotplug_callback *new_callback;
|
||||
struct usbi_hotplug_callback *hotplug_cb;
|
||||
|
||||
/* check for sane values */
|
||||
if ((!events || (~VALID_HOTPLUG_EVENTS & events)) ||
|
||||
if (!events || (~VALID_HOTPLUG_EVENTS & events) ||
|
||||
(~VALID_HOTPLUG_FLAGS & flags) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
|
||||
|
@ -247,47 +328,45 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
|
|||
}
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
ctx = usbi_get_context(ctx);
|
||||
|
||||
new_callback = calloc(1, sizeof(*new_callback));
|
||||
if (!new_callback) {
|
||||
hotplug_cb = calloc(1, sizeof(*hotplug_cb));
|
||||
if (!hotplug_cb)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
new_callback->flags = (uint8_t)events;
|
||||
hotplug_cb->flags = (uint8_t)events;
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
|
||||
new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
|
||||
new_callback->vendor_id = (uint16_t)vendor_id;
|
||||
hotplug_cb->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
|
||||
hotplug_cb->vendor_id = (uint16_t)vendor_id;
|
||||
}
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
|
||||
new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
|
||||
new_callback->product_id = (uint16_t)product_id;
|
||||
hotplug_cb->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
|
||||
hotplug_cb->product_id = (uint16_t)product_id;
|
||||
}
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
|
||||
new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
|
||||
new_callback->dev_class = (uint8_t)dev_class;
|
||||
hotplug_cb->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
|
||||
hotplug_cb->dev_class = (uint8_t)dev_class;
|
||||
}
|
||||
new_callback->cb = cb_fn;
|
||||
new_callback->user_data = user_data;
|
||||
hotplug_cb->cb = cb_fn;
|
||||
hotplug_cb->user_data = user_data;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
/* protect the handle by the context hotplug lock */
|
||||
new_callback->handle = ctx->next_hotplug_cb_handle++;
|
||||
hotplug_cb->handle = ctx->next_hotplug_cb_handle++;
|
||||
|
||||
/* handle the unlikely case of overflow */
|
||||
if (ctx->next_hotplug_cb_handle < 0)
|
||||
ctx->next_hotplug_cb_handle = 1;
|
||||
|
||||
list_add(&new_callback->list, &ctx->hotplug_cbs);
|
||||
list_add(&hotplug_cb->list, &ctx->hotplug_cbs);
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
|
||||
usbi_dbg("new hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle);
|
||||
|
||||
if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
|
||||
ssize_t i, len;
|
||||
|
@ -295,23 +374,21 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
|
|||
|
||||
len = libusb_get_device_list(ctx, &devs);
|
||||
if (len < 0) {
|
||||
libusb_hotplug_deregister_callback(ctx,
|
||||
new_callback->handle);
|
||||
libusb_hotplug_deregister_callback(ctx, hotplug_cb->handle);
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
usbi_hotplug_match_cb(ctx, devs[i],
|
||||
usbi_hotplug_match_cb(devs[i],
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
|
||||
new_callback);
|
||||
hotplug_cb);
|
||||
}
|
||||
|
||||
libusb_free_device_list(devs, 1);
|
||||
}
|
||||
|
||||
|
||||
if (callback_handle)
|
||||
*callback_handle = new_callback->handle;
|
||||
*callback_handle = hotplug_cb->handle;
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
@ -319,13 +396,12 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
|
|||
void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle callback_handle)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb;
|
||||
struct usbi_hotplug_callback *hotplug_cb;
|
||||
int deregistered = 0;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
return;
|
||||
}
|
||||
|
||||
usbi_dbg("deregister hotplug cb %d", callback_handle);
|
||||
|
||||
|
@ -334,9 +410,10 @@ void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx,
|
|||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
for_each_hotplug_cb(ctx, hotplug_cb) {
|
||||
if (callback_handle == hotplug_cb->handle) {
|
||||
/* Mark this callback for deregistration */
|
||||
/* mark this callback for deregistration */
|
||||
hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
|
||||
deregistered = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
@ -357,15 +434,14 @@ DEFAULT_VISIBILITY
|
|||
void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle callback_handle)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb;
|
||||
struct usbi_hotplug_callback *hotplug_cb;
|
||||
void *user_data = NULL;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
usbi_dbg("get hotplug user data %d", callback_handle);
|
||||
usbi_dbg("get hotplug cb %d user data", callback_handle);
|
||||
|
||||
ctx = usbi_get_context(ctx);
|
||||
|
||||
|
@ -373,25 +449,10 @@ void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
|
|||
for_each_hotplug_cb(ctx, hotplug_cb) {
|
||||
if (callback_handle == hotplug_cb->handle) {
|
||||
user_data = hotplug_cb->user_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
return user_data;
|
||||
}
|
||||
|
||||
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb, *next;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
for_each_hotplug_cb_safe(ctx, hotplug_cb, next) {
|
||||
if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
|
||||
usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
|
||||
hotplug_cb->handle);
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
}
|
||||
|
|
105
libusb/hotplug.h
105
libusb/hotplug.h
|
@ -1,105 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */
|
||||
/*
|
||||
* Hotplug support for libusb
|
||||
* Copyright © 2012-2013 Nathan Hjelm <hjelmn@mac.com>
|
||||
* Copyright © 2012-2013 Peter Stuge <peter@stuge.se>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef USBI_HOTPLUG_H
|
||||
#define USBI_HOTPLUG_H
|
||||
|
||||
#include "libusbi.h"
|
||||
|
||||
enum usbi_hotplug_flags {
|
||||
/* This callback is interested in device arrivals */
|
||||
USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
|
||||
|
||||
/* This callback is interested in device removals */
|
||||
USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
|
||||
|
||||
/* IMPORTANT: The values for the below entries must start *after*
|
||||
* the highest value of the above entries!!!
|
||||
*/
|
||||
|
||||
/* The vendor_id field is valid for matching */
|
||||
USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
|
||||
|
||||
/* The product_id field is valid for matching */
|
||||
USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
|
||||
|
||||
/* The dev_class field is valid for matching */
|
||||
USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
|
||||
|
||||
/* This callback has been unregistered and needs to be freed */
|
||||
USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
|
||||
};
|
||||
|
||||
/** \ingroup hotplug
|
||||
* The hotplug callback structure. The user populates this structure with
|
||||
* libusb_hotplug_prepare_callback() and then calls libusb_hotplug_register_callback()
|
||||
* to receive notification of hotplug events.
|
||||
*/
|
||||
struct libusb_hotplug_callback {
|
||||
/** Flags that control how this callback behaves */
|
||||
uint8_t flags;
|
||||
|
||||
/** Vendor ID to match (if flags says this is valid) */
|
||||
uint16_t vendor_id;
|
||||
|
||||
/** Product ID to match (if flags says this is valid) */
|
||||
uint16_t product_id;
|
||||
|
||||
/** Device class to match (if flags says this is valid) */
|
||||
uint8_t dev_class;
|
||||
|
||||
/** Callback function to invoke for matching event/device */
|
||||
libusb_hotplug_callback_fn cb;
|
||||
|
||||
/** Handle for this callback (used to match on deregister) */
|
||||
libusb_hotplug_callback_handle handle;
|
||||
|
||||
/** User data that will be passed to the callback function */
|
||||
void *user_data;
|
||||
|
||||
/** List this callback is registered in (ctx->hotplug_cbs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct libusb_hotplug_message {
|
||||
/** The hotplug event that occurred */
|
||||
libusb_hotplug_event event;
|
||||
|
||||
/** The device for which this hotplug event occurred */
|
||||
struct libusb_device *device;
|
||||
|
||||
/** List this message is contained in (ctx->hotplug_msgs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
#define for_each_hotplug_cb(ctx, c) \
|
||||
for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
|
||||
|
||||
#define for_each_hotplug_cb_safe(ctx, c, n) \
|
||||
for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback)
|
||||
|
||||
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
|
||||
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event);
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event);
|
||||
|
||||
#endif
|
26
libusb/io.c
26
libusb/io.c
|
@ -22,7 +22,6 @@
|
|||
*/
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
/**
|
||||
* \page libusb_io Synchronous and asynchronous device I/O
|
||||
|
@ -2073,6 +2072,7 @@ static void handle_timeouts(struct libusb_context *ctx)
|
|||
static int handle_event_trigger(struct libusb_context *ctx)
|
||||
{
|
||||
struct list_head hotplug_msgs;
|
||||
int hotplug_event = 0;
|
||||
int r = 0;
|
||||
|
||||
usbi_dbg("event triggered");
|
||||
|
@ -2091,6 +2091,12 @@ static int handle_event_trigger(struct libusb_context *ctx)
|
|||
ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
|
||||
}
|
||||
|
||||
if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
|
||||
usbi_dbg("someone unregistered a hotplug cb");
|
||||
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
|
||||
hotplug_event = 1;
|
||||
}
|
||||
|
||||
/* check if someone is closing a device */
|
||||
if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE)
|
||||
usbi_dbg("someone is closing a device");
|
||||
|
@ -2099,6 +2105,7 @@ static int handle_event_trigger(struct libusb_context *ctx)
|
|||
if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) {
|
||||
usbi_dbg("hotplug message received");
|
||||
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING;
|
||||
hotplug_event = 1;
|
||||
assert(!list_empty(&ctx->hotplug_msgs));
|
||||
list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
|
||||
}
|
||||
|
@ -2136,20 +2143,9 @@ static int handle_event_trigger(struct libusb_context *ctx)
|
|||
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
|
||||
/* process the hotplug messages, if any */
|
||||
while (!list_empty(&hotplug_msgs)) {
|
||||
struct libusb_hotplug_message *message =
|
||||
list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
|
||||
|
||||
usbi_hotplug_match(ctx, message->device, message->event);
|
||||
|
||||
/* the device left, dereference the device */
|
||||
if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
libusb_unref_device(message->device);
|
||||
|
||||
list_del(&message->list);
|
||||
free(message);
|
||||
}
|
||||
/* process the hotplug events, if any */
|
||||
if (hotplug_event)
|
||||
usbi_hotplug_process(ctx, &hotplug_msgs);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -368,6 +368,9 @@ struct libusb_context {
|
|||
libusb_hotplug_callback_handle next_hotplug_cb_handle;
|
||||
usbi_mutex_t hotplug_cbs_lock;
|
||||
|
||||
/* A flag to indicate that the context is ready for hotplug notifications */
|
||||
usbi_atomic_t hotplug_ready;
|
||||
|
||||
/* this is a list of in-flight transfer handles, sorted by timeout
|
||||
* expiration. URBs to timeout the soonest are placed at the beginning of
|
||||
* the list, URBs that will time out later are placed after, and urbs with
|
||||
|
@ -689,8 +692,75 @@ union usbi_bos_desc_buf {
|
|||
uint16_t align; /* Force 2-byte alignment */
|
||||
};
|
||||
|
||||
enum usbi_hotplug_flags {
|
||||
/* This callback is interested in device arrivals */
|
||||
USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
|
||||
|
||||
/* This callback is interested in device removals */
|
||||
USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
|
||||
|
||||
/* IMPORTANT: The values for the below entries must start *after*
|
||||
* the highest value of the above entries!!!
|
||||
*/
|
||||
|
||||
/* The vendor_id field is valid for matching */
|
||||
USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3),
|
||||
|
||||
/* The product_id field is valid for matching */
|
||||
USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4),
|
||||
|
||||
/* The dev_class field is valid for matching */
|
||||
USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5),
|
||||
|
||||
/* This callback has been unregistered and needs to be freed */
|
||||
USBI_HOTPLUG_NEEDS_FREE = (1U << 6),
|
||||
};
|
||||
|
||||
struct usbi_hotplug_callback {
|
||||
/* Flags that control how this callback behaves */
|
||||
uint8_t flags;
|
||||
|
||||
/* Vendor ID to match (if flags says this is valid) */
|
||||
uint16_t vendor_id;
|
||||
|
||||
/* Product ID to match (if flags says this is valid) */
|
||||
uint16_t product_id;
|
||||
|
||||
/* Device class to match (if flags says this is valid) */
|
||||
uint8_t dev_class;
|
||||
|
||||
/* Callback function to invoke for matching event/device */
|
||||
libusb_hotplug_callback_fn cb;
|
||||
|
||||
/* Handle for this callback (used to match on deregister) */
|
||||
libusb_hotplug_callback_handle handle;
|
||||
|
||||
/* User data that will be passed to the callback function */
|
||||
void *user_data;
|
||||
|
||||
/* List this callback is registered in (ctx->hotplug_cbs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct usbi_hotplug_message {
|
||||
/* The hotplug event that occurred */
|
||||
libusb_hotplug_event event;
|
||||
|
||||
/* The device for which this hotplug event occurred */
|
||||
struct libusb_device *device;
|
||||
|
||||
/* List this message is contained in (ctx->hotplug_msgs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* shared data and functions */
|
||||
|
||||
void usbi_hotplug_init(struct libusb_context *ctx);
|
||||
void usbi_hotplug_exit(struct libusb_context *ctx);
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event);
|
||||
void usbi_hotplug_process(struct libusb_context *ctx, struct list_head *hotplug_msgs);
|
||||
|
||||
int usbi_io_init(struct libusb_context *ctx);
|
||||
void usbi_io_exit(struct libusb_context *ctx);
|
||||
|
||||
|
@ -818,7 +888,8 @@ struct usbi_os_backend {
|
|||
* data structures for later, etc.
|
||||
*
|
||||
* This function is called when a libusb user initializes the library
|
||||
* prior to use.
|
||||
* prior to use. Mutual exclusion with other init and exit calls is
|
||||
* guaranteed when this function is called.
|
||||
*
|
||||
* Return 0 on success, or a LIBUSB_ERROR code on failure.
|
||||
*/
|
||||
|
@ -828,6 +899,8 @@ struct usbi_os_backend {
|
|||
* that was set up by init.
|
||||
*
|
||||
* This function is called when the user deinitializes the library.
|
||||
* Mutual exclusion with other init and exit calls is guaranteed when
|
||||
* this function is called.
|
||||
*/
|
||||
void (*exit)(struct libusb_context *ctx);
|
||||
|
||||
|
@ -1390,6 +1463,12 @@ extern const struct usbi_os_backend usbi_backend;
|
|||
#define for_each_removed_event_source_safe(ctx, e, n) \
|
||||
for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source)
|
||||
|
||||
#define for_each_hotplug_cb(ctx, c) \
|
||||
for_each_helper(c, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
|
||||
|
||||
#define for_each_hotplug_cb_safe(ctx, c, n) \
|
||||
for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct usbi_hotplug_callback)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
|
||||
#include "darwin_usb.h"
|
||||
|
||||
static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int init_count = 0;
|
||||
|
||||
/* async event thread */
|
||||
|
@ -581,8 +580,6 @@ static int darwin_init(struct libusb_context *ctx) {
|
|||
bool first_init;
|
||||
int rc;
|
||||
|
||||
pthread_mutex_lock (&libusb_darwin_init_mutex);
|
||||
|
||||
first_init = (1 == ++init_count);
|
||||
|
||||
do {
|
||||
|
@ -638,16 +635,12 @@ static int darwin_init(struct libusb_context *ctx) {
|
|||
--init_count;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&libusb_darwin_init_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void darwin_exit (struct libusb_context *ctx) {
|
||||
UNUSED(ctx);
|
||||
|
||||
pthread_mutex_lock (&libusb_darwin_init_mutex);
|
||||
|
||||
if (0 == --init_count) {
|
||||
/* stop the event runloop and wait for the thread to terminate. */
|
||||
pthread_mutex_lock (&libusb_darwin_at_mutex);
|
||||
|
@ -665,8 +658,6 @@ static void darwin_exit (struct libusb_context *ctx) {
|
|||
mach_port_deallocate(mach_task_self(), clock_monotonic);
|
||||
#endif
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&libusb_darwin_init_mutex);
|
||||
}
|
||||
|
||||
static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) {
|
||||
|
|
|
@ -100,8 +100,6 @@ static int init_count = 0;
|
|||
static int weak_authority = 0;
|
||||
#endif
|
||||
|
||||
/* Serialize hotplug start/stop */
|
||||
static usbi_mutex_static_t linux_hotplug_startstop_lock = USBI_MUTEX_INITIALIZER;
|
||||
/* Serialize scan-devices, event-thread, and poll */
|
||||
usbi_mutex_static_t linux_hotplug_lock = USBI_MUTEX_INITIALIZER;
|
||||
|
||||
|
@ -407,7 +405,6 @@ static int op_init(struct libusb_context *ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
|
||||
r = LIBUSB_SUCCESS;
|
||||
if (init_count == 0) {
|
||||
/* start up hotplug event handler */
|
||||
|
@ -422,7 +419,6 @@ static int op_init(struct libusb_context *ctx)
|
|||
} else {
|
||||
usbi_err(ctx, "error starting hotplug event monitor");
|
||||
}
|
||||
usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -437,13 +433,11 @@ static void op_exit(struct libusb_context *ctx)
|
|||
}
|
||||
#endif
|
||||
|
||||
usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
|
||||
assert(init_count != 0);
|
||||
if (!--init_count) {
|
||||
/* tear down event handler */
|
||||
linux_stop_event_monitor();
|
||||
}
|
||||
usbi_mutex_static_unlock(&linux_hotplug_startstop_lock);
|
||||
}
|
||||
|
||||
static int op_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
|
||||
|
|
|
@ -477,26 +477,9 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
|
|||
static int windows_init(struct libusb_context *ctx)
|
||||
{
|
||||
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
|
||||
char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
|
||||
HANDLE mutex;
|
||||
bool winusb_backend_init = false;
|
||||
int r;
|
||||
|
||||
sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
|
||||
mutex = CreateMutexA(NULL, FALSE, mutex_name);
|
||||
if (mutex == NULL) {
|
||||
usbi_err(ctx, "could not create mutex: %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
// A successful wait gives this thread ownership of the mutex
|
||||
// => any concurrent wait stalls until the mutex is released
|
||||
if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
|
||||
usbi_err(ctx, "failure to access mutex: %s", windows_error_str(0));
|
||||
CloseHandle(mutex);
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
// NB: concurrent usage supposes that init calls are equally balanced with
|
||||
// exit calls. If init is called more than exit, we will not exit properly
|
||||
if (++init_count == 1) { // First init?
|
||||
|
@ -565,29 +548,12 @@ init_exit: // Holds semaphore here
|
|||
--init_count;
|
||||
}
|
||||
|
||||
ReleaseMutex(mutex);
|
||||
CloseHandle(mutex);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void windows_exit(struct libusb_context *ctx)
|
||||
{
|
||||
struct windows_context_priv *priv = usbi_get_context_priv(ctx);
|
||||
char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
|
||||
HANDLE mutex;
|
||||
|
||||
sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
|
||||
mutex = CreateMutexA(NULL, FALSE, mutex_name);
|
||||
if (mutex == NULL)
|
||||
return;
|
||||
|
||||
// A successful wait gives this thread ownership of the mutex
|
||||
// => any concurrent wait stalls until the mutex is released
|
||||
if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
|
||||
usbi_err(ctx, "failed to access mutex: %s", windows_error_str(0));
|
||||
CloseHandle(mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
// A NULL completion status will indicate to the thread that it is time to exit
|
||||
if (!PostQueuedCompletionStatus(priv->completion_port, 0, (ULONG_PTR)ctx, NULL))
|
||||
|
@ -608,9 +574,6 @@ static void windows_exit(struct libusb_context *ctx)
|
|||
winusb_backend.exit(ctx);
|
||||
htab_destroy();
|
||||
}
|
||||
|
||||
ReleaseMutex(mutex);
|
||||
CloseHandle(mutex);
|
||||
}
|
||||
|
||||
static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -84,7 +84,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -103,7 +103,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -103,7 +103,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -79,7 +79,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -80,7 +80,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -99,7 +99,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -99,7 +99,6 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include=".\config.h" />
|
||||
<ClInclude Include="..\libusb\os\events_windows.h" />
|
||||
<ClInclude Include="..\libusb\hotplug.h" />
|
||||
<ClInclude Include="..\libusb\libusb.h" />
|
||||
<ClInclude Include="..\libusb\libusbi.h" />
|
||||
<ClInclude Include="..\libusb\os\threads_windows.h" />
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
<ClInclude Include="..\libusb\os\events_windows.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\hotplug.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\libusb\libusb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Reference in a new issue