Skip to content

Commit 2cab3a8

Browse files
jfischer-nocarlescufi
authored andcommitted
drivers: usb: udc_kinetis: fix race condition in Kinetis USBFSOTG
Periodic enqeueu of buffers can cause a attempt to start a new transfer even though an endpoint is already busy. Split usbfsotg_xfer_start() into two function, one to start next transfer and another to continue the transfers, and use busy state flags to explicitly mark an endpoint busy. Signed-off-by: Johann Fischer <[email protected]>
1 parent 00adb2a commit 2cab3a8

File tree

1 file changed

+31
-16
lines changed

1 file changed

+31
-16
lines changed

drivers/usb/udc/udc_kinetis.c

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,20 @@ static ALWAYS_INLINE void usbfsotg_resume_tx(const struct device *dev)
170170
base->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
171171
}
172172

173-
/* Initiate a new transfer, must not be used for control endpoint OUT */
174-
static int usbfsotg_xfer_start(const struct device *dev,
175-
struct udc_ep_config *const cfg)
173+
static int usbfsotg_xfer_continue(const struct device *dev,
174+
struct udc_ep_config *const cfg,
175+
struct net_buf *const buf)
176176
{
177177
const struct usbfsotg_config *config = dev->config;
178178
USB_Type *base = config->base;
179179
struct usbfsotg_bd *bd;
180-
struct net_buf *buf;
181180
uint8_t *data_ptr;
182181
size_t len;
183182

184-
buf = udc_buf_peek(dev, cfg->addr);
185-
if (buf == NULL) {
186-
return -ENODATA;
187-
}
188-
189183
bd = usbfsotg_get_ebd(dev, cfg, false);
190-
if (usbfsotg_bd_is_busy(bd)) {
191-
LOG_DBG("ep 0x%02x buf busy", cfg->addr);
184+
if (unlikely(usbfsotg_bd_is_busy(bd))) {
185+
LOG_ERR("ep 0x%02x buf busy", cfg->addr);
186+
__ASSERT_NO_MSG(false);
192187
return -EBUSY;
193188
}
194189

@@ -213,6 +208,20 @@ static int usbfsotg_xfer_start(const struct device *dev,
213208
return 0;
214209
}
215210

211+
/* Initiate a new transfer, must not be used for control endpoint OUT */
212+
static int usbfsotg_xfer_next(const struct device *dev,
213+
struct udc_ep_config *const cfg)
214+
{
215+
struct net_buf *buf;
216+
217+
buf = udc_buf_peek(dev, cfg->addr);
218+
if (buf == NULL) {
219+
return -ENODATA;
220+
}
221+
222+
return usbfsotg_xfer_continue(dev, cfg, buf);
223+
}
224+
216225
static inline int usbfsotg_ctrl_feed_start(const struct device *dev,
217226
struct net_buf *const buf)
218227
{
@@ -465,9 +474,11 @@ static void xfer_work_handler(struct k_work *item)
465474
break;
466475
case USBFSOTG_EVT_DOUT:
467476
err = work_handler_out(ev->dev, ev->ep);
477+
udc_ep_set_busy(ev->dev, ev->ep, false);
468478
break;
469479
case USBFSOTG_EVT_DIN:
470480
err = work_handler_in(ev->dev, ev->ep);
481+
udc_ep_set_busy(ev->dev, ev->ep, false);
471482
break;
472483
case USBFSOTG_EVT_CLEAR_HALT:
473484
err = usbfsotg_ep_clear_halt(ev->dev, ep_cfg);
@@ -481,8 +492,10 @@ static void xfer_work_handler(struct k_work *item)
481492
}
482493

483494
/* Peek next transer */
484-
if (ev->ep != USB_CONTROL_EP_OUT) {
485-
usbfsotg_xfer_start(ev->dev, ep_cfg);
495+
if (ev->ep != USB_CONTROL_EP_OUT && !udc_ep_is_busy(ev->dev, ev->ep)) {
496+
if (usbfsotg_xfer_next(ev->dev, ep_cfg) == 0) {
497+
udc_ep_set_busy(ev->dev, ev->ep, true);
498+
}
486499
}
487500

488501
xfer_work_error:
@@ -576,7 +589,7 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev,
576589
if (ep == USB_CONTROL_EP_OUT) {
577590
usbfsotg_ctrl_feed_start(dev, buf);
578591
} else {
579-
usbfsotg_xfer_start(dev, ep_cfg);
592+
usbfsotg_xfer_continue(dev, ep_cfg, buf);
580593
}
581594
} else {
582595
if (ep == USB_CONTROL_EP_OUT) {
@@ -600,10 +613,10 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev,
600613

601614
net_buf_pull(buf, len);
602615
if (buf->len) {
603-
usbfsotg_xfer_start(dev, ep_cfg);
616+
usbfsotg_xfer_continue(dev, ep_cfg, buf);
604617
} else {
605618
if (udc_ep_buf_has_zlp(buf)) {
606-
usbfsotg_xfer_start(dev, ep_cfg);
619+
usbfsotg_xfer_continue(dev, ep_cfg, buf);
607620
udc_ep_buf_clear_zlp(buf);
608621
break;
609622
}
@@ -718,6 +731,8 @@ static int usbfsotg_ep_dequeue(const struct device *dev,
718731
udc_submit_event(dev, UDC_EVT_EP_REQUEST, -ECONNABORTED, buf);
719732
}
720733

734+
udc_ep_set_busy(dev, cfg->addr, false);
735+
721736
return 0;
722737
}
723738

0 commit comments

Comments
 (0)