Skip to content

Commit 00e2b86

Browse files
tmon-nordiccarlescufi
authored andcommitted
drivers: udc_dwc2: Process SETUP stage when allowed
DWC2 programming guide mentions that SETUP can only be processed by application after DOEPINTn.SETUP interrupt. Not respecting this requirement makes setting Global OUT NAK hang while waiting for GOUTNAKEFF interrupt until the host starts next control transfer. Global OUT NAK is necessary if the application ever wants to properly cancel any ongoing transfer. Change the processing to comply with the programming guide. Signed-off-by: Tomasz Moń <[email protected]>
1 parent fa912f4 commit 00e2b86

File tree

1 file changed

+38
-16
lines changed

1 file changed

+38
-16
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ static inline int dwc2_read_fifo(const struct device *dev, const uint8_t ep,
349349
struct net_buf *const buf, const size_t size)
350350
{
351351
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
352-
size_t len = MIN(size, net_buf_tailroom(buf));
352+
size_t len = buf ? MIN(size, net_buf_tailroom(buf)) : 0;
353353
const size_t d = sizeof(uint32_t);
354354

355355
/* FIFO access is always in 32-bit words */
@@ -389,7 +389,7 @@ static void dwc2_prep_rx(const struct device *dev,
389389

390390
doeptsiz = (1 << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) | cfg->mps;
391391
if (cfg->addr == USB_CONTROL_EP_OUT) {
392-
doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS);
392+
doeptsiz |= (3 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS);
393393
}
394394

395395
sys_write32(doeptsiz, doeptsiz_reg);
@@ -650,7 +650,7 @@ static void dwc2_on_bus_reset(const struct device *dev)
650650
}
651651
}
652652

653-
sys_write32(0UL, (mem_addr_t)&base->doepmsk);
653+
sys_write32(USB_DWC2_DOEPINT_SETUP, (mem_addr_t)&base->doepmsk);
654654
sys_set_bits((mem_addr_t)&base->gintmsk, USB_DWC2_GINTSTS_RXFLVL);
655655
sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL);
656656

@@ -668,22 +668,36 @@ static void dwc2_handle_enumdone(const struct device *dev)
668668
priv->enumspd = usb_dwc2_get_dsts_enumspd(dsts);
669669
}
670670

671-
static inline int dwc2_read_fifo_setup(const struct device *dev)
671+
static inline int dwc2_read_fifo_setup(const struct device *dev, uint8_t ep,
672+
const size_t size)
672673
{
673674
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
674675
struct udc_dwc2_data *const priv = udc_get_private(dev);
676+
size_t offset;
675677

676678
/* FIFO access is always in 32-bit words */
677679

680+
if (size != 8) {
681+
LOG_ERR("%d bytes SETUP", size);
682+
}
683+
678684
/*
679685
* We store the setup packet temporarily in the driver's private data
680686
* because there is always a race risk after the status stage OUT
681687
* packet from the host and the new setup packet. This is fine in
682688
* bottom-half processing because the events arrive in a queue and
683689
* there will be a next net_buf for the setup packet.
684690
*/
685-
sys_put_le32(sys_read32(UDC_DWC2_EP_FIFO(base, 0)), priv->setup);
686-
sys_put_le32(sys_read32(UDC_DWC2_EP_FIFO(base, 0)), &priv->setup[4]);
691+
for (offset = 0; offset < MIN(size, 8); offset += 4) {
692+
sys_put_le32(sys_read32(UDC_DWC2_EP_FIFO(base, ep)),
693+
&priv->setup[offset]);
694+
}
695+
696+
/* On protocol error simply discard extra data */
697+
while (offset < size) {
698+
sys_read32(UDC_DWC2_EP_FIFO(base, ep));
699+
offset += 4;
700+
}
687701

688702
return 0;
689703
}
@@ -706,26 +720,23 @@ static inline void dwc2_handle_rxflvl(const struct device *dev)
706720

707721
switch (pktsts) {
708722
case USB_DWC2_GRXSTSR_PKTSTS_SETUP:
709-
evt.type = DWC2_DRV_EVT_SETUP;
710-
711-
__ASSERT(evt.bcnt == 8, "Incorrect setup packet length");
712-
dwc2_read_fifo_setup(dev);
713-
714-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
723+
dwc2_read_fifo_setup(dev, evt.ep, evt.bcnt);
715724
break;
716725
case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA:
717726
evt.type = DWC2_DRV_EVT_DOUT;
718727
ep_cfg = udc_get_ep_cfg(dev, evt.ep);
719728

720729
buf = udc_buf_peek(dev, ep_cfg->addr);
730+
731+
/* RxFIFO data must be retrieved even when buf is NULL */
732+
dwc2_read_fifo(dev, evt.ep, buf, evt.bcnt);
733+
721734
if (buf == NULL) {
722735
LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr);
723736
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
724737
break;
725738
}
726739

727-
dwc2_read_fifo(dev, USB_CONTROL_EP_OUT, buf, evt.bcnt);
728-
729740
if (net_buf_tailroom(buf) && evt.bcnt == ep_cfg->mps) {
730741
dwc2_prep_rx(dev, ep_cfg, 0);
731742
} else {
@@ -734,9 +745,11 @@ static inline void dwc2_handle_rxflvl(const struct device *dev)
734745

735746
break;
736747
case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA_DONE:
737-
case USB_DWC2_GRXSTSR_PKTSTS_SETUP_DONE:
738748
LOG_DBG("RX pktsts DONE");
739749
break;
750+
case USB_DWC2_GRXSTSR_PKTSTS_SETUP_DONE:
751+
LOG_DBG("SETUP pktsts DONE");
752+
break;
740753
default:
741754
break;
742755
}
@@ -813,7 +826,6 @@ static inline void dwc2_handle_oepint(const struct device *dev)
813826
doepmsk = sys_read32((mem_addr_t)&base->doepmsk);
814827
daint = sys_read32((mem_addr_t)&base->daint);
815828

816-
/* No OUT interrupt expected in FIFO mode, just clear interrupt */
817829
for (uint8_t n = 0U; n < n_max; n++) {
818830
mem_addr_t doepint_reg = (mem_addr_t)&base->out_ep[n].doepint;
819831
uint32_t doepint;
@@ -826,6 +838,16 @@ static inline void dwc2_handle_oepint(const struct device *dev)
826838
sys_write32(status, doepint_reg);
827839

828840
LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status);
841+
842+
if (status & USB_DWC2_DOEPINT_SETUP) {
843+
struct dwc2_drv_event evt = {
844+
.type = DWC2_DRV_EVT_SETUP,
845+
.ep = USB_CONTROL_EP_OUT,
846+
.bcnt = 8,
847+
};
848+
849+
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
850+
}
829851
}
830852
}
831853

0 commit comments

Comments
 (0)