summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Batard <pbatard@gmail.com>2010-10-08 17:12:15 +0100
committerPete Batard <pbatard@gmail.com>2010-10-08 17:12:15 +0100
commit6cb025b4c853f2c034f72e12b1ab243d9d6e2a59 (patch)
treebbccbc296cb924fac96df0cb5ccd2c70975b9dc5
parentf62bae968fc07aff6e7f1e77b70e10010652a0df (diff)
downloadlibusb-6cb025b4c853f2c034f72e12b1ab243d9d6e2a59.tar.gz
hotplug - core, Linux, Darwin
-rw-r--r--configure.ac6
-rw-r--r--libusb/core.c112
-rw-r--r--libusb/io.c16
-rw-r--r--libusb/libusb.h8
-rw-r--r--libusb/libusbi.h37
-rw-r--r--libusb/os/darwin_usb.c414
-rw-r--r--libusb/os/darwin_usb.h11
-rw-r--r--libusb/os/linux_usbfs.c218
8 files changed, 645 insertions, 177 deletions
diff --git a/configure.ac b/configure.ac
index dc2d029..43c9e56 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,7 +51,7 @@ case $host in
THREAD_CFLAGS="-pthread"
PC_LIBS_PRIVATE="${PC_LIBS_PRIVATE} -pthread"
AM_CFLAGS="-std=gnu99"
- AM_LDFLAGS=""
+ AM_LDFLAGS="-ludev"
;;
*-darwin*)
AC_DEFINE(OS_DARWIN, [], [Darwin backend])
@@ -77,7 +77,7 @@ case $host in
LIBS="${LIBS} ${PC_LIBS_PRIVATE}"
# -avoid-version to avoid a naming scheme such as libusb-0.dll
AM_LDFLAGS="-no-undefined -avoid-version -Wl,--add-stdcall-alias"
- AM_CFLAGS="-std=c99"
+ AM_CFLAGS="-std=c99 -DWINVER=0x500"
AC_CHECK_TOOL(RC, windres, no)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
;;
@@ -90,7 +90,7 @@ case $host in
threads="posix"
PC_LIBS_PRIVATE="-lsetupapi -lole32 -ladvapi32"
LIBS="${LIBS} ${PC_LIBS_PRIVATE}"
- AM_CFLAGS="-std=c99"
+ AM_CFLAGS="-std=c99 -DWINVER=0x500"
AM_LDFLAGS="-no-undefined -avoid-version"
AC_CHECK_TOOL(RC, windres, no)
;;
diff --git a/libusb/core.c b/libusb/core.c
index 46c2b9c..12c0c9c 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -522,6 +522,15 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
return NULL;
}
+ r = usbi_mutex_init(&dev->status_online_lock, NULL);
+ if (r)
+ return NULL;
+
+ r = usbi_mutex_init(&dev->devaddr_lock, NULL);
+ if (r)
+ return NULL;
+
+ dev->status_online = 1;
dev->ctx = ctx;
dev->refcnt = 1;
dev->session_data = session_id;
@@ -580,6 +589,24 @@ struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
return ret;
}
+/* TODO: doc */
+struct libusb_device *usbi_get_device_by_session_id_ref(struct libusb_context *ctx,
+ unsigned long session_id)
+{
+ struct libusb_device *dev;
+ struct libusb_device *ret = NULL;
+ usbi_mutex_lock(&ctx->usb_devs_lock);
+ list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device)
+ if (dev->session_data == session_id) {
+ ret = dev;
+ libusb_ref_device(dev);
+ break;
+ }
+ usbi_mutex_unlock(&ctx->usb_devs_lock);
+
+ return ret;
+}
+
/** @ingroup dev
* Returns a list of USB devices currently attached to the system. This is
* your entry point into finding a USB device to operate.
@@ -679,7 +706,18 @@ uint8_t API_EXPORTED libusb_get_bus_number(libusb_device *dev)
*/
uint8_t API_EXPORTED libusb_get_device_address(libusb_device *dev)
{
- return dev->device_address;
+ uint8_t devaddr;
+
+ usbi_mutex_lock(&dev->devaddr_lock);
+ devaddr = dev->device_address;
+ usbi_mutex_unlock(&dev->devaddr_lock);
+ return devaddr;
+}
+
+/* TODO: doc */
+unsigned long API_EXPORTED libusb_get_session_id(libusb_device *dev)
+{
+ return dev->session_data;
}
static const struct libusb_endpoint_descriptor *find_endpoint(
@@ -844,6 +882,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
usbi_mutex_unlock(&dev->ctx->usb_devs_lock);
usbi_mutex_destroy(&dev->lock);
+ usbi_mutex_destroy(&dev->devaddr_lock);
+ usbi_mutex_destroy(&dev->status_online_lock);
free(dev);
}
}
@@ -1506,7 +1546,8 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
int API_EXPORTED libusb_init(libusb_context **context)
{
char *dbg = getenv("LIBUSB_DEBUG");
- struct libusb_context *ctx;
+ size_t priv_size = usbi_backend->context_priv_size;
+ struct libusb_context *ctx = malloc(sizeof(*ctx) + priv_size);
int r;
usbi_mutex_static_lock(&default_context_lock);
@@ -1518,12 +1559,12 @@ int API_EXPORTED libusb_init(libusb_context **context)
return 0;
}
- ctx = malloc(sizeof(*ctx));
+ ctx = malloc(sizeof(*ctx) + priv_size);
if (!ctx) {
r = LIBUSB_ERROR_NO_MEM;
goto err_unlock;
}
- memset(ctx, 0, sizeof(*ctx));
+ memset(ctx, 0, sizeof(*ctx) + priv_size);
if (dbg) {
ctx->debug = atoi(dbg);
@@ -1547,13 +1588,17 @@ int API_EXPORTED libusb_init(libusb_context **context)
usbi_mutex_init(&ctx->usb_devs_lock, NULL);
usbi_mutex_init(&ctx->open_devs_lock, NULL);
+ usbi_mutex_init(&ctx->hotplug_listener_lock, NULL);
list_init(&ctx->usb_devs);
list_init(&ctx->open_devs);
+ ctx->hotplug_connected_listener = 0;
+ ctx->hotplug_disconnected_listener = 0;
+ ctx->hotplug_listener_user_data = 0;
r = usbi_io_init(ctx);
if (r < 0) {
if (usbi_backend->exit)
- usbi_backend->exit();
+ usbi_backend->exit(ctx);
goto err_destroy_mutex;
}
@@ -1611,13 +1656,41 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
usbi_io_exit(ctx);
if (usbi_backend->exit)
- usbi_backend->exit();
+ usbi_backend->exit(ctx);
usbi_mutex_destroy(&ctx->open_devs_lock);
usbi_mutex_destroy(&ctx->usb_devs_lock);
free(ctx);
}
+/* TODO: doc */
+void API_EXPORTED libusb_register_hotplug_listeners(
+ libusb_context *ctx, libusb_hotplug_cb_fn connected_cb,
+ libusb_hotplug_cb_fn disconnected_cb, void *user_data)
+{
+ usbi_mutex_lock(&ctx->hotplug_listener_lock);
+ ctx->hotplug_connected_listener = connected_cb;
+ ctx->hotplug_disconnected_listener = disconnected_cb;
+ ctx->hotplug_listener_user_data = user_data;
+ usbi_mutex_unlock(&ctx->hotplug_listener_lock);
+}
+
+/* TODO: doc */
+void API_EXPORTED libusb_unregister_hotplug_listeners(libusb_context *ctx)
+{
+ usbi_mutex_lock(&ctx->hotplug_listener_lock);
+ ctx->hotplug_connected_listener = 0;
+ ctx->hotplug_disconnected_listener = 0;
+ ctx->hotplug_listener_user_data = 0;
+ usbi_mutex_unlock(&ctx->hotplug_listener_lock);
+}
+
+/* TODO: doc */
+int API_EXPORTED libusb_get_status(libusb_device *dev)
+{
+ return dev->status_online;
+}
+
void usbi_log_v(struct libusb_context *ctx, enum usbi_log_level level,
const char *function, const char *format, va_list args)
{
@@ -1736,3 +1809,30 @@ const struct libusb_version * LIBUSB_CALL libusb_getversion(void)
{
return &libusb_version_internal;
}
+
+/* TODO: doc */
+int usbi_notify_device_state(struct libusb_device *dev, int new_state)
+{
+ int status_online;
+ libusb_hotplug_cb_fn connected_cb;
+ libusb_hotplug_cb_fn disconnected_cb;
+ void* user_data;
+
+ usbi_mutex_lock(&dev->ctx->hotplug_listener_lock);
+ connected_cb = dev->ctx->hotplug_connected_listener;
+ disconnected_cb = dev->ctx->hotplug_disconnected_listener;
+ user_data = dev->ctx->hotplug_listener_user_data;
+ usbi_mutex_unlock(&dev->ctx->hotplug_listener_lock);
+ status_online = new_state == 0 ? 0 : 1;
+ if (connected_cb && status_online) {
+ usbi_dbg("calling registered callback for device attach");
+ connected_cb(dev, user_data);
+ return 1;
+ } else if (disconnected_cb) {
+ usbi_dbg("calling registered callback for device detach");
+ disconnected_cb(dev, user_data);
+ return 0;
+ }
+ return -1;
+}
+
diff --git a/libusb/io.c b/libusb/io.c
index d4f6f3d..194b392 100644
--- a/libusb/io.c
+++ b/libusb/io.c
@@ -1009,10 +1009,26 @@ int usbi_io_init(struct libusb_context *ctx)
{
int r;
+// mutexes on Windows are recursive by default
+// TODO: something more elegant
+#if !defined(OS_WINDOWS)
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ r = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ if (r) {
+ pthread_mutexattr_destroy(&attr);
+ return r;;
+ }
+#endif
usbi_mutex_init(&ctx->flying_transfers_lock, NULL);
usbi_mutex_init(&ctx->pollfds_lock, NULL);
usbi_mutex_init(&ctx->pollfd_modify_lock, NULL);
+#if defined(OS_WINDOWS)
usbi_mutex_init(&ctx->events_lock, NULL);
+#else
+ pthread_mutex_init(&ctx->events_lock, &attr);
+ pthread_mutexattr_destroy(&attr);
+#endif
usbi_mutex_init(&ctx->event_waiters_lock, NULL);
usbi_cond_init(&ctx->event_waiters_cond, NULL);
list_init(&ctx->flying_transfers);
diff --git a/libusb/libusb.h b/libusb/libusb.h
index d46c68b..f5eb811 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -844,12 +844,20 @@ struct libusb_transfer {
;
};
+typedef void (*libusb_hotplug_cb_fn)(
+ struct libusb_device *device, void *user_data);
int LIBUSB_CALL libusb_init(libusb_context **ctx);
void LIBUSB_CALL libusb_exit(libusb_context *ctx);
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode);
const struct libusb_version * LIBUSB_CALL libusb_getversion(void);
+void LIBUSB_CALL libusb_register_hotplug_listeners(libusb_context *ctx,
+ libusb_hotplug_cb_fn connected_cb,
+ libusb_hotplug_cb_fn disconnected_cb, void *user_data);
+void LIBUSB_CALL libusb_unregister_hotplug_listeners(libusb_context *ctx);
+int LIBUSB_CALL libusb_get_status(libusb_device *dev);
+
ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
libusb_device ***list);
void LIBUSB_CALL libusb_free_device_list(libusb_device **list,
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 99bf6ac..2963a9a 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -179,6 +179,26 @@ static inline void usbi_dbg(const char *format, ...)
#endif /* !defined(_MSC_VER) || _MSC_VER > 1200 */
+/*
+ * djb2
+ *
+ * This algorithm (k=33) was first reported by Dan Bernstein many years
+ * ago in comp.lang.c.
+ */
+// TODO: maintain a hash table and detect collisions
+static inline unsigned long usbi_hash(const char* sz)
+{
+ unsigned long r = 5381;
+ int c;
+ while ((c = *sz++)) {
+ r = ((r << 5) + r) + c; /* r * 33 + c */
+ }
+ if (r == 0) {
+ usbi_warn(NULL, "'%s''s hash is 0!", sz);
+ }
+ return r;
+}
+
#define USBI_GET_CONTEXT(ctx) if (!(ctx)) (ctx) = usbi_default_context
#define DEVICE_CTX(dev) ((dev)->ctx)
#define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev))
@@ -254,6 +274,12 @@ struct libusb_context {
* this timerfd is maintained to trigger on the next pending timeout */
int timerfd;
#endif
+
+ libusb_hotplug_cb_fn hotplug_connected_listener;
+ libusb_hotplug_cb_fn hotplug_disconnected_listener;
+ void* hotplug_listener_user_data;
+ usbi_mutex_t hotplug_listener_lock;
+ unsigned char os_priv[0];
};
#ifdef USBI_TIMERFD_AVAILABLE
@@ -268,10 +294,13 @@ struct libusb_device {
usbi_mutex_t lock;
int refcnt;
+ int status_online;
+ usbi_mutex_t status_online_lock;
struct libusb_context *ctx;
uint8_t bus_number;
uint8_t device_address;
+ usbi_mutex_t devaddr_lock;
uint8_t num_configurations;
struct list_head list;
@@ -354,10 +383,13 @@ struct usb_descriptor_header {
int usbi_io_init(struct libusb_context *ctx);
void usbi_io_exit(struct libusb_context *ctx);
+int usbi_notify_device_state(struct libusb_device* dev, int new_state);
struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
unsigned long session_id);
struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx,
unsigned long session_id);
+struct libusb_device *usbi_get_device_by_session_id_ref(struct libusb_context *ctx,
+ unsigned long session_id);
int usbi_sanitize_device(struct libusb_device *dev);
void usbi_handle_disconnect(struct libusb_device_handle *handle);
@@ -423,7 +455,7 @@ struct usbi_os_backend {
*
* This function is called when the user deinitializes the library.
*/
- void (*exit)(void);
+ void (*exit)(struct libusb_context *ctx);
/* Enumerate all the USB devices on the system, returning them in a list
* of discovered devices.
@@ -840,6 +872,9 @@ struct usbi_os_backend {
clockid_t (*get_timerfd_clockid)(void);
#endif
+ /* TODO: doc */
+ size_t context_priv_size;
+
/* Number of bytes to reserve for per-device private backend data.
* This private data area is accessible through the "os_priv" field of
* struct libusb_device. */
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c
index 646c938..3781f72 100644
--- a/libusb/os/darwin_usb.c
+++ b/libusb/os/darwin_usb.c
@@ -42,19 +42,26 @@
#include "darwin_usb.h"
-static mach_port_t libusb_darwin_mp = 0; /* master port */
-static CFRunLoopRef libusb_darwin_acfl = NULL; /* async cf loop */
-static int initCount = 0;
-
-/* async event thread */
-static pthread_t libusb_darwin_at;
-
static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian);
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface);
static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface);
static int darwin_reset_device(struct libusb_device_handle *dev_handle);
static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0);
+static int add_new_device (
+ struct libusb_context* ctx, usb_device_t** device, UInt32 locationID,
+ struct libusb_device** dev);
+
+inline static struct darwin_context_priv* __ctx_priv (struct libusb_context *ctx)
+{
+ return (struct darwin_context_priv*) ctx->os_priv;
+}
+
+inline static struct darwin_device_priv* __dev_priv (struct libusb_device *dev)
+{
+ return (struct darwin_device_priv*) dev->os_priv;
+}
+
static const char *darwin_error_str (int result) {
switch (result) {
case kIOReturnSuccess:
@@ -145,8 +152,10 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui
return -1;
}
-static int usb_setup_device_iterator (io_iterator_t *deviceIterator) {
- return IOServiceGetMatchingServices(libusb_darwin_mp, IOServiceMatching(kIOUSBDeviceClassName), deviceIterator);
+static int usb_setup_device_iterator (
+ struct darwin_context_priv* ctx_priv, io_iterator_t * deviceIterator) {
+ return IOServiceGetMatchingServices (
+ ctx_priv->libusb_darwin_mp, IOServiceMatching (kIOUSBDeviceClassName), deviceIterator);
}
static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32 *locationp) {
@@ -187,12 +196,13 @@ static usb_device_t **usb_get_next_device (io_iterator_t deviceIterator, UInt32
return device;
}
-static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***darwin_device) {
+static kern_return_t darwin_get_device (struct darwin_context_priv* ctx_priv,
+ uint32_t dev_location, usb_device_t ***darwin_device) {
kern_return_t kresult;
UInt32 location;
io_iterator_t deviceIterator;
- kresult = usb_setup_device_iterator (&deviceIterator);
+ kresult = usb_setup_device_iterator (ctx_priv, &deviceIterator);
if (kresult)
return kresult;
@@ -212,13 +222,53 @@ static kern_return_t darwin_get_device (uint32_t dev_location, usb_device_t ***d
return kIOReturnSuccess;
}
+static int check_notify (struct libusb_device *dev, int new_state) {
+ struct darwin_device_priv *dpriv = __dev_priv(dev);
+ int notify = 0;
+ usbi_mutex_lock (&dev->status_online_lock);
+ usbi_mutex_lock (&dpriv->status_changed_lock);
+ dev->status_online = new_state;
+ if (!dpriv->status_changed) {
+ dpriv->status_changed = 1;
+ notify = 1;
+ }
+ usbi_mutex_unlock (&dpriv->status_changed_lock);
+ usbi_mutex_unlock (&dev->status_online_lock);
-static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
+ return notify;
+}
+
+static void darwin_devices_attached (void *ptr, io_iterator_t add_devices){
+ struct libusb_context *ctx = (struct libusb_context *) ptr;
+ struct darwin_context_priv *ctx_priv = __ctx_priv (ctx);
+ struct libusb_device *dev;
+ usb_device_t **device;
+ UInt32 location;
+ int ret;
+ usbi_info (ctx, "a device has been attached");
+ while ((device = usb_get_next_device(add_devices, &location)) != NULL) {
+ dev = usbi_get_device_by_session_id_ref (ctx, location);
+ if (dev && check_notify(dev, 1)) {
+ write (ctx_priv->hotplug_pipe[1], &location, sizeof(location));
+ }
+ else {
+ ret = add_new_device (ctx, device, location, &dev);
+ __dev_priv (dev)->status_changed = 1;
+ if (!ret) {
+ write (ctx_priv->hotplug_pipe[1], &location, sizeof(location));
+ }
+ (*(device))->Release (device);
+ }
+ }
+}
+static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
struct libusb_context *ctx = (struct libusb_context *)ptr;
+ struct darwin_context_priv *ctx_priv = __ctx_priv(ctx);
struct libusb_device_handle *handle;
struct darwin_device_priv *dpriv;
struct darwin_device_handle_priv *priv;
+ struct libusb_device *dev;
io_service_t device;
long location;
@@ -229,7 +279,10 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
while ((device = IOIteratorNext (rem_devices)) != 0) {
/* get the location from the i/o registry */
- locationCF = IORegistryEntryCreateCFProperty (device, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
+ locationCF =
+ IORegistryEntryCreateCFProperty (
+ device, CFSTR (kUSBDevicePropertyLocationID),
+ kCFAllocatorDefault, 0);
CFNumberGetValue(locationCF, kCFNumberLongType, &location);
CFRelease (locationCF);
@@ -249,6 +302,10 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
}
usbi_mutex_unlock(&ctx->open_devs_lock);
+ dev = usbi_get_device_by_session_id_ref (ctx, location);
+ if (dev && check_notify(dev, 0)) {
+ write (ctx_priv->hotplug_pipe[1], &location, sizeof(location));
+ }
}
}
@@ -262,10 +319,12 @@ static void darwin_clear_iterator (io_iterator_t iter) {
static void *event_thread_main (void *arg0) {
IOReturn kresult;
struct libusb_context *ctx = (struct libusb_context *)arg0;
+ struct darwin_context_priv *ctx_priv = __ctx_priv (ctx);
/* hotplug (device removal) source */
CFRunLoopSourceRef libusb_notification_cfsource;
io_notification_port_t libusb_notification_port;
+ io_iterator_t libusb_add_device_iterator;
io_iterator_t libusb_rem_device_iterator;
usbi_info (ctx, "creating hotplug event source");
@@ -273,10 +332,25 @@ static void *event_thread_main (void *arg0) {
CFRetain (CFRunLoopGetCurrent ());
/* add the notification port to the run loop */
- libusb_notification_port = IONotificationPortCreate (libusb_darwin_mp);
+ libusb_notification_port =
+ IONotificationPortCreate (ctx_priv->libusb_darwin_mp);
libusb_notification_cfsource = IONotificationPortGetRunLoopSource (libusb_notification_port);
CFRunLoopAddSource(CFRunLoopGetCurrent (), libusb_notification_cfsource, kCFRunLoopDefaultMode);
+ kresult =
+ IOServiceAddMatchingNotification (
+ libusb_notification_port,
+ kIOMatchedNotification,
+ IOServiceMatching (kIOUSBDeviceClassName),
+ (IOServiceMatchingCallback) darwin_devices_attached,
+ (void *) ctx,
+ &libusb_add_device_iterator);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (ctx, "could not add hotplug event source: %s",
+ darwin_error_str (kresult));
+ /* TODO: pthread_exit() -> usbi_exit()? */
+ pthread_exit ((void *) &kresult);
+ }
/* create notifications for removed devices */
kresult = IOServiceAddMatchingNotification (libusb_notification_port, kIOTerminatedNotification,
IOServiceMatching(kIOUSBDeviceClassName),
@@ -293,7 +367,7 @@ static void *event_thread_main (void *arg0) {
darwin_clear_iterator (libusb_rem_device_iterator);
/* let the main thread know about the async runloop */
- libusb_darwin_acfl = CFRunLoopGetCurrent ();
+ ctx_priv->libusb_darwin_acfl = CFRunLoopGetCurrent ();
usbi_info (ctx, "thread ready to receive events");
@@ -308,44 +382,60 @@ static void *event_thread_main (void *arg0) {
CFRelease (CFRunLoopGetCurrent ());
- libusb_darwin_acfl = NULL;
+ ctx_priv->libusb_darwin_acfl = NULL;
pthread_exit (0);
}
static int darwin_init(struct libusb_context *ctx) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv (ctx);
IOReturn kresult;
+ int r;
- if (!(initCount++)) {
+ if (!(ctx_priv->initCount++)) {
+ r = pipe (ctx_priv->hotplug_pipe);
+ if (r < 0) {
+ return r;
+ }
+ r = usbi_add_pollfd (ctx, ctx_priv->hotplug_pipe[0], POLLIN);
+ if (r < 0) {
+ return r;
+ }
/* Create the master port for talking to IOKit */
- if (!libusb_darwin_mp) {
- kresult = IOMasterPort (MACH_PORT_NULL, &libusb_darwin_mp);
+ if (!ctx_priv->libusb_darwin_mp) {
+ kresult = IOMasterPort(MACH_PORT_NULL, &ctx_priv->libusb_darwin_mp);
- if (kresult != kIOReturnSuccess || !libusb_darwin_mp)
+ if (kresult != kIOReturnSuccess || !ctx_priv->libusb_darwin_mp)
return darwin_to_libusb (kresult);
}
- pthread_create (&libusb_darwin_at, NULL, event_thread_main, (void *)ctx);
+ /* TODO: pthread_create -> usbi_create? */
+ pthread_create (&ctx_priv->libusb_darwin_at, NULL, event_thread_main, (void *) ctx);
- while (!libusb_darwin_acfl)
+ while (!ctx_priv->libusb_darwin_acfl)
usleep (10);
}
return 0;
}
-static void darwin_exit (void) {
- if (!(--initCount)) {
+static void darwin_exit (libusb_context* ctx) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv (ctx);
+ if (!(--ctx_priv->initCount)) {
void *ret;
/* stop the async runloop */
- CFRunLoopStop (libusb_darwin_acfl);
- pthread_join (libusb_darwin_at, &ret);
+ CFRunLoopStop (ctx_priv->libusb_darwin_acfl);
+ /* TODO: pthread_join -> usbi_join? */
+ pthread_join (ctx_priv->libusb_darwin_at, &ret);
- if (libusb_darwin_mp)
- mach_port_deallocate(mach_task_self(), libusb_darwin_mp);
+ if (ctx_priv->libusb_darwin_mp)
+ mach_port_deallocate(mach_task_self(), ctx_priv->libusb_darwin_mp);
- libusb_darwin_mp = 0;
+ ctx_priv->libusb_darwin_mp = 0;
+ usbi_remove_pollfd (ctx, ctx_priv->hotplug_pipe[0]);
+ close (ctx_priv->hotplug_pipe[0]);
+ close (ctx_priv->hotplug_pipe[1]);
}
}
@@ -406,7 +496,7 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi
return LIBUSB_ERROR_OTHER;
if (!priv->device) {
- kresult = darwin_get_device (priv->location, &device);
+ kresult = darwin_get_device (__ctx_priv(dev->ctx), priv->location, &device);
if (kresult || !device) {
usbi_err (DEVICE_CTX (dev), "could not find device: %s", darwin_error_str (kresult));
@@ -435,88 +525,24 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi
return darwin_to_libusb (kresult);
}
-/* check whether the os has configured the device */
-static int darwin_check_configuration (struct libusb_context *ctx, struct libusb_device *dev, usb_device_t **darwin_device) {
- struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
-
- IOUSBConfigurationDescriptorPtr configDesc;
- IOUSBFindInterfaceRequest request;
- kern_return_t kresult;
- io_iterator_t interface_iterator;
- io_service_t firstInterface;
-
- if (priv->dev_descriptor.bNumConfigurations < 1) {
- usbi_err (ctx, "device has no configurations");
- return LIBUSB_ERROR_OTHER; /* no configurations at this speed so we can't use it */
- }
-
- /* find the first configuration */
- kresult = (*darwin_device)->GetConfigurationDescriptorPtr (darwin_device, 0, &configDesc);
- priv->first_config = (kIOReturnSuccess == kresult) ? configDesc->bConfigurationValue : 1;
-
- /* check if the device is already configured. there is probably a better way than iterating over the
- to accomplish this (the trick is we need to avoid a call to GetConfigurations since buggy devices
- might lock up on the device request) */
-
- /* Setup the Interface Request */
- request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
- request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
- request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
-
- kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator);
- if (kresult)
- return darwin_to_libusb (kresult);
-
- /* iterate once */
- firstInterface = IOIteratorNext(interface_iterator);
-
- /* done with the interface iterator */
- IOObjectRelease(interface_iterator);
-
- if (firstInterface) {
- IOObjectRelease (firstInterface);
-
- /* device is configured */
- if (priv->dev_descriptor.bNumConfigurations == 1)
- /* to avoid problems with some devices get the configurations value from the configuration descriptor */
- priv->active_config = priv->first_config;
- else
- /* devices with more than one configuration should work with GetConfiguration */
- (*darwin_device)->GetConfiguration (darwin_device, &priv->active_config);
- } else
- /* not configured */
- priv->active_config = 0;
-
- usbi_info (ctx, "active config: %u, first config: %u", priv->active_config, priv->first_config);
-
- return 0;
-}
-
-static int process_new_device (struct libusb_context *ctx, usb_device_t **device, UInt32 locationID, struct discovered_devs **_discdevs) {
+static int add_new_device (
+ struct libusb_context *ctx, usb_device_t **device, UInt32 locationID,
+ struct libusb_device **dev)
+{
struct darwin_device_priv *priv;
- struct libusb_device *dev;
- struct discovered_devs *discdevs;
UInt16 address, idVendor, idProduct;
UInt8 bDeviceClass, bDeviceSubClass;
IOUSBDevRequest req;
int ret = 0, need_unref = 0;
- do {
- dev = usbi_get_device_by_session_id(ctx, locationID);
- if (!dev) {
- usbi_info (ctx, "allocating new device for location 0x%08x", locationID);
- dev = usbi_alloc_device(ctx, locationID);
- need_unref = 1;
- } else
- usbi_info (ctx, "using existing device for location 0x%08x", locationID);
+ usbi_info (ctx, "allocating new device for location 0x%08x", locationID);
+ *dev = usbi_alloc_device(ctx, locationID);
- if (!dev) {
- ret = LIBUSB_ERROR_NO_MEM;
- break;
- }
+ if (!*dev) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
- priv = (struct darwin_device_priv *)dev->os_priv;
+ priv = (struct darwin_device_priv *)(*dev)->os_priv;
/* Set up request for device descriptor */
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
@@ -533,8 +559,9 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
(*(device))->GetDeviceSubClass (device, &bDeviceSubClass);
/**** retrieve device descriptors ****/
- /* according to Apple's documentation the device must be open for DeviceRequest but we may not be able to open some
- * devices and Apple's USB Prober doesn't bother to open the device before issuing a descriptor request */
+ /* device must be open for DeviceRequest */
+ (*device)->USBDeviceOpen(device);
+
ret = (*(device))->DeviceRequest (device, &req);
if (ret != kIOReturnSuccess) {
int try_unsuspend = 1;
@@ -567,7 +594,7 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
if (ret != kIOReturnSuccess) {
usbi_warn (ctx, "could not retrieve device descriptor: %s. skipping device", darwin_error_str (ret));
ret = -1;
- break;
+ goto end;
}
/**** end: retrieve device descriptors ****/
@@ -578,53 +605,83 @@ static int process_new_device (struct libusb_context *ctx, usb_device_t **device
usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device",
idProduct, libusb_le16_to_cpu (priv->dev_descriptor.idProduct));
ret = -1;
- break;
+ goto end;
}
- dev->bus_number = locationID >> 24;
- dev->device_address = address;
+ (*dev)->bus_number = locationID >> 24;
+ (*dev)->device_address = address;
/* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */
+/* TODO: where does this come from???
ret = darwin_check_configuration (ctx, dev, device);
if (ret < 0)
break;
-
+*/
/* save our location, we'll need this later */
priv->location = locationID;
+ priv->status_changed = 0;
snprintf(priv->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", address, idVendor, idProduct, bDeviceClass, bDeviceSubClass);
- ret = usbi_sanitize_device (dev);
- if (ret < 0)
- break;
+ ret = usbi_sanitize_device (*dev);
+
+end:
+ if (ret < 0) {
+ libusb_unref_device(*dev);
+ return ret;
+ }
+ return 0;
+}
+static int process_new_device (
+ struct libusb_context *ctx, usb_device_t **device, UInt32 locationID,
+ struct discovered_devs **_discdevs)
+{
+ struct darwin_device_priv *priv;
+ struct libusb_device *dev;
+ struct discovered_devs *discdevs;
+ int ret = 0, need_unref = 0;
+ dev = usbi_get_device_by_session_id (ctx, locationID);
+ if (!dev) {
+ ret = add_new_device (ctx, device, locationID, &dev);
+ if (ret) {
+ return ret;
+ }
+ need_unref = 1;
+ }
+ else if (dev->status_online == 0) {
+ return -1;
+ }
+ else {
+ usbi_info (ctx, "using existing device for location 0x%08x", locationID);
+ }
+ priv = (struct darwin_device_priv *) dev->os_priv;
/* append the device to the list of discovered devices */
discdevs = discovered_devs_append(*_discdevs, dev);
if (!discdevs) {
- ret = LIBUSB_ERROR_NO_MEM;
- break;
+ return LIBUSB_ERROR_NO_MEM;
}
-
+
*_discdevs = discdevs;
-
+
usbi_info (ctx, "found device with address %d at %s", dev->device_address, priv->sys_path);
- } while (0);
if (need_unref)
libusb_unref_device(dev);
- return ret;
+ return LIBUSB_SUCCESS;
}
static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv(ctx);
io_iterator_t deviceIterator;
usb_device_t **device;
kern_return_t kresult;
UInt32 location;
- if (!libusb_darwin_mp)
+ if (!ctx_priv->libusb_darwin_mp)
return LIBUSB_ERROR_INVALID_PARAM;
- kresult = usb_setup_device_iterator (&deviceIterator);
+ kresult = usb_setup_device_iterator (ctx_priv, &deviceIterator);
if (kresult != kIOReturnSuccess)
return darwin_to_libusb (kresult);
@@ -640,13 +697,14 @@ static int darwin_get_device_list(struct libusb_context *ctx, struct discovered_
}
static int darwin_open (struct libusb_device_handle *dev_handle) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv (dev_handle->dev->ctx);
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
usb_device_t **darwin_device;
IOReturn kresult;
if (0 == dpriv->open_count) {
- kresult = darwin_get_device (dpriv->location, &darwin_device);
+ kresult = darwin_get_device (ctx_priv, dpriv->location, &darwin_device);
if (kresult) {
usbi_err (HANDLE_CTX (dev_handle), "could not find device: %s", darwin_error_str (kresult));
return darwin_to_libusb (kresult);
@@ -677,10 +735,10 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
/* create async event source */
kresult = (*(dpriv->device))->CreateDeviceAsyncEventSource (dpriv->device, &priv->cfSource);
- CFRetain (libusb_darwin_acfl);
+ CFRetain (ctx_priv->libusb_darwin_acfl);
/* add the cfSource to the aync run loop */
- CFRunLoopAddSource(libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
+ CFRunLoopAddSource(ctx_priv->libusb_darwin_acfl, priv->cfSource, kCFRunLoopCommonModes);
}
}
@@ -701,6 +759,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) {
}
static void darwin_close (struct libusb_device_handle *dev_handle) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv(dev_handle->dev->ctx);
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
IOReturn kresult;
@@ -723,7 +782,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) {
if (priv->is_open) {
/* delete the device's async event source */
if (priv->cfSource) {
- CFRunLoopRemoveSource (libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
+ CFRunLoopRemoveSource (ctx_priv->libusb_darwin_acfl, priv->cfSource, kCFRunLoopDefaultMode);
CFRelease (priv->cfSource);
}
@@ -861,6 +920,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
}
static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv(dev_handle->dev->ctx);
struct darwin_device_priv *dpriv = (struct darwin_device_priv *)dev_handle->dev->os_priv;
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
io_service_t usbInterface = IO_OBJECT_NULL;
@@ -879,6 +939,52 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
if (!usbInterface && dpriv->first_config != 0) {
usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+ /* set the configuration */
+ kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
+ if (kresult != LIBUSB_SUCCESS) {
+ usbi_err (HANDLE_CTX (dev_handle), "could not set configuration");
+ return kresult;
+ }
+
+ kresult = darwin_get_interface (dpriv->device, iface, &usbInterface);
+ if (kresult) {
+ usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult));
+ return darwin_to_libusb (kresult);
+ }
+ }
+
+/* TODO: where does the following come from? */
+#if 0
+ /* make sure we have an interface */
+ if (!usbInterface && dpriv->first_config != 0) {
+ usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config);
+
+ kresult = (*(dpriv->device))->GetNumberOfConfigurations (dpriv->device, &nConfig);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "GetNumberOfConfigurations: %s", darwin_error_str(kresult));
+ return darwin_to_libusb(kresult);
+ }
+
+ if (nConfig < 1) {
+ usbi_err (HANDLE_CTX (dev_handle), "GetNumberOfConfigurations: no configurations");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ usbi_info (HANDLE_CTX (dev_handle), "device has %d configuration%s. using the first",
+ (int)nConfig, (nConfig > 1 ? "s" : "") );
+
+ /* Always use the first configuration */
+ kresult = (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, 0, &configDesc);
+ if (kresult != kIOReturnSuccess) {
+ usbi_err (HANDLE_CTX (dev_handle), "GetConfigurationDescriptorPtr: %s",
+ darwin_error_str(kresult));
+
+ new_config = 1;
+ } else
+ new_config = configDesc->bConfigurationValue;
+
+ usbi_info (HANDLE_CTX (dev_handle), "new configuration value is %d", new_config);
+
/* set the configuration */
kresult = darwin_set_configuration (dev_handle, dpriv->first_config);
if (kresult != LIBUSB_SUCCESS) {
@@ -892,7 +998,8 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
return darwin_to_libusb (kresult);
}
}
-
+#endif
+
if (!usbInterface) {
usbi_err (HANDLE_CTX (dev_handle), "interface not found");
return LIBUSB_ERROR_NOT_FOUND;
@@ -956,7 +1063,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
}
/* add the cfSource to the async thread's run loop */
- CFRunLoopAddSource(libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+ CFRunLoopAddSource(ctx_priv->libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
usbi_info (HANDLE_CTX (dev_handle), "interface opened");
@@ -964,6 +1071,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i
}
static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv(dev_handle->dev->ctx);
struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv;
IOReturn kresult;
@@ -979,7 +1087,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int
/* delete the interface's async event source */
if (cInterface->cfSource) {
- CFRunLoopRemoveSource (libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
+ CFRunLoopRemoveSource (ctx_priv->libusb_darwin_acfl, cInterface->cfSource, kCFRunLoopDefaultMode);
CFRelease (cInterface->cfSource);
}
@@ -1413,12 +1521,37 @@ static void darwin_handle_callback (struct usbi_transfer *itransfer, kern_return
usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, result));
}
+static void handle_hotplug_event(struct libusb_context* ctx, UInt32 location)
+{
+ struct darwin_device_priv *dev_priv;
+ struct libusb_device* dev;
+ int status_changed = 0, status_online;
+ dev = usbi_get_device_by_session_id(ctx, location);
+ if (!dev) {
+ return;
+ }
+ dev_priv = __dev_priv(dev);
+ usbi_mutex_lock(&dev->status_online_lock);
+ usbi_mutex_lock(&dev_priv->status_changed_lock);
+ status_changed = dev_priv->status_changed;
+ dev_priv->status_changed = 0;
+ status_online = dev->status_online;
+ usbi_mutex_unlock(&dev_priv->status_changed_lock);
+ usbi_mutex_unlock(&dev->status_online_lock);
+ if (status_changed) {
+ usbi_notify_device_state(dev, status_online);
+ if (!status_online) {
+ list_del(&dev->list);
+ }
+ }
+}
static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds, int num_ready) {
+ struct darwin_context_priv *ctx_priv = __ctx_priv(ctx);
struct usbi_transfer *itransfer;
UInt32 io_size;
IOReturn kresult;
- int i = 0, ret;
- UInt32 message;
+ int i = 0, ret = 0, hotplug_event = 0;
+ UInt32 message, location;
usbi_mutex_lock(&ctx->open_devs_lock);
for (i = 0; i < nfds && num_ready > 0; i++) {
@@ -1431,6 +1564,10 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds
if (!pollfd->revents)
continue;
+ if (pollfd->fd == ctx_priv->hotplug_pipe[0]) {
+ hotplug_event = 1;
+ continue;
+ }
num_ready--;
list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
hpriv = (struct darwin_device_handle_priv *)handle->os_priv;
@@ -1438,7 +1575,9 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds
break;
}
- if (!(pollfd->revents & POLLERR)) {
+ if (!hpriv) {
+ continue;
+ } else if (!(pollfd->revents & POLLERR)) {
ret = read (hpriv->fds[0], &message, sizeof (message));
if (ret < sizeof (message))
continue;
@@ -1450,8 +1589,8 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds
case MESSAGE_DEVICE_GONE:
/* remove the device's async port from the runloop */
if (hpriv->cfSource) {
- if (libusb_darwin_acfl)
- CFRunLoopRemoveSource (libusb_darwin_acfl, hpriv->cfSource, kCFRunLoopDefaultMode);
+ if (ctx_priv->libusb_darwin_acfl)
+ CFRunLoopRemoveSource (ctx_priv->libusb_darwin_acfl, hpriv->cfSource, kCFRunLoopDefaultMode);
CFRelease (hpriv->cfSource);
hpriv->cfSource = NULL;
}
@@ -1474,6 +1613,12 @@ static int op_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds
}
usbi_mutex_unlock(&ctx->open_devs_lock);
+ if (hotplug_event) {
+ ret = read(ctx_priv->hotplug_pipe[0], &location, sizeof(location));
+ if (ret == sizeof(location)) {
+ handle_hotplug_event(ctx, location);
+ }
+ }
return 0;
}
@@ -1537,6 +1682,7 @@ const struct usbi_os_backend darwin_backend = {
.clock_gettime = darwin_clock_gettime,
+ .context_priv_size = sizeof(struct darwin_context_priv),
.device_priv_size = sizeof(struct darwin_device_priv),
.device_handle_priv_size = sizeof(struct darwin_device_handle_priv),
.transfer_priv_size = sizeof(struct darwin_transfer_priv),
diff --git a/libusb/os/darwin_usb.h b/libusb/os/darwin_usb.h
index a71d464..4e43f75 100644
--- a/libusb/os/darwin_usb.h
+++ b/libusb/os/darwin_usb.h
@@ -121,6 +121,15 @@ typedef IOCFPlugInInterface *io_cf_plugin_ref_t;
typedef IONotificationPortRef io_notification_port_t;
/* private structures */
+struct darwin_context_priv {
+ mach_port_t libusb_darwin_mp; /* master port */
+ CFRunLoopRef libusb_darwin_acfl; /* async cf loop */
+ int initCount;
+ /* TODO: pthread_t -> usbi_t? */
+ pthread_t libusb_darwin_at; /* async event thread */
+ int hotplug_pipe[2];
+};
+
struct darwin_device_priv {
IOUSBDeviceDescriptor dev_descriptor;
UInt32 location;
@@ -128,6 +137,8 @@ struct darwin_device_priv {
usb_device_t **device;
int open_count;
UInt8 first_config, active_config;
+ usbi_mutex_t status_changed_lock;
+ int status_changed;
};
struct darwin_device_handle_priv {
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index 0c6ed95..eed8a9c 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -33,6 +33,7 @@
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
+#include <libudev.h>
#include "libusb.h"
#include "libusbi.h"
@@ -55,7 +56,7 @@
* The busnum file is important as that is the only way we can relate sysfs
* devices to usbfs nodes.
*
- * If we also have descriptors, we can obtain the device descriptor and active
+ * If we also have descriptors, we can obtain the device descriptor and active
* configuration without touching usbfs at all.
*
* The descriptors file originally only contained the active configuration
@@ -100,6 +101,12 @@ static int sysfs_can_relate_devices = -1;
/* do we have a descriptors file? */
static int sysfs_has_descriptors = -1;
+struct linux_context_priv {
+ struct udev *udev_ctx;
+ struct udev_monitor *udev_monitor;
+ int udev_monitor_fd;
+};
+
struct linux_device_priv {
char *sysfs_dir;
unsigned char *dev_descriptor;
@@ -143,6 +150,11 @@ static void __get_usbfs_path(struct libusb_device *dev, char *path)
dev->device_address);
}
+inline static struct linux_context_priv *__ctx_priv(struct libusb_context *ctx)
+{
+ return (struct linux_context_priv*) ctx->os_priv;
+}
+
static struct linux_device_priv *__device_priv(struct libusb_device *dev)
{
return (struct linux_device_priv *) dev->os_priv;
@@ -232,6 +244,8 @@ static int check_flag_bulk_continuation(void)
static int op_init(struct libusb_context *ctx)
{
+ struct linux_context_priv* ctx_priv = __ctx_priv(ctx);
+
struct stat statbuf;
int r;
@@ -264,9 +278,44 @@ static int op_init(struct libusb_context *ctx)
sysfs_can_relate_devices = 0;
}
+ ctx_priv->udev_ctx = udev_new();
+ ctx_priv->udev_monitor =
+ udev_monitor_new_from_netlink(ctx_priv->udev_ctx, "udev");
+ if (!ctx_priv->udev_monitor) {
+ usbi_err(ctx, "could not initialize udev monitor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ r = udev_monitor_filter_add_match_subsystem_devtype(
+ ctx_priv->udev_monitor, "usb", 0);
+ if (r) {
+ usbi_err(ctx, "could not initialize udev monitor filter for \"usb\" subsystem");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ if (udev_monitor_enable_receiving(ctx_priv->udev_monitor)) {
+ usbi_err(ctx, "failed to enable the udev monitor");
+ return LIBUSB_ERROR_OTHER;
+ }
+
+ ctx_priv->udev_monitor_fd = udev_monitor_get_fd(ctx_priv->udev_monitor);
+
+ r = usbi_add_pollfd(ctx, ctx_priv->udev_monitor_fd, POLLIN);
+ if (r < 0) {
+ return r;
+ }
+
return 0;
}
+static void op_exit(struct libusb_context *ctx)
+{
+ struct linux_context_priv* ctx_priv = __ctx_priv(ctx);
+
+ udev_monitor_unref(ctx_priv->udev_monitor);
+ udev_unref(ctx_priv->udev_ctx);
+}
+
static int usbfs_get_device_descriptor(struct libusb_device *dev,
unsigned char *buffer)
{
@@ -360,7 +409,7 @@ static int sysfs_get_active_config(struct libusb_device *dev, int *config)
r = read(fd, tmp, sizeof(tmp));
close(fd);
if (r < 0) {
- usbi_err(DEVICE_CTX(dev),
+ usbi_err(DEVICE_CTX(dev),
"read bConfigurationValue failed ret=%d errno=%d", r, errno);
return LIBUSB_ERROR_IO;
} else if (r == 0) {
@@ -587,7 +636,7 @@ static int op_get_config_descriptor(struct libusb_device *dev,
}
/* cache the active config descriptor in memory. a value of -1 means that
- * we aren't sure which one is active, so just assume the first one.
+ * we aren't sure which one is active, so just assume the first one.
* only for usbfs. */
static int cache_active_config(struct libusb_device *dev, int fd,
int active_config)
@@ -784,51 +833,55 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
return 0;
}
-static int enumerate_device(struct libusb_context *ctx,
- struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr,
- const char *sysfs_dir)
+static int get_device(struct libusb_context* ctx,
+ uint8_t busnum, uint8_t devaddr, struct libusb_device** dev, const char* sysfs_dir)
{
- struct discovered_devs *discdevs;
unsigned long session_id;
- int need_unref = 0;
- struct libusb_device *dev;
int r = 0;
- /* FIXME: session ID is not guaranteed unique as addresses can wrap and
- * will be reused. instead we should add a simple sysfs attribute with
- * a session ID. */
- session_id = busnum << 8 | devaddr;
+ session_id = usbi_hash(sysfs_dir);
usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
session_id);
- dev = usbi_get_device_by_session_id(ctx, session_id);
- if (dev) {
+ *dev = usbi_get_device_by_session_id_ref(ctx, session_id);
+ if (*dev) {
usbi_dbg("using existing device for %d/%d (session %ld)",
busnum, devaddr, session_id);
} else {
usbi_dbg("allocating new device for %d/%d (session %ld)",
busnum, devaddr, session_id);
- dev = usbi_alloc_device(ctx, session_id);
- if (!dev)
+ *dev = usbi_alloc_device(ctx, session_id);
+ if (!*dev)
return LIBUSB_ERROR_NO_MEM;
- need_unref = 1;
- r = initialize_device(dev, busnum, devaddr, sysfs_dir);
+ r = initialize_device(*dev, busnum, devaddr, sysfs_dir);
if (r < 0)
- goto out;
- r = usbi_sanitize_device(dev);
+ return r;
+ r = usbi_sanitize_device(*dev);
if (r < 0)
- goto out;
+ return r;
+ }
+ return r;
+}
+
+static int enumerate_device(struct libusb_context *ctx,
+ struct discovered_devs **_discdevs, uint8_t busnum, uint8_t devaddr,
+ const char *sysfs_dir)
+{
+ struct discovered_devs *discdevs;
+ struct libusb_device *dev;
+ int r = 0;
+
+ r = get_device(ctx, busnum, devaddr, &dev, sysfs_dir);
+ if (r >= 0) {
+ discdevs = discovered_devs_append(*_discdevs, dev);
+ if (!discdevs)
+ r = LIBUSB_ERROR_NO_MEM;
+ else
+ *_discdevs = discdevs;
}
- discdevs = discovered_devs_append(*_discdevs, dev);
- if (!discdevs)
- r = LIBUSB_ERROR_NO_MEM;
- else
- *_discdevs = discdevs;
+ libusb_unref_device(dev);
-out:
- if (need_unref)
- libusb_unref_device(dev);
return r;
}
@@ -1016,7 +1069,7 @@ static int sysfs_get_device_list(struct libusb_context *ctx,
if (r < 0)
goto out;
discdevs = discdevs_new;
- }
+ }
out:
closedir(devices);
@@ -1405,7 +1458,7 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer,
"submiturb failed error %d errno=%d", r, errno);
r = LIBUSB_ERROR_IO;
}
-
+
/* if the first URB submission fails, we can simply free up and
* return failure immediately. */
if (i == 0) {
@@ -2107,9 +2160,97 @@ static int reap_for_handle(struct libusb_device_handle *handle)
}
}
+static void handle_hotplug_event(struct libusb_context *ctx)
+{
+ struct linux_context_priv* ctx_priv = __ctx_priv(ctx);
+ struct libusb_device* dev;
+ struct udev_device* udev_dev;
+ const char* udev_action;
+ const char* dev_node;
+ const char* sys_name;
+ uint8_t busnum, devaddr;
+ char* delim = "/";
+ char* substr;
+ char* dev_node_dup;
+ char* save_ptr;
+ int status_online;
+
+ udev_dev = udev_monitor_receive_device(ctx_priv->udev_monitor);
+ if (!udev_dev) {
+ usbi_err(ctx, "failed to read data from udev monitor socket");
+ return;
+ }
+
+ dev_node = udev_device_get_devnode(udev_dev);
+ if (!dev_node) {
+ goto end;
+ }
+
+ sys_name = udev_device_get_sysname(udev_dev);
+ if (!sys_name) {
+ goto end;
+ }
+
+ udev_action = udev_device_get_action(udev_dev);
+ if (!udev_action) {
+ goto end;
+ }
+
+ dev_node_dup = strdup(dev_node);
+ substr = strtok_r(dev_node_dup, delim, &save_ptr);
+ while(substr) {
+ if (isdigit(substr[0])) {
+ if (!busnum) {
+ busnum = atoi(substr);
+ } else {
+ devaddr = atoi(substr);
+ break;
+ }
+ }
+ substr = strtok_r(0, delim, &save_ptr);
+ }
+ free(dev_node_dup);
+
+ if (strncmp(udev_action, "add", 3) == 0) {
+ if (!get_device(ctx, busnum, devaddr, &dev, sys_name)) {
+ pthread_mutex_lock(&dev->devaddr_lock);
+ dev->device_address = devaddr;
+ pthread_mutex_unlock(&dev->devaddr_lock);
+
+ pthread_mutex_lock(&dev->status_online_lock);
+ dev->status_online = 1;
+ pthread_mutex_unlock(&dev->status_online_lock);
+
+ status_online = 1;
+ usbi_notify_device_state(dev, status_online);
+ libusb_unref_device(dev);
+ }
+ }
+ else if (strncmp(udev_action, "remove", 6) == 0) {
+ dev = usbi_get_device_by_session_id_ref(ctx, usbi_hash(sys_name));
+ if (dev) {
+ pthread_mutex_lock(&dev->status_online_lock);
+ dev->status_online = 0;
+ pthread_mutex_unlock(&dev->status_online_lock);
+
+ status_online = 0;
+ usbi_notify_device_state(dev, status_online);
+ libusb_unref_device(dev);
+ }
+ } else {
+ usbi_err(ctx, "ignoring udev action %s", udev_action);
+ }
+
+end:
+ udev_device_unref(udev_dev);
+}
+
static int op_handle_events(struct libusb_context *ctx,
struct pollfd *fds, nfds_t nfds, int num_ready)
{
+ struct linux_context_priv* ctx_priv = __ctx_priv(ctx);
+ int hotplug_event = 0;
+
int r;
int i = 0;
@@ -2123,6 +2264,12 @@ static int op_handle_events(struct libusb_context *ctx,
continue;
num_ready--;
+
+ if (pollfd->fd == ctx_priv->udev_monitor_fd) {
+ hotplug_event = 1;
+ continue;
+ }
+
list_for_each_entry(handle, &ctx->open_devs, list, struct libusb_device_handle) {
hpriv = __device_handle_priv(handle);
if (hpriv->fd == pollfd->fd)
@@ -2145,6 +2292,10 @@ static int op_handle_events(struct libusb_context *ctx,
r = 0;
out:
usbi_mutex_unlock(&ctx->open_devs_lock);
+
+ if (hotplug_event) {
+ handle_hotplug_event(ctx);
+ }
return r;
}
@@ -2171,7 +2322,7 @@ static clockid_t op_get_timerfd_clockid(void)
const struct usbi_os_backend linux_usbfs_backend = {
.name = "Linux usbfs",
.init = op_init,
- .exit = NULL,
+ .exit = op_exit,
.get_device_list = op_get_device_list,
.get_device_descriptor = op_get_device_descriptor,
.get_active_config_descriptor = op_get_active_config_descriptor,
@@ -2206,6 +2357,7 @@ const struct usbi_os_backend linux_usbfs_backend = {
.get_timerfd_clockid = op_get_timerfd_clockid,
#endif
+ .context_priv_size = sizeof(struct linux_context_priv),
.device_priv_size = sizeof(struct linux_device_priv),
.device_handle_priv_size = sizeof(struct linux_device_handle_priv),
.transfer_priv_size = sizeof(struct linux_transfer_priv),