Commit f52f682a authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: EHCI disconnect cleanup

So here's the EHCI implementation of that new callback,
morphed/simplified from the old "free_config" one (which
is now gone).  It looks almost identical to the OHCI
version, except the dummy TDs work a bit differently.

Again, drivers that clean themselves up in disconnect()
shouldn't notice this change.  I didn't re-test this;
I don't have devices with the other kind of driver.  You
should do so with one of yours (high speed hub and TT).

There are still about half a dozen places in usbcore
and the HCDs that would benefit from using this new
callback, FWIW.  I'd call them non-critical bugfixes
that should wait a bit, while this batch shakes out.
parent 2e579834
...@@ -1353,9 +1353,6 @@ static int hcd_free_dev (struct usb_device *udev) ...@@ -1353,9 +1353,6 @@ static int hcd_free_dev (struct usb_device *udev)
return -EINVAL; return -EINVAL;
} }
if (hcd->driver->free_config)
hcd->driver->free_config (hcd, udev);
spin_lock_irqsave (&hcd_data_lock, flags); spin_lock_irqsave (&hcd_data_lock, flags);
list_del (&dev->dev_list); list_del (&dev->dev_list);
udev->hcpriv = NULL; udev->hcpriv = NULL;
......
...@@ -196,11 +196,6 @@ struct hc_driver { ...@@ -196,11 +196,6 @@ struct hc_driver {
int mem_flags); int mem_flags);
int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb); int (*urb_dequeue) (struct usb_hcd *hcd, struct urb *urb);
// frees configuration resources -- allocated as needed during
// urb_enqueue, and not freed by urb_dequeue
void (*free_config) (struct usb_hcd *hcd,
struct usb_device *dev);
/* hw synch, freeing endpoint resources that urb_dequeue can't */ /* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd, void (*endpoint_disable)(struct usb_hcd *hcd,
struct hcd_dev *dev, int bEndpointAddress); struct hcd_dev *dev, int bEndpointAddress);
......
...@@ -870,80 +870,55 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) ...@@ -870,80 +870,55 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
// bulk qh holds the data toggle // bulk qh holds the data toggle
static void ehci_free_config (struct usb_hcd *hcd, struct usb_device *udev) static void
ehci_endpoint_disable (struct usb_hcd *hcd, struct hcd_dev *dev, int ep)
{ {
struct hcd_dev *dev = (struct hcd_dev *)udev->hcpriv;
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int i; int epnum;
unsigned long flags; unsigned long flags;
struct ehci_qh *qh;
/* ASSERT: no requests/urbs are still linked (so no TDs) */ /* ASSERT: any requests/urbs are being unlinked */
/* ASSERT: nobody can be submitting urbs for this any more */ /* ASSERT: nobody can be submitting urbs for this any more */
ehci_dbg (ehci, "free_config %s devnum %d\n", ehci_dbg (ehci, "ep %02x disable\n", ep);
udev->devpath, udev->devnum); epnum = ep & USB_ENDPOINT_NUMBER_MASK;
if (epnum != 0 && (ep & USB_DIR_IN))
epnum |= 0x10;
rescan:
spin_lock_irqsave (&ehci->lock, flags); spin_lock_irqsave (&ehci->lock, flags);
for (i = 0; i < 32; i++) { qh = (struct ehci_qh *) dev->ep [epnum];
if (dev->ep [i]) { if (!qh)
struct ehci_qh *qh; goto done;
char *why;
/* dev->ep never has ITDs or SITDs */
qh = (struct ehci_qh *) dev->ep [i];
/* detect/report non-recoverable errors */
if (in_interrupt ())
why = "disconnect() didn't";
else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0
&& qh->qh_state != QH_STATE_IDLE)
why = "(active periodic)";
else
why = 0;
if (why) {
err ("dev %s-%s ep %d-%s error: %s",
hcd_to_bus (hcd)->bus_name,
udev->devpath,
i & 0xf, (i & 0x10) ? "IN" : "OUT",
why);
BUG ();
}
dev->ep [i] = 0;
if (qh->qh_state == QH_STATE_IDLE)
goto idle;
ehci_dbg (ehci, "free_config, async ep 0x%02x qh %p",
i, qh);
/* scan_async() empties the ring as it does its work, if (!HCD_IS_RUNNING (ehci->hcd.state))
* using IAA, but doesn't (yet?) turn it off. if it qh->qh_state = QH_STATE_IDLE;
* doesn't empty this qh, likely it's the last entry. switch (qh->qh_state) {
*/ case QH_STATE_UNLINK: /* wait for hw to finish? */
while (qh->qh_state == QH_STATE_LINKED
&& ehci->reclaim
&& HCD_IS_RUNNING (ehci->hcd.state)
) {
spin_unlock_irqrestore (&ehci->lock, flags);
/* wait_ms() won't spin, we're a thread;
* and we know IRQ/timer/... can progress
*/
wait_ms (1);
spin_lock_irqsave (&ehci->lock, flags);
}
if (qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
while (qh->qh_state != QH_STATE_IDLE
&& ehci->hcd.state != USB_STATE_HALT) {
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
wait_ms (1); set_current_state (TASK_UNINTERRUPTIBLE);
spin_lock_irqsave (&ehci->lock, flags); schedule_timeout (1);
} goto rescan;
idle: case QH_STATE_IDLE: /* fully unlinked */
if (list_empty (&qh->qtd_list)) {
qh_put (ehci, qh); qh_put (ehci, qh);
break;
} }
/* else FALL THROUGH */
default:
/* caller was supposed to have unlinked any requests;
* that's not our job. just leak this memory.
*/
ehci_err (ehci, "qh %p (#%d) state %d%s\n",
qh, epnum, qh->qh_state,
list_empty (&qh->qtd_list) ? "" : "(has tds)");
break;
} }
dev->ep [epnum] = 0;
done:
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return;
} }
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -978,7 +953,7 @@ static const struct hc_driver ehci_driver = { ...@@ -978,7 +953,7 @@ static const struct hc_driver ehci_driver = {
*/ */
.urb_enqueue = ehci_urb_enqueue, .urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue, .urb_dequeue = ehci_urb_dequeue,
.free_config = ehci_free_config, .endpoint_disable = ehci_endpoint_disable,
/* /*
* scheduling support * scheduling support
......
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