diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index eb0abb803cee3d1..5ccce47b69dc534 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -48,6 +48,7 @@ UDC_BUF_POOL_DEFINE(cdc_acm_ep_pool, #define CDC_ACM_IRQ_TX_ENABLED 3 #define CDC_ACM_RX_FIFO_BUSY 4 #define CDC_ACM_LOCK 5 +#define CDC_ACM_TX_FIFO_BUSY 6 static struct k_work_q cdc_acm_work_q; static K_KERNEL_STACK_DEFINE(cdc_acm_stack, @@ -217,6 +218,11 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, atomic_clear_bit(&data->state, CDC_ACM_RX_FIFO_BUSY); } + if (bi->ep == cdc_acm_get_bulk_in(c_data)) { + atomic_clear_bit(&data->state, CDC_ACM_TX_FIFO_BUSY); + LOG_ERR("TX ERROR !!"); + } + goto ep_request_error; } @@ -236,9 +242,19 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data, if (bi->ep == cdc_acm_get_bulk_in(c_data)) { /* TX transfer completion */ - if (data->cb) { - cdc_acm_work_submit(&data->irq_cb_work); + atomic_clear_bit(&data->state, CDC_ACM_TX_FIFO_BUSY); + + + if (ring_buf_is_empty(data->tx_fifo.rb)) { + /* Raise TX ready interrupt */ + if (data->cb) { + cdc_acm_work_submit(&data->irq_cb_work); + } + } else { + /* Queue pending TX data on IN endpoint */ + cdc_acm_work_submit(&data->tx_fifo_work); } + } if (bi->ep == cdc_acm_get_int_in(c_data)) { @@ -535,14 +551,20 @@ static void cdc_acm_tx_fifo_handler(struct k_work *work) return; } + if (atomic_test_and_set_bit(&data->state, CDC_ACM_TX_FIFO_BUSY)) { + LOG_WRN("TX transfer already in progress"); + return; + } + if (atomic_test_and_set_bit(&data->state, CDC_ACM_LOCK)) { + atomic_clear_bit(&data->state, CDC_ACM_RX_FIFO_BUSY); cdc_acm_work_submit(&data->tx_fifo_work); return; } 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_submit(&data->tx_fifo_work); goto tx_fifo_handler_exit; } @@ -551,6 +573,7 @@ static void cdc_acm_tx_fifo_handler(struct k_work *work) ret = usbd_ep_enqueue(c_data, buf); if (ret) { + atomic_clear_bit(&data->state, CDC_ACM_RX_FIFO_BUSY); LOG_ERR("Failed to enqueue"); net_buf_unref(buf); } @@ -824,7 +847,8 @@ static void cdc_acm_irq_cb_handler(struct k_work *work) cdc_acm_work_submit(&data->rx_fifo_work); } - if (data->tx_fifo.altered) { + if (!atomic_test_bit(&data->state, CDC_ACM_TX_FIFO_BUSY) && + data->tx_fifo.altered) { LOG_DBG("tx fifo altered, submit work"); cdc_acm_work_submit(&data->tx_fifo_work); }