hotplug: Add a hotplug_poll backend function
authorHans de Goede <hdegoede@redhat.com>
Fri, 24 May 2013 14:15:51 +0000 (16:15 +0200)
committerHans de Goede <hdegoede@redhat.com>
Thu, 30 May 2013 12:20:36 +0000 (14:20 +0200)
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 <hdegoede@redhat.com>
libusb/core.c
libusb/libusbi.h
libusb/os/linux_netlink.c
libusb/os/linux_udev.c
libusb/os/linux_usbfs.c
libusb/os/linux_usbfs.h
libusb/os/openbsd_usb.c
libusb/os/wince_usb.c
libusb/os/windows_usb.c
libusb/version_nano.h

index 88757f8..1c3748d 100644 (file)
@@ -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);
index 090ac5b..4dda97e 100644 (file)
@@ -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.
index 7b1fdcd..3a68f69 100644 (file)
@@ -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);
+}
index cf5f08c..4a45c4b 100644 (file)
@@ -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);
+}
index f86d37a..a27ed44 100644 (file)
@@ -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,
index 8525418..26f052d 100644 (file)
@@ -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);
index e1c91f4..ccad768 100644 (file)
@@ -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,
 
index a9c0ef8..d8e8f55 100644 (file)
@@ -978,6 +978,7 @@ const struct usbi_os_backend wince_backend = {
         wince_exit,
 
         wince_get_device_list,
+       NULL,                           /* hotplug_poll */
         wince_open,
         wince_close,
 
index 6d06128..63357b1 100644 (file)
@@ -2270,6 +2270,7 @@ const struct usbi_os_backend windows_backend = {
        windows_exit,
 
        windows_get_device_list,
+       NULL,                           /* hotplug_poll */
        windows_open,
        windows_close,
 
index 8064f14..a4f4d24 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10723
+#define LIBUSB_NANO 10724