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
This commit is contained in:
parent
af8f4185fb
commit
b4df5b00f2
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=283922
@ -180,6 +180,22 @@ dwc_otg_get_hw_ep_profile(struct usb_device *udev,
|
||||
*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 *sc, uint8_t mode)
|
||||
}
|
||||
|
||||
/* 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 *sc, struct dwc_otg_td *td)
|
||||
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) {
|
||||
/* release FIFO */
|
||||
dwc_otg_common_rx_ack(sc);
|
||||
goto not_complete;
|
||||
}
|
||||
|
||||
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);
|
||||
return (0); /* complete */
|
||||
}
|
||||
|
||||
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 *sc, struct dwc_otg_td *td)
|
||||
/* 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 *sc, struct dwc_otg_td *td)
|
||||
}
|
||||
|
||||
/* 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 @@ dwc_otg_setup_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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, struct dwc_otg_td *td)
|
||||
|
||||
/* 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, struct dwc_otg_td *td)
|
||||
|
||||
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 @@ dwc_otg_data_rx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
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 @@ dwc_otg_data_tx(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
(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 @@ dwc_otg_data_tx_sync(struct dwc_otg_softc *sc, struct dwc_otg_td *td)
|
||||
(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 @@ dwc_otg_interrupt_poll_locked(struct dwc_otg_softc *sc)
|
||||
|
||||
/* 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 dwc_otg_softc *sc, uint32_t mps,
|
||||
|
||||
/* 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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user