Author: hselasky
Date: Mon Oct  4 22:21:30 2010
New Revision: 213432
URL: http://svn.freebsd.org/changeset/base/213432

Log:
  Serialise USB re-enumeration with the USB explore thread.
  This patch can solve problems when multiple USB devices are
  re-enumerated at the same time on the same bus.
  
  Approved by:    thompsa (mentor)

Modified:
  head/sys/dev/usb/usb_dev.c
  head/sys/dev/usb/usb_device.h
  head/sys/dev/usb/usb_generic.c
  head/sys/dev/usb/usb_hub.c

Modified: head/sys/dev/usb/usb_dev.c
==============================================================================
--- head/sys/dev/usb/usb_dev.c  Mon Oct  4 22:04:22 2010        (r213431)
+++ head/sys/dev/usb/usb_dev.c  Mon Oct  4 22:21:30 2010        (r213432)
@@ -964,7 +964,6 @@ usb_dev_uninit(void *arg)
        if (usb_dev != NULL) {
                destroy_dev(usb_dev);
                usb_dev = NULL;
-       
        }
        mtx_destroy(&usb_ref_lock);
        sx_destroy(&usb_sym_lock);
@@ -1058,21 +1057,45 @@ usb_ioctl(struct cdev *dev, u_long cmd, 
                err = usb_ioctl_f_sub(f, cmd, addr, td);
        }
        KASSERT(f != NULL, ("fifo not found"));
-       if (err == ENOIOCTL) {
-               err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
-               DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
-               if (err == ENOIOCTL) {
-                       if (usb_usb_ref_device(cpd, &refs)) {
-                               err = ENXIO;
-                               goto done;
-                       }
-                       err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
-                       DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
-               }
+       if (err != ENOIOCTL)
+               goto done;
+
+       err = (f->methods->f_ioctl) (f, cmd, addr, fflags);
+
+       DPRINTFN(2, "f_ioctl cmd 0x%lx = %d\n", cmd, err);
+
+       if (err != ENOIOCTL)
+               goto done;
+
+       if (usb_usb_ref_device(cpd, &refs)) {
+               err = ENXIO;
+               goto done;
        }
-       if (err == ENOIOCTL) {
+
+       err = (f->methods->f_ioctl_post) (f, cmd, addr, fflags);
+
+       DPRINTFN(2, "f_ioctl_post cmd 0x%lx = %d\n", cmd, err);
+
+       if (err == ENOIOCTL)
                err = ENOTTY;
+
+       if (err)
+               goto done;
+
+       /* Wait for re-enumeration, if any */
+
+       while (f->udev->re_enumerate_wait != 0) {
+
+               usb_unref_device(cpd, &refs);
+
+               usb_pause_mtx(NULL, hz / 128);
+
+               if (usb_ref_device(cpd, &refs, 1 /* need uref */)) {
+                       err = ENXIO;
+                       goto done;
+               }
        }
+
 done:
        usb_unref_device(cpd, &refs);
        return (err);

Modified: head/sys/dev/usb/usb_device.h
==============================================================================
--- head/sys/dev/usb/usb_device.h       Mon Oct  4 22:04:22 2010        
(r213431)
+++ head/sys/dev/usb/usb_device.h       Mon Oct  4 22:21:30 2010        
(r213432)
@@ -160,6 +160,7 @@ struct usb_device {
        uint8_t hs_port_no;             /* high-speed HUB port number */
        uint8_t driver_added_refcount;  /* our driver added generation count */
        uint8_t power_mode;             /* see USB_POWER_XXX */
+       uint8_t re_enumerate_wait;      /* set if re-enum. is in progress */
        uint8_t ifaces_max;             /* number of interfaces present */
        uint8_t endpoints_max;          /* number of endpoints present */
 

Modified: head/sys/dev/usb/usb_generic.c
==============================================================================
--- head/sys/dev/usb/usb_generic.c      Mon Oct  4 22:04:22 2010        
(r213431)
+++ head/sys/dev/usb/usb_generic.c      Mon Oct  4 22:21:30 2010        
(r213432)
@@ -109,7 +109,7 @@ static int  usb_gen_fill_deviceinfo(struc
 static int     ugen_re_enumerate(struct usb_fifo *);
 static int     ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
 static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
-static int ugen_fs_uninit(struct usb_fifo *f);
+static int     ugen_fs_uninit(struct usb_fifo *f);
 
 /* structures */
 
@@ -951,23 +951,19 @@ ugen_re_enumerate(struct usb_fifo *f)
        if (error) {
                return (error);
        }
-       /* get the device unconfigured */
-       error = ugen_set_config(f, USB_UNCONFIG_INDEX);
-       if (error) {
-               return (error);
+       if (udev->flags.usb_mode != USB_MODE_HOST) {
+               /* not possible in device side mode */
+               return (ENOTTY);
        }
-       /* do a bus-reset */
-       mtx_lock(f->priv_mtx);
-       error = usbd_req_re_enumerate(udev, f->priv_mtx);
-       mtx_unlock(f->priv_mtx);
-
-       if (error) {
-               return (ENXIO);
+       /* make sure all FIFO's are gone */
+       /* else there can be a deadlock */
+       if (ugen_fs_uninit(f)) {
+               /* ignore any errors */
+               DPRINTFN(6, "no FIFOs\n");
        }
-       /* restore configuration to index 0 */
-       error = ugen_set_config(f, 0);
-       if (error) {
-               return (error);
+       if (udev->re_enumerate_wait == 0) {
+               udev->re_enumerate_wait = 1;
+               usb_needs_explore(udev->bus, 0);
        }
        return (0);
 }
@@ -1775,9 +1771,11 @@ ugen_set_power_mode(struct usb_fifo *f, 
 
        /* if we are powered off we need to re-enumerate first */
        if (old_mode == USB_POWER_MODE_OFF) {
-               err = ugen_re_enumerate(f);
-               if (err)
-                       return (err);
+               if (udev->flags.usb_mode == USB_MODE_HOST) {
+                       if (udev->re_enumerate_wait == 0)
+                               udev->re_enumerate_wait = 1;
+               }
+               /* set power mode will wake up the explore thread */
        }
 
        /* set new power mode */

Modified: head/sys/dev/usb/usb_hub.c
==============================================================================
--- head/sys/dev/usb/usb_hub.c  Mon Oct  4 22:04:22 2010        (r213431)
+++ head/sys/dev/usb/usb_hub.c  Mon Oct  4 22:21:30 2010        (r213432)
@@ -236,6 +236,26 @@ uhub_explore_sub(struct uhub_softc *sc, 
                /* nothing to do */
                goto done;
        }
+       /* check if device should be re-enumerated */
+
+       if (child->flags.usb_mode == USB_MODE_HOST) {
+               usbd_enum_lock(child);
+               if (child->re_enumerate_wait) {
+                       err = usbd_set_config_index(child, USB_UNCONFIG_INDEX);
+                       if (err == 0)
+                               err = usbd_req_re_enumerate(child, NULL);
+                       if (err == 0)
+                               err = usbd_set_config_index(child, 0);
+                       if (err == 0) {
+                               err = usb_probe_and_attach(child,
+                                   USB_IFACE_INDEX_ANY);
+                       }
+                       child->re_enumerate_wait = 0;
+                       err = 0;
+               }
+               usbd_enum_unlock(child);
+       }
+
        /* check if probe and attach should be done */
 
        if (child->driver_added_refcount != refcount) {
@@ -1763,6 +1783,8 @@ static uint8_t
 usb_peer_should_wakeup(struct usb_device *udev)
 {
        return ((udev->power_mode == USB_POWER_MODE_ON) ||
+           (udev->driver_added_refcount != udev->bus->driver_added_refcount) ||
+           (udev->re_enumerate_wait != 0) ||
            (udev->pwr_save.type_refs[UE_ISOCHRONOUS] != 0) ||
            (udev->pwr_save.write_refs != 0) ||
            ((udev->pwr_save.read_refs != 0) &&
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to