<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">ChangeSet 1.781.21.17, 2002/10/18 09:34:20-07:00, ddstreet@ieee.org

[PATCH] uhci interrupt resubmit fixes

After the interrupt queueing was added, I don't think the old way of
resetting interrupts will work anymore.  This patch changes it to simply
do a full unlink and resubmission automatically.  Note that since
usb_hcd_giveback_urb() is never called for a resubmitting interrupt URB,
that means whatever gets released in usb_hcd_giveback_urb() won't get
released for that URB.  The only way to work around that is call
usb_hcd_giveback_urb after the user unlinks in their completion handler,
which will call the completion handler again with -ECONNRESET
status...which wouldn't be all that bad, but the drivers have to expect
it.

Hopefully the interrupt resubmission will go away soon...


diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Fri Oct 18 14:43:10 2002
+++ b/drivers/usb/host/uhci-hcd.c	Fri Oct 18 14:43:10 2002
@@ -1230,26 +1230,6 @@
 	return ret;
 }
 
-static void uhci_reset_interrupt(struct uhci_hcd *uhci, struct urb *urb)
-{
-	struct urb_priv *urbp = (struct urb_priv *)urb-&gt;hcpriv;
-	struct uhci_td *td;
-	unsigned long flags;
-
-	spin_lock_irqsave(&amp;urb-&gt;lock, flags);
-
-	td = list_entry(urbp-&gt;td_list.next, struct uhci_td, list);
-
-	td-&gt;status = (td-&gt;status &amp; cpu_to_le32(0x2F000000)) | cpu_to_le32(TD_CTRL_ACTIVE | TD_CTRL_IOC);
-	td-&gt;token &amp;= ~cpu_to_le32(TD_TOKEN_TOGGLE);
-	td-&gt;token |= cpu_to_le32(usb_gettoggle(urb-&gt;dev, usb_pipeendpoint(urb-&gt;pipe), usb_pipeout(urb-&gt;pipe)) &lt;&lt; TD_TOKEN_TOGGLE_SHIFT);
-	usb_dotoggle(urb-&gt;dev, usb_pipeendpoint(urb-&gt;pipe), usb_pipeout(urb-&gt;pipe));
-
-	urb-&gt;status = -EINPROGRESS;
-
-	spin_unlock_irqrestore(&amp;urb-&gt;lock, flags);
-}
-
 static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb)
 {
 	int ret;
@@ -1568,16 +1548,16 @@
 		uhci_unlink_generic(uhci, urb);
 		break;
 	case PIPE_INTERRUPT:
-		/* Interrupts are an exception */
-		if (urb-&gt;interval)
-			goto out_complete;
-
 		/* Release bandwidth for Interrupt or Isoc. transfers */
 		/* Make sure we don't release if we have a queued URB */
 		spin_lock(&amp;uhci-&gt;frame_list_lock);
 		/* Spinlock needed ? */
 		if (list_empty(&amp;urbp-&gt;queue_list) &amp;&amp; urb-&gt;bandwidth)
 			usb_release_bandwidth(urb-&gt;dev, urb, 0);
+		else
+			/* bandwidth was passed on to queued URB, */
+			/* so don't let usb_unlink_urb() release it */
+			urb-&gt;bandwidth = 0;
 		spin_unlock(&amp;uhci-&gt;frame_list_lock);
 		uhci_unlink_generic(uhci, urb);
 		break;
@@ -1589,7 +1569,6 @@
 	/* Remove it from uhci-&gt;urb_list */
 	list_del_init(&amp;urbp-&gt;urb_list);
 
-out_complete:
 	uhci_add_complete(uhci, urb);
 
 out:
@@ -1658,6 +1637,13 @@
 	unsigned long flags;
 	struct urb_priv *urbp = urb-&gt;hcpriv;
 
+	/* If this is an interrupt URB that is being killed in urb-&gt;complete, */
+	/* then just set its status and return */
+	if (!urbp) {
+	  urb-&gt;status = -ECONNRESET;
+	  return 0;
+	}
+
 	spin_lock_irqsave(&amp;uhci-&gt;urb_list_lock, flags);
 
 	list_del_init(&amp;urbp-&gt;urb_list);
@@ -1820,38 +1806,42 @@
 	struct urb_priv *urbp = (struct urb_priv *)urb-&gt;hcpriv;
 	struct usb_device *dev = urb-&gt;dev;
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-	int killed, resubmit_interrupt, status;
+	int killed, resubmit_interrupt, status, ret;
 	unsigned long flags;
 
 	spin_lock_irqsave(&amp;urb-&gt;lock, flags);
 
  	killed = (urb-&gt;status == -ENOENT || urb-&gt;status == -ECONNRESET);
 	resubmit_interrupt = (usb_pipetype(urb-&gt;pipe) == PIPE_INTERRUPT &amp;&amp;
-			urb-&gt;interval);
+			urb-&gt;interval &amp;&amp; !killed);
 
 	status = urbp-&gt;status;
-	if (!resubmit_interrupt || killed)
-		/* We don't need urb_priv anymore */
-		uhci_destroy_urb_priv(uhci, urb);
+	uhci_destroy_urb_priv(uhci, urb);
 
 	if (!killed)
 		urb-&gt;status = status;
 	spin_unlock_irqrestore(&amp;urb-&gt;lock, flags);
 
-	if (resubmit_interrupt)
+	if (resubmit_interrupt) {
 		urb-&gt;complete(urb);
-	else
-		usb_hcd_giveback_urb(hcd, urb);
 
-	if (resubmit_interrupt)
 		/* Recheck the status. The completion handler may have */
-		/*  unlinked the resubmitting interrupt URB */
-		killed = (urb-&gt;status == -ENOENT || urb-&gt;status == -ECONNRESET);
+		/* unlinked the resubmitting interrupt URB */
+		/* Note that this doesn't do what usb_hcd_giveback_urb() */
+		/* normally does, so that doesn't ever get done. */
+		if (urb-&gt;status == -ECONNRESET) {
+			usb_put_urb(urb);
+			return;
+		}
 
-	if (resubmit_interrupt &amp;&amp; !killed) {
 		urb-&gt;dev = dev;
-		uhci_reset_interrupt(uhci, urb);
-	}
+		urb-&gt;status = -EINPROGRESS;
+		urb-&gt;actual_length = 0;
+		urb-&gt;bandwidth = 0;
+		if ((ret = uhci_urb_enqueue(&amp;uhci-&gt;hcd, urb, 0)))
+			printk(KERN_ERR __FILE__ ": could not resubmit interrupt URB : %d\n", ret);		  
+	} else
+		usb_hcd_giveback_urb(hcd, urb);
 }
 
 static void uhci_finish_completion(struct usb_hcd *hcd)
</pre></body></html>