patch-2.3.99-pre1 linux/drivers/usb/uhci.c

Next file: linux/drivers/usb/uhci.h
Previous file: linux/drivers/usb/serial/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.51/linux/drivers/usb/uhci.c linux/drivers/usb/uhci.c
@@ -236,7 +236,7 @@
 static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
 	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-	struct uhci_td *td, *prevtd = NULL;
+	struct uhci_td *td, *prevtd;
 
 	if (!urbp)
 		return;
@@ -617,6 +617,8 @@
 	return -EINPROGRESS;
 }
 
+static int usb_control_retrigger_status(urb_t *urb);
+
 static int uhci_result_control(urb_t *urb)
 {
 	struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@
 	if (!td)
 		return -EINVAL;
 
+	if (urbp->short_control_packet)
+		goto status_phase;
+
 	/* The first TD is the SETUP phase, check the status, but skip */
 	/*  the count */
 	status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@
 
 		/* If SPD is set then we received a short packet */
 		/*  There will be no status phase at the end */
-		/* FIXME: Re-setup the queue to run the STATUS phase? */
 		if ((td->status & TD_CTRL_SPD) &&
 		    (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
-			return 0;
+			return usb_control_retrigger_status(urb);
 
 		if (status)
 			goto td_error;
@@ -664,12 +668,13 @@
 		td = td->list.next;
 	}
 
+status_phase:
 	/* Control status phase */
 	status = uhci_status_bits(td->status);
 
 	/* APC BackUPS Pro kludge */
-	/* It tries to send all of the descriptor instead of */
-	/*  the amount we requested */
+	/* It tries to send all of the descriptor instead of the amount */
+	/*  we requested */
 	if (td->status & TD_CTRL_IOC &&
 	    status & TD_CTRL_ACTIVE &&
 	    status & TD_CTRL_NAK)
@@ -698,6 +703,47 @@
 	    			uhci_packetout(td->info));
 
 	return uhci_map_status(status, uhci_packetout(td->info));
+}
+
+static int usb_control_retrigger_status(urb_t *urb)
+{
+	struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+	struct uhci *uhci = urb->dev->bus->hcpriv;
+	struct uhci_td *td, *nexttd;
+
+	urbp->short_control_packet = 1;
+
+	/* Delete all of the TD's except for the status TD at the end */
+	td = urbp->list.begin;
+	while (td && td->list.next) {
+		nexttd = td->list.next;
+
+		uhci_remove_td_from_urb(urb, td);
+
+		uhci_remove_td(uhci, td);
+
+		uhci_free_td(td);
+		
+		td = nexttd;
+	}
+
+	/* Create a new QH to avoid pointer overwriting problems */
+	uhci_remove_qh(uhci, urbp->qh);
+
+	urbp->qh = uhci_alloc_qh(urb->dev);
+	if (!urbp->qh)
+		return -ENOMEM;
+
+	/* One TD, who cares about Breadth first? */
+	uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+	/* Low speed or small transfers gets a different queue and treatment */
+	if (urb->pipe & TD_CTRL_LS)
+		uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+	else
+		uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+	return -EINPROGRESS;
 }
 
 /*

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