Skip to content

Commit 6e3f8d4

Browse files
tmon-nordickartben
authored andcommitted
drivers: udc_dwc2: Set control endpoint CNAK only when necessary
SETUP data is unconditionally ACKed by the controller. Other DATA packets sent to OUT control endpoint 0 (i.e. OUT Data Stage packets and OUT Status Stage packet) are ACKed by the device only if the endpoint was enabled with CNAK bit set. In Buffer DMA mode controller will lock up in following scenario: * OUT EP0 is not enabled, e.g. OUT Status Stage has finished * Host starts Control Write transfer, i.e. sends SETUP DATA0 and device ACKs (regardless if endpoint is enabled or not) * host sends OUT Data Stage (OUT DATA1) - software enables endpoint to be able to receive next SETUP data while host is transmitting the OUT token. If CNAK bit is set alongside the EPENA bit, the device will ACK the OUT Data Stage. If CNAK bit is not set, the device will NAK the OUT Data Stage. When the lockup occurs, from host perspective the OUT Data Stage packet was successfully transmitted. This can result in host starting IN Status Stage if there was only one OUT Data Stage packet. This in turn results in device never getting the DOEPTINT0 SetUp interrupt. Besides just not getting the SetUp interrupt, any subsequent control transfer won't be noticed by device at all. The lockup was first observed while stress testing. The host was issuing endless sequence of Control Write, Control Read, Control Write, Control Read, ... commands. When the controller did lock up in Buffer DMA mode, from host perspective the device was timing out all control transfers. Avoid the Buffer DMA lockup by setting CNAK bit only when necessary. Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
1 parent 05abdf5 commit 6e3f8d4

File tree

1 file changed

+26
-1
lines changed

1 file changed

+26
-1
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,32 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,
669669

670670
/* Clear NAK and set endpoint enable */
671671
doepctl = sys_read32(doepctl_reg);
672-
doepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK;
672+
doepctl |= USB_DWC2_DEPCTL_EPENA;
673+
if (cfg->addr == USB_CONTROL_EP_OUT) {
674+
struct udc_data *data = dev->data;
675+
676+
/* During OUT Data Stage every packet has to have CNAK set.
677+
* In Buffer DMA mode the OUT endpoint is armed during IN Data
678+
* Stage to accept either Status stage or new SETUP token. The
679+
* Status stage (premature or not) can only be received if CNAK
680+
* was set. In Completer mode the OUT endpoint armed during OUT
681+
* Status stage needs CNAK.
682+
*
683+
* Setting CNAK in other cases opens up possibility for Buffer
684+
* DMA controller to lock up completely if the endpoint is
685+
* enabled (to receive SETUP data) when the host is transmitting
686+
* subsequent control transfer OUT Data Stage packet (SETUP DATA
687+
* is unconditionally ACKed regardless of software state).
688+
*/
689+
if (data->stage == CTRL_PIPE_STAGE_DATA_OUT ||
690+
data->stage == CTRL_PIPE_STAGE_DATA_IN ||
691+
data->stage == CTRL_PIPE_STAGE_STATUS_OUT) {
692+
doepctl |= USB_DWC2_DEPCTL_CNAK;
693+
}
694+
} else {
695+
/* Non-control endpoint, set CNAK for all transfers */
696+
doepctl |= USB_DWC2_DEPCTL_CNAK;
697+
}
673698

674699
if (dwc2_ep_is_iso(cfg)) {
675700
xfersize = USB_MPS_TO_TPL(cfg->mps);

0 commit comments

Comments
 (0)