patch-2.3.46 linux/drivers/usb/usb-uhci.c
Next file: linux/drivers/usb/usb-uhci.h
Previous file: linux/drivers/usb/usb-serial.h
Back to the patch index
Back to the overall index
- Lines: 430
- Date:
Tue Feb 15 11:53:24 2000
- Orig file:
v2.3.45/linux/drivers/usb/usb-uhci.c
- Orig date:
Thu Feb 10 17:11:15 2000
diff -u --recursive --new-file v2.3.45/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
@@ -12,9 +12,10 @@
* (C) Copyright 1999 Johannes Erdfelt
* (C) Copyright 1999 Randy Dunlap
*
- * $Id: usb-uhci.c,v 1.185 2000/02/05 21:29:19 acher Exp $
+ * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -54,7 +55,6 @@
#define dbg(format, arg...) do {} while (0)
#include <linux/pm.h>
-static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data);
#ifdef DEBUG_SYMBOLS
#define _static
@@ -70,9 +70,18 @@
static kmem_cache_t *urb_priv_kmem;
#endif
-#define USE_CTRL_DEPTH_FIRST 1 // 0: Breadth first, 1: Depth first (standard)
+#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
+#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+#define USE_RECLAMATION_LOOP
+#else
//#define USE_RECLAMATION_LOOP
+#endif
+
+// stop bandwidth reclamation after (roughly) 50ms (depends also on
+// hub polling interval)
+#define IDLE_TIMEOUT (HZ/20)
_static int rh_submit_urb (urb_t *urb);
_static int rh_unlink_urb (urb_t *urb);
@@ -101,22 +110,81 @@
}
}
/*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, struct list_head *p)
+#ifdef USE_RECLAMATION_LOOP
+_static void enable_desc_loop(uhci_t *s, urb_t *urb)
+{
+ int flags;
+
+ dbg("enable_desc_loop: enter");
+
+ spin_lock_irqsave (&s->qh_lock, flags);
+ s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+ s->loop_usage++;
+ ((urb_priv_t*)urb->hcpriv)->use_loop=1;
+ spin_unlock_irqrestore (&s->qh_lock, flags);
+
+ dbg("enable_desc_loop: finished");
+}
+/*-------------------------------------------------------------------*/
+_static void disable_desc_loop(uhci_t *s, urb_t *urb)
+{
+ int flags;
+
+ dbg("disable_desc_loop: enter\n");
+
+ spin_lock_irqsave (&s->qh_lock, flags);
+
+ if (((urb_priv_t*)urb->hcpriv)->use_loop) {
+ s->loop_usage--;
+
+ if (!s->loop_usage)
+ s->chain_end->hw.qh.head=UHCI_PTR_TERM;
+
+ ((urb_priv_t*)urb->hcpriv)->use_loop=0;
+ }
+ spin_unlock_irqrestore (&s->qh_lock, flags);
+
+ dbg("disable_desc_loop: finished");
+
+}
+#endif
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
{
unsigned long flags=0;
+ struct list_head *p=&urb->urb_list;
+
spin_lock_irqsave (&s->urb_list_lock, flags);
+#ifdef USE_RECLAMATION_LOOP
+ {
+ int type;
+ type=usb_pipetype (urb->pipe);
+
+ if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+ enable_desc_loop(s, urb);
+ }
+#endif
+ ((urb_priv_t*)urb->hcpriv)->started=jiffies;
list_add_tail (p, &s->urb_list);
spin_unlock_irqrestore (&s->urb_list_lock, flags);
}
/*-------------------------------------------------------------------*/
-_static void dequeue_urb (uhci_t *s, struct list_head *p)
+_static void dequeue_urb (uhci_t *s, urb_t *urb)
{
- list_del (p);
+#ifdef USE_RECLAMATION_LOOP
+ int type;
+
+ type=usb_pipetype (urb->pipe);
+
+ if ((type == PIPE_BULK) || (type == PIPE_CONTROL))
+ disable_desc_loop(s, urb);
+#endif
+ list_del (&urb->urb_list);
}
/*-------------------------------------------------------------------*/
_static int alloc_td (uhci_desc_t ** new, int flags)
@@ -219,6 +287,7 @@
return 0;
}
+
/*-------------------------------------------------------------------*/
_static int delete_desc (uhci_desc_t *element)
{
@@ -451,11 +520,6 @@
insert_qh (s, s->bulk_chain, qh, 0);
s->control_chain = qh;
-#ifdef USE_RECLAMATION_LOOP
- s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
- info("Using loop for bandwidth reclamation.");
-#endif
-
for (n = 0; n < 8; n++)
s->int_chain[n] = 0;
@@ -625,7 +689,7 @@
list_add (&qh->desc_list, &urb_priv->desc_list);
urb->status = -EINPROGRESS;
- queue_urb (s, &urb->urb_list); // queue before inserting in desc chain
+ queue_urb (s, urb); // queue before inserting in desc chain
qh->hw.qh.element&=~UHCI_PTR_TERM;
@@ -635,8 +699,8 @@
insert_qh (s, s->control_chain, qh, 1); // insert after control chain
else
insert_qh (s, s->bulk_chain, qh, 0); // insert before bulk chain
-
//uhci_show_queue(qh);
+
dbg("uhci_submit_control end");
return 0;
}
@@ -651,11 +715,12 @@
unsigned int pipe = urb->pipe;
int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
int info, len;
+ int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method
- /* shouldn't the clear_halt be done in the USB core or in the client driver? - Thomas */
- if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) &&
- usb_clear_halt (urb->dev, usb_pipeendpoint (pipe) | (pipe & USB_DIR_IN)))
+
+ 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;
@@ -685,7 +750,7 @@
do { // TBD: Really allow zero-length packets?
int pktsze = len;
- alloc_td (&td, UHCI_PTR_DEPTH);
+ alloc_td (&td, UHCI_PTR_DEPTH * depth_first);
if (!td) {
delete_qh (s, qh);
@@ -708,7 +773,7 @@
td->hw.td.status |= TD_CTRL_IOC; // last one generates INT
//dbg("insert td %p, len %i",td,pktsze);
- insert_td (s, qh, td, UHCI_PTR_DEPTH);
+ insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
/* Alternate Data0/1 (start with Data0) */
usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
@@ -717,7 +782,7 @@
list_add (&qh->desc_list, &urb_priv->desc_list);
urb->status = -EINPROGRESS;
- queue_urb (s, &urb->urb_list);
+ queue_urb (s, urb);
qh->hw.qh.element&=~UHCI_PTR_TERM;
@@ -727,6 +792,7 @@
dbg("uhci_submit_bulk_urb: exit");
return 0;
}
+
/*-------------------------------------------------------------------*/
// unlinks an urb by dequeuing its qh, waits some frames and forgets it
// Problem: unlinking in interrupt requires waiting for one frame (udelay)
@@ -758,7 +824,7 @@
if (urb->status == -EINPROGRESS) {
// URB probably still in work
- dequeue_urb (s, &urb->urb_list);
+ dequeue_urb (s, urb);
s->unlink_urb_done=1;
spin_unlock_irqrestore (&s->urb_list_lock, flags);
@@ -773,7 +839,7 @@
unlink_td (s, td, 1);
}
// wait at least 1 Frame
- uhci_wait_ms(1);
+ uhci_wait_ms(1);
while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
td = list_entry (p, uhci_desc_t, desc_list);
list_del (p);
@@ -991,7 +1057,7 @@
list_add_tail (&td->desc_list, &urb_priv->desc_list);
urb->status = -EINPROGRESS;
- queue_urb (s, &urb->urb_list);
+ queue_urb (s, urb);
insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs
@@ -1083,7 +1149,7 @@
if (n == last) {
urb->status = -EINPROGRESS;
- queue_urb (s, &urb->urb_list);
+ 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);
@@ -1194,14 +1260,44 @@
#endif
return ret;
}
-/*
- urb->status = -EINPROGRESS;
- queue_urb (s, &urb->urb_list);
- dbg("submit_urb: exit");
-*/
return 0;
}
+#ifdef USE_RECLAMATION_LOOP
+// Removes bandwidth reclamation if URB idles too long
+void check_idling_urbs(uhci_t *s)
+{
+ struct list_head *p,*p2;
+ urb_t *urb;
+ int type;
+
+ //dbg("check_idling_urbs: enter i:%d",in_interrupt());
+
+ spin_lock (&s->urb_list_lock);
+ p = s->urb_list.prev;
+
+ while (p != &s->urb_list) {
+ p2 = p;
+ p = p->prev;
+ urb=list_entry (p2, urb_t, urb_list);
+ type=usb_pipetype (urb->pipe);
+
+#if 0
+ err("URB timers: %li now: %li %i\n",
+ ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
+ type);
+#endif
+ if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&
+ (((urb_priv_t*)urb->hcpriv)->use_loop) &&
+ ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
+ disable_desc_loop(s,urb);
+
+ }
+ spin_unlock (&s->urb_list_lock);
+
+ //dbg("check_idling_urbs: finished");
+}
+#endif
/*-------------------------------------------------------------------
Virtual Root Hub
-------------------------------------------------------------------*/
@@ -1313,10 +1409,13 @@
_static void rh_int_timer_do (unsigned long ptr)
{
int len;
-
urb_t *urb = (urb_t*) ptr;
uhci_t *uhci = urb->dev->bus->hcpriv;
+#ifdef USE_RECLAMATION_LOOP
+ check_idling_urbs(uhci);
+#endif
+
if (uhci->rh.send) {
len = rh_send_irq (urb);
if (len > 0) {
@@ -1558,9 +1657,11 @@
{
uhci_t *uhci = urb->dev->bus->hcpriv;
- dbg("Root-Hub unlink IRQ");
- uhci->rh.send = 0;
- del_timer (&uhci->rh.rh_int_timer);
+ if (uhci->rh.urb==urb) {
+ dbg("Root-Hub unlink IRQ");
+ uhci->rh.send = 0;
+ del_timer (&uhci->rh.rh_int_timer);
+ }
return 0;
}
/*-------------------------------------------------------------------*/
@@ -1741,7 +1842,7 @@
// got less data than requested
if ( (actual_length < maxlength)) {
if (urb->transfer_flags & USB_DISABLE_SPD) {
- ret = -EREMOTEIO; // treat as real error
+ status = -EREMOTEIO; // treat as real error
dbg("process_transfer: SPD!!");
break; // exit after this TD because SP was detected
}
@@ -1777,6 +1878,10 @@
urb->status = status;
+#ifdef USE_RECLAMATION_LOOP
+ disable_desc_loop(s,urb);
+#endif
+
dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
//dbg("process_transfer: exit");
@@ -1971,7 +2076,7 @@
int proceed = 0;
dbg("dequeued urb: %p", urb);
- dequeue_urb (s, p);
+ dequeue_urb (s, urb);
#ifdef DEBUG_SLAB
kmem_cache_free(urb_priv_kmem, urb->hcpriv);
@@ -2181,6 +2286,23 @@
return 0;
}
+_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+ uhci_t *s = (uhci_t*) dev->data;
+ dbg("handle_apm_event(%d)", rqst);
+ if (s) {
+ switch (rqst) {
+ case PM_SUSPEND:
+ reset_hc (s);
+ break;
+ case PM_RESUME:
+ start_hc (s);
+ break;
+ }
+ }
+ return 0;
+}
+
_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
{
uhci_t *s;
@@ -2236,7 +2358,7 @@
}
s->rh.numports = s->maxports;
-
+ s->loop_usage=0;
if (init_skel (s)) {
usb_free_bus (bus);
kfree(s);
@@ -2306,23 +2428,6 @@
return -1;
}
-_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
-{
- uhci_t *s = (uhci_t*) dev->data;
- dbg("handle_apm_event(%d)", rqst);
- if (s) {
- switch (rqst) {
- case PM_SUSPEND:
- reset_hc (s);
- break;
- case PM_RESUME:
- start_hc (s);
- break;
- }
- }
- return 0;
-}
-
int __init uhci_init (void)
{
int retval = -ENODEV;
@@ -2385,11 +2490,9 @@
uhci_cleanup_dev(s);
}
#ifdef DEBUG_SLAB
-
-
if(kmem_cache_destroy(uhci_desc_kmem))
err("uhci_desc_kmem remained");
-
+
if(kmem_cache_destroy(urb_priv_kmem))
err("urb_priv_kmem remained");
#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)