Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions subsys/usb/device_next/class/Kconfig.cdc_acm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ config USBD_CDC_ACM_STACK_SIZE
help
USB CDC ACM workqueue stack size.

config USBD_CDC_ACM_TX_DELAY
int
range 1 1000
default 100
help
Time in milliseconds to wait before sending actual payload to host.
This is needed to prevent tty ECHO on Linux.

module = USBD_CDC_ACM
module-str = usbd cdc_acm
default-count = 1
Expand Down
56 changes: 47 additions & 9 deletions subsys/usb/device_next/class/usbd_cdc_acm.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ struct cdc_acm_uart_data {
* roughly emulating flow control.
*/
bool flow_ctrl;
/*
* This is used to indicate that a client has started using a serial
* device on the host side. We use it to enqueue the first transfer as
* ZLP and later to delay the first data packet.
*
* The reason for this is to wait for initialization timeout before
* sending actual payload to make it possible for application to
* disable ECHO. The echo is long known problem related to the fact
* that POSIX defaults to ECHO ON and thus every application that opens
* tty device (on Linux) will have ECHO enabled in the short window
* between open() and ioctl() that disables the echo (if application
* wishes to disable the echo).
*
* The variable is set on line coding request.
*/
bool first_tx_pkt;
/* USBD CDC ACM TX fifo work */
struct k_work_delayable tx_fifo_work;
/* USBD CDC ACM RX fifo work */
Expand Down Expand Up @@ -259,8 +275,15 @@ static int usbd_cdc_acm_request(struct usbd_class_data *const c_data,
atomic_clear_bit(&data->state, CDC_ACM_TX_FIFO_BUSY);

if (!ring_buf_is_empty(data->tx_fifo.rb)) {
k_timeout_t timeout = K_NO_WAIT;

if (data->first_tx_pkt) {
timeout = K_MSEC(CONFIG_USBD_CDC_ACM_TX_DELAY);
data->first_tx_pkt = false;
}

/* Queue pending TX data on IN endpoint */
cdc_acm_work_schedule(&data->tx_fifo_work, K_NO_WAIT);
cdc_acm_work_schedule(&data->tx_fifo_work, timeout);
}

}
Expand Down Expand Up @@ -296,11 +319,17 @@ static void usbd_cdc_acm_enable(struct usbd_class_data *const c_data)
if (ring_buf_space_get(data->tx_fifo.rb)) {
/* Raise TX ready interrupt */
cdc_acm_work_submit(&data->irq_cb_work);
} else {
/* Queue pending TX data on IN endpoint */
cdc_acm_work_schedule(&data->tx_fifo_work, K_NO_WAIT);
}
}

if (!ring_buf_is_empty(data->tx_fifo.rb)) {
/* Queue pending TX data on the IN endpoint with a delay to
* allow first_tx_pkt to be set if there is a Set Line Coding
* request.
*/
cdc_acm_work_schedule(&data->tx_fifo_work,
K_MSEC(CONFIG_USBD_CDC_ACM_TX_DELAY));
}
}

static void usbd_cdc_acm_disable(struct usbd_class_data *const c_data)
Expand Down Expand Up @@ -463,6 +492,8 @@ static int usbd_cdc_acm_ctd(struct usbd_class_data *const c_data,
memcpy(&data->line_coding, buf->data, len);
cdc_acm_update_uart_cfg(data);
usbd_msg_pub_device(uds_ctx, USBD_MSG_CDC_ACM_LINE_CODING, dev);
data->first_tx_pkt = true;

return 0;

case SET_CONTROL_LINE_STATE:
Expand Down Expand Up @@ -495,8 +526,8 @@ static int usbd_cdc_acm_init(struct usbd_class_data *const c_data)
return 0;
}

static int cdc_acm_send_notification(const struct device *dev,
const uint16_t serial_state)
static inline int cdc_acm_send_notification(const struct device *dev,
const uint16_t serial_state)
{
struct cdc_acm_notification notification = {
.bmRequestType = 0xA1,
Expand Down Expand Up @@ -530,7 +561,11 @@ static int cdc_acm_send_notification(const struct device *dev,

net_buf_add_mem(buf, &notification, sizeof(struct cdc_acm_notification));
ret = usbd_ep_enqueue(c_data, buf);
/* FIXME: support for sync transfers */
if (ret) {
net_buf_unref(buf);
return ret;
}

k_sem_take(&data->notif_sem, K_FOREVER);

return ret;
Expand All @@ -545,7 +580,7 @@ static void cdc_acm_tx_fifo_handler(struct k_work *work)
struct cdc_acm_uart_data *data;
struct usbd_class_data *c_data;
struct net_buf *buf;
size_t len;
size_t len = 0;
int ret;

data = CONTAINER_OF(dwork, struct cdc_acm_uart_data, tx_fifo_work);
Expand Down Expand Up @@ -573,7 +608,10 @@ static void cdc_acm_tx_fifo_handler(struct k_work *work)
return;
}

len = ring_buf_get(data->tx_fifo.rb, buf->data, buf->size);
if (!data->first_tx_pkt) {
len = ring_buf_get(data->tx_fifo.rb, buf->data, buf->size);
}

net_buf_add(buf, len);

ret = usbd_ep_enqueue(c_data, buf);
Expand Down
Loading