/*
* 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>
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)
{
// ASSERT (urb->hcpriv != 0);
urb_free_priv (ohci, urb->hcpriv);
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
&& quirk_amdiso(ohci))
quirk_amd_pll(1);
break;
case PIPE_INTERRUPT:
ohci_to_hcd(ohci)->self.bandwidth_int_reqs--;
break;
}
#ifdef OHCI_VERBOSE_DEBUG
urb_print(urb, "RET", usb_pipeout (urb->pipe), status);
#endif
/* 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);
}
}
/*-------------------------------------------------------------------------*
* 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 *ohci, int interval, int load)
{
int i, branch = -ENOSPC;
/* iso periods can be huge; iso tds specify frame numbers */
if (interval > NUM_INTS)
interval = NUM_INTS;
/* search for the least loaded schedule branch of that period
* that has enough bandwidth left unreserved.
*/
for (i = 0; i < interval ; i++) {
if (branch < 0 || ohci->load [branch] > ohci->load [i]) {
int j;
/* usb 1.1 says 90% of one frame */
for (j = i; j < NUM_INTS; j += interval) {
if ((ohci->load [j] + load) > 900)
break;
}
if (j < NUM_INTS)
continue;
branch = i;
}
}
return branch;
}
/*-------------------------------------------------------------------------*/
/* both iso and interrupt requests have periods; this routine puts them
* into the schedule tree in the apppropriate place. most iso devices use
* 1msec pe