diff --git a/drivers/usb/udc/Kconfig.nrf b/drivers/usb/udc/Kconfig.nrf index 93c5090bed2..bcb0e1f017b 100644 --- a/drivers/usb/udc/Kconfig.nrf +++ b/drivers/usb/udc/Kconfig.nrf @@ -7,6 +7,7 @@ config UDC_NRF depends on DT_HAS_NORDIC_NRF_USBD_ENABLED select NRF_USBD_COMMON select NRFX_POWER + select EVENTS help nRF USB device controller driver. @@ -18,11 +19,4 @@ config UDC_NRF_THREAD_STACK_SIZE help Size of the stack used in the driver for nRF USBD ISR event handling. -config UDC_NRF_MAX_QMESSAGES - int "nRF UDC driver maximum number of ISR event messages" - range 4 64 - default 8 - help - Maximum number of messages for handling of nRF USBD ISR events. - endif # UDC_NRF diff --git a/drivers/usb/udc/udc_nrf.c b/drivers/usb/udc/udc_nrf.c index ee4ab6d42a7..934bc43423f 100644 --- a/drivers/usb/udc/udc_nrf.c +++ b/drivers/usb/udc/udc_nrf.c @@ -39,24 +39,28 @@ LOG_MODULE_REGISTER(udc_nrf, CONFIG_UDC_DRIVER_LOG_LEVEL); #define UDC_NRF_EP0_SIZE 64 enum udc_nrf_event_type { - /* An event generated by the HAL driver */ - UDC_NRF_EVT_HAL, - /* Shim driver event to trigger next transfer */ + /* Trigger next transfer (buffer enqueued) */ UDC_NRF_EVT_XFER, + /* Transfer finished */ + UDC_NRF_EVT_EP_FINISHED, + /* SETUP data received */ + UDC_NRF_EVT_SETUP, + /* USB bus suspended */ + UDC_NRF_EVT_SUSPEND, + /* USB bus resumed */ + UDC_NRF_EVT_RESUME, + /* Remote Wakeup initiated */ + UDC_NRF_EVT_WUREQ, /* Let controller perform status stage */ UDC_NRF_EVT_STATUS_IN, }; -struct udc_nrf_evt { - enum udc_nrf_event_type type; - union { - nrf_usbd_common_evt_t hal_evt; - uint8_t ep; - }; -}; - -K_MSGQ_DEFINE(drv_msgq, sizeof(struct udc_nrf_evt), - CONFIG_UDC_NRF_MAX_QMESSAGES, sizeof(uint32_t)); +/* Main events the driver thread waits for */ +static K_EVENT_DEFINE(drv_evt); +/* Transfer triggers */ +static atomic_t xfer_new; +/* Finished transactions */ +static atomic_t xfer_finished; static K_KERNEL_STACK_DEFINE(drv_stack, CONFIG_UDC_NRF_THREAD_STACK_SIZE); static struct k_thread drv_stack_data; @@ -175,20 +179,6 @@ const static struct device *udc_nrf_dev; ? NRF_USBD_COMMON_EPIN_BITPOS_0 : NRF_USBD_COMMON_EPOUT_BITPOS_0) \ + NRF_USBD_COMMON_EP_NUM(ep)) -/** - * @brief Helper macro for creating an endpoint transfer event. - * - * @param[in] name Name of the created transfer event variable. - * @param[in] endpoint Endpoint number. - * @param[in] ep_stat Endpoint state to report. - * - * @return Initialized event constant variable. - */ -#define NRF_USBD_COMMON_EP_TRANSFER_EVENT(name, endpont, ep_stat) \ - const struct udc_nrf_evt name = {.type = UDC_NRF_EVT_HAL, \ - .hal_evt = {NRF_USBD_COMMON_EVT_EPTRANSFER, \ - .data = {.eptransfer = {.ep = endpont, .status = ep_stat}}}} - /* Check it the bit positions values match defined DATAEPSTATUS bit positions */ BUILD_ASSERT( (NRF_USBD_COMMON_EP_BITPOS(NRF_USBD_COMMON_EPIN1) == USBD_EPDATASTATUS_EPIN1_Pos) && @@ -553,8 +543,8 @@ static void nrf_usbd_dma_finished(nrf_usbd_common_ep_t ep) /* Send event to the user - for an ISO IN or any OUT endpoint, * the whole transfer is finished in this moment */ - NRF_USBD_COMMON_EP_TRANSFER_EVENT(evt, ep, NRF_USBD_COMMON_EP_OK); - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + atomic_set_bit(&xfer_finished, ep2bit(ep)); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_EP_FINISHED)); } } else if (ep == NRF_USBD_COMMON_EPOUT0) { nrf_usbd_legacy_setup_data_clear(); @@ -590,8 +580,8 @@ static void usbd_in_packet_sent(uint8_t ep) } else { LOG_DBG("USBD event: EndpointData: In finished"); /* No more data to be send - transmission finished */ - NRF_USBD_COMMON_EP_TRANSFER_EVENT(evt, ep, NRF_USBD_COMMON_EP_OK); - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + atomic_set_bit(&xfer_finished, ep2bit(ep)); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_EP_FINISHED)); } } @@ -615,11 +605,7 @@ static void ev_setup_handler(void) m_ep_ready &= ~(1U << ep2bit(NRF_USBD_COMMON_EPOUT0)); m_ep_ready |= 1U << ep2bit(NRF_USBD_COMMON_EPIN0); - const struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_HAL, - .hal_evt = { .type = NRF_USBD_COMMON_EVT_SETUP, }, - }; - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_SETUP)); } static void ev_usbevent_handler(void) @@ -637,21 +623,13 @@ static void ev_usbevent_handler(void) LOG_DBG("USBD event: SUSPEND"); m_bus_suspend = true; - const struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_HAL, - .hal_evt = { .type = NRF_USBD_COMMON_EVT_SUSPEND, }, - }; - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_SUSPEND)); } if (event & USBD_EVENTCAUSE_RESUME_Msk) { LOG_DBG("USBD event: RESUME"); m_bus_suspend = false; - const struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_HAL, - .hal_evt = { .type = NRF_USBD_COMMON_EVT_RESUME, }, - }; - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_RESUME)); } if (event & USBD_EVENTCAUSE_USBWUALLOWED_Msk) { LOG_DBG("USBD event: WUREQ (%s)", m_bus_suspend ? "In Suspend" : "Active"); @@ -663,11 +641,7 @@ static void ev_usbevent_handler(void) << USBD_DPDMVALUE_STATE_Pos; NRF_USBD->TASKS_DPDMDRIVE = 1; - const struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_HAL, - .hal_evt = { .type = NRF_USBD_COMMON_EVT_WUREQ, }, - }; - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_WUREQ)); } } } @@ -1467,38 +1441,25 @@ static void udc_event_fake_status_in(const struct device *dev) udc_event_xfer_ctrl_in(dev, buf); } -static void udc_event_xfer_in(const struct device *dev, - nrf_usbd_common_evt_t const *const event) +static void udc_event_xfer_in(const struct device *dev, const uint8_t ep) { struct udc_ep_config *ep_cfg; - uint8_t ep = event->data.eptransfer.ep; struct net_buf *buf; ep_cfg = udc_get_ep_cfg(dev, ep); - switch (event->data.eptransfer.status) { - case NRF_USBD_COMMON_EP_OK: - buf = udc_buf_get(ep_cfg); - if (buf == NULL) { - LOG_ERR("ep 0x%02x queue is empty", ep); - __ASSERT_NO_MSG(false); - return; - } - - udc_ep_set_busy(ep_cfg, false); - if (ep == USB_CONTROL_EP_IN) { - udc_event_xfer_ctrl_in(dev, buf); - return; - } + buf = udc_buf_get(ep_cfg); + if (buf == NULL) { + LOG_ERR("ep 0x%02x queue is empty", ep); + __ASSERT_NO_MSG(false); + return; + } + udc_ep_set_busy(ep_cfg, false); + if (ep == USB_CONTROL_EP_IN) { + udc_event_xfer_ctrl_in(dev, buf); + } else { udc_submit_ep_event(dev, buf, 0); - break; - - default: - LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x", - event->data.eptransfer.status, ep); - udc_submit_event(dev, UDC_EVT_ERROR, -EIO); - break; } } @@ -1543,46 +1504,29 @@ static void udc_event_xfer_out_next(const struct device *dev, const uint8_t ep) } } -static void udc_event_xfer_out(const struct device *dev, - nrf_usbd_common_evt_t const *const event) +static void udc_event_xfer_out(const struct device *dev, const uint8_t ep) { struct udc_ep_config *ep_cfg; - uint8_t ep = event->data.eptransfer.ep; struct net_buf *buf; - switch (event->data.eptransfer.status) { - case NRF_USBD_COMMON_EP_OK: - ep_cfg = udc_get_ep_cfg(dev, ep); - buf = udc_buf_get(ep_cfg); - if (buf == NULL) { - LOG_ERR("ep 0x%02x ok, queue is empty", ep); - return; - } + ep_cfg = udc_get_ep_cfg(dev, ep); + buf = udc_buf_get(ep_cfg); + if (buf == NULL) { + LOG_ERR("ep 0x%02x ok, queue is empty", ep); + return; + } - udc_ep_set_busy(ep_cfg, false); - if (ep == USB_CONTROL_EP_OUT) { - udc_event_xfer_ctrl_out(dev, buf); - } else { - udc_submit_ep_event(dev, buf, 0); - } - - break; - - default: - LOG_ERR("Unexpected event (nrfx_usbd): %d, ep 0x%02x", - event->data.eptransfer.status, ep); - udc_submit_event(dev, UDC_EVT_ERROR, -EIO); - break; + udc_ep_set_busy(ep_cfg, false); + if (ep == USB_CONTROL_EP_OUT) { + udc_event_xfer_ctrl_out(dev, buf); + } else { + udc_submit_ep_event(dev, buf, 0); } } static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length) { - struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_XFER, - .ep = USB_CONTROL_EP_OUT, - }; struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); struct net_buf *buf; @@ -1594,7 +1538,8 @@ static int usbd_ctrl_feed_dout(const struct device *dev, udc_buf_put(cfg, buf); udc_nrf_clear_control_out(dev); - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + atomic_set_bit(&xfer_new, ep2bit(USB_CONTROL_EP_OUT)); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_XFER)); return 0; } @@ -1706,6 +1651,80 @@ static int udc_event_xfer_setup(const struct device *dev) return err; } +static void udc_nrf_thread_handler(const struct device *dev) +{ + uint32_t evt; + + /* Wait for at least one event */ + k_event_wait(&drv_evt, UINT32_MAX, false, K_FOREVER); + + /* Process all events that are set */ + evt = k_event_clear(&drv_evt, UINT32_MAX); + + if (evt & BIT(UDC_NRF_EVT_SUSPEND)) { + LOG_INF("SUSPEND state detected"); + nrf_usbd_legacy_suspend(); + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + } + + if (evt & BIT(UDC_NRF_EVT_RESUME)) { + LOG_INF("RESUMING from suspend"); + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + } + + if (evt & BIT(UDC_NRF_EVT_WUREQ)) { + LOG_INF("Remote wakeup initiated"); + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + } + + if (evt & BIT(UDC_NRF_EVT_EP_FINISHED)) { + uint32_t eps = atomic_clear(&xfer_finished); + + while (eps) { + uint8_t bitpos = NRF_CTZ(eps); + nrf_usbd_common_ep_t ep = bit2ep(bitpos); + + eps &= ~BIT(bitpos); + + if (USB_EP_DIR_IS_IN(ep)) { + udc_event_xfer_in(dev, ep); + udc_event_xfer_in_next(dev, ep); + } else { + udc_event_xfer_out(dev, ep); + udc_event_xfer_out_next(dev, ep); + } + } + } + + if (evt & BIT(UDC_NRF_EVT_XFER)) { + uint32_t eps = atomic_clear(&xfer_new); + + while (eps) { + uint8_t bitpos = NRF_CTZ(eps); + nrf_usbd_common_ep_t ep = bit2ep(bitpos); + + eps &= ~BIT(bitpos); + + if (USB_EP_DIR_IS_IN(ep)) { + udc_event_xfer_in_next(dev, ep); + } else { + udc_event_xfer_out_next(dev, ep); + } + } + } + + if (evt & BIT(UDC_NRF_EVT_STATUS_IN)) { + udc_event_fake_status_in(dev); + } + + if (evt & BIT(UDC_NRF_EVT_SETUP)) { + udc_event_xfer_setup(dev); + } +} + static void udc_nrf_thread(void *p1, void *p2, void *p3) { ARG_UNUSED(p2); @@ -1714,63 +1733,7 @@ static void udc_nrf_thread(void *p1, void *p2, void *p3) const struct device *dev = p1; while (true) { - bool start_xfer = false; - struct udc_nrf_evt evt; - uint8_t ep; - - k_msgq_get(&drv_msgq, &evt, K_FOREVER); - - switch (evt.type) { - case UDC_NRF_EVT_HAL: - ep = evt.hal_evt.data.eptransfer.ep; - switch (evt.hal_evt.type) { - case NRF_USBD_COMMON_EVT_SUSPEND: - LOG_INF("SUSPEND state detected"); - nrf_usbd_legacy_suspend(); - udc_set_suspended(udc_nrf_dev, true); - udc_submit_event(udc_nrf_dev, UDC_EVT_SUSPEND, 0); - break; - case NRF_USBD_COMMON_EVT_RESUME: - LOG_INF("RESUMING from suspend"); - udc_set_suspended(udc_nrf_dev, false); - udc_submit_event(udc_nrf_dev, UDC_EVT_RESUME, 0); - break; - case NRF_USBD_COMMON_EVT_WUREQ: - LOG_INF("Remote wakeup initiated"); - udc_set_suspended(udc_nrf_dev, false); - udc_submit_event(udc_nrf_dev, UDC_EVT_RESUME, 0); - break; - case NRF_USBD_COMMON_EVT_EPTRANSFER: - start_xfer = true; - if (USB_EP_DIR_IS_IN(ep)) { - udc_event_xfer_in(dev, &evt.hal_evt); - } else { - udc_event_xfer_out(dev, &evt.hal_evt); - } - break; - case NRF_USBD_COMMON_EVT_SETUP: - udc_event_xfer_setup(dev); - break; - default: - break; - } - break; - case UDC_NRF_EVT_XFER: - start_xfer = true; - ep = evt.ep; - break; - case UDC_NRF_EVT_STATUS_IN: - udc_event_fake_status_in(dev); - break; - } - - if (start_xfer) { - if (USB_EP_DIR_IS_IN(ep)) { - udc_event_xfer_in_next(dev, ep); - } else { - udc_event_xfer_out_next(dev, ep); - } - } + udc_nrf_thread_handler(dev); } } @@ -1796,15 +1759,10 @@ static void udc_nrf_power_handler(nrfx_power_usb_evt_t pwr_evt) static bool udc_nrf_fake_status_in(const struct device *dev) { - struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_STATUS_IN, - .ep = USB_CONTROL_EP_IN, - }; - if (nrf_usbd_legacy_last_setup_dir_get() == USB_CONTROL_EP_OUT || udc_nrf_fake_setup) { /* Let controller perform status IN stage */ - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_STATUS_IN)); return true; } @@ -1815,11 +1773,6 @@ static int udc_nrf_ep_enqueue(const struct device *dev, struct udc_ep_config *cfg, struct net_buf *buf) { - struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_XFER, - .ep = cfg->addr, - }; - udc_buf_put(cfg, buf); if (cfg->addr == USB_CONTROL_EP_IN && buf->len == 0) { @@ -1828,7 +1781,8 @@ static int udc_nrf_ep_enqueue(const struct device *dev, } } - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + atomic_set_bit(&xfer_new, ep2bit(cfg->addr)); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_XFER)); return 0; } @@ -1932,17 +1886,10 @@ static int udc_nrf_set_address(const struct device *dev, const uint8_t addr) udc_nrf_address = addr; if (udc_nrf_fake_setup) { - struct udc_nrf_evt evt = { - .type = UDC_NRF_EVT_HAL, - .hal_evt = { - .type = NRF_USBD_COMMON_EVT_SETUP, - }, - }; - /* Finished handling lost Set Address, now handle the pending * SETUP transfer. */ - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + k_event_post(&drv_evt, BIT(UDC_NRF_EVT_SETUP)); } return 0;