# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
# ChangeSet 1.383.20.1 -> 1.383.20.2
# drivers/usb/hcd/ehci-q.c 1.1 -> 1.2
# drivers/usb/hcd/ehci-hub.c 1.1 -> 1.2
# drivers/usb/hcd/ehci.h 1.1 -> 1.2
# drivers/usb/hcd/ehci-sched.c 1.1 -> 1.2
# drivers/usb/hcd/ehci-mem.c 1.2 -> 1.3
# drivers/usb/hcd/ehci-hcd.c 1.3 -> 1.4
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/05/02 [EMAIL PROTECTED] 1.383.20.2
# [PATCH] PATCH 2.4.19-pre7 sync ehci-hcd with 2.5
#
# USB ehci update
#
# Short version of the story: syncs the driver with the 2.5 version.
# Includes preliminary high speed ISO support, fixes for interrupt
# transfer scheduling and some (rare) submission path errors.
# That's most of the patch, by volume; there are other updates.
# --------------------------------------------
#
diff -Nru a/drivers/usb/hcd/ehci-hcd.c b/drivers/usb/hcd/ehci-hcd.c
--- a/drivers/usb/hcd/ehci-hcd.c Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-hcd.c Fri May 3 14:51:44 2002
@@ -70,6 +70,11 @@
*
* HISTORY:
*
+ * 2002-04-19 Control/bulk/interrupt submit no longer uses giveback() on
+ * errors in submit path. Bugfixes to interrupt scheduling/processing.
+ * 2002-03-05 Initial high-speed ISO support; reduce ITD memory; shift
+ * more checking to generic hcd framework (db). Make it work with
+ * Philips EHCI; reduce PCI traffic; shorten IRQ path (Rory Bolt).
* 2002-01-14 Minor cleanup; version synch.
* 2002-01-08 Fix roothub handoff of FS/LS to companion controllers.
* 2002-01-04 Control/Bulk queuing behaves.
@@ -84,7 +89,7 @@
// #define EHCI_VERBOSE_DEBUG
-// #define have_iso
+// #define have_split_iso
/* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
@@ -180,6 +185,9 @@
dbg_hcs_params (ehci, "ehci_start");
dbg_hcc_params (ehci, "ehci_start");
+ /* cache this readonly data; minimize PCI reads */
+ ehci->hcs_params = readl (&ehci->caps->hcs_params);
+
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -197,11 +205,12 @@
ehci->async = 0;
ehci->reclaim = 0;
- ehci->next_frame = -1;
+ ehci->next_uframe = -1;
/* controller state: unknown --> reset */
/* EHCI spec section 4.1 */
+ // FIXME require STS_HALT before reset...
ehci_reset (ehci);
writel (INTR_MASK, &ehci->regs->intr_enable);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
@@ -307,7 +316,7 @@
// root hub is shut down separately (first, when possible)
scan_async (ehci);
- if (ehci->next_frame != -1)
+ if (ehci->next_uframe != -1)
scan_periodic (ehci);
ehci_mem_cleanup (ehci);
@@ -329,14 +338,12 @@
static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- u32 params;
int ports;
int i;
dbg ("%s: suspend to %d", hcd->bus_name, state);
- params = readl (&ehci->caps->hcs_params);
- ports = HCS_N_PORTS (params);
+ ports = HCS_N_PORTS (ehci->hcs_params);
// FIXME: This assumes what's probably a D3 level suspend...
@@ -372,14 +379,12 @@
static int ehci_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- u32 params;
int ports;
int i;
dbg ("%s: resume", hcd->bus_name);
- params = readl (&ehci->caps->hcs_params);
- ports = HCS_N_PORTS (params);
+ ports = HCS_N_PORTS (ehci->hcs_params);
// FIXME: if controller didn't retain state,
// return and let generic code clean it up
@@ -425,7 +430,7 @@
if (ehci->reclaim_ready)
end_unlink_async (ehci);
scan_async (ehci);
- if (ehci->next_frame != -1)
+ if (ehci->next_uframe != -1)
scan_periodic (ehci);
// FIXME: when nothing is connected to the root hub,
@@ -510,8 +515,7 @@
case PIPE_BULK:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
return -ENOMEM;
- submit_async (ehci, urb, &qtd_list, mem_flags);
- break;
+ return submit_async (ehci, urb, &qtd_list, mem_flags);
case PIPE_INTERRUPT:
if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags))
@@ -519,20 +523,19 @@
return intr_submit (ehci, urb, &qtd_list, mem_flags);
case PIPE_ISOCHRONOUS:
-#ifdef have_iso
if (urb->dev->speed == USB_SPEED_HIGH)
- return itd_submit (ehci, urb);
+ return itd_submit (ehci, urb, mem_flags);
+#ifdef have_split_iso
else
- return sitd_submit (ehci, urb);
+ return sitd_submit (ehci, urb, mem_flags);
#else
- // FIXME highspeed iso stuff is written but never run/tested.
- // and the split iso support isn't even written yet.
- dbg ("no iso support yet");
+ dbg ("no split iso support yet");
return -ENOSYS;
-#endif /* have_iso */
+#endif /* have_split_iso */
+ default: /* can't happen */
+ return -ENOSYS;
}
- return 0;
}
/* remove from hardware lists
@@ -587,7 +590,7 @@
// itd or sitd ...
// wait till next completion, do it then.
- // completion irqs can wait up to 128 msec,
+ // completion irqs can wait up to 1024 msec,
urb->transfer_flags |= EHCI_STATE_UNLINK;
return 0;
}
@@ -614,10 +617,7 @@
if (dev->ep [i]) {
struct ehci_qh *qh;
- // FIXME: this might be an itd/sitd too ...
- // or an interrupt urb (not on async list)
- // can use "union ehci_shadow"
-
+ /* dev->ep never has ITDs or SITDs */
qh = (struct ehci_qh *) dev->ep [i];
vdbg ("free_config, ep 0x%02x qh %p", i, qh);
if (!list_empty (&qh->qtd_list)) {
@@ -644,7 +644,7 @@
spin_lock_irqsave (&ehci->lock, flags);
}
}
- qh_unput (ehci, qh);
+ qh_put (ehci, qh);
}
}
diff -Nru a/drivers/usb/hcd/ehci-hub.c b/drivers/usb/hcd/ehci-hub.c
--- a/drivers/usb/hcd/ehci-hub.c Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-hub.c Fri May 3 14:51:44 2002
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -68,8 +68,7 @@
/* init status to no-changes */
buf [0] = 0;
- temp = readl (&ehci->caps->hcs_params);
- ports = HCS_N_PORTS (temp);
+ ports = HCS_N_PORTS (ehci->hcs_params);
if (ports > 7) {
buf [1] = 0;
retval++;
@@ -92,7 +91,10 @@
if (!(temp & PORT_CONNECT))
ehci->reset_done [i] = 0;
if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
- set_bit (i, buf);
+ if (i < 7)
+ buf [0] |= 1 << (i + 1);
+ else
+ buf [1] |= 1 << (i - 7);
status = STS_PCD;
}
}
@@ -107,8 +109,7 @@
struct ehci_hcd *ehci,
struct usb_hub_descriptor *desc
) {
- u32 params = readl (&ehci->caps->hcs_params);
- int ports = HCS_N_PORTS (params);
+ int ports = HCS_N_PORTS (ehci->hcs_params);
u16 temp;
desc->bDescriptorType = 0x29;
@@ -124,10 +125,10 @@
memset (&desc->bitmap [temp], 0xff, temp);
temp = 0x0008; /* per-port overcurrent reporting */
- if (HCS_PPC (params)) /* per-port power control */
- temp |= 0x0001;
- if (HCS_INDICATOR (params)) /* per-port indicators (LEDs) */
- temp |= 0x0080;
+ if (HCS_PPC (ehci->hcs_params))
+ temp |= 0x0001; /* per-port power control */
+ if (HCS_INDICATOR (ehci->hcs_params))
+ temp |= 0x0080; /* per-port indicators (LEDs) */
desc->wHubCharacteristics = cpu_to_le16 (temp);
}
@@ -142,9 +143,8 @@
u16 wLength
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
- u32 params = readl (&ehci->caps->hcs_params);
- int ports = HCS_N_PORTS (params);
- u32 temp;
+ int ports = HCS_N_PORTS (ehci->hcs_params);
+ u32 temp, status;
unsigned long flags;
int retval = 0;
@@ -189,7 +189,7 @@
/* ? */
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC (params))
+ if (HCS_PPC (ehci->hcs_params))
writel (temp & ~PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
@@ -222,22 +222,22 @@
if (!wIndex || wIndex > ports)
goto error;
wIndex--;
- memset (buf, 0, 4);
+ status = 0;
temp = readl (&ehci->regs->port_status [wIndex]);
// wPortChange bits
if (temp & PORT_CSC)
- set_bit (USB_PORT_FEAT_C_CONNECTION, buf);
+ status |= 1 << USB_PORT_FEAT_C_CONNECTION;
if (temp & PORT_PEC)
- set_bit (USB_PORT_FEAT_C_ENABLE, buf);
+ status |= 1 << USB_PORT_FEAT_C_ENABLE;
// USB_PORT_FEAT_C_SUSPEND
if (temp & PORT_OCC)
- set_bit (USB_PORT_FEAT_C_OVER_CURRENT, buf);
+ status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
/* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET)
&& jiffies > ehci->reset_done [wIndex]) {
- set_bit (USB_PORT_FEAT_C_RESET, buf);
+ status |= 1 << USB_PORT_FEAT_C_RESET;
/* force reset to complete */
writel (temp & ~PORT_RESET,
@@ -255,26 +255,27 @@
// don't show wPortStatus if it's owned by a companion hc
if (!(temp & PORT_OWNER)) {
if (temp & PORT_CONNECT) {
- set_bit (USB_PORT_FEAT_CONNECTION, buf);
- set_bit (USB_PORT_FEAT_HIGHSPEED, buf);
+ status |= 1 << USB_PORT_FEAT_CONNECTION;
+ status |= 1 << USB_PORT_FEAT_HIGHSPEED;
}
if (temp & PORT_PE)
- set_bit (USB_PORT_FEAT_ENABLE, buf);
+ status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & PORT_SUSPEND)
- set_bit (USB_PORT_FEAT_SUSPEND, buf);
+ status |= 1 << USB_PORT_FEAT_SUSPEND;
if (temp & PORT_OC)
- set_bit (USB_PORT_FEAT_OVER_CURRENT, buf);
+ status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (temp & PORT_RESET)
- set_bit (USB_PORT_FEAT_RESET, buf);
+ status |= 1 << USB_PORT_FEAT_RESET;
if (temp & PORT_POWER)
- set_bit (USB_PORT_FEAT_POWER, buf);
+ status |= 1 << USB_PORT_FEAT_POWER;
}
#ifndef EHCI_VERBOSE_DEBUG
- if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
+ if (status & ~0xffff) /* only if wPortChange is interesting */
#endif
dbg_port (hcd, "GetStatus", wIndex + 1, temp);
- cpu_to_le32s ((u32 *) buf);
+ // we "know" this alignment is good, caller used kmalloc()...
+ *((u32 *) buf) = cpu_to_le32 (status);
break;
case SetHubFeature:
switch (wValue) {
@@ -300,7 +301,7 @@
&ehci->regs->port_status [wIndex]);
break;
case USB_PORT_FEAT_POWER:
- if (HCS_PPC (params))
+ if (HCS_PPC (ehci->hcs_params))
writel (temp | PORT_POWER,
&ehci->regs->port_status [wIndex]);
break;
diff -Nru a/drivers/usb/hcd/ehci-mem.c b/drivers/usb/hcd/ehci-mem.c
--- a/drivers/usb/hcd/ehci-mem.c Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-mem.c Fri May 3 14:51:44 2002
@@ -98,16 +98,16 @@
}
/* to share a qh (cpu threads, or hc) */
-static inline struct ehci_qh *qh_put (/* ehci, */ struct ehci_qh *qh)
+static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
{
- // dbg ("put %p (%d++)", qh, qh->refcount.counter);
+ // dbg ("get %p (%d++)", qh, qh->refcount.counter);
atomic_inc (&qh->refcount);
return qh;
}
-static void qh_unput (struct ehci_hcd *ehci, struct ehci_qh *qh)
+static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- // dbg ("unput %p (--%d)", qh, qh->refcount.counter);
+ // dbg ("put %p (--%d)", qh, qh->refcount.counter);
if (!atomic_dec_and_test (&qh->refcount))
return;
/* clean qtds first, and know this is not linked */
diff -Nru a/drivers/usb/hcd/ehci-q.c b/drivers/usb/hcd/ehci-q.c
--- a/drivers/usb/hcd/ehci-q.c Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-q.c Fri May 3 14:51:44 2002
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -194,7 +194,7 @@
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
if (likely (urb->hcpriv != 0)) {
- qh_unput (ehci, (struct ehci_qh *) urb->hcpriv);
+ qh_put (ehci, (struct ehci_qh *) urb->hcpriv);
urb->hcpriv = 0;
}
@@ -280,6 +280,8 @@
/* if these qtds were queued to the HC, some may be active.
* else we're cleaning up after a failed URB submission.
+ *
+ * FIXME can simplify: cleanup case is gone now.
*/
if (likely (qh != 0)) {
int qh_halted;
@@ -406,6 +408,49 @@
/*-------------------------------------------------------------------------*/
/*
+ * reverse of qh_urb_transaction: free a list of TDs.
+ * used for cleanup after errors, before HC sees an URB's TDs.
+ */
+static void qtd_list_free (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ struct list_head *qtd_list
+) {
+ struct list_head *entry, *temp;
+ int unmapped = 0;
+
+ list_for_each_safe (entry, temp, qtd_list) {
+ struct ehci_qtd *qtd;
+
+ qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+ list_del (&qtd->qtd_list);
+ if (unmapped != 2) {
+ int direction;
+ size_t size;
+
+ /* for ctrl unmap twice: SETUP and DATA;
+ * else (bulk, intr) just once: DATA
+ */
+ if (!unmapped++ && usb_pipecontrol (urb->pipe)) {
+ direction = PCI_DMA_TODEVICE;
+ size = sizeof (devrequest);
+ } else {
+ direction = usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE;
+ size = qtd->urb->transfer_buffer_length;
+ unmapped++;
+ }
+ if (qtd->buf_dma)
+ pci_unmap_single (ehci->hcd.pdev,
+ qtd->buf_dma,
+ size, direction);
+ }
+ ehci_qtd_free (ehci, qtd);
+ }
+}
+
+/*
* create a list of filled qtds for this URB; won't link into qh.
*/
static struct list_head *
@@ -436,14 +481,6 @@
if (usb_pipecontrol (urb->pipe)) {
/* control request data is passed in the "setup" pid */
-
- /* NOTE: this isn't smart about 64bit DMA, since it uses the
- * default (32bit) mask rather than using the whole address
- * space. we could set pdev->dma_mask to all-ones while
- * getting this mapping, locking it and restoring before
- * allocating qtd/qh/... or maybe only do that for the main
- * data phase (below).
- */
qtd->buf_dma = pci_map_single (
ehci->hcd.pdev,
urb->setup_packet,
@@ -472,7 +509,6 @@
*/
len = urb->transfer_buffer_length;
if (likely (len > 0)) {
- /* NOTE: sub-optimal mapping with 64bit DMA (see above) */
buf = map_buf = pci_map_single (ehci->hcd.pdev,
urb->transfer_buffer, len,
usb_pipein (urb->pipe)
@@ -556,8 +592,7 @@
return head;
cleanup:
- urb->status = -ENOMEM;
- qh_completions (ehci, head, 1);
+ qtd_list_free (ehci, urb, head);
return 0;
}
@@ -643,12 +678,19 @@
if (usb_pipecontrol (urb->pipe)) {
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
+ info2 |= (EHCI_TUNE_MULT_HS << 30);
} else if (usb_pipebulk (urb->pipe)) {
info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30);
- } else
- info1 |= usb_maxpacket (urb->dev, urb->pipe,
- usb_pipeout (urb->pipe)) << 16;
+ } else {
+ u32 temp;
+ temp = usb_maxpacket (urb->dev, urb->pipe,
+ usb_pipeout (urb->pipe));
+ info1 |= (temp & 0x3ff) << 16; /* maxpacket */
+ /* HS intr can be "high bandwidth" */
+ temp = 1 + ((temp >> 11) & 0x03);
+ info2 |= temp << 30; /* mult */
+ }
break;
default:
#ifdef DEBUG
@@ -715,7 +757,7 @@
/*-------------------------------------------------------------------------*/
-static void
+static int
submit_async (
struct ehci_hcd *ehci,
struct urb *urb,
@@ -809,21 +851,23 @@
if (likely (qh != 0)) {
// dbg_qh ("new qh", ehci, qh);
dev->ep [epnum] = qh;
- } else
- urb->status = -ENOMEM;
+ }
}
/* Control/bulk operations through TTs don't need scheduling,
* the HC and TT handle it when the TT has a buffer ready.
*/
if (likely (qh != 0)) {
- urb->hcpriv = qh_put (qh);
+ urb->hcpriv = qh_get (qh);
if (likely (qh->qh_state == QH_STATE_IDLE))
- qh_link_async (ehci, qh_put (qh));
+ qh_link_async (ehci, qh_get (qh));
}
spin_unlock_irqrestore (&ehci->lock, flags);
- if (unlikely (!qh))
- qh_completions (ehci, qtd_list, 1);
+ if (unlikely (qh == 0)) {
+ qtd_list_free (ehci, urb, qtd_list);
+ return -ENOMEM;
+ }
+ return 0;
}
/*-------------------------------------------------------------------------*/
@@ -837,7 +881,7 @@
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = 0;
- qh_unput (ehci, qh); // refcount from reclaim
+ qh_put (ehci, qh); // refcount from reclaim
ehci->reclaim = 0;
ehci->reclaim_ready = 0;
@@ -849,7 +893,7 @@
&& HCD_IS_RUNNING (ehci->hcd.state))
qh_link_async (ehci, qh);
else
- qh_unput (ehci, qh); // refcount from async list
+ qh_put (ehci, qh); // refcount from async list
}
@@ -874,7 +918,7 @@
#endif
qh->qh_state = QH_STATE_UNLINK;
- ehci->reclaim = qh = qh_put (qh);
+ ehci->reclaim = qh = qh_get (qh);
// dbg_qh ("start unlink", ehci, qh);
@@ -939,14 +983,14 @@
/* clean any finished work for this qh */
if (!list_empty (&qh->qtd_list)) {
// dbg_qh ("scan_async", ehci, qh);
- qh = qh_put (qh);
+ qh = qh_get (qh);
spin_unlock_irqrestore (&ehci->lock, flags);
/* concurrent unlink could happen here */
qh_completions (ehci, &qh->qtd_list, 1);
spin_lock_irqsave (&ehci->lock, flags);
- qh_unput (ehci, qh);
+ qh_put (ehci, qh);
}
/* unlink idle entries (reduces PCI usage) */
diff -Nru a/drivers/usb/hcd/ehci-sched.c b/drivers/usb/hcd/ehci-sched.c
--- a/drivers/usb/hcd/ehci-sched.c Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci-sched.c Fri May 3 14:51:44 2002
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -55,12 +55,12 @@
return &periodic->qh->qh_next;
case Q_TYPE_FSTN:
return &periodic->fstn->fstn_next;
-#ifdef have_iso
case Q_TYPE_ITD:
return &periodic->itd->itd_next;
+#ifdef have_split_iso
case Q_TYPE_SITD:
return &periodic->sitd->sitd_next;
-#endif /* have_iso */
+#endif /* have_split_iso */
}
dbg ("BAD shadow %p tag %d", periodic->ptr, tag);
// BUG ();
@@ -109,9 +109,6 @@
u32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame];
unsigned usecs = 0;
-#ifdef have_iso
- u32 temp = 0;
-#endif
while (q->ptr) {
switch (Q_NEXT_TYPE (*hw_p)) {
@@ -130,15 +127,13 @@
}
q = &q->fstn->fstn_next;
break;
-#ifdef have_iso
case Q_TYPE_ITD:
- temp = le32_to_cpu (q->itd->transaction [uframe]);
- temp >>= 16;
- temp &= 0x0fff;
- if (temp)
- usecs += HS_USECS_ISO (temp);
+ /* NOTE the "one uframe per itd" policy */
+ if (q->itd->hw_transaction [uframe] != 0)
+ usecs += q->itd->usecs;
q = &q->itd->itd_next;
break;
+#ifdef have_split_iso
case Q_TYPE_SITD:
temp = q->sitd->hw_fullspeed_ep &
__constant_cpu_to_le32 (1 << 31);
@@ -163,7 +158,7 @@
}
q = &q->sitd->sitd_next;
break;
-#endif /* have_iso */
+#endif /* have_split_iso */
default:
BUG ();
}
@@ -178,6 +173,45 @@
/*-------------------------------------------------------------------------*/
+static void enable_periodic (struct ehci_hcd *ehci)
+{
+ u32 cmd;
+
+ /* did clearing PSE did take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (readl (&ehci->regs->status) & STS_PSS)
+ udelay (20);
+
+ cmd = readl (&ehci->regs->command) | CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... PSS happens later */
+ ehci->hcd.state = USB_STATE_RUNNING;
+
+ /* make sure tasklet scans these */
+ ehci->next_uframe = readl (&ehci->regs->frame_index)
+ % (ehci->periodic_size << 3);
+}
+
+static void disable_periodic (struct ehci_hcd *ehci)
+{
+ u32 cmd;
+
+ /* did setting PSE not take effect yet?
+ * takes effect only at frame boundaries...
+ */
+ while (!(readl (&ehci->regs->status) & STS_PSS))
+ udelay (20);
+
+ cmd = readl (&ehci->regs->command) & ~CMD_PSE;
+ writel (cmd, &ehci->regs->command);
+ /* posted write ... */
+
+ ehci->next_uframe = -1;
+}
+
+/*-------------------------------------------------------------------------*/
+
static void intr_deschedule (
struct ehci_hcd *ehci,
unsigned frame,
@@ -186,11 +220,13 @@
) {
unsigned long flags;
+ period >>= 3; // FIXME microframe periods not handled yet
+
spin_lock_irqsave (&ehci->lock, flags);
do {
periodic_unlink (ehci, frame, qh);
- qh_unput (ehci, qh);
+ qh_put (ehci, qh);
frame += period;
} while (frame < ehci->periodic_size);
@@ -199,21 +235,9 @@
ehci->periodic_urbs--;
/* maybe turn off periodic schedule */
- if (!ehci->periodic_urbs) {
- u32 cmd = readl (&ehci->regs->command);
-
- /* did setting PSE not take effect yet?
- * takes effect only at frame boundaries...
- */
- while (!(readl (&ehci->regs->status) & STS_PSS))
- udelay (20);
-
- cmd &= ~CMD_PSE;
- writel (cmd, &ehci->regs->command);
- /* posted write ... */
-
- ehci->next_frame = -1;
- } else
+ if (!ehci->periodic_urbs)
+ disable_periodic (ehci);
+ else
vdbg ("periodic schedule still enabled");
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -234,6 +258,38 @@
atomic_read (&qh->refcount), ehci->periodic_urbs);
}
+static int check_period (
+ struct ehci_hcd *ehci,
+ unsigned frame,
+ int uframe,
+ unsigned period,
+ unsigned usecs
+) {
+ /*
+ * 80% periodic == 100 usec/uframe available
+ * convert "usecs we need" to "max already claimed"
+ */
+ usecs = 100 - usecs;
+
+ do {
+ int claimed;
+
+// FIXME delete when intr_submit handles non-empty queues
+// this gives us a one intr/frame limit (vs N/uframe)
+ if (ehci->pshadow [frame].ptr)
+ return 0;
+
+ claimed = periodic_usecs (ehci, frame, uframe);
+ if (claimed > usecs)
+ return 0;
+
+// FIXME update to handle sub-frame periods
+ } while ((frame += period) < ehci->periodic_size);
+
+ // success!
+ return 1;
+}
+
static int intr_submit (
struct ehci_hcd *ehci,
struct urb *urb,
@@ -241,8 +297,7 @@
int mem_flags
) {
unsigned epnum, period;
- unsigned temp;
- unsigned short mult, usecs;
+ unsigned short usecs;
unsigned long flags;
struct ehci_qh *qh;
struct hcd_dev *dev;
@@ -250,17 +305,9 @@
/* get endpoint and transfer data */
epnum = usb_pipeendpoint (urb->pipe);
- if (usb_pipein (urb->pipe)) {
- temp = urb->dev->epmaxpacketin [epnum];
+ if (usb_pipein (urb->pipe))
epnum |= 0x10;
- } else
- temp = urb->dev->epmaxpacketout [epnum];
- mult = 1;
- if (urb->dev->speed == USB_SPEED_HIGH) {
- /* high speed "high bandwidth" is coded in ep maxpacket */
- mult += (temp >> 11) & 0x03;
- temp &= 0x03ff;
- } else {
+ if (urb->dev->speed != USB_SPEED_HIGH) {
dbg ("no intr/tt scheduling yet");
status = -ENOSYS;
goto done;
@@ -270,6 +317,13 @@
* NOTE: current completion/restart logic doesn't handle more than
* one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this.
* such big requests need many periods to transfer.
+ *
+ * FIXME want to change hcd core submit model to expect queuing
+ * for all transfer types ... not just ISO and (with flag) BULK.
+ * that means: getting rid of this check; handling the "interrupt
+ * urb already queued" case below like bulk queuing is handled (no
+ * errors possible!); and completly getting rid of that annoying
+ * qh restart logic. simpler/smaller overall, and more flexible.
*/
if (unlikely (qtd_list->next != qtd_list->prev)) {
dbg ("only one intr qtd per urb allowed");
@@ -279,20 +333,13 @@
usecs = HS_USECS (urb->transfer_buffer_length);
- /*
- * force a power-of-two (frames) sized polling interval
- *
- * NOTE: endpoint->bInterval for highspeed is measured in uframes,
- * while for full/low speeds it's in frames. Here we "know" that
- * urb->interval doesn't give acccess to high interrupt rates.
- */
- period = ehci->periodic_size;
- temp = period;
- if (unlikely (urb->interval < 1))
- urb->interval = 1;
- while (temp > urb->interval)
- temp >>= 1;
- period = urb->interval = temp;
+ /* FIXME handle HS periods of less than 1 frame. */
+ period = urb->interval >> 3;
+ if (period < 1) {
+ dbg ("intr period %d uframes, NYET!", urb->interval);
+ status = -EINVAL;
+ goto done;
+ }
spin_lock_irqsave (&ehci->lock, flags);
@@ -318,55 +365,47 @@
list_splice (qtd_list, &qh->qtd_list);
qh_update (qh, list_entry (qtd_list->next,
struct ehci_qtd, qtd_list));
+ qtd_list = &qh->qtd_list;
}
} else {
/* can't sleep here, we have ehci->lock... */
qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
- qtd_list = &qh->qtd_list;
if (likely (qh != 0)) {
// dbg ("new INTR qh %p", qh);
dev->ep [epnum] = qh;
+ qtd_list = &qh->qtd_list;
} else
status = -ENOMEM;
}
/* Schedule this periodic QH. */
if (likely (status == 0)) {
- unsigned frame = urb->interval;
+ unsigned frame = period;
qh->hw_next = EHCI_LIST_END;
- qh->hw_info2 |= cpu_to_le32 (mult << 30);
qh->usecs = usecs;
- urb->hcpriv = qh_put (qh);
+ urb->hcpriv = qh_get (qh);
status = -ENOSPC;
/* pick a set of schedule slots, link the QH into them */
do {
int uframe;
- /* Select some frame 0..(urb->interval - 1) with a
- * microframe that can hold this transaction.
+ /* pick a set of slots such that all uframes have
+ * enough periodic bandwidth available.
*
* FIXME for TT splits, need uframes for start and end.
* FSTNs can put end into next frame (uframes 0 or 1).
*/
frame--;
for (uframe = 0; uframe < 8; uframe++) {
- int claimed;
- claimed = periodic_usecs (ehci, frame, uframe);
- /* 80% periodic == 100 usec max committed */
- if ((claimed + usecs) <= 100) {
- vdbg ("frame %d.%d: %d usecs, plus %d",
- frame, uframe, claimed, usecs);
+ if (check_period (ehci, frame, uframe,
+ period, usecs) != 0)
break;
- }
}
if (uframe == 8)
continue;
-// FIXME delete when code below handles non-empty queues
- if (ehci->pshadow [frame].ptr)
- continue;
/* QH will run once each period, starting there */
urb->start_frame = frame;
@@ -378,15 +417,16 @@
/* stuff into the periodic schedule */
qh->qh_state = QH_STATE_LINKED;
- vdbg ("qh %p usecs %d period %d starting frame %d.%d",
+ vdbg ("qh %p usecs %d period %d starting %d.%d",
qh, qh->usecs, period, frame, uframe);
do {
if (unlikely (ehci->pshadow [frame].ptr != 0)) {
-// FIXME -- just link to the end, before any qh with a shorter period,
+// FIXME -- just link toward the end, before any qh with a shorter period,
// AND handle it already being (implicitly) linked into this frame
+// AS WELL AS updating the check_period() logic
BUG ();
} else {
- ehci->pshadow [frame].qh = qh_put (qh);
+ ehci->pshadow [frame].qh = qh_get (qh);
ehci->periodic [frame] =
QH_NEXT (qh->qh_dma);
}
@@ -394,40 +434,20 @@
} while (frame < ehci->periodic_size);
/* update bandwidth utilization records (for usbfs) */
- usb_claim_bandwidth (urb->dev, urb, usecs, 0);
+ usb_claim_bandwidth (urb->dev, urb, usecs/period, 0);
/* maybe enable periodic schedule processing */
- if (!ehci->periodic_urbs++) {
- u32 cmd;
-
- /* did clearing PSE did take effect yet?
- * takes effect only at frame boundaries...
- */
- while (readl (&ehci->regs->status) & STS_PSS)
- udelay (20);
-
- cmd = readl (&ehci->regs->command) | CMD_PSE;
- writel (cmd, &ehci->regs->command);
- /* posted write ... PSS happens later */
- ehci->hcd.state = USB_STATE_RUNNING;
-
- /* make sure tasklet scans these */
- ehci->next_frame = ehci_get_frame (&ehci->hcd);
- }
+ if (!ehci->periodic_urbs++)
+ enable_periodic (ehci);
break;
} while (frame);
}
spin_unlock_irqrestore (&ehci->lock, flags);
done:
- if (status) {
- usb_complete_t complete = urb->complete;
+ if (status)
+ qtd_list_free (ehci, urb, qtd_list);
- urb->complete = 0;
- urb->status = status;
- qh_completions (ehci, qtd_list, 1);
- urb->complete = complete;
- }
return status;
}
@@ -446,6 +466,10 @@
if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE))
!= 0))
return flags;
+ if (unlikely (list_empty (&qh->qtd_list))) {
+ dbg ("intr qh %p no TDs?", qh);
+ return flags;
+ }
qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list);
urb = qtd->urb;
@@ -489,53 +513,56 @@
/*-------------------------------------------------------------------------*/
-#ifdef have_iso
-
-static inline void itd_free (struct ehci_hcd *ehci, struct ehci_itd *itd)
+static void
+itd_free_list (struct ehci_hcd *ehci, struct urb *urb)
{
- pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+ struct ehci_itd *first_itd = urb->hcpriv;
+
+ pci_unmap_single (ehci->hcd.pdev,
+ first_itd->buf_dma, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ while (!list_empty (&first_itd->itd_list)) {
+ struct ehci_itd *itd;
+
+ itd = list_entry (
+ first_itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ list_del (&itd->itd_list);
+ pci_pool_free (ehci->itd_pool, itd, itd->itd_dma);
+ }
+ pci_pool_free (ehci->itd_pool, first_itd, first_itd->itd_dma);
+ urb->hcpriv = 0;
}
-/*
- * Create itd and allocate into uframes within specified frame.
- * Caller must update the resulting uframe links.
- */
-static struct ehci_itd *
-itd_make (
+static int
+itd_fill (
struct ehci_hcd *ehci,
+ struct ehci_itd *itd,
struct urb *urb,
unsigned index, // urb->iso_frame_desc [index]
- unsigned frame, // scheduled start
- dma_addr_t dma, // mapped transfer buffer
- int mem_flags
+ dma_addr_t dma // mapped transfer buffer
) {
- struct ehci_itd *itd;
u64 temp;
u32 buf1;
- unsigned epnum, maxp, multi, usecs;
+ unsigned i, epnum, maxp, multi;
unsigned length;
- unsigned i, bufnum;
-
- /* allocate itd, start to fill it */
- itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &dma);
- if (!itd)
- return itd;
itd->hw_next = EHCI_LIST_END;
itd->urb = urb;
itd->index = index;
- INIT_LIST_HEAD (&itd->itd_list);
- itd->uframe = (frame * 8) % ehci->periodic_size;
- /* tell itd about the buffer its transfers will consume */
+ /* tell itd about its transfer buffer, max 2 pages */
length = urb->iso_frame_desc [index].length;
dma += urb->iso_frame_desc [index].offset;
temp = dma & ~0x0fff;
- for (i = 0; i < 7; i++) {
+ for (i = 0; i < 2; i++) {
itd->hw_bufp [i] = cpu_to_le32 ((u32) temp);
itd->hw_bufp_hi [i] = cpu_to_le32 ((u32)(temp >> 32));
- temp += 0x0fff;
+ temp += 0x1000;
}
+ itd->buf_dma = dma;
/*
* this might be a "high bandwidth" highspeed endpoint,
@@ -544,282 +571,407 @@
epnum = usb_pipeendpoint (urb->pipe);
if (usb_pipein (urb->pipe)) {
maxp = urb->dev->epmaxpacketin [epnum];
- buf1 = (1 << 11) | maxp;
+ buf1 = (1 << 11);
} else {
maxp = urb->dev->epmaxpacketout [epnum];
- buf1 = maxp;
+ buf1 = 0;
}
+ buf1 |= (maxp & 0x03ff);
multi = 1;
- multi += (temp >> 11) & 0x03;
+ multi += (maxp >> 11) & 0x03;
maxp &= 0x03ff;
+ maxp *= multi;
+
+ /* transfer can't fit in any uframe? */
+ if (length < 0 || maxp < length) {
+ dbg ("BAD iso packet: %d bytes, max %d, urb %p [%d] (of %d)",
+ length, maxp, urb, index,
+ urb->iso_frame_desc [index].length);
+ return -ENOSPC;
+ }
+ itd->usecs = HS_USECS_ISO (length);
/* "plus" info in low order bits of buffer pointers */
itd->hw_bufp [0] |= cpu_to_le32 ((epnum << 8) | urb->dev->devnum);
itd->hw_bufp [1] |= cpu_to_le32 (buf1);
itd->hw_bufp [2] |= cpu_to_le32 (multi);
- /* schedule as many uframes as needed */
- maxp *= multi;
- usecs = HS_USECS_ISO (maxp);
- bufnum = 0;
- for (i = 0; i < 8; i++) {
- unsigned t, offset, scratch;
+ /* figure hw_transaction[] value (it's scheduled later) */
+ itd->transaction = EHCI_ISOC_ACTIVE;
+ itd->transaction |= dma & 0x0fff; /* offset; buffer=0 */
+ if ((index + 1) == urb->number_of_packets)
+ itd->transaction |= EHCI_ITD_IOC; /* end-of-urb irq */
+ itd->transaction |= length << 16;
+ cpu_to_le32s (&itd->transaction);
- if (length <= 0) {
- itd->hw_transaction [i] = 0;
- continue;
- }
+ return 0;
+}
- /* don't commit more than 80% periodic == 100 usec */
- if ((periodic_usecs (ehci, itd->uframe, i) + usecs) > 100)
- continue;
+static int
+itd_urb_transaction (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ int mem_flags
+) {
+ int frame_index;
+ struct ehci_itd *first_itd, *itd;
+ int status;
+ dma_addr_t buf_dma, itd_dma;
- /* we'll use this uframe; figure hw_transaction */
- t = EHCI_ISOC_ACTIVE;
- t |= bufnum << 12; // which buffer?
- offset = temp & 0x0fff; // offset therein
- t |= offset;
- if ((offset + maxp) >= 4096) // hc auto-wraps end-of-"page"
- bufnum++;
- if (length <= maxp) {
- // interrupt only needed at end-of-urb
- if ((index + 1) == urb->number_of_packets)
- t |= EHCI_ITD_IOC;
- scratch = length;
- } else
- scratch = maxp;
- t |= scratch << 16;
- t = cpu_to_le32 (t);
-
- itd->hw_transaction [i] = itd->transaction [i] = t;
- length -= scratch;
- }
- if (length > 0) {
- dbg ("iso frame too big, urb %p [%d], %d extra (of %d)",
- urb, index, length, urb->iso_frame_desc [index].length);
- itd_free (ehci, itd);
- itd = 0;
+ /* set up one dma mapping for this urb */
+ buf_dma = pci_map_single (ehci->hcd.pdev,
+ urb->transfer_buffer, urb->transfer_buffer_length,
+ usb_pipein (urb->pipe)
+ ? PCI_DMA_FROMDEVICE
+ : PCI_DMA_TODEVICE);
+ if (buf_dma == 0)
+ return -ENOMEM;
+
+ /* allocate/init ITDs */
+ for (frame_index = 0, first_itd = 0;
+ frame_index < urb->number_of_packets;
+ frame_index++) {
+ itd = pci_pool_alloc (ehci->itd_pool, mem_flags, &itd_dma);
+ if (!itd) {
+ status = -ENOMEM;
+ goto fail;
+ }
+ memset (itd, 0, sizeof *itd);
+ itd->itd_dma = itd_dma;
+
+ status = itd_fill (ehci, itd, urb, frame_index, buf_dma);
+ if (status != 0)
+ goto fail;
+
+ if (first_itd)
+ list_add_tail (&itd->itd_list,
+ &first_itd->itd_list);
+ else {
+ INIT_LIST_HEAD (&itd->itd_list);
+ urb->hcpriv = first_itd = itd;
+ }
}
- return itd;
+ urb->error_count = 0;
+ return 0;
+
+fail:
+ if (urb->hcpriv)
+ itd_free_list (ehci, urb);
+ return status;
}
+/*-------------------------------------------------------------------------*/
+
static inline void
itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
{
- u32 ptr;
+ /* always prepend ITD/SITD ... only QH tree is order-sensitive */
+ itd->itd_next = ehci->pshadow [frame];
+ itd->hw_next = ehci->periodic [frame];
+ ehci->pshadow [frame].itd = itd;
+ ehci->periodic [frame] = cpu_to_le32 (itd->itd_dma) | Q_TYPE_ITD;
+}
- ptr = cpu_to_le32 (itd->itd_dma); // type 0 == itd
- if (ehci->pshadow [frame].ptr) {
- if (!itd->itd_next.ptr) {
- itd->itd_next = ehci->pshadow [frame];
- itd->hw_next = ehci->periodic [frame];
- } else if (itd->itd_next.ptr != ehci->pshadow [frame].ptr) {
- dbg ("frame %d itd link goof", frame);
- BUG ();
+/*
+ * return zero on success, else -errno
+ * - start holds first uframe to start scheduling into
+ * - max is the first uframe it's NOT (!) OK to start scheduling into
+ * math to be done modulo "mod" (ehci->periodic_size << 3)
+ */
+static int get_iso_range (
+ struct ehci_hcd *ehci,
+ struct urb *urb,
+ unsigned *start,
+ unsigned *max,
+ unsigned mod
+) {
+ struct list_head *lh;
+ struct hcd_dev *dev = urb->dev->hcpriv;
+ int last = -1;
+ unsigned now, span, end;
+
+ span = urb->interval * urb->number_of_packets;
+
+ /* first see if we know when the next transfer SHOULD happen */
+ list_for_each (lh, &dev->urb_list) {
+ struct urb *u;
+ struct ehci_itd *itd;
+ unsigned s;
+
+ u = list_entry (lh, struct urb, urb_list);
+ if (u == urb || u->pipe != urb->pipe)
+ continue;
+ if (u->interval != urb->interval) { /* must not change! */
+ dbg ("urb %p interval %d ... != %p interval %d",
+ u, u->interval, urb, urb->interval);
+ return -EINVAL;
+ }
+
+ /* URB for this endpoint... covers through when? */
+ itd = urb->hcpriv;
+ s = itd->uframe + u->interval * u->number_of_packets;
+ if (last < 0)
+ last = s;
+ else {
+ /*
+ * So far we can only queue two ISO URBs...
+ *
+ * FIXME do interval math, figure out whether
+ * this URB is "before" or not ... also, handle
+ * the case where the URB might have completed,
+ * but hasn't yet been processed.
+ */
+ dbg ("NYET: queue >2 URBs per ISO endpoint");
+ return -EDOM;
}
}
- ehci->pshadow [frame].itd = itd;
- ehci->periodic [frame] = ptr;
-}
-#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+ /* calculate the legal range [start,max) */
+ now = readl (&ehci->regs->frame_index) + 1; /* next uframe */
+ if (!ehci->periodic_urbs)
+ now += 8; /* startup delay */
+ now %= mod;
+ end = now + mod;
+ if (last < 0) {
+ *start = now + ehci->i_thresh + /* paranoia */ 1;
+ *max = end - span;
+ if (*max < *start + 1)
+ *max = *start + 1;
+ } else {
+ *start = last % mod;
+ *max = (last + 1) % mod;
+ }
-static unsigned long
-itd_complete (struct ehci_hcd *ehci, struct ehci_itd *itd, unsigned long flags)
+ /* explicit start frame? */
+ if (!(urb->transfer_flags & USB_ISO_ASAP)) {
+ unsigned temp;
+
+ /* sanity check: must be in range */
+ urb->start_frame %= ehci->periodic_size;
+ temp = urb->start_frame << 3;
+ if (temp < *start)
+ temp += mod;
+ if (temp > *max)
+ return -EDOM;
+
+ /* use that explicit start frame */
+ *start = urb->start_frame << 3;
+ temp += 8;
+ if (temp < *max)
+ *max = temp;
+ }
+
+ // FIXME minimize wraparound to "now" ... insist max+span
+ // (and start+span) remains a few frames short of "end"
+
+ *max %= ehci->periodic_size;
+ if ((*start + span) < end)
+ return 0;
+ return -EFBIG;
+}
+
+static int
+itd_schedule (struct ehci_hcd *ehci, struct urb *urb)
{
- struct urb *urb = itd->urb;
+ unsigned start, max, i;
+ int status;
+ unsigned mod = ehci->periodic_size << 3;
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ urb->iso_frame_desc [i].status = -EINPROGRESS;
+ urb->iso_frame_desc [i].actual_length = 0;
+ }
- /* if not unlinking: */
- if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
- && ehci->hcd.state != USB_STATE_HALT) {
- int i;
- iso_packet_descriptor_t *desc;
- struct ehci_itd *first_itd = urb->hcpriv;
+ if ((status = get_iso_range (ehci, urb, &start, &max, mod)) != 0)
+ return status;
- /* update status for this frame's transfers */
- desc = &urb->iso_frame_desc [itd->index];
- desc->status = 0;
- desc->actual_length = 0;
- for (i = 0; i < 8; i++) {
- u32 t = itd->hw_transaction [i];
- if (t & (ISO_ERRS | EHCI_ISOC_ACTIVE)) {
- if (t & EHCI_ISOC_ACTIVE)
- desc->status = -EXDEV;
- else if (t & EHCI_ISOC_BUF_ERR)
- desc->status = usb_pipein (urb->pipe)
- ? -ENOSR /* couldn't read */
- : -ECOMM; /* couldn't write */
- else if (t & EHCI_ISOC_BABBLE)
- desc->status = -EOVERFLOW;
- else /* (t & EHCI_ISOC_XACTERR) */
- desc->status = -EPROTO;
+ do {
+ unsigned uframe;
+ unsigned usecs;
+ struct ehci_itd *itd;
+
+ /* check schedule: enough space? */
+ itd = urb->hcpriv;
+ uframe = start;
+ for (i = 0, uframe = start;
+ i < urb->number_of_packets;
+ i++, uframe += urb->interval) {
+ uframe %= mod;
+
+ /* can't commit more than 80% periodic == 100 usec */
+ if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
+ > (100 - itd->usecs)) {
+ itd = 0;
break;
}
- desc->actual_length += EHCI_ITD_LENGTH (t);
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
}
+ if (!itd)
+ continue;
+
+ /* that's where we'll schedule this! */
+ itd = urb->hcpriv;
+ urb->start_frame = start >> 3;
+ vdbg ("ISO urb %p (%d packets period %d) starting %d.%d",
+ urb, urb->number_of_packets, urb->interval,
+ urb->start_frame, start & 0x7);
+ for (i = 0, uframe = start, usecs = 0;
+ i < urb->number_of_packets;
+ i++, uframe += urb->interval) {
+ uframe %= mod;
+
+ itd->uframe = uframe;
+ itd->hw_transaction [uframe & 0x07] = itd->transaction;
+ itd_link (ehci, (uframe >> 3) % ehci->periodic_size,
+ itd);
+ usecs += itd->usecs;
- /* handle completion now? */
- if ((itd->index + 1) != urb->number_of_packets)
- return flags;
+ itd = list_entry (itd->itd_list.next,
+ struct ehci_itd, itd_list);
+ }
- i = usb_pipein (urb->pipe);
- if (i)
- pci_dma_sync_single (ehci->hcd.pdev,
- first_itd->buf_dma,
- urb->transfer_buffer_length,
- PCI_DMA_FROMDEVICE);
+ /* update bandwidth utilization records (for usbfs) */
+ /* FIXME usbcore expects per-frame average, which isn't
+ * most accurate model... this provides the total claim,
+ * and expects the average to be computed only display.
+ */
+ usb_claim_bandwidth (urb->dev, urb, usecs, 1);
- /* call completion with no locks; it can unlink ... */
- spin_unlock_irqrestore (&ehci->lock, flags);
- urb->complete (urb);
- spin_lock_irqsave (&ehci->lock, flags);
-
- /* re-activate this URB? or unlink? */
- if (!(urb->transfer_flags & EHCI_STATE_UNLINK)
- && ehci->hcd.state != USB_STATE_HALT) {
- if (!i)
- pci_dma_sync_single (ehci->hcd.pdev,
- first_itd->buf_dma,
- urb->transfer_buffer_length,
- PCI_DMA_TODEVICE);
+ /* maybe enable periodic schedule processing */
+ if (!ehci->periodic_urbs++)
+ enable_periodic (ehci);
- itd = urb->hcpriv;
- do {
- for (i = 0; i < 8; i++)
- itd->hw_transaction [i]
- = itd->transaction [i];
- itd = list_entry (itd->itd_list.next,
- struct ehci_itd, itd_list);
- } while (itd != urb->hcpriv);
- return flags;
- }
+ return 0;
+
+ } while ((start = ++start % mod) != max);
+
+ /* no room in the schedule */
+ dbg ("urb %p, CAN'T SCHEDULE", urb);
+ return -ENOSPC;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define ISO_ERRS (EHCI_ISOC_BUF_ERR | EHCI_ISOC_BABBLE | EHCI_ISOC_XACTERR)
+
+static unsigned long
+itd_complete (
+ struct ehci_hcd *ehci,
+ struct ehci_itd *itd,
+ unsigned uframe,
+ unsigned long flags
+) {
+ struct urb *urb = itd->urb;
+ iso_packet_descriptor_t *desc;
+ u32 t;
+
+ /* update status for this uframe's transfers */
+ desc = &urb->iso_frame_desc [itd->index];
+
+ t = itd->hw_transaction [uframe];
+ itd->hw_transaction [uframe] = 0;
+ if (t & EHCI_ISOC_ACTIVE)
+ desc->status = -EXDEV;
+ else if (t & ISO_ERRS) {
+ urb->error_count++;
+ if (t & EHCI_ISOC_BUF_ERR)
+ desc->status = usb_pipein (urb->pipe)
+ ? -ENOSR /* couldn't read */
+ : -ECOMM; /* couldn't write */
+ else if (t & EHCI_ISOC_BABBLE)
+ desc->status = -EOVERFLOW;
+ else /* (t & EHCI_ISOC_XACTERR) */
+ desc->status = -EPROTO;
+
+ /* HC need not update length with this error */
+ if (!(t & EHCI_ISOC_BABBLE))
+ desc->actual_length += EHCI_ITD_LENGTH (t);
+ } else {
+ desc->status = 0;
+ desc->actual_length += EHCI_ITD_LENGTH (t);
+ }
- /* unlink done only on the last itd */
- } else if ((itd->index + 1) != urb->number_of_packets)
+ vdbg ("itd %p urb %p packet %d/%d trans %x status %d len %d",
+ itd, urb, itd->index + 1, urb->number_of_packets,
+ t, desc->status, desc->actual_length);
+
+ /* handle completion now? */
+ if ((itd->index + 1) != urb->number_of_packets)
return flags;
- /* we're unlinking ... */
+ /*
+ * For now, always give the urb back to the driver ... expect it
+ * to submit a new urb (or resubmit this), and to have another
+ * already queued when un-interrupted transfers are needed.
+ * No, that's not what OHCI or UHCI are now doing.
+ *
+ * FIXME Revisit the ISO URB model. It's cleaner not to have all
+ * the special case magic, but it'd be faster to reuse existing
+ * ITD/DMA setup and schedule state. Easy to dma_sync/complete(),
+ * then either reschedule or, if unlinking, free and giveback().
+ * But we can't overcommit like the full and low speed HCs do, and
+ * there's no clean way to report an error when rescheduling...
+ *
+ * NOTE that for now we don't accelerate ISO unlinks; they just
+ * happen according to the current schedule. Means a delay of
+ * up to about a second (max).
+ */
+ itd_free_list (ehci, urb);
+ if (urb->status == -EINPROGRESS)
+ urb->status = 0;
- /* decouple urb from the hcd */
spin_unlock_irqrestore (&ehci->lock, flags);
- if (ehci->hcd.state == USB_STATE_HALT)
- urb->status = -ESHUTDOWN;
- itd = urb->hcpriv;
- urb->hcpriv = 0;
- ehci_urb_done (ehci, itd->buf_dma, urb);
+ usb_hcd_giveback_urb (&ehci->hcd, urb);
spin_lock_irqsave (&ehci->lock, flags);
- /* take itds out of the hc's periodic schedule */
- list_entry (itd->itd_list.prev, struct ehci_itd, itd_list)
- ->itd_list.next = 0;
- do {
- struct ehci_itd *next;
-
- if (itd->itd_list.next)
- next = list_entry (itd->itd_list.next,
- struct ehci_itd, itd_list);
- else
- next = 0;
+ /* defer stopping schedule; completion can submit */
+ ehci->periodic_urbs--;
+ if (!ehci->periodic_urbs)
+ disable_periodic (ehci);
- // FIXME: hc WILL (!) lap us here, if we get behind
- // by 128 msec (or less, with smaller periodic_size).
- // Reading/caching these itds will cause trouble...
-
- periodic_unlink (ehci, itd->uframe, itd);
- itd_free (ehci, itd);
- itd = next;
- } while (itd);
return flags;
}
/*-------------------------------------------------------------------------*/
-static int itd_submit (struct ehci_hcd *ehci, struct urb *urb)
+static int itd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
{
- struct ehci_itd *first_itd = 0, *itd;
- unsigned frame_index;
- dma_addr_t dma;
- unsigned long flags;
-
- dbg ("itd_submit");
+ int status;
+ unsigned long flags;
- /* set up one dma mapping for this urb */
- dma = pci_map_single (ehci->hcd.pdev,
- urb->transfer_buffer, urb->transfer_buffer_length,
- usb_pipein (urb->pipe)
- ? PCI_DMA_FROMDEVICE
- : PCI_DMA_TODEVICE);
- if (dma == 0)
- return -ENOMEM;
+ dbg ("itd_submit urb %p", urb);
+ /* NOTE DMA mapping assumes this ... */
+ if (urb->iso_frame_desc [0].offset != 0)
+ return -EINVAL;
+
/*
- * Schedule as needed. This is VERY optimistic about free
- * bandwidth! But the API assumes drivers can pick frames
- * intelligently (how?), so there's no other good option.
- *
- * FIXME this doesn't handle urb->next rings, or try to
- * use the iso periodicity.
+ * NOTE doing this for now, anticipating periodic URB models
+ * get updated to be "explicit resubmit".
*/
- if (urb->transfer_flags & USB_ISO_ASAP) {
- urb->start_frame = ehci_get_frame (&ehci->hcd);
- urb->start_frame++;
+ if (urb->next) {
+ dbg ("use explicit resubmit for ISO");
+ return -EINVAL;
}
- urb->start_frame %= ehci->periodic_size;
- /* create and populate itds (doing uframe scheduling) */
- spin_lock_irqsave (&ehci->lock, flags);
- for (frame_index = 0;
- frame_index < urb->number_of_packets;
- frame_index++) {
- itd = itd_make (ehci, urb, frame_index,
- urb->start_frame + frame_index,
- dma, SLAB_ATOMIC);
- if (itd) {
- if (first_itd)
- list_add_tail (&itd->itd_list,
- &first_itd->itd_list);
- else
- first_itd = itd;
- } else {
- spin_unlock_irqrestore (&ehci->lock, flags);
- if (first_itd) {
- while (!list_empty (&first_itd->itd_list)) {
- itd = list_entry (
- first_itd->itd_list.next,
- struct ehci_itd, itd_list);
- list_del (&itd->itd_list);
- itd_free (ehci, itd);
- }
- itd_free (ehci, first_itd);
- }
- pci_unmap_single (ehci->hcd.pdev,
- dma, urb->transfer_buffer_length,
- usb_pipein (urb->pipe)
- ? PCI_DMA_FROMDEVICE
- : PCI_DMA_TODEVICE);
- return -ENOMEM;
- }
- }
-
- /* stuff into the schedule */
- itd = first_itd;
- do {
- unsigned i;
-
- for (i = 0; i < 8; i++) {
- if (!itd->hw_transaction [i])
- continue;
- itd_link (ehci, itd->uframe + i, itd);
- }
- itd = list_entry (itd->itd_list.next,
- struct ehci_itd, itd_list);
- } while (itd != first_itd);
- urb->hcpriv = first_itd;
+ /* allocate ITDs w/o locking anything */
+ status = itd_urb_transaction (ehci, urb, mem_flags);
+ if (status < 0)
+ return status;
+ /* schedule ... need to lock */
+ spin_lock_irqsave (&ehci->lock, flags);
+ status = itd_schedule (ehci, urb);
spin_unlock_irqrestore (&ehci->lock, flags);
- return 0;
+ if (status < 0)
+ itd_free_list (ehci, urb);
+
+ return status;
}
+#ifdef have_split_iso
+
/*-------------------------------------------------------------------------*/
/*
@@ -827,7 +979,7 @@
* the TTs in USB 2.0 hubs.
*/
-static inline void
+static void
sitd_free (struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
pci_pool_free (ehci->sitd_pool, sitd, sitd->sitd_dma);
@@ -861,7 +1013,7 @@
}
-static inline void
+static void
sitd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_sitd *sitd)
{
u32 ptr;
@@ -894,12 +1046,11 @@
/*-------------------------------------------------------------------------*/
-static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb)
+static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb, int mem_flags)
{
// struct ehci_sitd *first_sitd = 0;
unsigned frame_index;
dma_addr_t dma;
- int mem_flags;
dbg ("NYI -- sitd_submit");
@@ -908,8 +1059,6 @@
// FIXME: setup one big dma mapping
dma = 0;
- mem_flags = SLAB_ATOMIC;
-
for (frame_index = 0;
frame_index < urb->number_of_packets;
frame_index++) {
@@ -941,54 +1090,63 @@
return -ENOSYS;
}
-
-#endif /* have_iso */
+#endif /* have_split_iso */
/*-------------------------------------------------------------------------*/
static void scan_periodic (struct ehci_hcd *ehci)
{
- unsigned frame;
- unsigned clock;
+ unsigned frame, clock, now_uframe, mod;
unsigned long flags;
+ mod = ehci->periodic_size << 3;
spin_lock_irqsave (&ehci->lock, flags);
/*
* When running, scan from last scan point up to "now"
+ * else clean up by scanning everything that's left.
* Touches as few pages as possible: cache-friendly.
- * It's safe to scan entries more than once, though.
+ * Don't scan ISO entries more than once, though.
*/
- if (HCD_IS_RUNNING (ehci->hcd.state)) {
- frame = ehci->next_frame;
- clock = ehci_get_frame (&ehci->hcd);
+ frame = ehci->next_uframe >> 3;
+ if (HCD_IS_RUNNING (ehci->hcd.state))
+ now_uframe = readl (&ehci->regs->frame_index);
+ else
+ now_uframe = (frame << 3) - 1;
+ now_uframe %= mod;
+ clock = now_uframe >> 3;
- /* when shutting down, scan everything for thoroughness */
- } else {
- frame = 0;
- clock = ehci->periodic_size - 1;
- }
for (;;) {
- union ehci_shadow q;
- u32 type;
+ union ehci_shadow q, *q_p;
+ u32 type, *hw_p;
+ unsigned uframes;
restart:
- q.ptr = ehci->pshadow [frame].ptr;
- type = Q_NEXT_TYPE (ehci->periodic [frame]);
+ /* scan schedule to _before_ current frame index */
+ if (frame == clock)
+ uframes = now_uframe & 0x07;
+ else
+ uframes = 8;
+
+ q_p = &ehci->pshadow [frame];
+ hw_p = &ehci->periodic [frame];
+ q.ptr = q_p->ptr;
+ type = Q_NEXT_TYPE (*hw_p);
/* scan each element in frame's queue for completions */
while (q.ptr != 0) {
int last;
+ unsigned uf;
union ehci_shadow temp;
switch (type) {
case Q_TYPE_QH:
last = (q.qh->hw_next == EHCI_LIST_END);
- flags = intr_complete (ehci, frame,
- qh_put (q.qh), flags);
- type = Q_NEXT_TYPE (q.qh->hw_next);
temp = q.qh->qh_next;
- qh_unput (ehci, q.qh);
+ type = Q_NEXT_TYPE (q.qh->hw_next);
+ flags = intr_complete (ehci, frame,
+ qh_get (q.qh), flags);
+ qh_put (ehci, q.qh);
q = temp;
break;
case Q_TYPE_FSTN:
@@ -1000,22 +1158,49 @@
dbg ("ignoring completions from FSTNs");
}
type = Q_NEXT_TYPE (q.fstn->hw_next);
- temp = q.fstn->fstn_next;
+ q = q.fstn->fstn_next;
break;
-#ifdef have_iso
case Q_TYPE_ITD:
last = (q.itd->hw_next == EHCI_LIST_END);
- flags = itd_complete (ehci, q.itd, flags);
- type = Q_NEXT_TYPE (q.itd->hw_next);
- q = q.itd->itd_next;
+
+ /* Unlink each (S)ITD we see, since the ISO
+ * URB model forces constant rescheduling.
+ * That complicates sharing uframes in ITDs,
+ * and means we need to skip uframes the HC
+ * hasn't yet processed.
+ */
+ for (uf = 0; uf < uframes; uf++) {
+ if (q.itd->hw_transaction [uf] != 0) {
+ temp = q;
+ *q_p = q.itd->itd_next;
+ *hw_p = q.itd->hw_next;
+ type = Q_NEXT_TYPE (*hw_p);
+
+ /* might free q.itd ... */
+ flags = itd_complete (ehci,
+ temp.itd, uf, flags);
+ break;
+ }
+ }
+ /* we might skip this ITD's uframe ... */
+ if (uf == uframes) {
+ q_p = &q.itd->itd_next;
+ hw_p = &q.itd->hw_next;
+ type = Q_NEXT_TYPE (q.itd->hw_next);
+ }
+
+ q = *q_p;
break;
+#ifdef have_split_iso
case Q_TYPE_SITD:
last = (q.sitd->hw_next == EHCI_LIST_END);
flags = sitd_complete (ehci, q.sitd, flags);
type = Q_NEXT_TYPE (q.sitd->hw_next);
+
+ // FIXME unlink SITD after split completes
q = q.sitd->sitd_next;
break;
-#endif /* have_iso */
+#endif /* have_split_iso */
default:
dbg ("corrupt type %d frame %d shadow %p",
type, frame, q.ptr);
@@ -1044,13 +1229,16 @@
if (!HCD_IS_RUNNING (ehci->hcd.state))
break;
- ehci->next_frame = clock;
- now = ehci_get_frame (&ehci->hcd);
- if (clock == now)
+ ehci->next_uframe = now_uframe;
+ now = readl (&ehci->regs->frame_index) % mod;
+ if (now_uframe == now)
break;
- clock = now;
- } else if (++frame >= ehci->periodic_size)
- frame = 0;
+
+ /* rescan the rest of this frame, then ... */
+ now_uframe = now;
+ clock = now_uframe >> 3;
+ } else
+ frame = (frame + 1) % ehci->periodic_size;
}
spin_unlock_irqrestore (&ehci->lock, flags);
- }
+}
diff -Nru a/drivers/usb/hcd/ehci.h b/drivers/usb/hcd/ehci.h
--- a/drivers/usb/hcd/ehci.h Fri May 3 14:51:44 2002
+++ b/drivers/usb/hcd/ehci.h Fri May 3 14:51:44 2002
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001 by David Brownell
+ * Copyright (c) 2001-2002 by David Brownell
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -49,7 +49,7 @@
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
- int next_frame; /* scan periodic, start here */
+ int next_uframe; /* scan periodic, start here */
unsigned periodic_urbs; /* how many urbs scheduled? */
/* deferred work from IRQ, etc */
@@ -62,6 +62,7 @@
struct usb_hcd hcd;
struct ehci_caps *caps;
struct ehci_regs *regs;
+ u32 hcs_params; /* cached register copy */
/* per-HC memory pools (could be per-PCI-bus, but ...) */
struct pci_pool *qh_pool; /* qh per active urb */
@@ -324,13 +325,14 @@
union ehci_shadow itd_next; /* ptr to periodic q entry */
struct urb *urb;
- unsigned index; /* in urb->iso_frame_desc */
struct list_head itd_list; /* list of urb frames' itds */
dma_addr_t buf_dma; /* frame's buffer address */
- unsigned uframe; /* in periodic schedule */
- u32 transaction [8]; /* copy of hw_transaction */
-
+ /* for now, only one hw_transaction per itd */
+ u32 transaction;
+ u16 index; /* in urb->iso_frame_desc */
+ u16 uframe; /* in periodic schedule */
+ u16 usecs;
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
_______________________________________________________________
Have big pipes? SourceForge.net is looking for download mirrors. We supply
the hardware. You get the recognition. Email Us: [EMAIL PROTECTED]
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel