patch-2.4.10 linux/drivers/usb/usb-uhci.c
Next file: linux/drivers/usb/usb-uhci.h
Previous file: linux/drivers/usb/usb-skeleton.c
Back to the patch index
Back to the overall index
- Lines: 1405
- Date:
Tue Sep 18 14:10:43 2001
- Orig file:
v2.4.9/linux/drivers/usb/usb-uhci.c
- Orig date:
Mon Aug 27 12:41:45 2001
diff -u --recursive --new-file v2.4.9/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
@@ -1,7 +1,7 @@
/*
* Universal Host Controller Interface driver for USB (take II).
*
- * (c) 1999-2000 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
+ * (c) 1999-2001 Georg Acher, acher@in.tum.de (executive slave) (base guitar)
* Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
* Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
* Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter)
@@ -16,7 +16,7 @@
* (C) Copyright 1999 Randy Dunlap
* (C) Copyright 1999 Gregory P. Smith
*
- * $Id: usb-uhci.c,v 1.259 2001/03/30 14:51:59 acher Exp $
+ * $Id: usb-uhci.c,v 1.268 2001/08/29 14:08:43 acher Exp $
*/
#include <linux/config.h>
@@ -52,7 +52,7 @@
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
-#define VERSTR "$Revision: 1.259 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.268 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
#include "usb-uhci.h"
@@ -61,7 +61,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v1.251"
+#define DRIVER_VERSION "v1.268"
#define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
#define DRIVER_DESC "USB Universal Host Controller Interface driver"
@@ -88,13 +88,34 @@
#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+/* CONFIG_USB_UHCI_HIGH_BANDWITH turns on Full Speed Bandwidth
+ * Reclamation: feature that puts loop on descriptor loop when
+ * there's some transfer going on. With FSBR, USB performance
+ * is optimal, but PCI can be slowed down up-to 5 times, slowing down
+ * system performance (eg. framebuffer devices).
+ */
#define CONFIG_USB_UHCI_HIGH_BANDWIDTH
+
+/* *_DEPTH_FIRST puts descriptor in depth-first mode. This has
+ * somehow similar effect to FSBR (higher speed), but does not
+ * slow PCI down. OTOH USB performace is slightly slower than
+ * in FSBR case and single device could hog whole USB, starving
+ * other devices.
+ */
#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
+/* Turning off both CONFIG_USB_UHCI_HIGH_BANDWITH and *_DEPTH_FIRST
+ * will lead to <64KB/sec performance over USB for bulk transfers targeting
+ * one device's endpoint. You probably do not want to do that.
+ */
+
// stop bandwidth reclamation after (roughly) 50ms
#define IDLE_TIMEOUT (HZ/20)
+// Suppress HC interrupt error messages for 5s
+#define ERROR_SUPPRESSION_TIME (HZ*5)
+
_static int rh_submit_urb (urb_t *urb);
_static int rh_unlink_urb (urb_t *urb);
_static int delete_qh (uhci_t *s, uhci_desc_t *qh);
@@ -133,20 +154,15 @@
_static void uhci_switch_timer_int(uhci_t *s)
{
- if (!list_empty(&s->urb_unlinked)) {
- s->td1ms->hw.td.status |= cpu_to_le32(TD_CTRL_IOC);
- }
- else {
- s->td1ms->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC);
- }
-
- if (s->timeout_urbs) {
- s->td32ms->hw.td.status |= cpu_to_le32(TD_CTRL_IOC);
- }
- else {
- s->td32ms->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC);
- }
+ if (!list_empty(&s->urb_unlinked))
+ set_td_ioc(s->td1ms);
+ else
+ clr_td_ioc(s->td1ms);
+ if (s->timeout_urbs)
+ set_td_ioc(s->td32ms);
+ else
+ clr_td_ioc(s->td32ms);
wmb();
}
/*-------------------------------------------------------------------*/
@@ -199,6 +215,7 @@
enable_desc_loop(s, urb);
}
#endif
+ urb->status = -EINPROGRESS;
((urb_priv_t*)urb->hcpriv)->started=jiffies;
list_add (p, &s->urb_list);
if (urb->timeout)
@@ -241,7 +258,7 @@
return -ENOMEM;
memset (*new, 0, sizeof (uhci_desc_t));
(*new)->dma_addr = dma_handle;
- (*new)->hw.td.link = cpu_to_le32(UHCI_PTR_TERM | (flags & UHCI_PTR_BITS)); // last by default
+ set_td_link((*new), UHCI_PTR_TERM | (flags & UHCI_PTR_BITS)); // last by default
(*new)->type = TD_TYPE;
mb();
INIT_LIST_HEAD (&(*new)->vertical);
@@ -257,7 +274,7 @@
spin_lock_irqsave (&s->td_lock, xxx);
- td->hw.td.link = cpu_to_le32(qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
+ set_td_link(td, qh->dma_addr | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH);
mb();
spin_unlock_irqrestore (&s->td_lock, xxx);
@@ -277,11 +294,11 @@
if (qh == prev ) {
// virgin qh without any tds
- qh->hw.qh.element = cpu_to_le32(new->dma_addr | UHCI_PTR_TERM);
+ set_qh_element(qh, new->dma_addr | UHCI_PTR_TERM);
}
else {
// already tds inserted, implicitely remove TERM bit of prev
- prev->hw.td.link = cpu_to_le32(new->dma_addr | (flags & UHCI_PTR_DEPTH));
+ set_td_link(prev, new->dma_addr | (flags & UHCI_PTR_DEPTH));
}
mb();
spin_unlock_irqrestore (&s->td_lock, xxx);
@@ -300,7 +317,7 @@
next = list_entry (td->horizontal.next, uhci_desc_t, horizontal);
list_add (&new->horizontal, &td->horizontal);
new->hw.td.link = td->hw.td.link;
- td->hw.td.link = cpu_to_le32(new->dma_addr);
+ set_td_link(td, new->dma_addr);
mb();
spin_unlock_irqrestore (&s->td_lock, flags);
@@ -361,8 +378,8 @@
return -ENOMEM;
memset (*new, 0, sizeof (uhci_desc_t));
(*new)->dma_addr = dma_handle;
- (*new)->hw.qh.head = cpu_to_le32(UHCI_PTR_TERM);
- (*new)->hw.qh.element = cpu_to_le32(UHCI_PTR_TERM);
+ set_qh_head(*new, UHCI_PTR_TERM);
+ set_qh_element(*new, UHCI_PTR_TERM);
(*new)->type = QH_TYPE;
mb();
@@ -387,16 +404,16 @@
// (OLD) (POS) -> (OLD) (NEW) (POS)
old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal);
list_add_tail (&new->horizontal, &pos->horizontal);
- new->hw.qh.head = cpu_to_le32(MAKE_QH_ADDR (pos)) ;
+ set_qh_head(new, MAKE_QH_ADDR (pos)) ;
if (!(old->hw.qh.head & cpu_to_le32(UHCI_PTR_TERM)))
- old->hw.qh.head = cpu_to_le32(MAKE_QH_ADDR (new)) ;
+ set_qh_head(old, MAKE_QH_ADDR (new)) ;
}
else {
// (POS) (OLD) -> (POS) (NEW) (OLD)
old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal);
list_add (&new->horizontal, &pos->horizontal);
- new->hw.qh.head = cpu_to_le32(MAKE_QH_ADDR (old));
- pos->hw.qh.head = cpu_to_le32(MAKE_QH_ADDR (new)) ;
+ set_qh_head(new, MAKE_QH_ADDR (old));
+ set_qh_head(pos, MAKE_QH_ADDR (new)) ;
}
mb ();
@@ -522,7 +539,7 @@
if (s->ls_control_chain)
delete_desc (s, s->ls_control_chain);
if (s->control_chain)
- delete_desc(s, s->control_chain);
+ delete_desc (s, s->control_chain);
if (s->bulk_chain)
delete_desc (s, s->bulk_chain);
if (s->chain_end)
@@ -576,24 +593,20 @@
dbg("allocating iso descs");
for (n = 0; n < 1024; n++) {
// allocate skeleton iso/irq-tds
- ret = alloc_td (s, &td, 0);
- if (ret)
+ if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
+
s->iso_td[n] = td;
s->framelist[n] = cpu_to_le32((__u32) td->dma_addr);
}
dbg("allocating qh: chain_end");
- ret = alloc_qh (s, &qh);
-
- if (ret)
+ if (alloc_qh (s, &qh))
goto init_skel_cleanup;
s->chain_end = qh;
- ret = alloc_td (s, &td, 0);
-
- if (ret)
+ if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand)
@@ -602,8 +615,7 @@
s->td1ms=td;
dbg("allocating qh: bulk_chain");
- ret = alloc_qh (s, &qh);
- if (ret)
+ if (alloc_qh (s, &qh))
goto init_skel_cleanup;
insert_qh (s, s->chain_end, qh, 0);
@@ -619,12 +631,11 @@
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
// disabled reclamation loop
- s->chain_end->hw.qh.head = cpu_to_le32(s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
+ set_qh_head(s->chain_end, s->control_chain->dma_addr | UHCI_PTR_QH | UHCI_PTR_TERM);
#endif
dbg("allocating qh: ls_control_chain");
- ret = alloc_qh (s, &qh);
- if (ret)
+ if (alloc_qh (s, &qh))
goto init_skel_cleanup;
insert_qh (s, s->control_chain, qh, 0);
@@ -638,15 +649,15 @@
for (n = 0; n < 8; n++) {
uhci_desc_t *td;
- alloc_td (s, &td, 0);
- if (!td)
+ if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
+
s->int_chain[n] = td;
if (n == 0) {
- s->int_chain[0]->hw.td.link = cpu_to_le32(s->ls_control_chain->dma_addr | UHCI_PTR_QH);
+ set_td_link(s->int_chain[0], s->ls_control_chain->dma_addr | UHCI_PTR_QH);
}
else {
- s->int_chain[n]->hw.td.link = cpu_to_le32(s->int_chain[0]->dma_addr);
+ set_td_link(s->int_chain[n], s->int_chain[0]->dma_addr);
}
}
@@ -661,16 +672,13 @@
else
for (o = 1, m = 2; m <= 128; o++, m += m)
if ((n & (m - 1)) == ((m - 1) / 2))
- ((uhci_desc_t*) s->iso_td[n])->hw.td.link =
- cpu_to_le32(s->int_chain[o]->dma_addr);
+ set_td_link(((uhci_desc_t*) s->iso_td[n]), s->int_chain[o]->dma_addr);
}
- ret = alloc_td (s, &td, 0);
-
- if (ret)
+ if (alloc_td (s, &td, 0))
goto init_skel_cleanup;
- fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt
+ fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt (activated later)
s->td32ms=td;
insert_td_horizontal (s, s->int_chain[5], td);
@@ -700,20 +708,12 @@
char *data;
int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method
- if (!maxsze) {
- err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
- return -EINVAL;
- }
-
dbg("uhci_submit_control start");
- alloc_qh (s, &qh); // alloc qh for this request
-
- if (!qh)
+ if (alloc_qh (s, &qh)) // alloc qh for this request
return -ENOMEM;
- alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first); // get td for setup stage
-
- if (!td) {
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first)) // get td for setup stage
+ {
delete_qh (s, qh);
return -ENOMEM;
}
@@ -749,8 +749,7 @@
while (len > 0) {
int pktsze = len;
- alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first);
- if (!td)
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
goto fail_unmap_enomem;
if (pktsze > maxsze)
@@ -768,7 +767,7 @@
len -= pktsze;
}
- /* Build the final TD for control status */
+ /* Build the final TD for control status */
/* It's only IN if the pipe is out AND we aren't expecting data */
destination &= ~UHCI_PID;
@@ -780,9 +779,7 @@
destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */
- alloc_td (s, &td, UHCI_PTR_DEPTH);
-
- if (!td)
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH))
goto fail_unmap_enomem;
status &=~TD_CTRL_SPD;
@@ -795,7 +792,6 @@
list_add (&qh->desc_list, &urb_priv->desc_list);
- urb->status = -EINPROGRESS;
queue_urb (s, urb); // queue before inserting in desc chain
qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM);
@@ -821,7 +817,7 @@
_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
- urb_priv_t *urb_priv = urb->hcpriv;
+ urb_priv_t *urb_priv = urb->hcpriv, *upriv, *bpriv=NULL;
uhci_desc_t *qh, *td, *nqh=NULL, *bqh=NULL, *first_td=NULL;
unsigned long destination, status;
char *data;
@@ -829,33 +825,22 @@
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len, last;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
- urb_priv_t *upriv, *bpriv=NULL;
if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
return -EPIPE;
- if (urb->transfer_buffer_length < 0) {
- err("Negative transfer length in submit_bulk");
- return -EINVAL;
- }
-
- if (!maxsze)
- return -EMSGSIZE;
-
queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
upriv = (urb_priv_t*)urb->hcpriv;
if (!bulk_urb) {
- alloc_qh (s, &qh); // get qh for this request
-
- if (!qh)
+ if (alloc_qh (s, &qh)) // get qh for this request
return -ENOMEM;
if (urb->transfer_flags & USB_QUEUE_BULK) {
- alloc_qh(s, &nqh); // placeholder for clean unlink
- if (!nqh) {
+ if (alloc_qh(s, &nqh)) // placeholder for clean unlink
+ {
delete_desc (s, qh);
return -ENOMEM;
}
@@ -872,17 +857,16 @@
}
if (urb->transfer_flags & USB_QUEUE_BULK) {
- alloc_qh (s, &bqh); // "bottom" QH,
-
- if (!bqh) {
+ if (alloc_qh (s, &bqh)) // "bottom" QH
+ {
if (!bulk_urb) {
delete_desc(s, qh);
delete_desc(s, nqh);
}
return -ENOMEM;
}
- bqh->hw.qh.element = cpu_to_le32(UHCI_PTR_TERM);
- bqh->hw.qh.head = cpu_to_le32(nqh->dma_addr | UHCI_PTR_QH); // element
+ set_qh_element(bqh, UHCI_PTR_TERM);
+ set_qh_head(bqh, nqh->dma_addr | UHCI_PTR_QH); // element
upriv->bottom_qh = bqh;
}
queue_dbg("uhci_submit_bulk: qh %p bqh %p nqh %p",qh, bqh, nqh);
@@ -901,9 +885,8 @@
do { // TBD: Really allow zero-length packets?
int pktsze = len;
- alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first);
-
- if (!td) {
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH * depth_first))
+ {
delete_qh (s, qh);
return -ENOMEM;
}
@@ -924,7 +907,7 @@
last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || !(urb->transfer_flags & USB_ZERO_PACKET)));
if (last)
- td->hw.td.status |= cpu_to_le32(TD_CTRL_IOC); // last one generates INT
+ set_td_ioc(td); // last one generates INT
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
if (!first_td)
@@ -941,11 +924,10 @@
if (urb->transfer_flags & USB_QUEUE_BULK)
append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
- urb->status = -EINPROGRESS;
queue_urb_unlocked (s, urb);
if (urb->transfer_flags & USB_QUEUE_BULK)
- qh->hw.qh.element = cpu_to_le32(first_td->dma_addr);
+ set_qh_element(qh, first_td->dma_addr);
else
qh->hw.qh.element &= cpu_to_le32(~UHCI_PTR_TERM); // arm QH
@@ -988,10 +970,11 @@
}
}
/*-------------------------------------------------------------------*/
-// mode: 0: unlink but no deletion mark (step 1 of async_unlink)
-// 1: regular (unlink/delete-mark)
-// 2: deletion mark for QH (step 2 of async_unlink)
-// looks a bit complicated because of all the bulk queueing goodies
+/* mode: CLEAN_TRANSFER_NO_DELETION: unlink but no deletion mark (step 1 of async_unlink)
+ CLEAN_TRANSFER_REGULAR: regular (unlink/delete-mark)
+ CLEAN_TRANSFER_DELETION_MARK: deletion mark for QH (step 2 of async_unlink)
+ looks a bit complicated because of all the bulk queueing goodies
+*/
_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
{
@@ -1005,18 +988,16 @@
if (!priv->next_queued_urb) { // no more appended bulk queues
- queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p",urb, qh, bqh, priv->next_qh);
+ queue_dbg("uhci_clean_transfer: No more bulks for urb %p, qh %p, bqh %p, nqh %p", urb, qh, bqh, priv->next_qh);
- if (priv->prev_queued_urb) { // qh not top of the queue
- urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+ if (priv->prev_queued_urb && mode != CLEAN_TRANSFER_DELETION_MARK) { // qh not top of the queue
+ unsigned long flags;
+ urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
- if (mode != 2) {
- unsigned long flags;
-
spin_lock_irqsave (&s->qh_lock, flags);
prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
prevtd = list_entry (prevqh->vertical.prev, uhci_desc_t, vertical);
- prevtd->hw.td.link = cpu_to_le32(priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh
+ set_td_link(prevtd, priv->bottom_qh->dma_addr | UHCI_PTR_QH); // skip current qh
mb();
queue_dbg("uhci_clean_transfer: relink pqh %p, ptd %p",prevqh, prevtd);
spin_unlock_irqrestore (&s->qh_lock, flags);
@@ -1024,19 +1005,18 @@
ppriv->bottom_qh = priv->bottom_qh;
ppriv->next_queued_urb = NULL;
}
- }
else { // queue is dead, qh is top of the queue
- if (mode!=2)
+ if (mode != CLEAN_TRANSFER_DELETION_MARK)
unlink_qh(s, qh); // remove qh from horizontal chain
if (bqh) { // remove remainings of bulk queue
nqh=priv->next_qh;
- if (mode != 2)
+ if (mode != CLEAN_TRANSFER_DELETION_MARK)
unlink_qh(s, nqh); // remove nqh from horizontal chain
- if (mode) {
+ if (mode != CLEAN_TRANSFER_NO_DELETION) { // add helper QHs to free desc list
nqh->last_used = bqh->last_used = now;
list_add_tail (&nqh->horizontal, &s->free_desc);
list_add_tail (&bqh->horizontal, &s->free_desc);
@@ -1049,7 +1029,7 @@
queue_dbg("uhci_clean_transfer: urb %p, prevurb %p, nexturb %p, qh %p, bqh %p, nqh %p",
urb, priv->prev_queued_urb, priv->next_queued_urb, qh, bqh, priv->next_qh);
- if (mode !=2) { // no work for cleanup at unlink-completion
+ if (mode != CLEAN_TRANSFER_DELETION_MARK) { // no work for cleanup at unlink-completion
urb_t *nurb;
unsigned long flags;
@@ -1059,7 +1039,7 @@
if (!priv->prev_queued_urb) { // top QH
prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
- prevqh->hw.qh.head = cpu_to_le32(bqh->dma_addr | UHCI_PTR_QH);
+ set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
list_del (&qh->horizontal); // remove this qh form horizontal chain
list_add (&bqh->horizontal, &prevqh->horizontal); // insert next bqh in horizontal chain
}
@@ -1072,18 +1052,18 @@
ppriv->bottom_qh = bnqh;
ppriv->next_queued_urb = nurb;
prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
- prevqh->hw.qh.head = cpu_to_le32(bqh->dma_addr | UHCI_PTR_QH);
+ set_qh_head(prevqh, bqh->dma_addr | UHCI_PTR_QH);
}
mb();
- spin_unlock_irqrestore (&s->qh_lock, flags);
((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;
+ spin_unlock_irqrestore (&s->qh_lock, flags);
}
}
- if (mode) {
+ if (mode != CLEAN_TRANSFER_NO_DELETION) {
qh->last_used = now;
- list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion/kfree
+ list_add_tail (&qh->horizontal, &s->free_desc); // mark qh for later deletion/kfree
}
}
/*-------------------------------------------------------------------*/
@@ -1134,9 +1114,56 @@
urb_priv->transfer_buffer_dma = 0;
}
}
+/*-------------------------------------------------------------------*/
+/* needs urb_list_lock!
+ mode: UNLINK_ASYNC_STORE_URB: unlink and move URB into unlinked list
+ UNLINK_ASYNC_DONT_STORE: unlink, don't move URB into unlinked list
+*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb, int mode)
+{
+ uhci_desc_t *qh;
+ urb_priv_t *urb_priv;
+
+ async_dbg("unlink_urb_async called %p",urb);
+
+ if ((urb->status == -EINPROGRESS) ||
+ ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))
+ {
+ ((urb_priv_t*)urb->hcpriv)->started = ~0; // mark
+ dequeue_urb (s, urb);
+
+ if (mode==UNLINK_ASYNC_STORE_URB)
+ list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+ uhci_switch_timer_int(s);
+ s->unlink_urb_done = 1;
+ uhci_release_bandwidth(urb);
+ urb->status = -ECONNABORTED; // mark urb as "waiting to be killed"
+ urb_priv = (urb_priv_t*)urb->hcpriv;
+
+ switch (usb_pipetype (urb->pipe)) {
+ case PIPE_INTERRUPT:
+ usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
+
+ case PIPE_ISOCHRONOUS:
+ uhci_clean_iso_step1 (s, urb_priv);
+ break;
+
+ case PIPE_BULK:
+ case PIPE_CONTROL:
+ qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+ uhci_clean_transfer (s, urb, qh, CLEAN_TRANSFER_NO_DELETION);
+ break;
+ }
+ ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ return -EINPROGRESS; // completion will follow
+ }
+
+ return 0; // URB already dead
+}
/*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+// kills an urb by unlinking descriptors and waiting for at least one frame
_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
{
uhci_desc_t *qh;
@@ -1146,45 +1173,31 @@
spin_lock_irqsave (&s->urb_list_lock, flags);
- if (!in_interrupt()) // shouldn't be called from interrupt at all...
- spin_lock(&urb->lock);
-
if (urb->status == -EINPROGRESS) {
- // URB probably still in work
- dequeue_urb (s, urb);
- uhci_switch_timer_int(s);
- s->unlink_urb_done=1;
-
- uhci_release_bandwidth(urb);
- urb->status = -ENOENT; // mark urb as killed
- if (!in_interrupt())
- spin_unlock(&urb->lock);
+ // move descriptors out the the running chains, dequeue urb
+ uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_DONT_STORE);
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
-
urb_priv = urb->hcpriv;
-
+ urb->status = -ENOENT; // prevent from double deletion after unlock
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
+ // cleanup the rest
switch (usb_pipetype (urb->pipe)) {
- case PIPE_INTERRUPT:
- usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-
case PIPE_ISOCHRONOUS:
- uhci_clean_iso_step1(s, urb_priv);
uhci_wait_ms(1);
uhci_clean_iso_step2(s, urb_priv);
break;
case PIPE_BULK:
case PIPE_CONTROL:
- spin_lock_irqsave (&s->urb_list_lock, flags);
qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
- uhci_clean_transfer(s, urb, qh, 1);
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
uhci_wait_ms(1);
}
-
+ urb->status = -ENOENT; // mark urb as killed
+
uhci_urb_dma_unmap(s, urb, urb->hcpriv);
#ifdef DEBUG_SLAB
@@ -1200,11 +1213,8 @@
}
usb_dec_dev_use (usb_dev);
}
- else {
- if (!in_interrupt())
- spin_unlock(&urb->lock);
+ else
spin_unlock_irqrestore (&s->urb_list_lock, flags);
- }
return 0;
}
@@ -1219,7 +1229,7 @@
struct list_head *q;
urb_t *urb;
struct usb_device *dev;
- int pipe,now;
+ int now, type;
urb_priv_t *urb_priv;
q=s->urb_unlinked.next;
@@ -1235,21 +1245,22 @@
if (!urb_priv) // avoid crash when URB is corrupted
break;
- if (force ||
- ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+ if (force || ((urb_priv->started != ~0) && (urb_priv->started != now))) {
async_dbg("async cleanup %p",urb);
- switch (usb_pipetype (urb->pipe)) { // process descriptors
+ type=usb_pipetype (urb->pipe);
+
+ switch (type) { // process descriptors
case PIPE_CONTROL:
- process_transfer (s, urb, 2); // 2: don't unlink (already done)
+ process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done)
break;
case PIPE_BULK:
if (!s->avoid_bulk.counter)
- process_transfer (s, urb, 2); // don't unlink (already done)
+ process_transfer (s, urb, CLEAN_TRANSFER_DELETION_MARK); // don't unlink (already done)
else
continue;
break;
case PIPE_ISOCHRONOUS:
- process_iso (s, urb, 1); // force, don't unlink
+ process_iso (s, urb, PROCESS_ISO_FORCE); // force, don't unlink
break;
case PIPE_INTERRUPT:
process_interrupt (s, urb);
@@ -1259,8 +1270,7 @@
if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
urb->status = -ECONNRESET; // mark as asynchronously killed
- pipe = urb->pipe; // completion may destroy all...
- dev = urb->dev;
+ dev = urb->dev; // completion may destroy all...
urb_priv = urb->hcpriv;
list_del (&urb->urb_list);
@@ -1274,7 +1284,8 @@
if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
urb->status = -ENOENT; // now the urb is really dead
- switch (usb_pipetype (pipe)) {
+
+ switch (type) {
case PIPE_ISOCHRONOUS:
case PIPE_INTERRUPT:
uhci_clean_iso_step2(s, urb_priv);
@@ -1293,50 +1304,7 @@
}
}
}
-
-/*-------------------------------------------------------------------*/
-// needs urb_list_lock!
-_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
-{
- uhci_desc_t *qh;
- urb_priv_t *urb_priv;
-
- async_dbg("unlink_urb_async called %p",urb);
-
- if ((urb->status == -EINPROGRESS) ||
- ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags))
- {
- ((urb_priv_t*)urb->hcpriv)->started = ~0;
-
- dequeue_urb (s, urb);
- list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
- uhci_switch_timer_int(s);
-
- s->unlink_urb_done = 1;
-
- urb->status = -ECONNABORTED; // mark urb as "waiting to be killed"
- urb_priv = (urb_priv_t*)urb->hcpriv;
-
- switch (usb_pipetype (urb->pipe)) {
- case PIPE_INTERRUPT:
- usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
-
- case PIPE_ISOCHRONOUS:
- uhci_clean_iso_step1 (s, urb_priv);
- break;
-
- case PIPE_BULK:
- case PIPE_CONTROL:
- qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
- uhci_clean_transfer (s, urb, qh, 0);
- break;
- }
- ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
- return -EINPROGRESS; // completion will follow
- }
-
- return 0; // URB already dead
-}
+
/*-------------------------------------------------------------------*/
_static int uhci_unlink_urb (urb_t *urb)
{
@@ -1356,22 +1324,12 @@
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
int ret;
-
spin_lock_irqsave (&s->urb_list_lock, flags);
-
- // The URB needs to be locked if called outside completion context
-
- if (!in_interrupt())
- spin_lock(&urb->lock);
-
+
uhci_release_bandwidth(urb);
- ret = uhci_unlink_urb_async(s, urb);
-
- if (!in_interrupt())
- spin_unlock(&urb->lock);
+ ret = uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
spin_unlock_irqrestore (&s->urb_list_lock, flags);
-
return ret;
}
else
@@ -1477,7 +1435,7 @@
{
uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
urb_priv_t *urb_priv = urb->hcpriv;
- int nint, n, ret;
+ int nint, n;
uhci_desc_t *td;
int status, destination;
int info;
@@ -1509,9 +1467,7 @@
if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)))
return -EINVAL;
- ret = alloc_td (s, &td, UHCI_PTR_DEPTH);
-
- if (ret)
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH))
return -ENOMEM;
status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC |
@@ -1526,7 +1482,6 @@
fill_td (td, status, info, urb_priv->transfer_buffer_dma);
list_add_tail (&td->desc_list, &urb_priv->desc_list);
- urb->status = -EINPROGRESS;
queue_urb (s, urb);
insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs
@@ -1563,27 +1518,27 @@
goto err;
}
- // First try to get all TDs
+ memset(tdm, 0, urb->number_of_packets * sizeof (uhci_desc_t*));
+
+ // First try to get all TDs. Cause: Removing already inserted TDs can only be done
+ // racefree in three steps: unlink TDs, wait one frame, delete TDs.
+ // So, this solutions seems simpler...
+
for (n = 0; n < urb->number_of_packets; n++) {
dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length);
- if (!urb->iso_frame_desc[n].length) {
- // allows ISO striping by setting length to zero in iso_descriptor
- tdm[n] = 0;
- continue;
- }
+ if (!urb->iso_frame_desc[n].length)
+ continue; // allows ISO striping by setting length to zero in iso_descriptor
+
#ifdef ISO_SANITY_CHECK
if(urb->iso_frame_desc[n].length > maxsze) {
err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
- tdm[n] = 0;
ret=-EINVAL;
}
else
#endif
- ret = alloc_td (s, &td, UHCI_PTR_DEPTH);
-
- if (ret) {
+ if (alloc_td (s, &td, UHCI_PTR_DEPTH)) {
int i; // Cleanup allocated TDs
for (i = 0; i < n; n++)
@@ -1596,31 +1551,26 @@
tdm[n] = td;
}
- status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (urb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD);
+ status = TD_CTRL_ACTIVE | TD_CTRL_IOS;
destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe);
-
// Queue all allocated TDs
for (n = 0; n < urb->number_of_packets; n++) {
td = tdm[n];
if (!td)
continue;
- if (n == last)
+ if (n == last) {
status |= TD_CTRL_IOC;
+ queue_urb (s, urb);
+ }
fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21),
urb_priv->transfer_buffer_dma + urb->iso_frame_desc[n].offset);
list_add_tail (&td->desc_list, &urb_priv->desc_list);
- if (n == last) {
- urb->status = -EINPROGRESS;
- queue_urb (s, urb);
- }
insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds
- //uhci_show_td(td);
-
}
kfree (tdm);
@@ -1630,7 +1580,6 @@
err:
__restore_flags(flags);
return ret;
-
}
/*-------------------------------------------------------------------*/
// returns: 0 (no transfer queued), urb* (this urb already queued)
@@ -1663,7 +1612,7 @@
{
uhci_t *s;
urb_priv_t *urb_priv;
- int ret = 0;
+ int ret = 0, type;
unsigned long flags;
urb_t *queued_urb=NULL;
int bustime;
@@ -1676,10 +1625,23 @@
if (!s->running)
return -ENODEV;
-
+
+ type = usb_pipetype (urb->pipe);
+
if (usb_pipedevice (urb->pipe) == s->rh.devnum)
return rh_submit_urb (urb); /* virtual root hub */
+ // Sanity checks
+ if (usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)) <= 0) {
+ err("uhci_submit_urb: pipesize for pipe %x is zero", urb->pipe);
+ return -EMSGSIZE;
+ }
+
+ if (urb->transfer_buffer_length < 0 && type != PIPE_ISOCHRONOUS) {
+ err("uhci_submit_urb: Negative transfer length for urb %p", urb);
+ return -EINVAL;
+ }
+
usb_inc_dev_use (urb->dev);
spin_lock_irqsave (&s->urb_list_lock, flags);
@@ -1690,8 +1652,8 @@
queue_dbg("found bulk urb %p\n", queued_urb);
- if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
- ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+ if (( type != PIPE_BULK) ||
+ ((type == PIPE_BULK) &&
(!(urb->transfer_flags & USB_QUEUE_BULK) || !(queued_urb->transfer_flags & USB_QUEUE_BULK)))) {
spin_unlock_irqrestore (&s->urb_list_lock, flags);
usb_dec_dev_use (urb->dev);
@@ -1711,20 +1673,15 @@
return -ENOMEM;
}
+ memset(urb_priv, 0, sizeof(urb_priv_t));
urb->hcpriv = urb_priv;
INIT_LIST_HEAD (&urb_priv->desc_list);
- urb_priv->flags = 0;
+
dbg("submit_urb: scheduling %p", urb);
- urb_priv->next_queued_urb = NULL;
- urb_priv->prev_queued_urb = NULL;
- urb_priv->bottom_qh = NULL;
- urb_priv->next_qh = NULL;
- if (usb_pipetype (urb->pipe) == PIPE_CONTROL)
+ if (type == PIPE_CONTROL)
urb_priv->setup_packet_dma = pci_map_single(s->uhci_pci, urb->setup_packet,
sizeof(devrequest), PCI_DMA_TODEVICE);
- else
- urb_priv->setup_packet_dma = 0;
if (urb->transfer_buffer_length)
urb_priv->transfer_buffer_dma = pci_map_single(s->uhci_pci,
@@ -1733,10 +1690,8 @@
usb_pipein(urb->pipe) ?
PCI_DMA_FROMDEVICE :
PCI_DMA_TODEVICE);
- else
- urb_priv->transfer_buffer_dma = 0;
- if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+ if (type == PIPE_BULK) {
if (queued_urb) {
while (((urb_priv_t*)queued_urb->hcpriv)->next_queued_urb) // find last queued bulk
@@ -1751,7 +1706,7 @@
}
else {
spin_unlock_irqrestore (&s->urb_list_lock, flags);
- switch (usb_pipetype (urb->pipe)) {
+ switch (type) {
case PIPE_ISOCHRONOUS:
if (urb->bandwidth == 0) { /* not yet checked/allocated */
if (urb->number_of_packets <= 0) {
@@ -1760,14 +1715,13 @@
}
bustime = usb_check_bandwidth (urb->dev, urb);
- if (bustime < 0) {
+ if (bustime < 0)
ret = bustime;
- break;
+ else {
+ ret = uhci_submit_iso_urb(urb);
+ if (ret == 0)
+ usb_claim_bandwidth (urb->dev, urb, bustime, 1);
}
-
- ret = uhci_submit_iso_urb(urb);
- if (ret == 0)
- usb_claim_bandwidth (urb->dev, urb, bustime, 1);
} else { /* bandwidth is already set */
ret = uhci_submit_iso_urb(urb);
}
@@ -1810,8 +1764,7 @@
return 0;
}
-// Checks for URB timeout and removes bandwidth reclamation
-// if URB idles too long
+// Checks for URB timeout and removes bandwidth reclamation if URB idles too long
_static void uhci_check_timeouts(uhci_t *s)
{
struct list_head *p,*p2;
@@ -1834,7 +1787,7 @@
((hcpriv->started + urb->timeout) < jiffies)) {
urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
async_dbg("uhci_check_timeout: timeout for %p",urb);
- uhci_unlink_urb_async(s, urb);
+ uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
}
#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
@@ -1973,7 +1926,6 @@
/*-------------------------------------------------------------------------*/
/* Root Hub INTs are polled by this timer, polling interval 20ms */
-/* This time is also used for URB-timeout checking */
_static int rh_init_int_timer (urb_t *urb)
{
@@ -2159,14 +2111,14 @@
case RH_GET_DESCRIPTOR:
switch ((wValue & 0xff00) >> 8) {
case (0x01): /* device descriptor */
- len = min(unsigned int, leni,
- min(unsigned int,
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int,
sizeof (root_hub_dev_des), wLength));
memcpy (data, root_hub_dev_des, len);
OK (len);
case (0x02): /* configuration descriptor */
- len = min(unsigned int, leni,
- min(unsigned int,
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int,
sizeof (root_hub_config_des), wLength));
memcpy (data, root_hub_config_des, len);
OK (len);
@@ -2175,7 +2127,7 @@
uhci->io_addr, "UHCI",
data, wLength);
if (len > 0) {
- OK(min(int, leni, len));
+ OK(min_t(int, leni, len));
} else
stat = -EPIPE;
}
@@ -2183,8 +2135,8 @@
case RH_GET_DESCRIPTOR | RH_CLASS:
root_hub_hub_des[2] = uhci->rh.numports;
- len = min(unsigned int, leni,
- min(unsigned int, sizeof (root_hub_hub_des), wLength));
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
memcpy (data, root_hub_hub_des, len);
OK (len);
@@ -2284,7 +2236,7 @@
spin_unlock_irqrestore (&s->urb_list_lock, flags);
warn("forced removing of queued URB %p due to disconnect",urb);
uhci_unlink_urb(urb);
- urb->dev = NULL; // avoid further processing of this UR
+ urb->dev = NULL; // avoid further processing of this URB
spin_lock_irqsave (&s->urb_list_lock, flags);
p = s->urb_list.prev;
}
@@ -2325,13 +2277,34 @@
uhci_unlink_urb
};
+_static void correct_data_toggles(urb_t *urb)
+{
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe),
+ !usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)));
+
+ while(urb) {
+ urb_priv_t *priv=urb->hcpriv;
+ uhci_desc_t *qh = list_entry (priv->desc_list.next, uhci_desc_t, desc_list);
+ struct list_head *p = qh->vertical.next;
+ uhci_desc_t *td;
+ dbg("URB to correct %p\n", urb);
+
+ for (; p != &qh->vertical; p = p->next) {
+ td = list_entry (p, uhci_desc_t, vertical);
+ td->hw.td.info^=cpu_to_le32(1<<TD_TOKEN_TOGGLE);
+ }
+ urb=priv->next_queued_urb;
+ }
+}
+
/*
* For IN-control transfers, process_transfer gets a bit more complicated,
* since there are devices that return less data (eg. strings) than they
* have announced. This leads to a queue abort due to the short packet,
* the status stage is not executed. If this happens, the status stage
* is manually re-executed.
- * mode: 1: regular (unlink QH), 2: QHs already unlinked (for async unlink_urb)
+ * mode: PROCESS_TRANSFER_REGULAR: regular (unlink QH)
+ * PROCESS_TRANSFER_DONT_UNLINK: QHs already unlinked (for async unlink_urb)
*/
_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
@@ -2356,7 +2329,7 @@
*/
if (urb_priv->flags &&
- ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) ||(!(last_desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)))))
+ ((qh->hw.qh.element == cpu_to_le32(UHCI_PTR_TERM)) || !is_td_active(desc)))
goto transfer_finished;
urb->actual_length=0;
@@ -2364,13 +2337,13 @@
for (; p != &qh->vertical; p = p->next) {
desc = list_entry (p, uhci_desc_t, vertical);
- if (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) { // do not process active TDs
- if (mode==2) // if called from async_unlink
- uhci_clean_transfer(s, urb, qh, mode);
+ if (is_td_active(desc)) { // do not process active TDs
+ if (mode == CLEAN_TRANSFER_DELETION_MARK) // if called from async_unlink
+ uhci_clean_transfer(s, urb, qh, CLEAN_TRANSFER_DELETION_MARK);
return ret;
}
- actual_length = (le32_to_cpu(desc->hw.td.status) + 1) & 0x7ff; // extract transfer parameters from TD
+ actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status)); // extract transfer parameters from TD
maxlength = (((le32_to_cpu(desc->hw.td.info) >> 21) & 0x7ff) + 1) & 0x7ff;
status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
@@ -2401,10 +2374,8 @@
if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) {
if (uhci_packetid(le32_to_cpu(last_desc->hw.td.info)) == USB_PID_OUT) {
- qh->hw.qh.element = cpu_to_le32(last_desc->dma_addr); // re-trigger status stage
+ set_qh_element(qh, last_desc->dma_addr); // re-trigger status stage
dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
- //uhci_show_td (desc);
- //uhci_show_td (last_desc);
urb_priv->flags = 1; // mark as short control packet
return 0;
}
@@ -2421,7 +2392,24 @@
}
- usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
+ if (usb_pipetype (urb->pipe) == PIPE_BULK ) { /* toggle correction for short bulk transfers (nonqueued/queued) */
+
+ urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
+ urb_t *next_queued_urb=priv->next_queued_urb;
+
+ if (next_queued_urb) {
+ urb_priv_t *next_priv=(urb_priv_t*)next_queued_urb->hcpriv;
+ uhci_desc_t *qh = list_entry (next_priv->desc_list.next, uhci_desc_t, desc_list);
+ uhci_desc_t *first_td=list_entry (qh->vertical.next, uhci_desc_t, vertical);
+
+ if (data_toggle == uhci_toggle (le32_to_cpu(first_td->hw.td.info))) {
+ err("process_transfer: fixed toggle");
+ correct_data_toggles(next_queued_urb);
+ }
+ }
+ else
+ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
+ }
transfer_finished:
@@ -2454,7 +2442,7 @@
{
desc = list_entry (p, uhci_desc_t, desc_list);
- if (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) {
+ if (is_td_active(desc)) {
// do not process active TDs
//dbg("TD ACT Status @%p %08x",desc,le32_to_cpu(desc->hw.td.status));
break;
@@ -2466,7 +2454,7 @@
}
// extract transfer parameters from TD
- actual_length = (le32_to_cpu(desc->hw.td.status) + 1) & 0x7ff;
+ actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
// see if EP is stalled
@@ -2523,8 +2511,8 @@
mb();
}
else {
- uhci_unlink_urb_async(s, urb);
- desc->hw.td.status &= cpu_to_le32(~TD_CTRL_IOC); // inactivate TD
+ uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
+ clr_td_ioc(desc); // inactivate TD
}
}
}
@@ -2532,7 +2520,9 @@
return ret;
}
-// mode: 1: force processing, don't unlink tds (already unlinked)
+// mode: PROCESS_ISO_REGULAR: processing only for done TDs, unlink TDs
+// mode: PROCESS_ISO_FORCE: force processing, don't unlink TDs (already unlinked)
+
_static int process_iso (uhci_t *s, urb_t *urb, int mode)
{
int i;
@@ -2542,7 +2532,7 @@
uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
dbg("urb contains iso request");
- if ((desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) && !mode)
+ if (is_td_active(desc) && mode==PROCESS_ISO_REGULAR)
return -EXDEV; // last TD not finished
urb->error_count = 0;
@@ -2555,7 +2545,7 @@
desc = list_entry (p, uhci_desc_t, desc_list);
//uhci_show_td(desc);
- if (desc->hw.td.status & cpu_to_le32(TD_CTRL_ACTIVE)) {
+ if (is_td_active(desc)) {
// means we have completed the last TD, but not the TDs before
desc->hw.td.status &= cpu_to_le32(~TD_CTRL_ACTIVE);
dbg("TD still active (%x)- grrr. paranoia!", le32_to_cpu(desc->hw.td.status));
@@ -2566,7 +2556,7 @@
goto err;
}
- if (!mode)
+ if (mode == PROCESS_ISO_REGULAR)
unlink_td (s, desc, 1);
if (urb->number_of_packets <= i) {
@@ -2575,7 +2565,7 @@
goto err;
}
- urb->iso_frame_desc[i].actual_length = (le32_to_cpu(desc->hw.td.status) + 1) & 0x7ff;
+ urb->iso_frame_desc[i].actual_length = uhci_actual_length(le32_to_cpu(desc->hw.td.status));
urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (le32_to_cpu(desc->hw.td.status)), usb_pipeout (urb->pipe));
urb->actual_length += urb->iso_frame_desc[i].actual_length;
@@ -2608,16 +2598,16 @@
switch (usb_pipetype (urb->pipe)) {
case PIPE_CONTROL:
- ret = process_transfer (s, urb, 1);
+ ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
break;
case PIPE_BULK:
if (!s->avoid_bulk.counter)
- ret = process_transfer (s, urb, 1);
+ ret = process_transfer (s, urb, CLEAN_TRANSFER_REGULAR);
else
return 0;
break;
case PIPE_ISOCHRONOUS:
- ret = process_iso (s, urb, 0);
+ ret = process_iso (s, urb, PROCESS_ISO_REGULAR);
break;
case PIPE_INTERRUPT:
ret = process_interrupt (s, urb);
@@ -2673,9 +2663,7 @@
if (next_urb == urb)
is_ring=1;
- }
-
- spin_lock(&urb->lock);
+ }
// Submit idle/non-killed URBs linked with urb->next
// Stop before the current URB
@@ -2712,19 +2700,18 @@
int was_unlinked = (urb->status == -ENOENT);
urb->dev = NULL;
spin_unlock(&s->urb_list_lock);
+
urb->complete ((struct urb *) urb);
+
// Re-submit the URB if ring-linked
if (is_ring && !was_unlinked && !contains_killed) {
urb->dev=usb_dev;
uhci_submit_urb (urb);
- } else
- urb = 0;
+ }
spin_lock(&s->urb_list_lock);
}
usb_dec_dev_use (usb_dev);
- if (urb)
- spin_unlock(&urb->lock);
}
}
@@ -2752,11 +2739,16 @@
dbg("interrupt");
if (status != 1) {
- warn("interrupt, status %x, frame# %i", status,
- UHCI_GET_CURRENT_FRAME(s));
-
+ // Avoid too much error messages at a time
+ if ((jiffies - s->last_error_time > ERROR_SUPPRESSION_TIME)) {
+ warn("interrupt, status %x, frame# %i", status,
+ UHCI_GET_CURRENT_FRAME(s));
+ s->last_error_time = jiffies;
+ }
+
// remove host controller halted state
if ((status&0x20) && (s->running)) {
+ err("Host controller halted, trying to restart.");
outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
}
//uhci_show_status (s);
@@ -2799,8 +2791,8 @@
if ((jiffies - s->timeout_check) > (HZ/30))
uhci_check_timeouts(s);
- clean_descs(s,0);
- uhci_cleanup_unlink(s, 0);
+ clean_descs(s, CLEAN_NOT_FORCED);
+ uhci_cleanup_unlink(s, CLEAN_NOT_FORCED);
uhci_switch_timer_int(s);
spin_unlock (&s->urb_list_lock);
@@ -2869,8 +2861,8 @@
reset_hc (s);
wait_ms (1);
- uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs
- uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs
+ uhci_unlink_urbs (s, 0, CLEAN_FORCED); // Forced unlink of remaining URBs
+ uhci_cleanup_unlink (s, CLEAN_FORCED); // force cleanup of async killed URBs
usb_deregister_bus (s->bus);
@@ -2943,11 +2935,9 @@
spin_lock_init (&s->qh_lock);
spin_lock_init (&s->td_lock);
atomic_set(&s->avoid_bulk, 0);
- s->timeout_urbs = 0;
s->irq = -1;
s->io_addr = io_addr;
s->io_size = io_size;
- s->timeout_check = 0;
s->uhci_pci=dev;
bus = usb_alloc_bus (&uhci_device_operations);
@@ -3031,6 +3021,11 @@
if (pci_enable_device(dev) < 0)
return -ENODEV;
+
+ if (!dev->irq) {
+ err("found UHCI device with no IRQ assigned. check BIOS settings!");
+ return -ENODEV;
+ }
pci_set_master(dev);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)