[Spice-devel] [PATCH v7 01/14] win-usb-dev: Fix device (un)plug notification handler
Jonathon Jongsma
jjongsma at redhat.com
Thu Mar 10 19:48:30 UTC 2016
Acked-by: Jonathon Jongsma <jjongsma at redhat.com>
On Tue, 2016-03-08 at 16:05 +0200, Dmitry Fleytman wrote:
> This patch fixes device list change notification handing
> logic for cases when more than one device being plugged
> or unplugged simultaneously.
>
> The simplest way to reproduce the problematic scenario
> is (un)plugging of a usb HUB with a few devices inserted.
>
> Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
> ---
> src/win-usb-dev.c | 93 +++++++++++++++++-------------------------------------
> -
> 1 file changed, 28 insertions(+), 65 deletions(-)
>
> diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c
> index 1e4b2d6..2cb3108 100644
> --- a/src/win-usb-dev.c
> +++ b/src/win-usb-dev.c
> @@ -34,7 +34,6 @@
>
> struct _GUdevClientPrivate {
> libusb_context *ctx;
> - gssize udev_list_size;
> GList *udev_list;
> HWND hwnd;
> };
> @@ -196,9 +195,7 @@ g_udev_client_initable_init(GInitable *initable,
> GCancellable *cancellable,
> }
>
> /* get initial device list */
> - priv->udev_list_size = g_udev_client_list_devices(self, &priv->udev_list,
> - err, __FUNCTION__);
> - if (priv->udev_list_size < 0) {
> + if (g_udev_client_list_devices(self, &priv->udev_list, err, __FUNCTION__)
> < 0) {
> goto g_udev_client_init_failed;
> }
>
> @@ -319,94 +316,60 @@ static gboolean get_usb_dev_info(libusb_device *dev,
> GUdevDeviceInfo *udevinfo)
> }
>
> /* Only vid:pid are compared */
> -static gboolean gudev_devices_are_equal(GUdevDevice *a, GUdevDevice *b)
> +static gint gudev_devices_differ(gconstpointer a, gconstpointer b)
> {
> GUdevDeviceInfo *ai, *bi;
> gboolean same_vid;
> gboolean same_pid;
>
> - ai = a->priv->udevinfo;
> - bi = b->priv->udevinfo;
> + ai = G_UDEV_DEVICE(a)->priv->udevinfo;
> + bi = G_UDEV_DEVICE(b)->priv->udevinfo;
>
> same_vid = (ai->vid == bi->vid);
> same_pid = (ai->pid == bi->pid);
>
> - return (same_pid && same_vid);
> + return (same_pid && same_vid) ? 0 : -1;
> }
>
> +static void notify_dev_state_change(GUdevClient *self,
> + GList *old_list,
> + GList *new_list,
> + const gchar *action)
> +{
> + GList *dev;
> +
> + for (dev = g_list_first(old_list); dev != NULL; dev = g_list_next(dev)) {
> + if (g_list_find_custom(new_list, dev->data, gudev_devices_differ) ==
> NULL) {
> + /* Found a device that changed its state */
> + g_udev_device_print(dev->data, action);
> + g_signal_emit(self, signals[UEVENT_SIGNAL], 0, action, dev
> ->data);
> + }
> + }
> +}
>
> -/* Assumes each event stands for a single device change (at most) */
> static void handle_dev_change(GUdevClient *self)
> {
> GUdevClientPrivate *priv = self->priv;
> - GUdevDevice *changed_dev = NULL;
> - ssize_t dev_count;
> - int is_dev_change;
> GError *err = NULL;
> GList *now_devs = NULL;
> - GList *llist, *slist; /* long-list and short-list*/
> - GList *lit, *sit; /* iterators for long-list and short-list */
> - GUdevDevice *ldev, *sdev; /* devices on long-list and short-list */
> -
> - dev_count = g_udev_client_list_devices(self, &now_devs, &err,
> - __FUNCTION__);
> - g_return_if_fail(dev_count >= 0);
>
> - SPICE_DEBUG("number of current devices %"G_GSSIZE_FORMAT
> - ", I know about %"G_GSSIZE_FORMAT" devices",
> - dev_count, priv->udev_list_size);
> -
> - is_dev_change = dev_count - priv->udev_list_size;
> - if (is_dev_change == 0) {
> - g_udev_client_free_device_list(&now_devs);
> + if(g_udev_client_list_devices(self, &now_devs, &err, __FUNCTION__) < 0) {
> + g_warning("could not retrieve device list");
> return;
> }
>
> - if (is_dev_change > 0) {
> - llist = now_devs;
> - slist = priv->udev_list;
> - } else {
> - llist = priv->udev_list;
> - slist = now_devs;
> - }
> -
> - g_udev_device_print_list(llist, "handle_dev_change: long list:");
> - g_udev_device_print_list(slist, "handle_dev_change: short list:");
> -
> - /* Go over the longer list */
> - for (lit = g_list_first(llist); lit != NULL; lit=g_list_next(lit)) {
> - ldev = lit->data;
> - /* Look for dev in the shorther list */
> - for (sit = g_list_first(slist); sit != NULL; sit=g_list_next(sit)) {
> - sdev = sit->data;
> - if (gudev_devices_are_equal(ldev, sdev))
> - break;
> - }
> - if (sit == NULL) {
> - /* Found a device which appears only in the longer list */
> - changed_dev = ldev;
> - break;
> - }
> - }
> + g_udev_device_print_list(now_devs, "handle_dev_change: current list:");
> + g_udev_device_print_list(priv->udev_list, "handle_dev_change: previous
> list:");
>
> - if (!changed_dev) {
> - g_warning("couldn't find any device change");
> - goto leave;
> - }
> + /* Unregister devices that are not present anymore */
> + notify_dev_state_change(self, priv->udev_list, now_devs, "remove");
>
> - if (is_dev_change > 0) {
> - g_udev_device_print(changed_dev, ">>> USB device inserted");
> - g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "add", changed_dev);
> - } else {
> - g_udev_device_print(changed_dev, "<<< USB device removed");
> - g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "remove",
> changed_dev);
> - }
> + /* Register newly inserted devices */
> + notify_dev_state_change(self, now_devs, priv->udev_list, "add");
>
> -leave:
> /* keep most recent info: free previous list, and keep current list */
> g_udev_client_free_device_list(&priv->udev_list);
> priv->udev_list = now_devs;
> - priv->udev_list_size = dev_count;
> }
>
> static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam,
> LPARAM lparam)
More information about the Spice-devel
mailing list