patch-2.4.0-test9 linux/drivers/usb/usb-uhci.c

Next file: linux/drivers/usb/usb.c
Previous file: linux/drivers/usb/usb-ohci.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test8/linux/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
@@ -12,7 +12,7 @@
  * (C) Copyright 1999 Johannes Erdfelt
  * (C) Copyright 1999 Randy Dunlap
  *
- * $Id: usb-uhci.c,v 1.237 2000/08/08 14:58:17 acher Exp $
+ * $Id: usb-uhci.c,v 1.239 2000/09/19 20:15:12 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.237 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.239 $ time " __TIME__ " " __DATE__
 
 #include <linux/usb.h>
 #include "usb-uhci.h"
@@ -141,6 +141,9 @@
 {
 	int flags;
 
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
+
 	spin_lock_irqsave (&s->qh_lock, flags);
 	s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; 
 	mb();
@@ -153,8 +156,10 @@
 {
 	int flags;
 
-	spin_lock_irqsave (&s->qh_lock, flags);
+	if (urb->transfer_flags & USB_NO_FSBR)
+		return;
 
+	spin_lock_irqsave (&s->qh_lock, flags);
 	if (((urb_priv_t*)urb->hcpriv)->use_loop) {
 		s->loop_usage--;
 
@@ -1029,12 +1034,30 @@
 	}
 }
 /*-------------------------------------------------------------------*/
+// Release bandwidth for Interrupt or Isoc. transfers 
+_static void uhci_release_bandwidth(urb_t *urb)
+{       
+	if (urb->bandwidth) {
+		switch (usb_pipetype(urb->pipe)) {
+		case PIPE_INTERRUPT:
+			usb_release_bandwidth (urb->dev, urb, 0);
+			break;
+		case PIPE_ISOCHRONOUS:
+			usb_release_bandwidth (urb->dev, urb, 1);
+			break;
+		default:
+			break;
+		}
+	}	
+}
+/*-------------------------------------------------------------------*/
 // unlinks an urb by dequeuing its qh, waits some frames and forgets it
 _static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
 {
 	uhci_desc_t *qh;
 	urb_priv_t *urb_priv;
 	unsigned long flags=0;
+	struct usb_device *usb_dev;
 
 	spin_lock_irqsave (&s->urb_list_lock, flags);
 
@@ -1050,6 +1073,7 @@
 		if (!in_interrupt())	
 			spin_unlock(&urb->lock); 
 
+		uhci_release_bandwidth(urb);
 		spin_unlock_irqrestore (&s->urb_list_lock, flags);		
 		
 		urb->status = -ENOENT;	// mark urb as killed		
@@ -1080,11 +1104,13 @@
 #else
 		kfree (urb->hcpriv);
 #endif
+		usb_dev = urb->dev;
 		if (urb->complete) {
 			dbg("unlink_urb: calling completion");
+			urb->dev = NULL;
 			urb->complete ((struct urb *) urb);
 		}
-		usb_dec_dev_use (urb->dev);
+		usb_dec_dev_use (usb_dev);
 	}
 	else {
 		if (!in_interrupt())	
@@ -1148,6 +1174,7 @@
 
 			if (urb->complete) {
 				spin_unlock(&s->urb_list_lock);
+				urb->dev = NULL;
 				urb->complete ((struct urb *) urb);
 				spin_lock(&s->urb_list_lock);
 			}
@@ -1242,6 +1269,7 @@
 		if (!in_interrupt())
 			spin_lock(&urb->lock);
 
+		uhci_release_bandwidth(urb);
 		ret = uhci_unlink_urb_async(s, urb);
 
 		if (!in_interrupt())
@@ -1543,6 +1571,7 @@
 	int ret = 0;
 	unsigned long flags;
 	urb_t *bulk_urb=NULL;
+	int bustime;
 		
 	if (!urb->dev || !urb->dev->bus)
 		return -ENODEV;
@@ -1612,11 +1641,39 @@
 	else {
 		spin_unlock_irqrestore (&s->urb_list_lock, flags);
 		switch (usb_pipetype (urb->pipe)) {
-		case PIPE_ISOCHRONOUS:
-			ret = uhci_submit_iso_urb (urb);
+		case PIPE_ISOCHRONOUS:			
+			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+				if (urb->number_of_packets <= 0) {
+					ret = -EINVAL;
+					break;
+				}
+
+				bustime = usb_check_bandwidth (urb->dev, urb);
+				if (bustime < 0) {
+					ret = bustime;
+					break;
+				}
+
+				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);
+			}
 			break;
 		case PIPE_INTERRUPT:
-			ret = uhci_submit_int_urb (urb);
+			if (urb->bandwidth == 0) {      /* not yet checked/allocated */
+				bustime = usb_check_bandwidth (urb->dev, urb);
+				if (bustime < 0)
+					ret = bustime;
+				else {
+					ret = uhci_submit_int_urb(urb);
+					if (ret == 0)
+						usb_claim_bandwidth (urb->dev, urb, bustime, 0);
+				}
+			} else {        /* bandwidth is already set */
+				ret = uhci_submit_int_urb(urb);
+			}
 			break;
 		case PIPE_CONTROL:
 			ret = uhci_submit_control_urb (urb);
@@ -2029,6 +2086,7 @@
 
 	urb->actual_length = len;
 	urb->status = stat;
+	urb->dev=NULL;
 	if (urb->complete)
 		urb->complete (urb);
 	return 0;
@@ -2431,7 +2489,6 @@
 	int ret = 0;
 	urb_t *urb;
 
-
 	urb=list_entry (p, urb_t, urb_list);
 	//dbg("process_urb: found queued urb: %p", urb);
 
@@ -2455,6 +2512,17 @@
 
 	if (urb->status != -EINPROGRESS) {
 		int proceed = 0;
+		struct usb_device *usb_dev;
+		
+		usb_dev=urb->dev;
+
+		/* Release bandwidth for Interrupt or Iso transfers */
+		if (urb->bandwidth) {
+			if (usb_pipetype(urb->pipe)==PIPE_ISOCHRONOUS)
+				usb_release_bandwidth (urb->dev, urb, 1);
+			else if (usb_pipetype(urb->pipe)==PIPE_INTERRUPT && urb->interval)
+				usb_release_bandwidth (urb->dev, urb, 0);
+		}
 
 		dbg("dequeued urb: %p", urb);
 		dequeue_urb (s, urb);
@@ -2488,9 +2556,12 @@
 			// 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->dev = NULL;
 				urb->complete ((struct urb *) urb);
-				if (!proceed && is_ring && (urb->status != -ENOENT))
+				if (!proceed && is_ring && (urb->status != -ENOENT)) {
+					urb->dev=usb_dev;
 					uhci_submit_urb (urb);
+				}
 			}
 
 			if (proceed && urb->next) {
@@ -2506,11 +2577,13 @@
 
 				if (urb->complete) {
 					dbg("process_transfer: calling completion");
+					urb->dev=NULL;
 					urb->complete ((struct urb *) urb);
 				}
 			}
-
-			usb_dec_dev_use (urb->dev);
+			
+			urb->dev=NULL; // Just in case no completion was called
+			usb_dec_dev_use (usb_dev);
 			spin_unlock(&urb->lock);		
 			spin_lock(&s->urb_list_lock);
 		}
@@ -2842,6 +2915,7 @@
 	
 	if(!urb_priv_kmem) {
 		err("kmem_cache_create for urb_priv_t failed (out of memory)");
+		kmem_cache_destroy(uhci_desc_kmem);
 		return -ENOMEM;
 	}
 #endif	
@@ -2876,6 +2950,15 @@
 			i++;
 	}
 
+#ifdef DEBUG_SLAB
+	if (retval < 0 ) {
+		if (kmem_cache_destroy(urb_priv_kmem))
+			err("urb_priv_kmem remained");
+		if (kmem_cache_destroy(uhci_desc_kmem))
+			err("uhci_desc_kmem remained");
+	}
+#endif
+	
 	return retval;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)