Author: hselasky
Date: Fri Jun  5 07:17:14 2015
New Revision: 284015
URL: https://svnweb.freebsd.org/changeset/base/284015

Log:
  MFC r283922:
  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.

Modified:
  stable/10/sys/dev/usb/controller/dwc_otg.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/dev/usb/controller/dwc_otg.c
==============================================================================
--- stable/10/sys/dev/usb/controller/dwc_otg.c  Fri Jun  5 06:49:08 2015        
(r284014)
+++ stable/10/sys/dev/usb/controller/dwc_otg.c  Fri Jun  5 07:17:14 2015        
(r284015)
@@ -181,6 +181,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)
 {
@@ -335,12 +351,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 {
@@ -951,15 +966,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;
@@ -973,14 +994,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);
@@ -1006,43 +1019,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);
@@ -1053,13 +1050,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 */
 }
 
@@ -1503,7 +1493,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
@@ -1588,15 +1580,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 {
@@ -1605,8 +1592,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 */
 }
 
@@ -2008,7 +1999,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);
@@ -2213,7 +2206,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!
@@ -2638,6 +2633,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;
@@ -3668,7 +3664,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);
 
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to