From stern@rowland.harvard.edu Tue Sep 11 00:49:38 2007 From: Alan Stern Date: Mon, 10 Sep 2007 11:33:05 -0400 (EDT) Subject: USB: break apart flush_endpoint and disable_endpoint To: Greg KH Message-ID: This patch (as988) breaks usb_hcd_endpoint_disable() apart into two routines. The first, usb_hcd_flush_endpoint() does the -ESHUTDOWN unlinking of all URBs in the endpoint's queue and waits for them to complete. The second, usb_hcd_disable_endpoint() -- renamed for better grammatical style -- merely calls the HCD's endpoint_disable method. The changeover is easy because the routine currently has only one caller. This separation of function will be exploited in the following patch: When a device is suspended, the core will be able to cancel all outstanding URBs for that device while leaving the HCD's endpoint-related data structures intact for later. As an added benefit, HCDs no longer need to check for existing URBs in their endpoint_disable methods. It is now guaranteed that there will be none. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 48 +++++++++++++++++++++++++-------------------- drivers/usb/core/hcd.h | 4 ++- drivers/usb/core/message.c | 3 +- 3 files changed, 32 insertions(+), 23 deletions(-) --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1289,24 +1289,22 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb); /*-------------------------------------------------------------------------*/ -/* disables the endpoint: cancels any pending urbs, then synchronizes with - * the hcd to make sure all endpoint state is gone from hardware, and then - * waits until the endpoint's queue is completely drained. use for - * set_configuration, set_interface, driver removal, physical disconnect. - * - * example: a qh stored in ep->hcpriv, holding state related to endpoint - * type, maxpacket size, toggle, halt status, and scheduling. +/* Cancel all URBs pending on this endpoint and wait for the endpoint's + * queue to drain completely. The caller must first insure that no more + * URBs can be submitted for this endpoint. */ -void usb_hcd_endpoint_disable (struct usb_device *udev, +void usb_hcd_flush_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep) { struct usb_hcd *hcd; struct urb *urb; + if (!ep) + return; might_sleep(); hcd = bus_to_hcd(udev->bus); - /* ep is already gone from udev->ep_{in,out}[]; no more submits */ + /* No more submits can occur */ rescan: spin_lock_irq(&hcd_urb_list_lock); list_for_each_entry (urb, &ep->urb_list, urb_list) { @@ -1345,18 +1343,7 @@ rescan: } spin_unlock_irq(&hcd_urb_list_lock); - /* synchronize with the hardware, so old configuration state - * clears out immediately (and will be freed). - */ - if (hcd->driver->endpoint_disable) - hcd->driver->endpoint_disable (hcd, ep); - - /* Wait until the endpoint queue is completely empty. Most HCDs - * will have done this already in their endpoint_disable method, - * but some might not. And there could be root-hub control URBs - * still pending since they aren't affected by the HCDs' - * endpoint_disable methods. - */ + /* Wait until the endpoint queue is completely empty */ while (!list_empty (&ep->urb_list)) { spin_lock_irq(&hcd_urb_list_lock); @@ -1376,6 +1363,25 @@ rescan: } } +/* Disables the endpoint: synchronizes with the hcd to make sure all + * endpoint state is gone from hardware. usb_hcd_flush_endpoint() must + * have been called previously. Use for set_configuration, set_interface, + * driver removal, physical disconnect. + * + * example: a qh stored in ep->hcpriv, holding state related to endpoint + * type, maxpacket size, toggle, halt status, and scheduling. + */ +void usb_hcd_disable_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep) +{ + struct usb_hcd *hcd; + + might_sleep(); + hcd = bus_to_hcd(udev->bus); + if (hcd->driver->endpoint_disable) + hcd->driver->endpoint_disable(hcd, ep); +} + /*-------------------------------------------------------------------------*/ /* called in any context */ --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -219,7 +219,9 @@ extern int usb_hcd_submit_urb (struct ur extern int usb_hcd_unlink_urb (struct urb *urb, int status); extern void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status); -extern void usb_hcd_endpoint_disable (struct usb_device *udev, +extern void usb_hcd_flush_endpoint(struct usb_device *udev, + struct usb_host_endpoint *ep); +extern void usb_hcd_disable_endpoint(struct usb_device *udev, struct usb_host_endpoint *ep); extern int usb_hcd_get_frame_number (struct usb_device *udev); --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1017,7 +1017,8 @@ void usb_disable_endpoint(struct usb_dev } if (ep) { ep->enabled = 0; - usb_hcd_endpoint_disable(dev, ep); + usb_hcd_flush_endpoint(dev, ep); + usb_hcd_disable_endpoint(dev, ep); } }