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;