From 2368623f32fde01e8421dd606162bd172f44b10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 20 May 2024 14:12:06 +0200 Subject: [PATCH] drivers: udc_dwc2: Avoid IN endpoint dequeue race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flushing TxFIFO is racing with actual use of the TxFIFO. The software controls only one side of the race (flush trigger) while the host controls the other side. Therefore, locking interrupts before flushing TxFIFO is not protecting against the race condition. Disable the endpoint on dequeue to make sure that TxFIFO flushing won't conflict with host actions (because the endpoint would be forced to NAK the IN tokens before the TxFIFO is flushed). Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 83 +++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 33444d07e1e..98ded9e4b5d 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -928,51 +928,6 @@ static void udc_dwc2_isr_handler(const struct device *dev) (void)dwc2_quirk_irq_clear(dev); } -static int udc_dwc2_ep_enqueue(const struct device *dev, - struct udc_ep_config *const cfg, - struct net_buf *const buf) -{ - struct dwc2_drv_event evt = { - .ep = cfg->addr, - .type = DWC2_DRV_EVT_XFER, - }; - - LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf); - udc_buf_put(cfg, buf); - - if (!cfg->stat.halted) { - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); - } - - return 0; -} - -static int udc_dwc2_ep_dequeue(const struct device *dev, - struct udc_ep_config *const cfg) -{ - unsigned int lock_key; - struct net_buf *buf; - - lock_key = irq_lock(); - - if (USB_EP_DIR_IS_IN(cfg->addr)) { - dwc2_flush_tx_fifo(dev, USB_EP_GET_IDX(cfg->addr)); - } - - buf = udc_buf_get_all(dev, cfg->addr); - if (buf) { - udc_submit_ep_event(dev, buf, -ECONNABORTED); - } - - irq_unlock(lock_key); - - udc_ep_set_busy(dev, cfg->addr, false); - - LOG_DBG("dequeue ep 0x%02x", cfg->addr); - - return 0; -} - static void dwc2_unset_unused_fifo(const struct device *dev) { struct udc_dwc2_data *const priv = udc_get_private(dev); @@ -1420,6 +1375,44 @@ static int udc_dwc2_ep_clear_halt(const struct device *dev, return 0; } +static int udc_dwc2_ep_enqueue(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + struct dwc2_drv_event evt = { + .ep = cfg->addr, + .type = DWC2_DRV_EVT_XFER, + }; + + LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf); + udc_buf_put(cfg, buf); + + if (!cfg->stat.halted) { + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + } + + return 0; +} + +static int udc_dwc2_ep_dequeue(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *buf; + + udc_dwc2_ep_disable(dev, cfg, false); + + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + udc_ep_set_busy(dev, cfg->addr, false); + + LOG_DBG("dequeue ep 0x%02x", cfg->addr); + + return 0; +} + static int udc_dwc2_set_address(const struct device *dev, const uint8_t addr) { struct usb_dwc2_reg *const base = dwc2_get_base(dev);