From fe4d8a678c56127b8e74830da4f8035c394d832c Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Mon, 9 Sep 2024 13:23:50 +0200 Subject: [PATCH] usb: device_next: use delayable work for TX FIFO in CDC ACM Use delayable work to reduce CPU load when there is no transfer flow in the host direction. This also improves the performance of poll out, as introduced in commit commit fed6bde78852 ("usb: device: cdc_acm: send more than 1 byte in poll out") for the legacy CDC ACM implementation. Signed-off-by: Johann Fischer --- subsys/usb/device_next/class/usbd_cdc_acm.c | 25 +++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index 1d683fa3cc4655..80f4a858386edd 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -109,7 +109,7 @@ struct cdc_acm_uart_data { */ bool flow_ctrl; /* USBD CDC ACM TX fifo work */ - struct k_work tx_fifo_work; + struct k_work_delayable tx_fifo_work; /* USBD CDC ACM RX fifo work */ struct k_work rx_fifo_work; atomic_t state; @@ -140,6 +140,12 @@ static ALWAYS_INLINE int cdc_acm_work_submit(struct k_work *work) return k_work_submit_to_queue(&cdc_acm_work_q, work); } +static ALWAYS_INLINE int cdc_acm_work_schedule(struct k_work_delayable *work, + k_timeout_t delay) +{ + return k_work_schedule_for_queue(&cdc_acm_work_q, work, delay); +} + static ALWAYS_INLINE bool check_wq_ctx(const struct device *dev) { return k_current_get() == k_work_queue_thread_get(&cdc_acm_work_q); @@ -277,7 +283,7 @@ static void usbd_cdc_acm_enable(struct usbd_class_data *const c_data) cdc_acm_work_submit(&data->irq_cb_work); } else { /* Queue pending TX data on IN endpoint */ - cdc_acm_work_submit(&data->tx_fifo_work); + cdc_acm_work_schedule(&data->tx_fifo_work, K_NO_WAIT); } } } @@ -520,13 +526,14 @@ static int cdc_acm_send_notification(const struct device *dev, */ static void cdc_acm_tx_fifo_handler(struct k_work *work) { + struct k_work_delayable *dwork = k_work_delayable_from_work(work); struct cdc_acm_uart_data *data; struct usbd_class_data *c_data; struct net_buf *buf; size_t len; int ret; - data = CONTAINER_OF(work, struct cdc_acm_uart_data, tx_fifo_work); + data = CONTAINER_OF(dwork, struct cdc_acm_uart_data, tx_fifo_work); c_data = data->c_data; if (!atomic_test_bit(&data->state, CDC_ACM_CLASS_ENABLED)) { @@ -541,7 +548,7 @@ static void cdc_acm_tx_fifo_handler(struct k_work *work) buf = cdc_acm_buf_alloc(cdc_acm_get_bulk_in(c_data)); if (buf == NULL) { - cdc_acm_work_submit(&data->tx_fifo_work); + cdc_acm_work_schedule(&data->tx_fifo_work, K_MSEC(1)); return; } @@ -819,7 +826,7 @@ static void cdc_acm_irq_cb_handler(struct k_work *work) if (data->tx_fifo.altered) { LOG_DBG("tx fifo altered, submit work"); - cdc_acm_work_submit(&data->tx_fifo_work); + cdc_acm_work_schedule(&data->tx_fifo_work, K_NO_WAIT); } if (atomic_test_bit(&data->state, CDC_ACM_IRQ_RX_ENABLED) && @@ -887,7 +894,11 @@ static void cdc_acm_poll_out(const struct device *dev, const unsigned char c) k_msleep(1); } - cdc_acm_work_submit(&data->tx_fifo_work); + /* Schedule with minimal timeout to make it possible to send more than + * one byte per USB transfer. The latency increase is negligible while + * the increased throughput and reduced CPU usage is easily observable. + */ + cdc_acm_work_schedule(&data->tx_fifo_work, K_MSEC(1)); } #ifdef CONFIG_UART_LINE_CTRL @@ -1006,7 +1017,7 @@ static int usbd_cdc_acm_preinit(const struct device *dev) k_thread_name_set(&cdc_acm_work_q.thread, "cdc_acm_work_q"); - k_work_init(&data->tx_fifo_work, cdc_acm_tx_fifo_handler); + k_work_init_delayable(&data->tx_fifo_work, cdc_acm_tx_fifo_handler); k_work_init(&data->rx_fifo_work, cdc_acm_rx_fifo_handler); k_work_init(&data->irq_cb_work, cdc_acm_irq_cb_handler);