/*
* OHCI HCD (Host Controller Driver) for USB.
*
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
*
* This file is licenced under the GPL.
*/
#include <linux/irq.h>
#include <linux/slab.h>
static void urb_free_priv (struct ohci_hcd *hc, urb_priv_t *urb_priv)
{
int last = urb_priv->length - 1;
if (last >= 0) {
int i;
struct td *td;
for (i = 0; i <= last; i++) {
td = urb_priv->td [i];
if (td)
td_free (hc, td);
}
}
list_del (&urb_priv->pending);
kfree (urb_priv);
}
/*-------------------------------------------------------------------------*/
/*
* URB goes back to driver, and isn't reissued.
* It's completely gone from HC data structures.
* PRECONDITION: ohci lock held, irqs blocked.
*/
static void
finish_urb(struct ohci_hcd *ohci, struct urb *urb, int status)
__releases(ohci->lock)
__acquires(ohci->lock)
{
struct device *dev = ohci_to_hcd(ohci)->self.controller;
struct usb_host_endpoint *ep = urb->ep;
struct urb_priv *urb_priv;
// ASSERT (urb->hcpriv != 0);
restart:
urb_free_priv (ohci, urb->hcpriv);
urb->hcpriv = NULL;
if (likely(status == -EINPROGRESS))
status = 0;
switch (usb_pipetype (urb->pipe)) {
case PIPE_ISOCHRONOUS:
ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci))
usb_amd_quirk_pll_enable();
if (quirk_amdprefetch(ohci))
sb800_prefetch(dev, 0);
}
break;
case PIPE_INTERRUPT:
ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
break;
}
/* urb->complete() can reenter this HCD */
usb_hcd_unlink_urb_from_ep(ohci_to_hcd(ohci), urb);
spin_unlock (&ohci->lock);
usb_hcd_giveback_urb(ohci_to_hcd(ohci), urb, status);
spin_lock (&ohci->lock);
/* stop periodic dma if it's not needed */
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0
&& ohci_to_hcd(ohci)->self.bandwidth_int_reqs == 0) {
ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
/*
* An isochronous URB that is sumitted too late won't have any TDs
* (marked by the fact that the td_cnt value is larger than the
* actual number of TDs). If the next URB on this endpoint is like
* that, give it back now.
*/
if (!list_empty(&ep->urb_list)) {
urb = list_first_entry(&ep->urb_list, struct urb, urb_list);
urb_priv = urb->hcpriv;
if (urb_priv->td_cnt > urb_priv->length) {
status = 0;
goto restart;
}
}
}
/*-------------------------------------------------------------------------*
* ED handling functions
*-------------------------------------------------------------------------*/
/* search for the right schedule branch to use for a periodic ed.
* does some load balancing; returns the branch, or negative errno.
*/
static int balance (struct ohci_hcd