ChangeSet 1.974, 2003/01/31 15:25:48+11:00, [EMAIL PROTECTED]
[PATCH] USB: ehci-hcd updates
This should apply to 2.5.59 too. It seems to get rid of some pesky
hangs, on at least some hardware, but I won't have time to test it
on either VIA version ... maybe someone else will make the time? :)
New QH state prevents a re-activation race
- nobody can un-halt a qh before its cleanup is done
- resubmit-from-completion had this race (some usbtest cases)
as could some normal submit paths on busy endpoints (storage)
- faster controllers would trip on this more consistently
Queues of qtds
- work harder to avoid ever modifing any qh in software
- short reads block queue advance much less often
- be more cautious with large (>~19KB) unaligned buffers
Unlinking urbs
- if qtd unlinked is at queue head, use its latest status
(main effect is reporting bytes from partial transfers)
- another new qh state: defer qh unlink if IAA is busy
(eliminates a busy-wait loop in a rare scenario)
Enable features to improve bus utilization
- PCI MWI ... can produce better write throughput; and by
using right cacheline size, sometimes read throughput too
- USB NAK throttle ... sometimes reduces PCI access rates
Other
- async dump shows more funky qh+qtd states, and NAK count
- cope with with some of the sprintf wierdness
- periodic dump is usually smaller (so is that schedule)
- minor cleanups
diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c Tue Feb 4 15:17:33 2003
+++ b/drivers/usb/host/ehci-dbg.c Tue Feb 4 15:17:33 2003
@@ -277,7 +277,26 @@
default: tmp = '?'; break; \
}; tmp; })
-static void qh_lines (struct ehci_qh *qh, char **nextp, unsigned *sizep)
+static inline char token_mark (u32 token)
+{
+ token = le32_to_cpu (token);
+ if (token & QTD_STS_ACTIVE)
+ return '*';
+ if (token & QTD_STS_HALT)
+ return '-';
+ if (QTD_PID (token) != 1 /* not IN: OUT or SETUP */
+ || QTD_LENGTH (token) == 0)
+ return ' ';
+ /* tries to advance through hw_alt_next */
+ return '/';
+}
+
+static void qh_lines (
+ struct ehci_hcd *ehci,
+ struct ehci_qh *qh,
+ char **nextp,
+ unsigned *sizep
+)
{
u32 scratch;
u32 hw_curr;
@@ -286,26 +305,49 @@
unsigned temp;
unsigned size = *sizep;
char *next = *nextp;
+ char mark;
+ mark = token_mark (qh->hw_token);
+ if (mark == '/') { /* qh_alt_next controls qh advance? */
+ if ((qh->hw_alt_next & QTD_MASK) == ehci->async->hw_alt_next)
+ mark = '#'; /* blocked */
+ else if (qh->hw_alt_next & cpu_to_le32 (0x01))
+ mark = '.'; /* use hw_qtd_next */
+ /* else alt_next points to some other qtd */
+ }
scratch = cpu_to_le32p (&qh->hw_info1);
- hw_curr = cpu_to_le32p (&qh->hw_current);
+ hw_curr = (mark == '*') ? cpu_to_le32p (&qh->hw_current) : 0;
temp = snprintf (next, size,
- "qh/%p dev%d %cs ep%d %08x %08x (%08x %08x)",
+ "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f,
scratch, cpu_to_le32p (&qh->hw_info2),
- hw_curr, cpu_to_le32p (&qh->hw_token));
+ cpu_to_le32p (&qh->hw_token), mark,
+ (cpu_to_le32 (0x8000000) & qh->hw_token)
+ ? "data0" : "data1",
+ (cpu_to_le32p (&qh->hw_alt_next) >> 1) & 0x0f);
size -= temp;
next += temp;
+ /* hc may be modifying the list as we read it ... */
list_for_each (entry, &qh->qtd_list) {
td = list_entry (entry, struct ehci_qtd, qtd_list);
scratch = cpu_to_le32p (&td->hw_token);
+ mark = ' ';
+ if (hw_curr == td->qtd_dma)
+ mark = '*';
+ else if (qh->hw_qtd_next == td->qtd_dma)
+ mark = '+';
+ else if (QTD_LENGTH (scratch)) {
+ if (td->hw_alt_next == ehci->async->hw_alt_next)
+ mark = '#';
+ else if (td->hw_alt_next != EHCI_LIST_END)
+ mark = '/';
+ }
temp = snprintf (next, size,
- "\n\t%std/%p %s len=%d %08x urb %p",
- (hw_curr == td->qtd_dma) ? "*" : "",
- td, ({ char *tmp;
+ "\n\t%p%c%s len=%d %08x urb %p",
+ td, mark, ({ char *tmp;
switch ((scratch>>8)&0x03) {
case 0: tmp = "out"; break;
case 1: tmp = "in"; break;
@@ -315,13 +357,27 @@
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
+ if (temp < 0)
+ temp = 0;
+ else if (size < temp)
+ temp = size;
size -= temp;
next += temp;
+ if (temp == size)
+ goto done;
}
temp = snprintf (next, size, "\n");
- *sizep = size - temp;
- *nextp = next + temp;
+ if (temp < 0)
+ temp = 0;
+ else if (size < temp)
+ temp = size;
+ size -= temp;
+ next += temp;
+
+done:
+ *sizep = size;
+ *nextp = next;
}
static ssize_t
@@ -344,14 +400,15 @@
* one QH per line, and TDs we know about
*/
spin_lock_irqsave (&ehci->lock, flags);
- for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh)
- qh_lines (qh, &next, &size);
- if (ehci->reclaim) {
+ for (qh = ehci->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh)
+ qh_lines (ehci, qh, &next, &size);
+ if (ehci->reclaim && size > 0) {
temp = snprintf (next, size, "\nreclaim =\n");
size -= temp;
next += temp;
- qh_lines (ehci->reclaim, &next, &size);
+ for (qh = ehci->reclaim; size > 0 && qh; qh = qh->reclaim)
+ qh_lines (ehci, qh, &next, &size);
}
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -421,7 +478,7 @@
scratch & 0x007f,
(scratch >> 8) & 0x000f,
p.qh->usecs, p.qh->c_usecs,
- scratch >> 16);
+ 0x7ff & (scratch >> 16));
/* FIXME TD info too */
@@ -490,7 +547,8 @@
/* Capability Registers */
i = readw (&ehci->caps->hci_version);
- temp = snprintf (next, size, "EHCI %x.%02x, hcd state %d\n",
+ temp = snprintf (next, size,
+ "EHCI %x.%02x, hcd state %d (version " DRIVER_VERSION ")\n",
i >> 8, i & 0x0ff, ehci->hcd.state);
size -= temp;
next += temp;
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c Tue Feb 4 15:17:33 2003
+++ b/drivers/usb/host/ehci-hcd.c Tue Feb 4 15:17:33 2003
@@ -94,7 +94,7 @@
* 2001-June Works with usb-storage and NEC EHCI on 2.4
*/
-#define DRIVER_VERSION "2002-Nov-29"
+#define DRIVER_VERSION "2003-Jan-22"
#define DRIVER_AUTHOR "David Brownell"
#define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
@@ -110,10 +110,11 @@
/* magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
-#define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */
+#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
+#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
#define EHCI_WATCHDOG_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
@@ -416,13 +417,26 @@
ehci_info (ehci, "enabled 64bit PCI DMA\n");
}
+ /* help hc dma work well with cachelines */
+ pci_set_mwi (ehci->hcd.pdev);
+
/* clear interrupt enables, set irq latency */
temp = readl (&ehci->regs->command) & 0xff;
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp |= 1 << (16 + log2_irq_thresh);
// if hc can park (ehci >= 0.96), default is 3 packets per async QH
- // keeping default periodic framelist size
+ if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
+ /* periodic schedule size can be smaller than default */
+ temp &= ~(3 << 2);
+ temp |= (EHCI_TUNE_FLS << 2);
+ switch (EHCI_TUNE_FLS) {
+ case 0: ehci->periodic_size = 1024; break;
+ case 1: ehci->periodic_size = 512; break;
+ case 2: ehci->periodic_size = 256; break;
+ default: BUG ();
+ }
+ }
temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
// Philips, Intel, and maybe others need CMD_RUN before the
// root hub will detect new devices (why?); NEC doesn't
@@ -759,7 +773,6 @@
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct ehci_qh *qh;
unsigned long flags;
- int maybe_irq = 1;
spin_lock_irqsave (&ehci->lock, flags);
switch (usb_pipetype (urb->pipe)) {
@@ -769,23 +782,23 @@
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
- while (qh->qh_state == QH_STATE_LINKED
+
+ /* if we need to use IAA and it's busy, defer */
+ if (qh->qh_state == QH_STATE_LINKED
&& ehci->reclaim
&& HCD_IS_RUNNING (ehci->hcd.state)
) {
- spin_unlock_irqrestore (&ehci->lock, flags);
+ struct ehci_qh *last;
- if (maybe_irq) {
- if (in_interrupt ())
- return -EAGAIN;
- maybe_irq = 0;
- }
- /* let pending unlinks complete, so this can start */
- wait_ms (1);
+ for (last = ehci->reclaim;
+ last->reclaim;
+ last = last->reclaim)
+ continue;
+ qh->qh_state = QH_STATE_UNLINK_WAIT;
+ last->reclaim = qh;
- spin_lock_irqsave (&ehci->lock, flags);
- }
- if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
+ /* bypass IAA if the hc can't care */
+ } else if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
end_unlink_async (ehci, NULL);
/* something else might have unlinked the qh by now */
diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c Tue Feb 4 15:17:33 2003
+++ b/drivers/usb/host/ehci-mem.c Tue Feb 4 15:17:33 2003
@@ -75,8 +75,6 @@
qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
if (qtd != 0) {
ehci_qtd_init (qtd, dma);
- if (ehci->async)
- qtd->hw_alt_next = ehci->async->hw_alt_next;
}
return qtd;
}
diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
--- a/drivers/usb/host/ehci-q.c Tue Feb 4 15:17:33 2003
+++ b/drivers/usb/host/ehci-q.c Tue Feb 4 15:17:33 2003
@@ -43,7 +43,8 @@
/* fill a qtd, returning how much of the buffer we were able to queue up */
static int
-qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token)
+qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len,
+ int token, int maxpacket)
{
int i, count;
u64 addr = buf;
@@ -69,6 +70,10 @@
else
count = len;
}
+
+ /* short packets may only terminate transfers */
+ if (count != len)
+ count -= (count % maxpacket);
}
qtd->hw_token = cpu_to_le32 ((count << 16) | token);
qtd->length = count;
@@ -85,7 +90,7 @@
{
qh->hw_current = 0;
qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
- qh->hw_alt_next = ehci->async->hw_alt_next;
+ qh->hw_alt_next = EHCI_LIST_END;
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
@@ -96,7 +101,7 @@
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
-static inline void qtd_copy_status (
+static void qtd_copy_status (
struct ehci_hcd *ehci,
struct urb *urb,
size_t length,
@@ -224,12 +229,24 @@
{
struct ehci_qtd *last = 0, *end = qh->dummy;
struct list_head *entry, *tmp;
- int stopped = 0;
+ int stopped;
unsigned count = 0;
+ int do_status = 0;
+ u8 state;
if (unlikely (list_empty (&qh->qtd_list)))
return count;
+ /* completions (or tasks on other cpus) must never clobber HALT
+ * till we've gone through and cleaned everything up, even when
+ * they add urbs to this qh's queue or mark them for unlinking.
+ *
+ * NOTE: unlinking expects to be done in queue order.
+ */
+ state = qh->qh_state;
+ qh->qh_state = QH_STATE_COMPLETING;
+ stopped = (state == QH_STATE_IDLE);
+
/* remove de-activated QTDs from front of queue.
* after faults (including short reads), cleanup this urb
* then let the queue advance.
@@ -261,7 +278,6 @@
rmb ();
token = le32_to_cpu (qtd->hw_token);
stopped = stopped
- || (qh->qh_state == QH_STATE_IDLE)
|| (HALT_BIT & qh->hw_token) != 0
|| (ehci->hcd.state == USB_STATE_HALT);
@@ -271,36 +287,53 @@
/* magic dummy for short reads; won't advance */
if (IS_SHORT_READ (token)
&& !(token & QTD_STS_HALT)
- && ehci->async->hw_alt_next
- == qh->hw_alt_next)
+ && (qh->hw_alt_next & QTD_MASK)
+ == ehci->async->hw_alt_next) {
+ stopped = 1;
goto halt;
+ }
/* stop scanning when we reach qtds the hc is using */
} else if (likely (!stopped)) {
- last = 0;
break;
} else {
- /* ignore active qtds unless some previous qtd
+ /* ignore active urbs unless some previous qtd
* for the urb faulted (including short read) or
* its urb was canceled. we may patch qh or qtds.
*/
- if ((token & QTD_STS_ACTIVE)
- && urb->status == -EINPROGRESS) {
- last = 0;
+ if (likely (urb->status == -EINPROGRESS))
+ continue;
+
+ /* issue status after short control reads */
+ if (unlikely (do_status != 0)
+ && QTD_PID (token) == 0 /* OUT */) {
+ do_status = 0;
continue;
}
+
+ /* token in overlay may be most current */
+ if (state == QH_STATE_IDLE
+ && cpu_to_le32 (qtd->qtd_dma)
+ == qh->hw_current)
+ token = le32_to_cpu (qh->hw_token);
+
+ /* force halt for unlinked or blocked qh, so we'll
+ * patch the qh later and so that completions can't
+ * activate it while we "know" it's stopped.
+ */
if ((HALT_BIT & qh->hw_token) == 0) {
halt:
qh->hw_token |= HALT_BIT;
wmb ();
- stopped = 1;
}
}
/* remove it from the queue */
spin_lock (&urb->lock);
qtd_copy_status (ehci, urb, qtd->length, token);
+ do_status = (urb->status == -EREMOTEIO)
+ && usb_pipecontrol (urb->pipe);
spin_unlock (&urb->lock);
if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
@@ -319,6 +352,9 @@
ehci_qtd_free (ehci, last);
}
+ /* restore original state; caller must unlink or relink */
+ qh->qh_state = state;
+
/* update qh after fault cleanup */
if (unlikely ((HALT_BIT & qh->hw_token) != 0)) {
qh_update (ehci, qh,
@@ -367,7 +403,7 @@
struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf;
int len, maxpacket;
- int is_input, status_patch = 0;
+ int is_input;
u32 token;
/*
@@ -388,7 +424,7 @@
if (usb_pipecontrol (urb->pipe)) {
/* SETUP pid */
qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
- token | (2 /* "setup" */ << 8));
+ token | (2 /* "setup" */ << 8), 8);
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
@@ -399,10 +435,6 @@
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
-
- if (len > 0 && is_input
- && !(urb->transfer_flags & URB_SHORT_NOT_OK))
- status_patch = 1;
}
/*
@@ -413,6 +445,7 @@
else
buf = 0;
+ // FIXME this 'buf' check break some zlps...
if (!buf || is_input)
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
@@ -427,9 +460,11 @@
for (;;) {
int this_qtd_len;
- this_qtd_len = qtd_fill (qtd, buf, len, token);
+ this_qtd_len = qtd_fill (qtd, buf, len, token, maxpacket);
len -= this_qtd_len;
buf += this_qtd_len;
+ if (is_input)
+ qtd->hw_alt_next = ehci->async->hw_alt_next;
/* qh makes control packets use qtd toggle; maybe switch it */
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
@@ -447,6 +482,13 @@
list_add_tail (&qtd->qtd_list, head);
}
+ /* unless the bulk/interrupt caller wants a chance to clean
+ * up after short reads, hc should advance qh past this urb
+ */
+ if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0
+ || usb_pipecontrol (urb->pipe)))
+ qtd->hw_alt_next = EHCI_LIST_END;
+
/*
* control requests may need a terminating data "status" ack;
* bulk ones may need a terminating short packet (zero length).
@@ -473,23 +515,10 @@
list_add_tail (&qtd->qtd_list, head);
/* never any data in such packets */
- qtd_fill (qtd, 0, 0, token);
+ qtd_fill (qtd, 0, 0, token, 0);
}
}
- /* if we're permitting a short control read, we want the hardware to
- * just continue after short data and send the status ack. it can do
- * that on the last data packet (typically the only one). for other
- * packets, software fixup is needed (in qh_completions).
- */
- if (status_patch) {
- struct ehci_qtd *prev;
-
- prev = list_entry (qtd->qtd_list.prev,
- struct ehci_qtd, qtd_list);
- prev->hw_alt_next = QTD_NEXT (qtd->qtd_dma);
- }
-
/* by default, enable interrupt on urb completion */
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
@@ -611,7 +640,8 @@
case USB_SPEED_FULL:
/* EPS 0 means "full" */
- info1 |= (EHCI_TUNE_RL_TT << 28);
+ if (type != PIPE_INTERRUPT)
+ info1 |= (EHCI_TUNE_RL_TT << 28);
if (type == PIPE_CONTROL) {
info1 |= (1 << 27); /* for TT */
info1 |= 1 << 14; /* toggle from qtd */
@@ -628,12 +658,13 @@
case USB_SPEED_HIGH: /* no TT involved */
info1 |= (2 << 12); /* EPS "high" */
- info1 |= (EHCI_TUNE_RL_HS << 28);
if (type == PIPE_CONTROL) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else if (type == PIPE_BULK) {
+ info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else { /* PIPE_INTERRUPT */
@@ -769,8 +800,7 @@
&& !usb_pipecontrol (urb->pipe)) {
/* "never happens": drivers do stall cleanup right */
if (qh->qh_state != QH_STATE_IDLE
- && (cpu_to_le32 (QTD_STS_HALT)
- & qh->hw_token) == 0)
+ && qh->qh_state != QH_STATE_COMPLETING)
ehci_warn (ehci, "clear toggle dev%d "
"ep%d%s: not idle\n",
usb_pipedevice (urb->pipe),
@@ -809,7 +839,6 @@
__list_splice (qtd_list, qh->qtd_list.prev);
ehci_qtd_init (qtd, qtd->qtd_dma);
- qtd->hw_alt_next = ehci->async->hw_alt_next;
qh->dummy = qtd;
/* hc must see the new dummy at list end */
@@ -877,9 +906,12 @@
/* the async qh for the qtds being reclaimed are now unlinked from the HC */
+static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh);
+
static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
{
struct ehci_qh *qh = ehci->reclaim;
+ struct ehci_qh *next;
del_timer (&ehci->watchdog);
@@ -890,6 +922,10 @@
ehci->reclaim = 0;
ehci->reclaim_ready = 0;
+ /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
+ next = qh->reclaim;
+ qh->reclaim = 0;
+
qh_completions (ehci, qh, regs);
if (!list_empty (&qh->qtd_list)
@@ -909,6 +945,9 @@
jiffies + EHCI_ASYNC_JIFFIES);
}
}
+
+ if (next)
+ start_unlink_async (ehci, next);
}
/* makes sure the async qh will become idle */
@@ -921,7 +960,8 @@
#ifdef DEBUG
if (ehci->reclaim
- || qh->qh_state != QH_STATE_LINKED
+ || (qh->qh_state != QH_STATE_LINKED
+ && qh->qh_state != QH_STATE_UNLINK_WAIT)
#ifdef CONFIG_SMP
// this macro lies except on SMP compiles
|| !spin_is_locked (&ehci->lock)
@@ -953,6 +993,9 @@
wmb ();
if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+ /* if (unlikely (qh->reclaim != 0))
+ * this will recurse, probably not much
+ */
end_unlink_async (ehci, NULL);
return;
}
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h Tue Feb 4 15:17:33 2003
+++ b/drivers/usb/host/ehci.h Tue Feb 4 15:17:33 2003
@@ -236,12 +236,12 @@
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
-
- /* dma same in urb's qtds, except 1st control qtd (setup buffer) */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __attribute__ ((aligned (32)));
+#define QTD_MASK cpu_to_le32 (~0x1f) /* mask NakCnt+T in qh->hw_alt_next */
+
/*-------------------------------------------------------------------------*/
/* type tag from {qh,itd,sitd,fstn}->hw_next */
@@ -305,6 +305,7 @@
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct ehci_qtd *dummy;
+ struct ehci_qh *reclaim; /* next to reclaim */
atomic_t refcount;
unsigned stamp;
@@ -313,6 +314,8 @@
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
+#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
+#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
-------------------------------------------------------
This SF.NET email is sponsored by:
SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See!
http://www.vasoftware.com
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel