drivers: udc_nrf: Replace driver queue with events
There is finite number of distinct events that are handled in thread context and the order of handling is flexible. Therefore use events instead of message queue because it is guaranteed to never get full. Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
This commit is contained in:
parent
5d79ef9f8a
commit
41ebbe0033
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user