This diff should help to avoid some races occurring when resuming
a machine with USB devices, please test!

Since the Host Controllers are halted during suspend and reset on
wakeup, all the connected USB devices are disconnected during a
suspend/resume cycle.

When a device is disconnected its pipes are aborted and closed,
right now this is done *after* resuming.  But since the HC have
already been reset and some timeout might also fire, that generates
a lot of problems.  So the diff below detaches the root hub of every
controller before suspending, so that no device stay "logically"
attached when suspended. 

Make sure you have my last commit to all *hci.c before trying this,
and report any regression/success story.

Index: usb.c
===================================================================
RCS file: /home/ncvs/src/sys/dev/usb/usb.c,v
retrieving revision 1.94
diff -u -p -r1.94 usb.c
--- usb.c       8 Mar 2014 11:49:19 -0000       1.94
+++ usb.c       25 Mar 2014 17:32:29 -0000
@@ -92,6 +92,7 @@ struct usb_softc {
        struct device    sc_dev;        /* base device */
        struct usbd_bus  *sc_bus;       /* USB controller */
        struct usbd_port sc_port;       /* dummy port for root hub */
+       int              sc_speed;
 
        struct usb_task  sc_explore_task;
 
@@ -125,6 +126,9 @@ void                 usb_attach(struct device *, struc
 int             usb_detach(struct device *, int); 
 int             usb_activate(struct device *, int); 
 
+int             usb_attach_roothub(struct usb_softc *);
+void            usb_detach_roothub(struct usb_softc *);
+
 struct cfdriver usb_cd = { 
        NULL, "usb", DV_DULL 
 }; 
@@ -147,10 +151,7 @@ void
 usb_attach(struct device *parent, struct device *self, void *aux)
 {
        struct usb_softc *sc = (struct usb_softc *)self;
-       struct usbd_device *dev;
-       usbd_status err;
        int usbrev;
-       int speed;
 
        if (usb_nbuses == 0) {
                rw_init(&usbpalock, "usbpalock");
@@ -171,13 +172,13 @@ usb_attach(struct device *parent, struct
        switch (usbrev) {
        case USBREV_1_0:
        case USBREV_1_1:
-               speed = USB_SPEED_FULL;
+               sc->sc_speed = USB_SPEED_FULL;
                break;
        case USBREV_2_0:
-               speed = USB_SPEED_HIGH;
+               sc->sc_speed = USB_SPEED_HIGH;
                break;
        case USBREV_3_0:
-               speed = USB_SPEED_SUPER;
+               sc->sc_speed = USB_SPEED_SUPER;
                break;
        default:
                printf(", not supported\n");
@@ -206,17 +207,10 @@ usb_attach(struct device *parent, struct
                return;
        }
 
-       err = usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, speed, 0,
-                 &sc->sc_port);
-       if (!err) {
-               dev = sc->sc_port.device;
-               if (dev->hub == NULL) {
-                       sc->sc_bus->dying = 1;
-                       printf("%s: root device is not a hub\n",
-                              sc->sc_dev.dv_xname);
-                       return;
-               }
-               sc->sc_bus->root_hub = dev;
+
+
+       if (!usb_attach_roothub(sc)) {
+               struct usbd_device *dev = sc->sc_bus->root_hub;
 #if 1
                /*
                 * Turning this code off will delay attachment of USB devices
@@ -226,11 +220,8 @@ usb_attach(struct device *parent, struct
                if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1))
                        dev->hub->explore(sc->sc_bus->root_hub);
 #endif
-       } else {
-               printf("%s: root hub problem, error=%d\n",
-                      sc->sc_dev.dv_xname, err);
-               sc->sc_bus->dying = 1;
        }
+
        if (cold)
                sc->sc_bus->use_polling--;
 
@@ -243,6 +234,41 @@ usb_attach(struct device *parent, struct
        }
 }
 
+int
+usb_attach_roothub(struct usb_softc *sc)
+{
+       struct usbd_device *dev;
+
+       if (usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, sc->sc_speed, 0,
+           &sc->sc_port)) {
+               printf("%s: root hub problem\n", sc->sc_dev.dv_xname);
+               sc->sc_bus->dying = 1;
+               return (1);
+       }
+
+       dev = sc->sc_port.device;
+       if (dev->hub == NULL) {
+               printf("%s: root device is not a hub\n", sc->sc_dev.dv_xname);
+               sc->sc_bus->dying = 1;
+               return (1);
+       }
+       sc->sc_bus->root_hub = dev;
+
+       return (0);
+}
+
+void
+usb_detach_roothub(struct usb_softc *sc)
+{
+       /* Make all devices disconnect. */
+       if (sc->sc_port.device != NULL)
+               usb_disconnect_port(&sc->sc_port, (struct device *)sc);
+
+       usb_rem_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
+
+       sc->sc_bus->root_hub = NULL;
+}
+
 void
 usb_create_task_threads(void *arg)
 {
@@ -866,23 +892,22 @@ int
 usb_activate(struct device *self, int act)
 {
        struct usb_softc *sc = (struct usb_softc *)self;
-       struct usbd_device *dev = sc->sc_port.device;
-       int i, rv = 0, r;
+       int rv = 0;
 
        switch (act) {
-       case DVACT_DEACTIVATE:
+       case DVACT_QUIESCE:
                sc->sc_bus->dying = 1;
-               if (dev != NULL && dev->cdesc != NULL &&
-                   dev->subdevs != NULL) {
-                       for (i = 0; dev->subdevs[i]; i++) {
-                               r = config_deactivate(dev->subdevs[i]);
-                               if (r)
-                                       rv = r;
-                       }
-               }
+               if (sc->sc_bus->root_hub != NULL)
+                       usb_detach_roothub(sc);
                break;
-       case DVACT_RESUME:
-               usb_needs_explore(sc->sc_bus->root_hub, 0);
+       case DVACT_WAKEUP:
+               sc->sc_bus->dying = 0;
+               if (!usb_attach_roothub(sc))
+                       usb_needs_explore(sc->sc_bus->root_hub, 0);
+               break;
+       case DVACT_DEACTIVATE:
+               rv = config_activate_children(self, act);
+               sc->sc_bus->dying = 1;
                break;
        default:
                rv = config_activate_children(self, act);
@@ -901,11 +926,7 @@ usb_detach(struct device *self, int flag
        sc->sc_bus->dying = 1;
 
        if (sc->sc_bus->root_hub != NULL) {
-               /* Make all devices disconnect. */
-               if (sc->sc_port.device != NULL)
-                       usb_disconnect_port(&sc->sc_port, self);
-
-               usb_rem_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
+               usb_detach_roothub(sc);
 
                if (--usb_nbuses == 0) {
                        usb_run_tasks = usb_run_abort_tasks = 0;

Reply via email to