Skip to content
Permalink
Browse files

usb: cdc_acm: Refactor USB serial code

Add TX ring buffer, change USB API to transfer API.

Signed-off-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com>
  • Loading branch information...
finikorg authored and nashif committed Apr 17, 2019
1 parent 0e57e4f commit 98c8a9517d79a355308dfcf5541deaa6364d01df
Showing with 79 additions and 69 deletions.
  1. +79 −69 subsys/usb/class/cdc_acm.c
@@ -65,7 +65,7 @@ LOG_MODULE_REGISTER(usb_cdc_acm);
#define CDC_ACM_DEFAUL_BAUDRATE {sys_cpu_to_le32(115200), 0, 0, 8}

/* Size of the internal buffer used for storing received data */
#define CDC_ACM_BUFFER_SIZE (2 * CONFIG_CDC_ACM_BULK_EP_MPS)
#define CDC_ACM_BUFFER_SIZE (CONFIG_CDC_ACM_BULK_EP_MPS)

/* Max CDC ACM class request max data size */
#define CDC_CLASS_REQ_MAX_DATA_SIZE 8
@@ -178,13 +178,16 @@ struct cdc_acm_dev_data_t {
uart_irq_callback_user_data_t cb;
void *cb_data;
struct k_work cb_work;
struct k_work tx_work;
/* Tx ready status. Signals when */
bool tx_ready;
bool rx_ready; /* Rx ready status */
bool tx_irq_ena; /* Tx interrupt enable status */
bool rx_irq_ena; /* Rx interrupt enable status */
u8_t rx_buf[CDC_ACM_BUFFER_SIZE];/* Internal Rx buffer */
bool rx_ready; /* Rx ready status */
bool tx_irq_ena; /* Tx interrupt enable status */
bool rx_irq_ena; /* Rx interrupt enable status */
u8_t rx_buf[CDC_ACM_BUFFER_SIZE]; /* Internal RX buffer */
u8_t tx_buf[CDC_ACM_BUFFER_SIZE]; /* Internal TX buffer */
struct ring_buf *rx_ringbuf;
struct ring_buf *tx_ringbuf;
/* Interface data buffer */
#ifndef CONFIG_USB_COMPOSITE_DEVICE
u8_t interface_data[CDC_CLASS_REQ_MAX_DATA_SIZE];
@@ -264,79 +267,80 @@ int cdc_acm_class_handle_req(struct usb_setup_packet *pSetup,
return 0;
}

/**
* @brief EP Bulk IN handler, used to send data to the Host
*
* @param ep Endpoint address.
* @param ep_status Endpoint status code.
*
* @return N/A.
*/
static void cdc_acm_bulk_in(u8_t ep, enum usb_dc_ep_cb_status_code ep_status)
static void cdc_acm_write_cb(u8_t ep, int size, void *priv)
{
struct cdc_acm_dev_data_t *dev_data;
struct usb_dev_data *common;

ARG_UNUSED(ep_status);

common = usb_get_dev_data_by_ep(&cdc_acm_data_devlist, ep);
if (common == NULL) {
LOG_WRN("Device data not found for endpoint %u", ep);
return;
}
struct cdc_acm_dev_data_t *dev_data = priv;

dev_data = CONTAINER_OF(common, struct cdc_acm_dev_data_t, common);
LOG_DBG("ep %x: written %d bytes dev_data %p", ep, size, dev_data);

dev_data->tx_ready = true;

k_sem_give(&poll_wait_sem);

/* Call callback only if tx irq ena */
if (dev_data->cb && dev_data->tx_irq_ena) {
k_work_submit(&dev_data->cb_work);
}

if (ring_buf_is_empty(dev_data->tx_ringbuf)) {
LOG_DBG("tx_ringbuf is empty");
return;
}

k_work_submit(&dev_data->tx_work);
}

/**
* @brief EP Bulk OUT handler, used to read the data received from the Host
*
* @param ep Endpoint address.
* @param ep_status Endpoint status code.
*
* @return N/A.
*/
static void cdc_acm_bulk_out(u8_t ep, enum usb_dc_ep_cb_status_code ep_status)
static void tx_work_handler(struct k_work *work)
{
struct cdc_acm_dev_data_t *dev_data;
struct usb_dev_data *common;
u32_t bytes_to_read, read;

ARG_UNUSED(ep_status);
struct cdc_acm_dev_data_t *dev_data =
CONTAINER_OF(work, struct cdc_acm_dev_data_t, tx_work);
struct device *dev = dev_data->common.dev;
struct usb_cfg_data *cfg = (void *)dev->config->config_info;
u8_t ep = cfg->endpoint[ACM_IN_EP_IDX].ep_addr;
size_t len;

common = usb_get_dev_data_by_ep(&cdc_acm_data_devlist, ep);
if (common == NULL) {
LOG_WRN("Device data not found for endpoint %u", ep);
if (usb_transfer_is_busy(ep)) {
LOG_DBG("Transfer is ongoing");
return;
}

dev_data = CONTAINER_OF(common, struct cdc_acm_dev_data_t, common);
len = ring_buf_get(dev_data->tx_ringbuf, dev_data->tx_buf,
sizeof(dev_data->tx_buf));

/* Check how many bytes were received */
usb_read(ep, NULL, 0, &read);
LOG_DBG("Got %d bytes from ringbuffer send to ep %x", len, ep);

bytes_to_read = MIN(read, sizeof(dev_data->rx_buf));
usb_transfer(ep, dev_data->tx_buf, len, USB_TRANS_WRITE,
cdc_acm_write_cb, dev_data);
}

static void cdc_acm_read_cb(u8_t ep, int size, void *priv)
{
struct cdc_acm_dev_data_t *dev_data = priv;
size_t wrote;

usb_read(ep, dev_data->rx_buf, bytes_to_read, &read);
LOG_DBG("ep %x size %d dev_data %p rx_ringbuf space %u",
ep, size, dev_data, ring_buf_space_get(dev_data->rx_ringbuf));

if (!ring_buf_put(dev_data->rx_ringbuf, dev_data->rx_buf, read)) {
LOG_ERR("Ring buffer full");
if (size <= 0) {
goto done;
}

wrote = ring_buf_put(dev_data->rx_ringbuf, dev_data->rx_buf, size);
if (wrote < size) {
LOG_ERR("Ring buffer full, drop %d bytes", size - wrote);
}

done:
dev_data->rx_ready = true;

/* Call callback only if rx irq ena */
if (dev_data->cb && dev_data->rx_irq_ena) {
k_work_submit(&dev_data->cb_work);
}

usb_transfer(ep, dev_data->rx_buf, sizeof(dev_data->rx_buf),
USB_TRANS_READ, cdc_acm_read_cb, dev_data);

}

/**
@@ -370,6 +374,8 @@ static void cdc_acm_do_cb(struct cdc_acm_dev_data_t *dev_data,
enum usb_dc_status_code status,
const u8_t *param)
{
struct device *dev = dev_data->common.dev;
struct usb_cfg_data *cfg = (void *)dev->config->config_info;

/* Store the new status */
if (status != USB_DC_SOF) {
@@ -388,6 +394,8 @@ static void cdc_acm_do_cb(struct cdc_acm_dev_data_t *dev_data,
LOG_DBG("USB device connected");
break;
case USB_DC_CONFIGURED:
cdc_acm_read_cb(cfg->endpoint[ACM_OUT_EP_IDX].ep_addr, 0,
dev_data);
dev_data->tx_ready = true;
LOG_DBG("USB device configured");
break;
@@ -524,6 +532,7 @@ static int cdc_acm_init(struct device *dev)
#endif
k_sem_init(&poll_wait_sem, 0, UINT_MAX);
k_work_init(&dev_data->cb_work, cdc_acm_irq_callback_work_handler);
k_work_init(&dev_data->tx_work, tx_work_handler);

return ret;
}
@@ -541,32 +550,25 @@ static int cdc_acm_fifo_fill(struct device *dev,
const u8_t *tx_data, int len)
{
struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
struct usb_cfg_data *cfg = (void *)dev->config->config_info;
u32_t wrote = 0U;
int err;
size_t wrote;

LOG_DBG("dev_data %p len %d tx_ringbuf space %u",
dev_data, len, ring_buf_space_get(dev_data->tx_ringbuf));

if (dev_data->usb_status != USB_DC_CONFIGURED) {
return 0;
}

dev_data->tx_ready = false;

/* FIXME: On Quark SE Family processor, restrict writing more than
* 4 bytes into TX USB Endpoint. When more than 4 bytes are written,
* sometimes (freq ~1/3000) first 4 bytes are repeated.
* (example: abcdef prints as abcdabcdef) (refer Jira GH-3515).
* Application should handle partial data transfer while writing
* into USB TX Endpoint.
*/
#ifdef CONFIG_SOC_SERIES_QUARK_SE
len = len > sizeof(u32_t) ? sizeof(u32_t) : len;
#endif

err = usb_write(cfg->endpoint[ACM_IN_EP_IDX].ep_addr,
tx_data, len, &wrote);
if (err != 0) {
return err;
wrote = ring_buf_put(dev_data->tx_ringbuf, tx_data, len);
if (wrote < len) {
LOG_WRN("Ring buffer full, drop %d bytes", len - wrote);
}

k_work_submit(&dev_data->tx_work);

/* Return written to ringbuf data len */
return wrote;
}

@@ -584,6 +586,9 @@ static int cdc_acm_fifo_read(struct device *dev, u8_t *rx_data, const int size)
struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
u32_t len;

LOG_DBG("dev %p size %d rx_ringbuf space %u",
dev, size, ring_buf_space_get(dev_data->rx_ringbuf));

len = ring_buf_get(dev_data->rx_ringbuf, rx_data, size);

if (ring_buf_is_empty(dev_data->rx_ringbuf)) {
@@ -962,8 +967,10 @@ static const struct uart_driver_api cdc_acm_driver_api = {
#define DEFINE_CDC_ACM_EP(x, int_ep_addr, out_ep_addr, in_ep_addr) \
static struct usb_ep_cfg_data cdc_acm_ep_data_##x[] = { \
INITIALIZER_EP_DATA(cdc_acm_int_in, int_ep_addr), \
INITIALIZER_EP_DATA(cdc_acm_bulk_out, out_ep_addr), \
INITIALIZER_EP_DATA(cdc_acm_bulk_in, in_ep_addr), \
INITIALIZER_EP_DATA(usb_transfer_ep_callback, \
out_ep_addr), \
INITIALIZER_EP_DATA(usb_transfer_ep_callback, \
in_ep_addr), \
}

#define DEFINE_CDC_ACM_CFG_DATA(x, _) \
@@ -1036,10 +1043,13 @@ static const struct uart_driver_api cdc_acm_driver_api = {
#define DEFINE_CDC_ACM_DEV_DATA(x, _) \
RING_BUF_DECLARE(rx_ringbuf_##x, \
CONFIG_USB_CDC_ACM_RINGBUF_SIZE); \
RING_BUF_DECLARE(tx_ringbuf_##x, \
CONFIG_USB_CDC_ACM_RINGBUF_SIZE); \
static struct cdc_acm_dev_data_t cdc_acm_dev_data_##x = { \
.usb_status = USB_DC_UNKNOWN, \
.line_coding = CDC_ACM_DEFAUL_BAUDRATE, \
.rx_ringbuf = &rx_ringbuf_##x, \
.tx_ringbuf = &tx_ringbuf_##x, \
};

#define DEFINE_CDC_ACM_DEVICE(x, _) \

0 comments on commit 98c8a95

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