<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.308   -&gt; 1.309  
#	  drivers/usb/uhci.c	1.23    -&gt; 1.24   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 02/04/02	johannes@erdfelt.com	1.309
# USB uhci
# 
# bugfixes
# --------------------------------------------
#
diff -Nru a/drivers/usb/uhci.c b/drivers/usb/uhci.c
--- a/drivers/usb/uhci.c	Wed Apr  3 10:48:10 2002
+++ b/drivers/usb/uhci.c	Wed Apr  3 10:48:10 2002
@@ -336,6 +336,7 @@
 	qh-&gt;link = UHCI_PTR_TERM;
 
 	qh-&gt;dev = dev;
+	qh-&gt;urbp = NULL;
 
 	INIT_LIST_HEAD(&amp;qh-&gt;list);
 	INIT_LIST_HEAD(&amp;qh-&gt;remove_list);
@@ -410,20 +411,19 @@
 	spin_unlock_irqrestore(&amp;uhci-&gt;frame_list_lock, flags);
 }
 
-static void uhci_remove_qh(struct uhci *uhci, struct urb *urb)
+static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh)
 {
-	struct urb_priv *urbp = (struct urb_priv *)urb-&gt;hcpriv;
 	unsigned long flags;
-	struct uhci_qh *qh = urbp-&gt;qh, *pqh;
+	struct uhci_qh *pqh;
 
 	if (!qh)
 		return;
 
+	qh-&gt;urbp = NULL;
+
 	/* Only go through the hoops if it's actually linked in */
 	spin_lock_irqsave(&amp;uhci-&gt;frame_list_lock, flags);
 	if (!list_empty(&amp;qh-&gt;list)) {
-		qh-&gt;urbp = NULL;
-
 		pqh = list_entry(qh-&gt;list.prev, struct uhci_qh, list);
 
 		if (pqh-&gt;urbp) {
@@ -1040,7 +1040,7 @@
 	urbp-&gt;short_control_packet = 1;
 
 	/* Create a new QH to avoid pointer overwriting problems */
-	uhci_remove_qh(uhci, urb);
+	uhci_remove_qh(uhci, urbp-&gt;qh);
 
 	/* Delete all of the TD's except for the status TD at the end */
 	head = &amp;urbp-&gt;td_list;
@@ -1259,20 +1259,29 @@
 			data);
 
 		data += pktsze;
-		len -= pktsze;
+		len -= maxsze;
 
 		usb_dotoggle(urb-&gt;dev, usb_pipeendpoint(urb-&gt;pipe),
 			usb_pipeout(urb-&gt;pipe));
 	} while (len &gt; 0);
 
+	/*
+	 * USB_ZERO_PACKET means adding a 0-length packet, if
+	 * direction is OUT and the transfer_length was an
+	 * exact multiple of maxsze, hence
+	 * (len = transfer_length - N * maxsze) == 0
+	 * however, if transfer_length == 0, the zero packet
+	 * was already prepared above.
+	 */
 	if (usb_pipeout(urb-&gt;pipe) &amp;&amp; (urb-&gt;transfer_flags &amp; USB_ZERO_PACKET) &amp;&amp;
-	   urb-&gt;transfer_buffer_length) {
+	   !len &amp;&amp; urb-&gt;transfer_buffer_length) {
 		td = uhci_alloc_td(uhci, urb-&gt;dev);
 		if (!td)
 			return -ENOMEM;
 
 		uhci_add_td_to_urb(urb, td);
-		uhci_fill_td(td, status, destination | UHCI_NULL_DATA_SIZE |
+		uhci_fill_td(td, status, destination |
+			(UHCI_NULL_DATA_SIZE &lt;&lt; 21) |
 			(usb_gettoggle(urb-&gt;dev, usb_pipeendpoint(urb-&gt;pipe),
 			 usb_pipeout(urb-&gt;pipe)) &lt;&lt; TD_TOKEN_TOGGLE),
 			data);
@@ -1728,7 +1737,8 @@
 	uhci_delete_queued_urb(uhci, urb);
 
 	/* The interrupt loop will reclaim the QH's */
-	uhci_remove_qh(uhci, urb);
+	uhci_remove_qh(uhci, urbp-&gt;qh);
+	urbp-&gt;qh = NULL;
 }
 
 static int uhci_unlink_urb(struct urb *urb)
@@ -2351,15 +2361,15 @@
 	urb-&gt;dev = NULL;
 	spin_unlock_irqrestore(&amp;urb-&gt;lock, flags);
 
-	if (urb-&gt;complete) {
+	if (urb-&gt;complete)
 		urb-&gt;complete(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 == -ECONNABORTED ||
 			  urb-&gt;status == -ECONNRESET);
-	}
 
 	if (resubmit_interrupt &amp;&amp; !killed) {
 		urb-&gt;dev = dev;
</pre></body></html>