summaryrefslogtreecommitdiffstats
path: root/drivers/usb/usb-uhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usb-uhci.c')
-rw-r--r--drivers/usb/usb-uhci.c78
1 files changed, 50 insertions, 28 deletions
diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c
index e82fa78a0..bd721cba2 100644
--- a/drivers/usb/usb-uhci.c
+++ b/drivers/usb/usb-uhci.c
@@ -1,10 +1,10 @@
/*
* Universal Host Controller Interface driver for USB (take II).
*
- * (c) 1999 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)
+ * (c) 1999-2000 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)
*
* HW-initalization based on material of
*
@@ -12,7 +12,7 @@
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.232 2000/06/11 13:18:30 acher Exp $
+ * $Id: usb-uhci.c,v 1.236 2000/08/02 20:28:28 acher Exp $
*/
#include <linux/config.h>
@@ -48,7 +48,7 @@
/* This enables an extra UHCI slab for memory debugging */
#define DEBUG_SLAB
-#define VERSTR "$Revision: 1.232 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.236 $ time " __TIME__ " " __DATE__
#include <linux/usb.h>
#include "usb-uhci.h"
@@ -791,7 +791,7 @@ _static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
char *data;
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
- int info, len;
+ int info, len, last;
int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
urb_priv_t *upriv, *bpriv;
@@ -888,13 +888,15 @@ _static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
data += pktsze;
len -= pktsze;
- if (!len)
+ last = (len == 0 && (usb_pipein(pipe) || pktsze < maxsze || (urb->transfer_flags & USB_DISABLE_SPD)));
+
+ if (last)
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
- } while (len > 0);
+ } while (!last);
list_add (&qh->desc_list, &urb_priv->desc_list);
@@ -1036,11 +1038,18 @@ _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
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;
+
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
+
spin_unlock_irqrestore (&s->urb_list_lock, flags);
urb->status = -ENOENT; // mark urb as killed
@@ -1076,10 +1085,13 @@ _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
urb->complete ((struct urb *) urb);
}
usb_dec_dev_use (urb->dev);
- return 0;
}
- else
+ else {
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
spin_unlock_irqrestore (&s->urb_list_lock, flags);
+ }
+
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1198,9 +1210,10 @@ _static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
break;
}
((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+ return -EINPROGRESS; // completion will follow
}
- return -EINPROGRESS;
+ return 0; // URB already dead
}
/*-------------------------------------------------------------------*/
_static int uhci_unlink_urb (urb_t *urb)
@@ -1222,9 +1235,20 @@ _static int uhci_unlink_urb (urb_t *urb)
if (urb->transfer_flags & USB_ASYNC_UNLINK) {
int ret;
- spin_lock_irqsave (&s->urb_list_lock, flags);
+ 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);
+
ret = uhci_unlink_urb_async(s, urb);
- spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
+ if (!in_interrupt())
+ spin_unlock(&urb->lock);
+
+ spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
return ret;
}
else
@@ -1427,10 +1451,10 @@ _static int uhci_submit_iso_urb (urb_t *urb)
#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;
- goto inval;
+ ret=-EINVAL;
}
else
#endif
@@ -2177,10 +2201,9 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode)
usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
}
- if (status != 0) { // if any error occured stop processing of further TDs
+ if (status && (status != -EPIPE)) { // if any error occurred stop processing of further TDs
// only set ret if status returned an error
- if (status != -EPIPE)
- uhci_show_td (desc);
+ is_error:
ret = status;
urb->error_count++;
break;
@@ -2212,6 +2235,8 @@ _static int process_transfer (uhci_t *s, urb_t *urb, int mode)
data_toggle = uhci_toggle (desc->hw.td.info);
break;
}
+ else if (status)
+ goto is_error;
data_toggle = uhci_toggle (desc->hw.td.info);
queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);
@@ -2457,10 +2482,11 @@ _static int process_urb (uhci_t *s, struct list_head *p)
is_ring = 1;
}
+ spin_lock(&urb->lock);
spin_unlock(&s->urb_list_lock);
- // In case you need the current URB status for your completion handler
- if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) {
+ // In case you need the current URB status for your completion handler (before resubmit)
+ if (urb->complete && (!proceed )) {
dbg("process_transfer: calling early completion");
urb->complete ((struct urb *) urb);
if (!proceed && is_ring && (urb->status != -ENOENT))
@@ -2478,15 +2504,15 @@ _static int process_urb (uhci_t *s, struct list_head *p)
}
while (tmp != NULL && tmp != urb->next); // submit until we reach NULL or our own pointer or submit fails
- if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) {
+ if (urb->complete) {
dbg("process_transfer: calling completion");
urb->complete ((struct urb *) urb);
}
}
- spin_lock(&s->urb_list_lock);
-
usb_dec_dev_use (urb->dev);
+ spin_unlock(&urb->lock);
+ spin_lock(&s->urb_list_lock);
}
}
@@ -2790,10 +2816,6 @@ _static int __init start_uhci (struct pci_dev *dev)
break;
/* disable legacy emulation */
pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
- if(dev->vendor==0x8086) {
- info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
- pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
- }
return alloc_uhci(dev, dev->irq, io_addr, io_size);
}