Skip to content
Permalink
Browse files

drivers: usb_dc_nrfx: Use dedicated work queue for handling ISR events

This patch introduces a dedicated work queue for handling the events
from ISR (i.e. for notifying the USB device stack, for executing the
enpoints callbacks, etc.). The system work queue cannot be used for
this purpose as it might be used in applications for scheduling USB
transfers and this could lead to a deadlock when the USB device stack
would not be notified about certain event because of a system work
queue item waiting for a USB transfer to be finished.

The FIFO named so far `work_queue` is renamed to `usbd_evt_fifo`
to better indicate its purpose and to avoid confusion.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
  • Loading branch information...
anangl authored and carlescufi committed Jul 22, 2019
1 parent ff0b76e commit f69e194ece46e17f01923ce2963ce4d63c14c927
Showing with 32 additions and 6 deletions.
  1. +9 −0 drivers/usb/device/Kconfig
  2. +23 −6 drivers/usb/device/usb_dc_nrfx.c
@@ -76,6 +76,15 @@ config USB_NRFX_EVT_QUEUE_SIZE
Size of the driver's internal event queue.
Required size will depend on number of endpoints (class instances) in use.

config USB_NRFX_WORK_QUEUE_STACK_SIZE
int "USBD work queue stack size"
default 1024
depends on USB_NRF52840
help
Size of the stack for the work queue thread that is used in the driver
for handling the events from the USBD ISR, i.e. executing endpoint
callbacks and providing proper notifications to the USB device stack.

config USB_KINETIS
bool "Kinetis USB Device Controller Driver"
select USB_DEVICE_DRIVER
@@ -242,7 +242,6 @@ K_MEM_POOL_DEFINE(ep_buf_pool, EP_BUF_POOL_BLOCK_MIN_SZ,
* @param attached USBD Attached flag
* @param ready USBD Ready flag set after pullup
* @param usb_work USBD work item
* @param work_queue FIFO used for queuing up events from ISR
* @param drv_lock Mutex for thread-safe nrfx driver use
* @param ep_ctx Endpoint contexts
* @param ctrl_read_len State of control read operation (EP0).
@@ -262,7 +261,20 @@ struct nrf_usbd_ctx {
};


K_FIFO_DEFINE(work_queue);
/* FIFO used for queuing up events from ISR. */
K_FIFO_DEFINE(usbd_evt_fifo);

/* Work queue used for handling the ISR events (i.e. for notifying the USB
* device stack, for executing the endpoints callbacks, etc.) out of the ISR
* context.
* The system work queue cannot be used for this purpose as it might be used in
* applications for scheduling USB transfers and this could lead to a deadlock
* when the USB device stack would not be notified about certain event because
* of a system work queue item waiting for a USB transfer to be finished.
*/
static struct k_work_q usbd_work_queue;
static K_THREAD_STACK_DEFINE(usbd_work_queue_stack,
CONFIG_USB_NRFX_WORK_QUEUE_STACK_SIZE);


static struct nrf_usbd_ctx usbd_ctx = {
@@ -372,7 +384,7 @@ static struct nrf_usbd_ep_ctx *out_endpoint_ctx(const u8_t ep)
*/
static inline void usbd_work_schedule(void)
{
k_work_submit(&get_usbd_ctx()->usb_work);
k_work_submit_to_queue(&usbd_work_queue, &get_usbd_ctx()->usb_work);
}

/**
@@ -394,15 +406,15 @@ static inline void usbd_evt_free(struct usbd_event *ev)
*/
static inline void usbd_evt_put(struct usbd_event *ev)
{
k_fifo_put(&work_queue, ev);
k_fifo_put(&usbd_evt_fifo, ev);
}

/**
* @brief Get next enqueued USBD event if present.
*/
static inline struct usbd_event *usbd_evt_get(void)
{
return k_fifo_get(&work_queue, K_NO_WAIT);
return k_fifo_get(&usbd_evt_fifo, K_NO_WAIT);
}

/**
@@ -1293,6 +1305,11 @@ int usb_dc_attach(void)
return 0;
}

k_work_q_start(&usbd_work_queue,
usbd_work_queue_stack,
K_THREAD_STACK_SIZEOF(usbd_work_queue_stack),
CONFIG_SYSTEM_WORKQUEUE_PRIORITY);

k_work_init(&ctx->usb_work, usbd_work_handler);
k_mutex_init(&ctx->drv_lock);

@@ -1313,7 +1330,7 @@ int usb_dc_attach(void)
ctx->attached = true;
}

if (!k_fifo_is_empty(&work_queue)) {
if (!k_fifo_is_empty(&usbd_evt_fifo)) {
usbd_work_schedule();
}

0 comments on commit f69e194

Please sign in to comment.
You can’t perform that action at this time.