Skip to content

Commit fed00f7

Browse files
tmon-nordicgithub-actions[bot]
authored andcommitted
[nrf fromtree] drivers: udc_dwc2: Fix large control write transfers
The DOEPTSIZ0 XferSize field is 7 bits long, meaning that maximum single transfer can be 127 bytes long. Configure the control write (OUT) transfers considering the XferSize field size to support transfers with data stage larger than 127 bytes. Signed-off-by: Tomasz Moń <[email protected]> (cherry picked from commit 18be1d0) (cherry picked from commit 817a6d2)
1 parent 3b62269 commit fed00f7

File tree

1 file changed

+35
-4
lines changed

1 file changed

+35
-4
lines changed

drivers/usb/udc/udc_dwc2.c

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,28 @@ static uint32_t dwc2_get_iept_xfersize(const struct device *dev, const uint32_t
250250
}
251251
}
252252

253+
static uint32_t dwc2_get_oept_pktctn(const struct device *dev, const uint32_t idx)
254+
{
255+
struct udc_dwc2_data *const priv = udc_get_private(dev);
256+
257+
if (idx == 0) {
258+
return usb_dwc2_get_doeptsiz0_pktcnt(UINT32_MAX);
259+
} else {
260+
return priv->max_pktcnt;
261+
}
262+
}
263+
264+
static uint32_t dwc2_get_oept_xfersize(const struct device *dev, const uint32_t idx)
265+
{
266+
struct udc_dwc2_data *const priv = udc_get_private(dev);
267+
268+
if (idx == 0) {
269+
return usb_dwc2_get_doeptsiz0_xfersize(UINT32_MAX);
270+
} else {
271+
return priv->max_xfersize;
272+
}
273+
}
274+
253275
static void dwc2_flush_rx_fifo(const struct device *dev)
254276
{
255277
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
@@ -582,18 +604,23 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,
582604
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
583605
mem_addr_t doeptsiz_reg = (mem_addr_t)&base->out_ep[ep_idx].doeptsiz;
584606
mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx);
607+
uint32_t max_xfersize, max_pktcnt;
608+
const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps);
585609
uint32_t pktcnt;
586610
uint32_t doeptsiz;
587611
uint32_t doepctl;
588612
uint32_t xfersize;
589613

614+
max_xfersize = dwc2_get_oept_xfersize(dev, ep_idx);
615+
max_pktcnt = dwc2_get_oept_pktctn(dev, ep_idx);
616+
590617
/* Clear NAK and set endpoint enable */
591618
doepctl = sys_read32(doepctl_reg);
592619
doepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK;
593620

594621
if (dwc2_ep_is_iso(cfg)) {
595622
xfersize = USB_MPS_TO_TPL(cfg->mps);
596-
pktcnt = 1 + USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps);
623+
pktcnt = 1 + addnl;
597624

598625
if (xfersize > net_buf_tailroom(buf)) {
599626
LOG_ERR("ISO RX buffer too small");
@@ -610,14 +637,18 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,
610637
xfersize = net_buf_tailroom(buf);
611638

612639
/* Do as many packets in a single transfer as possible */
613-
if (xfersize > priv->max_xfersize) {
614-
xfersize = ROUND_DOWN(priv->max_xfersize, USB_MPS_TO_TPL(cfg->mps));
640+
if (xfersize > max_xfersize) {
641+
xfersize = ROUND_DOWN(max_xfersize, USB_MPS_TO_TPL(cfg->mps));
615642
}
616643

617644
pktcnt = DIV_ROUND_UP(xfersize, USB_MPS_EP_SIZE(cfg->mps));
618645
}
619646

620-
pktcnt = DIV_ROUND_UP(xfersize, udc_mps_ep_size(cfg));
647+
if (pktcnt > max_pktcnt) {
648+
pktcnt = ROUND_DOWN(max_pktcnt, (1 + addnl));
649+
xfersize = pktcnt * udc_mps_ep_size(cfg);
650+
}
651+
621652
doeptsiz = usb_dwc2_set_doeptsizn_pktcnt(pktcnt) |
622653
usb_dwc2_set_doeptsizn_xfersize(xfersize);
623654
if (cfg->addr == USB_CONTROL_EP_OUT) {

0 commit comments

Comments
 (0)