Commit 44fedd70 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: Implement endpoint_disable() for UHCI

This patch implements the endpoint_disable method for the UHCI driver, as
you requested a while back.  It guarantees that during unbinding events
(disconnect, configuration change, rmmod) the UHCI driver will have
finished using every URB for the interface being unbound.  It doesn't
quite guarantee that the completion handlers will have finished running,
but it would take a pretty unlikely race to violate that assumption.  (I
think it's the same with the OHCI and EHCI drivers.)

Despite the patch numbering this one applies _after_ as249, which is a
more important bugfix.
parent bfdcf3ef
...@@ -1786,6 +1786,9 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs) ...@@ -1786,6 +1786,9 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
spin_unlock(&uhci->schedule_lock); spin_unlock(&uhci->schedule_lock);
/* Wake up anyone waiting for an URB to complete */
wake_up_all(&uhci->waitqh);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -2086,6 +2089,8 @@ static int uhci_start(struct usb_hcd *hcd) ...@@ -2086,6 +2089,8 @@ static int uhci_start(struct usb_hcd *hcd)
INIT_LIST_HEAD(&uhci->complete_list); INIT_LIST_HEAD(&uhci->complete_list);
init_waitqueue_head(&uhci->waitqh);
uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl), uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
&dma_handle, 0); &dma_handle, 0);
if (!uhci->fl) { if (!uhci->fl) {
...@@ -2297,6 +2302,9 @@ static void uhci_stop(struct usb_hcd *hcd) ...@@ -2297,6 +2302,9 @@ static void uhci_stop(struct usb_hcd *hcd)
uhci_free_pending_tds(uhci); uhci_free_pending_tds(uhci);
spin_unlock_irq(&uhci->schedule_lock); spin_unlock_irq(&uhci->schedule_lock);
/* Wake up anyone waiting for an URB to complete */
wake_up_all(&uhci->waitqh);
release_uhci(uhci); release_uhci(uhci);
} }
...@@ -2361,6 +2369,46 @@ static void uhci_hcd_free(struct usb_hcd *hcd) ...@@ -2361,6 +2369,46 @@ static void uhci_hcd_free(struct usb_hcd *hcd)
kfree(hcd_to_uhci(hcd)); kfree(hcd_to_uhci(hcd));
} }
/* Are there any URBs for a particular device/endpoint on a given list? */
static int urbs_for_ep_list(struct list_head *head,
struct hcd_dev *hdev, int ep)
{
struct urb_priv *urbp;
list_for_each_entry(urbp, head, urb_list) {
struct urb *urb = urbp->urb;
if (hdev == urb->dev->hcpriv && ep ==
(usb_pipeendpoint(urb->pipe) |
usb_pipein(urb->pipe)))
return 1;
}
return 0;
}
/* Are there any URBs for a particular device/endpoint? */
static int urbs_for_ep(struct uhci_hcd *uhci, struct hcd_dev *hdev, int ep)
{
int rc;
spin_lock_irq(&uhci->schedule_lock);
rc = (urbs_for_ep_list(&uhci->urb_list, hdev, ep) ||
urbs_for_ep_list(&uhci->complete_list, hdev, ep) ||
urbs_for_ep_list(&uhci->urb_remove_list, hdev, ep));
spin_unlock_irq(&uhci->schedule_lock);
return rc;
}
/* Wait until all the URBs for a particular device/endpoint are gone */
static void uhci_hcd_endpoint_disable(struct usb_hcd *hcd,
struct hcd_dev *hdev, int endpoint)
{
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
wait_event_interruptible(uhci->waitqh,
!urbs_for_ep(uhci, hdev, endpoint));
}
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{ {
return uhci_get_current_frame_number(hcd_to_uhci(hcd)); return uhci_get_current_frame_number(hcd_to_uhci(hcd));
...@@ -2390,6 +2438,7 @@ static const struct hc_driver uhci_driver = { ...@@ -2390,6 +2438,7 @@ static const struct hc_driver uhci_driver = {
.urb_enqueue = uhci_urb_enqueue, .urb_enqueue = uhci_urb_enqueue,
.urb_dequeue = uhci_urb_dequeue, .urb_dequeue = uhci_urb_dequeue,
.endpoint_disable = uhci_hcd_endpoint_disable,
.get_frame_number = uhci_hcd_get_frame_number, .get_frame_number = uhci_hcd_get_frame_number,
.hub_status_data = uhci_hub_status_data, .hub_status_data = uhci_hub_status_data,
......
...@@ -370,6 +370,8 @@ struct uhci_hcd { ...@@ -370,6 +370,8 @@ struct uhci_hcd {
int rh_numports; int rh_numports;
struct timer_list stall_timer; struct timer_list stall_timer;
wait_queue_head_t waitqh; /* endpoint_disable waiters */
}; };
struct urb_priv { struct urb_priv {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment