diff options
author | Pete Batard <pbatard@gmail.com> | 2010-10-08 17:12:15 +0100 |
---|---|---|
committer | Pete Batard <pbatard@gmail.com> | 2010-10-08 17:12:15 +0100 |
commit | 6cb025b4c853f2c034f72e12b1ab243d9d6e2a59 (patch) | |
tree | bbccbc296cb924fac96df0cb5ccd2c70975b9dc5 /libusb | |
parent | f62bae968fc07aff6e7f1e77b70e10010652a0df (diff) | |
download | libusb-6cb025b4c853f2c034f72e12b1ab243d9d6e2a59.tar.gz |
hotplug - core, Linux, Darwin
Diffstat (limited to 'libusb')
-rw-r--r-- | libusb/core.c | 112 | ||||
-rw-r--r-- | libusb/io.c | 16 | ||||
-rw-r--r-- | libusb/libusb.h | 8 | ||||
-rw-r--r-- | libusb/libusbi.h | 37 | ||||
-rw-r--r-- | libusb/os/darwin_usb.c | 414 | ||||
-rw-r--r-- | libusb/os/darwin_usb.h | 11 | ||||
-rw-r--r-- | libusb/os/linux_usbfs.c | 218 |
7 files changed, 642 insertions, 174 deletions
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), |