Skip to content

Commit f58eaa6

Browse files
committed
[nrf fromlist] drivers: udc_dwc2: Set EP0 IN CNAK only when necessary
Adhere to Programming Guide with regards to when the DIEPCTL0.CNAK is set. This allows the driver to survive abusive control transfers with extremely short host timeouts. Upstream PR #: 92892 Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
1 parent 57e819a commit f58eaa6

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ struct udc_dwc2_data {
139139
unsigned int hibernated : 1;
140140
unsigned int enumdone : 1;
141141
unsigned int enumspd : 2;
142+
unsigned int pending_dout_feed : 1;
142143
enum dwc2_suspend_type suspend_type;
143144
/* Number of endpoints including control endpoint */
144145
uint8_t numdeveps;
@@ -428,10 +429,28 @@ static void dwc2_ensure_setup_ready(const struct device *dev)
428429
if (dwc2_in_completer_mode(dev)) {
429430
/* In Completer mode EP0 can always receive SETUP data */
430431
return;
432+
} else {
433+
struct udc_dwc2_data *const priv = udc_get_private(dev);
434+
435+
/* Enable EP0 OUT only if there is no pending EP0 IN transfer
436+
* after which the stack has to enable EP0 OUT.
437+
*/
438+
if (!priv->pending_dout_feed) {
439+
dwc2_ctrl_feed_dout(dev, 8);
440+
}
431441
}
442+
}
432443

433-
if (!udc_buf_peek(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN))) {
434-
dwc2_ctrl_feed_dout(dev, 8);
444+
static void dwc2_clear_control_in_nak(const struct device *dev)
445+
{
446+
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
447+
mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[0].diepctl;
448+
uint32_t diepctl = sys_read32(diepctl_reg);
449+
450+
if (diepctl & USB_DWC2_DEPCTL_NAKSTS) {
451+
diepctl &= ~USB_DWC2_DEPCTL_EPENA;
452+
diepctl |= USB_DWC2_DEPCTL_CNAK;
453+
sys_write32(diepctl, diepctl_reg);
435454
}
436455
}
437456

@@ -590,7 +609,11 @@ static int dwc2_tx_fifo_write(const struct device *dev,
590609
}
591610

592611
/* Clear NAK and set endpoint enable */
593-
diepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK;
612+
diepctl |= USB_DWC2_DEPCTL_EPENA;
613+
if (cfg->addr != USB_CONTROL_EP_IN) {
614+
/* Non-control endpoint, set CNAK for all transfers */
615+
diepctl |= USB_DWC2_DEPCTL_CNAK;
616+
}
594617
sys_write32(diepctl, diepctl_reg);
595618

596619
/* Clear IN Endpoint NAK Effective interrupt in case it was set */
@@ -776,6 +799,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,
776799
static void dwc2_handle_xfer_next(const struct device *dev,
777800
struct udc_ep_config *const cfg)
778801
{
802+
struct udc_dwc2_data *const priv = udc_get_private(dev);
779803
struct net_buf *buf;
780804

781805
buf = udc_buf_peek(cfg);
@@ -803,7 +827,8 @@ static void dwc2_handle_xfer_next(const struct device *dev,
803827
* avoid race condition where the next Control Write
804828
* Transfer Data Stage is received into the buffer.
805829
*/
806-
if (dwc2_in_buffer_dma_mode(dev)) {
830+
if (dwc2_in_buffer_dma_mode(dev) && priv->pending_dout_feed) {
831+
priv->pending_dout_feed = 0;
807832
dwc2_ctrl_feed_dout(dev, 8);
808833
}
809834
}
@@ -872,18 +897,39 @@ static int dwc2_handle_evt_setup(const struct device *dev)
872897
/* Allocate and feed buffer for data OUT stage */
873898
LOG_DBG("s:%p|feed for -out-", buf);
874899

900+
priv->pending_dout_feed = 0;
901+
902+
if (dwc2_in_completer_mode(dev)) {
903+
/* Programming Guide does not clearly describe to clear
904+
* control IN endpoint NAK for Control Write Transfers
905+
* when operating in Completer mode. Set CNAK here,
906+
* because IN endpoint is not armed at this point and
907+
* forced NAKs are not necessary. IN Status stage will
908+
* only finish after IN endpoint is armed.
909+
*/
910+
dwc2_clear_control_in_nak(dev);
911+
}
912+
875913
err = dwc2_ctrl_feed_dout(dev, udc_data_stage_length(buf));
876914
if (err == -ENOMEM) {
877915
err = udc_submit_ep_event(dev, buf, err);
878916
}
879917
} else if (udc_ctrl_stage_is_data_in(dev)) {
880918
LOG_DBG("s:%p|feed for -in-status", buf);
881919

920+
dwc2_clear_control_in_nak(dev);
921+
882922
err = udc_ctrl_submit_s_in_status(dev);
923+
924+
priv->pending_dout_feed = 1;
883925
} else {
884926
LOG_DBG("s:%p|feed >setup", buf);
885927

928+
dwc2_clear_control_in_nak(dev);
929+
886930
err = udc_ctrl_submit_s_status(dev);
931+
932+
priv->pending_dout_feed = 1;
887933
}
888934

889935
return err;
@@ -892,6 +938,7 @@ static int dwc2_handle_evt_setup(const struct device *dev)
892938
static inline int dwc2_handle_evt_dout(const struct device *dev,
893939
struct udc_ep_config *const cfg)
894940
{
941+
struct udc_dwc2_data *const priv = udc_get_private(dev);
895942
struct udc_data *data = dev->data;
896943
struct net_buf *buf;
897944
int err = 0;
@@ -924,6 +971,8 @@ static inline int dwc2_handle_evt_dout(const struct device *dev,
924971

925972
if (udc_ctrl_stage_is_status_in(dev)) {
926973
err = udc_ctrl_submit_s_out_status(dev, buf);
974+
975+
priv->pending_dout_feed = 1;
927976
}
928977

929978
if (data->stage == CTRL_PIPE_STAGE_ERROR) {
@@ -1623,6 +1672,15 @@ static void udc_dwc2_ep_disable(const struct device *dev,
16231672
/* For IN endpoints STALL is set in addition to SNAK */
16241673
dxepctl |= USB_DWC2_DEPCTL_STALL;
16251674
}
1675+
1676+
/* EP0 status stage enabled, but CNAK not yet written.
1677+
* Set EPDIS to prevent timeout.
1678+
*/
1679+
if ((dxepctl & USB_DWC2_DEPCTL_NAKSTS) &&
1680+
(dxepctl & USB_DWC2_DEPCTL_EPENA)) {
1681+
dxepctl |= USB_DWC2_DEPCTL_EPDIS;
1682+
}
1683+
16261684
sys_write32(dxepctl, dxepctl_reg);
16271685

16281686
/* Endpoint gets disabled in INEPNAKEFF handler */
@@ -2751,13 +2809,15 @@ static inline void dwc2_handle_oepint(const struct device *dev)
27512809
}
27522810

27532811
if (status & USB_DWC2_DOEPINT_STSPHSERCVD) {
2754-
/* Driver doesn't need any special handling, but it is
2755-
* mandatory that the bit is cleared in Buffer DMA mode.
2812+
/* Allow IN Status stage after control transfer with
2813+
* data stage from device to host. Clear the CNAK and
2814+
* this interrupt bit is mandatory in Buffer DMA mode.
27562815
* If the bit is not cleared (i.e. when this interrupt
27572816
* bit is masked), then SETUP interrupts will cease
27582817
* after first control transfer with data stage from
27592818
* device to host.
27602819
*/
2820+
dwc2_clear_control_in_nak(dev);
27612821
}
27622822

27632823
if (status & USB_DWC2_DOEPINT_XFERCOMPL) {

0 commit comments

Comments
 (0)