svn commit: r284015 - stable/10/sys/dev/usb/controller
Hans Petter Selasky
hselasky at FreeBSD.org
Fri Jun 5 07:17:15 UTC 2015
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);
More information about the svn-src-all
mailing list