Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 19 Mar 2009 21:02:05 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 159479 for review
Message-ID:  <200903192102.n2JL25iE035643@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=159479

Change 159479 by hselasky@hselasky_laptop001 on 2009/03/19 21:01:59

	
	USB controller + USB core
	
	- Workaround for buggy USB hardware not handling
	new SETUP packet before STATUS stage is complete!
	
	Reported by: Andrew Thompson

Affected files ...

.. //depot/projects/usb/src/sys/dev/usb/controller/ehci.c#8 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/ohci.c#5 edit
.. //depot/projects/usb/src/sys/dev/usb/controller/uhci.c#4 edit
.. //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#132 edit

Differences ...

==== //depot/projects/usb/src/sys/dev/usb/controller/ehci.c#8 (text+ko) ====

@@ -117,7 +117,7 @@
 	uint8_t	shortpkt;
 	uint8_t	auto_data_toggle;
 	uint8_t	setup_alt_next;
-	uint8_t	short_frames_ok;
+	uint8_t	last_frame;
 };
 
 void
@@ -1546,10 +1546,12 @@
 	uint32_t buf_offset;
 	uint32_t average;
 	uint32_t len_old;
+	uint32_t terminate;
 	uint8_t shortpkt_old;
 	uint8_t precompute;
 
-	qtd_altnext = htohc32(temp->sc, EHCI_LINK_TERMINATE);
+	terminate = htohc32(temp->sc, EHCI_LINK_TERMINATE);
+	qtd_altnext = terminate;
 	td_alt_next = NULL;
 	buf_offset = 0;
 	shortpkt_old = temp->shortpkt;
@@ -1696,14 +1698,17 @@
 		precompute = 0;
 
 		/* setup alt next pointer, if any */
-		if (temp->short_frames_ok) {
+		if (temp->last_frame) {
+			td_alt_next = NULL;
+			qtd_altnext = terminate;
+		} else {
+			/* we use this field internally */
+			td_alt_next = td_next;
 			if (temp->setup_alt_next) {
-				td_alt_next = td_next;
 				qtd_altnext = td_next->qtd_self;
+			} else {
+				qtd_altnext = terminate;
 			}
-		} else {
-			/* we use this field internally */
-			td_alt_next = td_next;
 		}
 
 		/* restore */
@@ -1746,8 +1751,7 @@
 	temp.td = NULL;
 	temp.td_next = td;
 	temp.qtd_status = 0;
-	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
-	temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+	temp.last_frame = 0;
 
 	if (xfer->flags_int.control_xfr) {
 		if (xfer->pipe->toggle_next) {
@@ -1780,7 +1784,14 @@
 			temp.len = xfer->frlengths[0];
 			temp.pc = xfer->frbuffers + 0;
 			temp.shortpkt = temp.len ? 1 : 0;
-
+			/* no "alt_next" for SETUP stage */
+			temp.setup_alt_next = 0;
+			/* check for last frame */
+			if (xfer->nframes == 1) {
+				/* no STATUS stage yet, SETUP is last */
+				if (xfer->flags_int.control_act)
+					temp.last_frame = 1;
+			}
 			ehci_setup_standard_chain_sub(&temp);
 		}
 		x = 1;
@@ -1788,6 +1799,8 @@
 		x = 0;
 	}
 
+	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+
 	while (x != xfer->nframes) {
 
 		/* DATA0 / DATA1 message */
@@ -1798,7 +1811,16 @@
 		x++;
 
 		if (x == xfer->nframes) {
-			temp.setup_alt_next = 0;
+			if (xfer->flags_int.control_xfr) {
+				/* no STATUS stage yet, DATA is last */
+				if (xfer->flags_int.control_act) {
+					temp.last_frame = 1;
+					temp.setup_alt_next = 0;
+				}
+			} else {
+				temp.last_frame = 1;
+				temp.setup_alt_next = 0;
+			}
 		}
 		/* keep previous data toggle and error count */
 
@@ -1855,6 +1877,8 @@
 		temp.len = 0;
 		temp.pc = NULL;
 		temp.shortpkt = 0;
+		temp.last_frame = 1;
+		temp.setup_alt_next = 0;
 
 		ehci_setup_standard_chain_sub(&temp);
 	}

==== //depot/projects/usb/src/sys/dev/usb/controller/ohci.c#5 (text+ko) ====

@@ -115,7 +115,7 @@
 	uint16_t max_frame_size;
 	uint8_t	shortpkt;
 	uint8_t	setup_alt_next;
-	uint8_t	short_frames_ok;
+	uint8_t last_frame;
 };
 
 static struct ohci_hcca *
@@ -1379,10 +1379,9 @@
 		precompute = 0;
 
 		/* setup alt next pointer, if any */
-		if (temp->short_frames_ok) {
-			if (temp->setup_alt_next) {
-				td_alt_next = td_next;
-			}
+		if (temp->last_frame) {
+			/* no alternate next */
+			td_alt_next = NULL;
 		} else {
 			/* we use this field internally */
 			td_alt_next = td_next;
@@ -1425,8 +1424,7 @@
 
 	temp.td = NULL;
 	temp.td_next = td;
-	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
-	temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+	temp.last_frame = 0;
 
 	methods = xfer->pipe->methods;
 
@@ -1441,7 +1439,14 @@
 			temp.len = xfer->frlengths[0];
 			temp.pc = xfer->frbuffers + 0;
 			temp.shortpkt = temp.len ? 1 : 0;
-
+			/* no "alt_next" for SETUP stage */
+			temp.setup_alt_next = 0;
+			/* check for last frame */
+			if (xfer->nframes == 1) {
+				/* no STATUS stage yet, SETUP is last */
+				if (xfer->flags_int.control_act)
+					temp.last_frame = 1;
+			}
 			ohci_setup_standard_chain_sub(&temp);
 
 			/*
@@ -1455,6 +1460,7 @@
 		x = 0;
 	}
 	temp.td_flags = htole32(OHCI_TD_NOCC | OHCI_TD_NOINTR);
+	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
 
 	/* set data toggle */
 
@@ -1482,7 +1488,16 @@
 		x++;
 
 		if (x == xfer->nframes) {
-			temp.setup_alt_next = 0;
+			if (xfer->flags_int.control_xfr) {
+				/* no STATUS stage yet, DATA is last */
+				if (xfer->flags_int.control_act) {
+					temp.last_frame = 1;
+					temp.setup_alt_next = 0;
+				}
+			} else {
+				temp.last_frame = 1;
+				temp.setup_alt_next = 0;
+			}
 		}
 		if (temp.len == 0) {
 
@@ -1523,11 +1538,14 @@
 		temp.len = 0;
 		temp.pc = NULL;
 		temp.shortpkt = 0;
+		temp.last_frame = 1;
+		temp.setup_alt_next = 0;
 
 		ohci_setup_standard_chain_sub(&temp);
 	}
 	td = temp.td;
 
+	/* Ensure that last TD is terminating: */
 	td->td_next = htole32(OHCI_TD_NEXT_END);
 	td->td_flags &= ~htole32(OHCI_TD_INTR_MASK);
 	td->td_flags |= htole32(OHCI_TD_SET_DI(1));

==== //depot/projects/usb/src/sys/dev/usb/controller/uhci.c#4 (text+ko) ====

@@ -124,7 +124,7 @@
 	uint16_t max_frame_size;
 	uint8_t	shortpkt;
 	uint8_t	setup_alt_next;
-	uint8_t	short_frames_ok;
+	uint8_t	last_frame;
 };
 
 extern struct usb2_bus_methods uhci_bus_methods;
@@ -1253,8 +1253,12 @@
 	td_self = td->td_self;
 	td_alt_next = td->alt_next;
 
+	if ((xfer->flags_int.control_xfr) &&
+	    (!xfer->flags_int.control_act) &&
+	    (((void *)td) == xfer->td_transfer_last))
+		goto skip; /* don't touch DT value on STATUS stage */
+
 	if ((td->td_token ^ td_token) & htole32(UHCI_TD_SET_DT(1))) {
-
 		/*
 	         * The data toggle is wrong and
 	         * we need to switch it !
@@ -1277,6 +1281,8 @@
 			}
 		}
 	}
+skip:
+
 	/* update the QH */
 	qh->qh_e_next = td_self;
 	usb2_pc_cpu_flush(qh->page_cache);
@@ -1631,10 +1637,8 @@
 		precompute = 0;
 
 		/* setup alt next pointer, if any */
-		if (temp->short_frames_ok) {
-			if (temp->setup_alt_next) {
-				td_alt_next = td_next;
-			}
+		if (temp->last_frame) {
+			td_alt_next = NULL;
 		} else {
 			/* we use this field internally */
 			td_alt_next = td_next;
@@ -1673,8 +1677,7 @@
 
 	temp.td = NULL;
 	temp.td_next = td;
-	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
-	temp.short_frames_ok = xfer->flags_int.short_frames_ok;
+	temp.last_frame = 0;
 
 	uhci_mem_layout_init(&temp.ml, xfer);
 
@@ -1707,7 +1710,14 @@
 			temp.len = xfer->frlengths[0];
 			temp.ml.buf_pc = xfer->frbuffers + 0;
 			temp.shortpkt = temp.len ? 1 : 0;
-
+			/* no "alt_next" for SETUP stage */
+			temp.setup_alt_next = 0;
+			/* check for last frame */
+			if (xfer->nframes == 1) {
+				/* no STATUS stage yet, SETUP is last */
+				if (xfer->flags_int.control_act)
+					temp.last_frame = 1;
+			}
 			uhci_setup_standard_chain_sub(&temp);
 		}
 		x = 1;
@@ -1715,6 +1725,8 @@
 		x = 0;
 	}
 
+	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
+
 	while (x != xfer->nframes) {
 
 		/* DATA0 / DATA1 message */
@@ -1725,7 +1737,16 @@
 		x++;
 
 		if (x == xfer->nframes) {
-			temp.setup_alt_next = 0;
+			if (xfer->flags_int.control_xfr) {
+				/* no STATUS stage yet, DATA is last */
+				if (xfer->flags_int.control_act) {
+					temp.last_frame = 1;
+					temp.setup_alt_next = 0;
+				}
+			} else {
+				temp.last_frame = 1;
+				temp.setup_alt_next = 0;
+			}
 		}
 		/*
 		 * Keep previous data toggle,
@@ -1780,11 +1801,14 @@
 		temp.len = 0;
 		temp.ml.buf_pc = NULL;
 		temp.shortpkt = 0;
+		temp.last_frame = 1;
+		temp.setup_alt_next = 0;
 
 		uhci_setup_standard_chain_sub(&temp);
 	}
 	td = temp.td;
 
+	/* Ensure that last TD is terminating: */
 	td->td_next = htole32(UHCI_PTR_T);
 
 	/* set interrupt bit */

==== //depot/projects/usb/src/sys/dev/usb/usb_transfer.c#132 (text+ko) ====

@@ -1504,6 +1504,15 @@
 
 			if (xfer->flags.short_xfer_ok) {
 				xfer->flags_int.short_xfer_ok = 1;
+				/*
+				 * Due to sometimes buggy device side
+				 * firmware we need to do a STATUS
+				 * stage in case of short control
+				 * transfers in USB host mode, via
+				 * the "alt_next" feature!
+				 */
+				if (udev->flags.usb2_mode == USB_MODE_HOST)
+					xfer->flags_int.short_frames_ok = 1;
 			}
 		} else {
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903192102.n2JL25iE035643>