Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 2 Jun 2015 17:40:53 +0000 (UTC)
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r283922 - head/sys/dev/usb/controller
Message-ID:  <201506021740.t52Her4Q046356@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: hselasky
Date: Tue Jun  2 17:40:52 2015
New Revision: 283922
URL: https://svnweb.freebsd.org/changeset/base/283922

Log:
  Fix for control endpoint handling in the DWC OTG driver. The data
  stage processing is only allowed after the setup complete event has
  been received. Else a race may occur and the OUT data can be corrupted.
  While at it ensure resetting a FIFO has the required wait loop.
  
  MFC after:	3 days

Modified:
  head/sys/dev/usb/controller/dwc_otg.c

Modified: head/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- head/sys/dev/usb/controller/dwc_otg.c	Tue Jun  2 17:11:47 2015	(r283921)
+++ head/sys/dev/usb/controller/dwc_otg.c	Tue Jun  2 17:40:52 2015	(r283922)
@@ -180,6 +180,22 @@ dwc_otg_get_hw_ep_profile(struct usb_dev
 		*ppf = NULL;
 }
 
+static void
+dwc_otg_tx_fifo_reset(struct dwc_otg_softc *sc, uint32_t value)
+{
+	uint32_t temp;
+
+  	/* reset FIFO */
+	DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL, value);
+
+	/* wait for reset to complete */
+	for (temp = 0; temp != 16; temp++) {
+		value = DWC_OTG_READ_4(sc, DOTG_GRSTCTL);
+		if (!(value & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)))
+			break;
+	}
+}
+
 static int
 dwc_otg_init_fifo(struct dwc_otg_softc *sc, uint8_t mode)
 {
@@ -331,12 +347,11 @@ dwc_otg_init_fifo(struct dwc_otg_softc *
 	}
 
 	/* reset RX FIFO */
-	DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
-	    GRSTCTL_RXFFLSH);
+	dwc_otg_tx_fifo_reset(sc, GRSTCTL_RXFFLSH);
 
 	if (mode != DWC_MODE_OTG) {
 		/* reset all TX FIFOs */
-		DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+		dwc_otg_tx_fifo_reset(sc,
 		    GRSTCTL_TXFIFO(0x10) |
 		    GRSTCTL_TXFFLSH);
 	} else {
@@ -947,15 +962,21 @@ dwc_otg_setup_rx(struct dwc_otg_softc *s
 	if (GRXSTSRD_CHNUM_GET(sc->sc_last_rx_status) != 0)
 		goto not_complete;
 
-	if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
-	    GRXSTSRD_DPID_DATA0) {
+	if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+	    GRXSTSRD_STP_DATA) {
+		if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
+		    GRXSTSRD_STP_COMPLETE || td->remainder != 0) {
+			/* release FIFO */
+			dwc_otg_common_rx_ack(sc);
+			goto not_complete;
+		}
 		/* release FIFO */
 		dwc_otg_common_rx_ack(sc);
-		goto not_complete;
+		return (0);     /* complete */
 	}
 
-	if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) !=
-	    GRXSTSRD_STP_DATA) {
+	if ((sc->sc_last_rx_status & GRXSTSRD_DPID_MASK) !=
+	    GRXSTSRD_DPID_DATA0) {
 		/* release FIFO */
 		dwc_otg_common_rx_ack(sc);
 		goto not_complete;
@@ -969,14 +990,6 @@ dwc_otg_setup_rx(struct dwc_otg_softc *s
 	/* get the packet byte count */
 	count = GRXSTSRD_BCNT_GET(sc->sc_last_rx_status);
 
-	/* verify data length */
-	if (count != td->remainder) {
-		DPRINTFN(0, "Invalid SETUP packet "
-		    "length, %d bytes\n", count);
-		/* release FIFO */
-		dwc_otg_common_rx_ack(sc);
-		goto not_complete;
-	}
 	if (count != sizeof(req)) {
 		DPRINTFN(0, "Unsupported SETUP packet "
 		    "length, %d bytes\n", count);
@@ -1002,43 +1015,27 @@ dwc_otg_setup_rx(struct dwc_otg_softc *s
 	}
 
 	/* don't send any data by default */
-	DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0),
-	    DXEPTSIZ_SET_NPKT(0) | 
-	    DXEPTSIZ_SET_NBYTES(0));
-
-	temp = sc->sc_in_ctl[0];
-
-	/* enable IN endpoint */
-	DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
-	    temp | DIEPCTL_EPENA);
-	DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
-	    temp | DIEPCTL_SNAK);
+	DWC_OTG_WRITE_4(sc, DOTG_DIEPTSIZ(0), DIEPCTL_EPDIS);
+	DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0), DOEPCTL_EPDIS);
 
 	/* reset IN endpoint buffer */
-	DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+	dwc_otg_tx_fifo_reset(sc,
 	    GRSTCTL_TXFIFO(0) |
 	    GRSTCTL_TXFFLSH);
 
 	/* acknowledge RX status */
 	dwc_otg_common_rx_ack(sc);
-	return (0);			/* complete */
+	td->did_stall = 1;
 
 not_complete:
 	/* abort any ongoing transfer, before enabling again */
-
-	temp = sc->sc_out_ctl[0];
-
-	temp |= DOEPCTL_EPENA |
-	    DOEPCTL_SNAK;
-
-	/* enable OUT endpoint */
-	DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0), temp);
-
 	if (!td->did_stall) {
 		td->did_stall = 1;
 
 		DPRINTFN(5, "stalling IN and OUT direction\n");
 
+		temp = sc->sc_out_ctl[0];
+
 		/* set stall after enabling endpoint */
 		DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(0),
 		    temp | DOEPCTL_STALL);
@@ -1049,13 +1046,6 @@ not_complete:
 		DWC_OTG_WRITE_4(sc, DOTG_DIEPCTL(0),
 		    temp | DIEPCTL_STALL);
 	}
-
-	/* setup number of buffers to receive */
-	DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
-	    DXEPTSIZ_SET_MULTI(3) |
-	    DXEPTSIZ_SET_NPKT(1) | 
-	    DXEPTSIZ_SET_NBYTES(sizeof(req)));
-
 	return (1);			/* not complete */
 }
 
@@ -1499,7 +1489,9 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc
 
 	/* check for SETUP packet */
 	if ((sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
-	    GRXSTSRD_STP_DATA) {
+	    GRXSTSRD_STP_DATA ||
+	    (sc->sc_last_rx_status & GRXSTSRD_PKTSTS_MASK) ==
+	    GRXSTSRD_STP_COMPLETE) {
 		if (td->remainder == 0) {
 			/*
 			 * We are actually complete and have
@@ -1584,15 +1576,10 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc
 
 not_complete:
 
-	temp = sc->sc_out_ctl[td->ep_no];
-
-	temp |= DOEPCTL_EPENA | DOEPCTL_CNAK;
-
-	DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp);
-
 	/* enable SETUP and transfer complete interrupt */
 	if (td->ep_no == 0) {
 		DWC_OTG_WRITE_4(sc, DOTG_DOEPTSIZ(0),
+		    DXEPTSIZ_SET_MULTI(3) |
 		    DXEPTSIZ_SET_NPKT(1) | 
 		    DXEPTSIZ_SET_NBYTES(td->max_packet_size));
 	} else {
@@ -1601,8 +1588,12 @@ not_complete:
 		    DXEPTSIZ_SET_MULTI(1) |
 		    DXEPTSIZ_SET_NPKT(4) | 
 		    DXEPTSIZ_SET_NBYTES(4 *
-		    ((td->max_packet_size + 3) & ~3)));
+		        ((td->max_packet_size + 3) & ~3)));
 	}
+	temp = sc->sc_out_ctl[td->ep_no];
+	DWC_OTG_WRITE_4(sc, DOTG_DOEPCTL(td->ep_no), temp |
+	    DOEPCTL_EPENA | DOEPCTL_CNAK);
+
 	return (1);			/* not complete */
 }
 
@@ -2004,7 +1995,9 @@ repeat:
 	    (GRXSTSRD_CHNUM_GET(temp) == 0)) {
 
 		if ((temp & GRXSTSRD_PKTSTS_MASK) !=
-		    GRXSTSRD_STP_DATA) {
+		    GRXSTSRD_STP_DATA &&
+		    (temp & GRXSTSRD_PKTSTS_MASK) !=
+		    GRXSTSRD_STP_COMPLETE) {
 
 			/* dump data - wrong direction */
 			dwc_otg_common_rx_ack(sc);
@@ -2209,7 +2202,9 @@ not_complete:
 	    (GRXSTSRD_CHNUM_GET(temp) == 0)) {
 
 		if ((temp & GRXSTSRD_PKTSTS_MASK) ==
-		    GRXSTSRD_STP_DATA) {
+		    GRXSTSRD_STP_DATA ||
+		    (temp & GRXSTSRD_PKTSTS_MASK) ==
+		    GRXSTSRD_STP_COMPLETE) {
 			DPRINTFN(5, "faking complete\n");
 			/*
 			 * Race condition: We are complete!
@@ -2634,6 +2629,7 @@ repeat:
 
 			/* non-data messages we simply skip */
 			if (temp != GRXSTSRD_STP_DATA &&
+			    temp != GRXSTSRD_STP_COMPLETE &&
 			    temp != GRXSTSRD_OUT_DATA) {
 				dwc_otg_common_rx_ack(sc);
 				goto repeat;
@@ -3669,7 +3665,7 @@ dwc_otg_clear_stall_sub_locked(struct dw
 
 	/* we only reset the transmit FIFO */
 	if (ep_dir) {
-		DWC_OTG_WRITE_4(sc, DOTG_GRSTCTL,
+		dwc_otg_tx_fifo_reset(sc,
 		    GRSTCTL_TXFIFO(ep_no) |
 		    GRSTCTL_TXFFLSH);
 



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