From cedc7f6e289c427c84a9175045b06614be56ec5a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 24 May 2013 16:15:51 +0200 Subject: [PATCH] hotplug: Add a hotplug_poll backend function Apps which were written before hotplug support, may listen for hotplug events on their own and call libusb_get_device_list on device addition. In this case libusb_get_device_list will likely return a list without the new device in there, as the hotplug event thread will still be busy enumerating the device, which may take a while, or may not even have seen the event yet. To avoid this add a new hotplug_poll backend function and make libusb_get_device_list call a this before copying ctx->usb_devs to the user. In this function the backend should ensure any pending hotplug events are fully processed before returning. This patch implements hotplug_poll for linux, it should probably be also implemented for darwin. Signed-off-by: Hans de Goede --- libusb/core.c | 3 +++ libusb/libusbi.h | 19 +++++++++++++++++++ libusb/os/linux_netlink.c | 11 +++++++++++ libusb/os/linux_udev.c | 35 +++++++++++++++++++++-------------- libusb/os/linux_usbfs.c | 10 ++++++++++ libusb/os/linux_usbfs.h | 2 ++ libusb/os/openbsd_usb.c | 1 + libusb/os/wince_usb.c | 1 + libusb/os/windows_usb.c | 1 + libusb/version_nano.h | 2 +- 10 files changed, 70 insertions(+), 15 deletions(-) diff --git a/libusb/core.c b/libusb/core.c index 88757f8..1c3748d 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -654,6 +654,9 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, /* backend provides hotplug support */ struct libusb_device *dev; + if (usbi_backend->hotplug_poll) + usbi_backend->hotplug_poll(); + usbi_mutex_lock(&ctx->usb_devs_lock); list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { discdevs = discovered_devs_append(discdevs, dev); diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 090ac5b..4dda97e 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -551,11 +551,30 @@ struct usbi_os_backend { * This function is executed when the user wishes to retrieve a list * of USB devices connected to the system. * + * If the backend has hotplug support, this function is not used! + * * Return 0 on success, or a LIBUSB_ERROR code on failure. */ int (*get_device_list)(struct libusb_context *ctx, struct discovered_devs **discdevs); + /* Apps which were written before hotplug support, may listen for + * hotplug events on their own and call libusb_get_device_list on + * device addition. In this case libusb_get_device_list will likely + * return a list without the new device in there, as the hotplug + * event thread will still be busy enumerating the device, which may + * take a while, or may not even have seen the event yet. + * + * To avoid this libusb_get_device_list will call this optional + * function for backends with hotplug support before copying + * ctx->usb_devs to the user. In this function the backend should + * ensure any pending hotplug events are fully processed before + * returning. + * + * Optional, should be implemented by backends with hotplug support. + */ + void (*hotplug_poll)(void); + /* Open a device for I/O and other USB operations. The device handle * is preallocated for you, you can retrieve the device in question * through handle->dev. diff --git a/libusb/os/linux_netlink.c b/libusb/os/linux_netlink.c index 7b1fdcd..3a68f69 100644 --- a/libusb/os/linux_netlink.c +++ b/libusb/os/linux_netlink.c @@ -241,3 +241,14 @@ static void *linux_netlink_event_thread_main(void *arg) return NULL; } + +void linux_netlink_hotplug_poll(void) +{ + int r; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + r = linux_netlink_read_message(); + } while (r == 0); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/libusb/os/linux_udev.c b/libusb/os/linux_udev.c index cf5f08c..4a45c4b 100644 --- a/libusb/os/linux_udev.c +++ b/libusb/os/linux_udev.c @@ -49,7 +49,7 @@ static int udev_monitor_fd = -1; static struct udev_monitor *udev_monitor = NULL; static pthread_t linux_event_thread; -static void udev_hotplug_event(void); +static void udev_hotplug_event(struct udev_device* udev_dev); static void *linux_udev_event_thread_main(void *arg); int linux_udev_start_event_monitor(void) @@ -125,6 +125,7 @@ int linux_udev_stop_event_monitor(void) static void *linux_udev_event_thread_main(void *arg) { + struct udev_device* udev_dev; struct pollfd fds = {.fd = udev_monitor_fd, .events = POLLIN}; @@ -136,7 +137,9 @@ static void *linux_udev_event_thread_main(void *arg) } usbi_mutex_static_lock(&linux_hotplug_lock); - udev_hotplug_event(); + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) + udev_hotplug_event(udev_dev); usbi_mutex_static_unlock(&linux_hotplug_lock); } @@ -164,26 +167,15 @@ static int udev_device_info(struct libusb_context *ctx, int detached, dev_node, *sys_name); } -static void udev_hotplug_event(void) +static void udev_hotplug_event(struct udev_device* udev_dev) { - struct udev_device* udev_dev; const char* udev_action; const char* sys_name = NULL; uint8_t busnum = 0, devaddr = 0; int detached; int r; - if (NULL == udev_monitor) { - return; - } - do { - udev_dev = udev_monitor_receive_device(udev_monitor); - if (!udev_dev) { - usbi_err(NULL, "failed to read data from udev monitor socket."); - return; - } - udev_action = udev_device_get_action(udev_dev); if (!udev_action) { break; @@ -250,3 +242,18 @@ int linux_udev_scan_devices(struct libusb_context *ctx) return LIBUSB_SUCCESS; } + +void linux_udev_hotplug_poll(void) +{ + struct udev_device* udev_dev; + + usbi_mutex_static_lock(&linux_hotplug_lock); + do { + udev_dev = udev_monitor_receive_device(udev_monitor); + if (udev_dev) { + usbi_dbg("Handling hotplug event from hotplug_poll"); + udev_hotplug_event(udev_dev); + } + } while (udev_dev); + usbi_mutex_static_unlock(&linux_hotplug_lock); +} diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index f86d37a..a27ed44 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -475,6 +475,15 @@ static int linux_scan_devices(struct libusb_context *ctx) #endif } +static void op_hotplug_poll(void) +{ +#if defined(USE_UDEV) + linux_udev_hotplug_poll(); +#else + linux_netlink_hotplug_poll(); +#endif +} + static int _open_sysfs_attr(struct libusb_device *dev, const char *attr) { struct linux_device_priv *priv = _device_priv(dev); @@ -2451,6 +2460,7 @@ const struct usbi_os_backend linux_usbfs_backend = { .init = op_init, .exit = op_exit, .get_device_list = NULL, + .hotplug_poll = op_hotplug_poll, .get_device_descriptor = op_get_device_descriptor, .get_active_config_descriptor = op_get_active_config_descriptor, .get_config_descriptor = op_get_config_descriptor, diff --git a/libusb/os/linux_usbfs.h b/libusb/os/linux_usbfs.h index 8525418..26f052d 100644 --- a/libusb/os/linux_usbfs.h +++ b/libusb/os/linux_usbfs.h @@ -152,9 +152,11 @@ extern usbi_mutex_static_t linux_hotplug_lock; int linux_udev_start_event_monitor(void); int linux_udev_stop_event_monitor(void); int linux_udev_scan_devices(struct libusb_context *ctx); +void linux_udev_hotplug_poll(void); #else int linux_netlink_start_event_monitor(void); int linux_netlink_stop_event_monitor(void); +void linux_netlink_hotplug_poll(void); #endif void linux_hotplug_enumerate(uint8_t busnum, uint8_t devaddr, const char *sys_name); diff --git a/libusb/os/openbsd_usb.c b/libusb/os/openbsd_usb.c index e1c91f4..ccad768 100644 --- a/libusb/os/openbsd_usb.c +++ b/libusb/os/openbsd_usb.c @@ -93,6 +93,7 @@ const struct usbi_os_backend openbsd_backend = { NULL, /* init() */ NULL, /* exit() */ obsd_get_device_list, + NULL, /* hotplug_poll */ obsd_open, obsd_close, diff --git a/libusb/os/wince_usb.c b/libusb/os/wince_usb.c index a9c0ef8..d8e8f55 100644 --- a/libusb/os/wince_usb.c +++ b/libusb/os/wince_usb.c @@ -978,6 +978,7 @@ const struct usbi_os_backend wince_backend = { wince_exit, wince_get_device_list, + NULL, /* hotplug_poll */ wince_open, wince_close, diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 6d06128..63357b1 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -2270,6 +2270,7 @@ const struct usbi_os_backend windows_backend = { windows_exit, windows_get_device_list, + NULL, /* hotplug_poll */ windows_open, windows_close, diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 8064f14..a4f4d24 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 10723 +#define LIBUSB_NANO 10724 -- 2.7.4