diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index ca43a6f824abd62d7acbb6549f18e8f82e7d3752..ba5bbc7eedcc89dff274d800e3b0315380cf8afa 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1102,9 +1102,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) if (udev->auto_pm) autosuspend_check(udev); - /* If the suspend succeeded, propagate it up the tree */ + /* If the suspend succeeded then prevent any more URB submissions, + * flush any outstanding URBs, and propagate the suspend up the tree. + */ } else { cancel_delayed_work(&udev->autosuspend); + udev->can_submit = 0; + for (i = 0; i < 16; ++i) { + usb_hcd_flush_endpoint(udev, udev->ep_out[i]); + usb_hcd_flush_endpoint(udev, udev->ep_in[i]); + } if (parent) usb_autosuspend_device(parent); } @@ -1154,6 +1161,7 @@ static int usb_resume_both(struct usb_device *udev) status = -ENODEV; goto done; } + udev->can_submit = 1; /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index e5874e8b8cbc58c5a6b0c09952107b92eab4499e..2c79aa6ca2b42ba0d67bfeb71b65bf131c4de3ff 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1014,6 +1014,11 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb) goto done; } + if (unlikely(!urb->dev->can_submit)) { + rc = -EHOSTUNREACH; + goto done; + } + /* * Check the host controller's state and add the URB to the * endpoint's queue. diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bd08d51d7f41d77d3a0aaba4e9ade0699dde67c7..691acf2223c2d6f11aba87f63c72433dae020e32 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1955,14 +1955,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_device *udev; udev = hdev->children [port1-1]; - if (udev && msg.event == PM_EVENT_SUSPEND && -#ifdef CONFIG_USB_SUSPEND - udev->state != USB_STATE_SUSPENDED -#else - udev->dev.power.power_state.event - == PM_EVENT_ON -#endif - ) { + if (udev && udev->can_submit) { if (!hdev->auto_pm) dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 76db76fdb4ec527eabbdc9242ba3709721dd2a2f..c20c03aaf01283bba746523ad5724f6e00a298d2 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -286,9 +286,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) return -EINVAL; if (!(dev = urb->dev) || dev->state < USB_STATE_DEFAULT) return -ENODEV; - if (dev->bus->controller->power.power_state.event != PM_EVENT_ON - || dev->state == USB_STATE_SUSPENDED) - return -EHOSTUNREACH; /* For now, get the endpoint from the pipe. Eventually drivers * will be required to set urb->ep directly and we will eliminate diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index e5ff161776fa6f254a1c0805ea911e3a6f16843e..8121edbd1494022fc734e34c8c345da3adb772dd 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -272,6 +272,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; /* ep0 maxpacket comes later, from device descriptor */ usb_enable_endpoint(dev, &dev->ep0); + dev->can_submit = 1; /* Save readable and stable topology id, distinguishing devices * by location for diagnostics, tools, driver model, etc. The diff --git a/include/linux/usb.h b/include/linux/usb.h index 5b14b4c81fd61c7e639e5550b803548d03225d56..e5b35e0dca23bd2916ba697088cdbcc6e0574436 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -383,6 +383,7 @@ struct usb_device { u8 portnum; /* Parent port number (origin 1) */ u8 level; /* Number of USB hub ancestors */ + unsigned can_submit:1; /* URBs may be submitted */ unsigned discon_suspended:1; /* Disconnected while suspended */ unsigned have_langid:1; /* whether string_langid is valid */ unsigned authorized:1; /* Policy has determined we can use it */