123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 |
- /*******************************************************************************
- *
- * Copyright(c) 2015,2016 Intel Corporation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- ******************************************************************************/
- /**
- * @file
- * @brief CDC ACM device class driver
- *
- * Driver for USB CDC ACM device class driver
- */
- #include <kernel.h>
- #include <init.h>
- #include <drivers/uart.h>
- #include <string.h>
- #include <sys/byteorder.h>
- #include <usb/class/usb_cdc.h>
- #include <usb/usb_device.h>
- #include <usb/usb_common.h>
- #ifdef CONFIG_NVRAM_CONFIG
- #include <drivers/nvram_config.h>
- #endif
- #include "cdc_acm_descriptor.h"
- #ifndef CONFIG_UART_INTERRUPT_DRIVEN
- #error "CONFIG_UART_INTERRUPT_DRIVEN must be set for CDC ACM driver"
- #endif
- /* fixing compiler warning */
- #ifdef DEV_DATA
- #undef DEV_DATA
- #endif
- #undef __DEPRECATED_MACRO
- #define __DEPRECATED_MACRO
- #define LOG_LEVEL CONFIG_SYS_LOG_USB_CDC_ACM_LEVEL
- #include <logging/log.h>
- LOG_MODULE_REGISTER(cdc_acm);
- #define DEV_DATA(dev) \
- ((struct cdc_acm_dev_data_t * const)(dev)->data)
- /* 115200bps, no parity, 1 stop bit, 8bit char */
- #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 * HS_BULK_EP_MPS)
- /* Max CDC ACM class request max data size */
- #define CDC_CLASS_REQ_MAX_DATA_SIZE 8
- /* Serial state notification timeout */
- #define CDC_CONTROL_SERIAL_STATE_TIMEOUT_US 100000
- #define ACM_INT_EP_IDX 0
- #define ACM_OUT_EP_IDX 1
- #define ACM_IN_EP_IDX 2
- #define ACM_IF0_STRING "ACM-CDC"
- const struct device *cdc_acm_dev;
- static struct k_sem poll_wait_sem;
- /* Device data structure */
- struct cdc_acm_dev_data_t {
- /* USB device status code */
- enum usb_dc_status_code usb_status;
- /* Callback function pointer */
- uart_irq_callback_user_data_t cb;
- void *cb_data;
- /* Tx ready status. Signals when */
- u8_t tx_ready;
- u8_t rx_ready; /* Rx ready status */
- u8_t tx_irq_ena; /* Tx interrupt enable status */
- u8_t rx_irq_ena; /* Rx interrupt enable status */
- u8_t rx_buf[CDC_ACM_BUFFER_SIZE];/* Internal Rx buffer */
- u32_t rx_buf_head; /* Head of the internal Rx buffer */
- u32_t rx_buf_tail; /* Tail of the internal Rx buffer */
- /* Interface data buffer */
- u8_t interface_data[CDC_CLASS_REQ_MAX_DATA_SIZE];
- /* CDC ACM line coding properties. LE order */
- struct cdc_acm_line_coding line_coding;
- /* CDC ACM line state bitmap, DTE side */
- u8_t line_state;
- /* CDC ACM serial state bitmap, DCE side */
- u8_t serial_state;
- /* CDC ACM notification sent status */
- u8_t notification_sent;
- };
- /**
- * @brief Handler called for Class requests not handled by the USB stack.
- *
- * @param pSetup Information about the request to execute.
- * @param len Size of the buffer.
- * @param data Buffer containing the request result.
- *
- * @return 0 on success, negative errno code on fail.
- */
- static int cdc_acm_class_handle_req(struct usb_setup_packet *pSetup,
- s32_t *len, u8_t **data)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
- switch (pSetup->bRequest) {
- case SET_LINE_CODING:
- memcpy(&dev_data->line_coding,
- *data, sizeof(dev_data->line_coding));
- LOG_DBG("CDC_SET_LINE_CODING %d %d %d %d",
- sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
- dev_data->line_coding.bCharFormat,
- dev_data->line_coding.bParityType,
- dev_data->line_coding.bDataBits);
- break;
- case SET_CONTROL_LINE_STATE:
- dev_data->line_state = (u8_t)sys_le16_to_cpu(pSetup->wValue);
- LOG_DBG("CDC_SET_CONTROL_LINE_STATE 0x%x",
- dev_data->line_state);
- break;
- case GET_LINE_CODING:
- *data = (u8_t *)(&dev_data->line_coding);
- *len = sizeof(dev_data->line_coding);
- LOG_DBG("CDC_GET_LINE_CODING %d %d %d %d",
- sys_le32_to_cpu(dev_data->line_coding.dwDTERate),
- dev_data->line_coding.bCharFormat,
- dev_data->line_coding.bParityType,
- dev_data->line_coding.bDataBits);
- break;
- default:
- LOG_DBG("CDC ACM request 0x%x, value 0x%x",
- pSetup->bRequest, pSetup->wValue);
- return -EINVAL;
- }
- 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)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
- ARG_UNUSED(ep_status);
- ARG_UNUSED(ep);
- dev_data->tx_ready = 1;
- k_sem_give(&poll_wait_sem);
- /* Call callback only if tx irq ena */
- if (dev_data->cb && dev_data->tx_irq_ena) {
- dev_data->cb(cdc_acm_dev, dev_data->cb_data);
- }
- }
- /**
- * @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)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
- u32_t actual;
- u32_t buf_head;
- ARG_UNUSED(ep_status);
- usb_read_actual(ep, &actual);
- buf_head = dev_data->rx_buf_head;
- if (((buf_head + actual) % CDC_ACM_BUFFER_SIZE) ==
- dev_data->rx_buf_tail) {
- /* FIFO full, discard data */
- LOG_ERR("CDC buffer full!");
- } else {
- buf_head = (buf_head + actual) % CDC_ACM_BUFFER_SIZE;
- }
- dev_data->rx_buf_head = buf_head;
- dev_data->rx_ready = 1;
- /* Call callback only if rx irq ena */
- if (dev_data->cb && dev_data->rx_irq_ena) {
- dev_data->cb(cdc_acm_dev, dev_data->cb_data);
- }
- }
- /**
- * @brief EP Interrupt handler
- *
- * @param ep Endpoint address.
- * @param ep_status Endpoint status code.
- *
- * @return N/A.
- */
- static void cdc_acm_int_in(u8_t ep, enum usb_dc_ep_cb_status_code ep_status)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
- ARG_UNUSED(ep_status);
- dev_data->notification_sent = 1;
- }
- /**
- * @brief Callback used to know the USB connection status
- *
- * @param status USB device status code.
- *
- * @return N/A.
- */
- static void cdc_acm_dev_status_cb(enum usb_dc_status_code status, u8_t *param)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev);
- ARG_UNUSED(param);
- /* Store the new status */
- dev_data->usb_status = status;
- /* Check the USB status and do needed action if required */
- switch (status) {
- case USB_DC_ERROR:
- LOG_DBG("USB device error");
- break;
- case USB_DC_RESET:
- LOG_DBG("USB device reset detected");
- break;
- case USB_DC_CONNECTED:
- LOG_DBG("USB device connected");
- break;
- case USB_DC_CONFIGURED:
- LOG_DBG("USB device configured");
- usb_read_async(CONFIG_CDC_ACM_BULK_OUT_EP_ADDR, dev_data->rx_buf, HS_BULK_EP_MPS, NULL);
- break;
- case USB_DC_DISCONNECTED:
- LOG_DBG("USB device disconnected");
- break;
- case USB_DC_SUSPEND:
- LOG_DBG("USB device suspended");
- break;
- case USB_DC_RESUME:
- LOG_DBG("USB device resumed");
- break;
- case USB_DC_HIGHSPEED:
- LOG_DBG("USB high-speed inter");
- break;
- case USB_DC_UNKNOWN:
- default:
- LOG_DBG("USB unknown state");
- break;
- }
- }
- /* Describe EndPoints configuration */
- static const struct usb_ep_cfg_data cdc_acm_ep_data[] = {
- {
- .ep_cb = cdc_acm_int_in,
- .ep_addr = CONFIG_CDC_ACM_INTERRUPT_EP_ADDR
- },
- {
- .ep_cb = cdc_acm_bulk_out,
- .ep_addr = CONFIG_CDC_ACM_BULK_OUT_EP_ADDR
- },
- {
- .ep_cb = cdc_acm_bulk_in,
- .ep_addr = CONFIG_CDC_ACM_BULK_IN_EP_ADDR
- }
- };
- static const struct usb_cfg_data cdc_acm_config = {
- .usb_device_description = NULL,
- .cb_usb_status = cdc_acm_dev_status_cb,
- .interface = {
- .class_handler = cdc_acm_class_handle_req,
- .custom_handler = NULL,
- .vendor_handler = NULL,
- },
- .num_endpoints = ARRAY_SIZE(cdc_acm_ep_data),
- .endpoint = cdc_acm_ep_data
- };
- /**
- * @brief Set the baud rate
- *
- * This routine set the given baud rate for the UART.
- *
- * @param dev CDC ACM device struct.
- * @param baudrate Baud rate.
- *
- * @return N/A.
- */
- static void cdc_acm_baudrate_set(const struct device *dev, u32_t baudrate)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->line_coding.dwDTERate = sys_cpu_to_le32(baudrate);
- }
- static int usb_cdc_acm_fix_dev_sn(void)
- {
- int ret;
- #ifdef CONFIG_NVRAM_CONFIG
- int read_len;
- u8_t mac_str[CONFIG_USB_DEVICE_STRING_DESC_MAX_LEN];
- read_len = nvram_config_get(CONFIG_USB_CDC_ACM_SN_NVRAM, mac_str, CONFIG_USB_DEVICE_STRING_DESC_MAX_LEN);
- if (read_len < 0) {
- LOG_DBG("no sn data in nvram: %d", read_len);
- ret = usb_device_register_string_descriptor(DEV_SN_DESC, CONFIG_USB_CDC_ACM_SN, strlen(CONFIG_USB_CDC_ACM_SN));
- if (ret)
- return ret;
- } else {
- ret = usb_device_register_string_descriptor(DEV_SN_DESC, mac_str, read_len);
- if (ret)
- return ret;
- }
- #else
- ret = usb_device_register_string_descriptor(DEV_SN_DESC, CONFIG_USB_CDC_ACM_SN, strlen(CONFIG_USB_CDC_ACM_SN));
- if (ret)
- return ret;
- #endif
- return 0;
- }
- /**
- * @brief Initialize UART channel
- *
- * This routine is called to reset the chip in a quiescent state.
- * It is assumed that this function is called only once per UART.
- *
- * @param dev CDC ACM device struct.
- *
- * @return 0 always.
- */
- static int cdc_acm_init(const struct device *dev)
- {
- cdc_acm_dev = dev;
- int ret;
- /* Register string descriptor */
- ret = usb_device_register_string_descriptor(MANUFACTURE_STR_DESC, CONFIG_USB_CDC_ACM_MANUFACTURER, strlen(CONFIG_USB_CDC_ACM_MANUFACTURER));
- if (ret) {
- return ret;
- }
- ret = usb_device_register_string_descriptor(PRODUCT_STR_DESC, CONFIG_USB_CDC_ACM_PRODUCT, strlen(CONFIG_USB_CDC_ACM_PRODUCT));
- if (ret) {
- return ret;
- }
- ret = usb_cdc_acm_fix_dev_sn();
- if (ret) {
- return ret;
- }
- /* Register device descriptor */
- usb_device_register_descriptors(cdc_acm_usb_fs_descriptor, cdc_acm_usb_hs_descriptor);
- /* Initialize the USB driver with the right configuration */
- ret = usb_set_config(&cdc_acm_config);
- if (ret < 0) {
- LOG_ERR("Failed to config USB");
- return ret;
- }
- /* Enable USB driver */
- ret = usb_enable(&cdc_acm_config);
- if (ret < 0) {
- LOG_ERR("Failed to enable USB");
- return ret;
- }
- k_sem_init(&poll_wait_sem, 0, UINT_MAX);
- return 0;
- }
- /**
- * @brief Fill FIFO with data
- *
- * @param dev CDC ACM device struct.
- * @param tx_data Data to transmit.
- * @param len Number of bytes to send.
- *
- * @return Number of bytes sent.
- */
- static int cdc_acm_fifo_fill(const struct device *dev,
- const u8_t *tx_data, int len)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- static u8_t cdc_acm_recursive;
- u32_t wrote = 0;
- int err;
- if (dev_data->usb_status != USB_DC_CONFIGURED) {
- return -ENODEV;
- }
- if (cdc_acm_recursive) {
- return -EIO;
- }
- dev_data->tx_ready = 0;
- /* 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
- cdc_acm_recursive = 1;
- err = usb_write(cdc_acm_ep_data[ACM_IN_EP_IDX].ep_addr,
- tx_data, len, &wrote);
- if (err != 0) {
- cdc_acm_recursive = 0;
- return err;
- }
- cdc_acm_recursive = 0;
- return wrote;
- }
- /**
- * @brief Read data from FIFO
- *
- * @param dev CDC ACM device struct.
- * @param rx_data Pointer to data container.
- * @param size Container size.
- *
- * @return Number of bytes read.
- */
- static int cdc_acm_fifo_read(const struct device *dev, u8_t *rx_data,
- const int size)
- {
- u32_t avail_data, bytes_read, i;
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- u8_t read = 0;
- avail_data = (CDC_ACM_BUFFER_SIZE + dev_data->rx_buf_head -
- dev_data->rx_buf_tail) % CDC_ACM_BUFFER_SIZE;
- if (avail_data > size) {
- bytes_read = size;
- } else {
- bytes_read = avail_data;
- read = 1;
- }
- for (i = 0; i < bytes_read; i++) {
- rx_data[i] = dev_data->rx_buf[(dev_data->rx_buf_tail + i) %
- CDC_ACM_BUFFER_SIZE];
- }
- dev_data->rx_buf_tail = (dev_data->rx_buf_tail + bytes_read) %
- CDC_ACM_BUFFER_SIZE;
- if (dev_data->rx_buf_tail == dev_data->rx_buf_head) {
- /* Buffer empty */
- dev_data->rx_ready = 0;
- }
- if (dev_data->usb_status != USB_DC_CONFIGURED) {
- read = 0;
- }
- if (read == 1) {
- dev_data->rx_buf_tail = 0;
- dev_data->rx_buf_head = 0;
- usb_read_async(CONFIG_CDC_ACM_BULK_OUT_EP_ADDR,
- dev_data->rx_buf,
- HS_BULK_EP_MPS, NULL);
- }
- return bytes_read;
- }
- /**
- * @brief Enable TX interrupt
- *
- * @param dev CDC ACM device struct.
- *
- * @return N/A.
- */
- static void cdc_acm_irq_tx_enable(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->tx_irq_ena = 1;
- }
- /**
- * @brief Disable TX interrupt
- *
- * @param dev CDC ACM device struct.
- *
- * @return N/A.
- */
- static void cdc_acm_irq_tx_disable(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->tx_irq_ena = 0;
- }
- /**
- * @brief Check if Tx IRQ has been raised
- *
- * @param dev CDC ACM device struct.
- *
- * @return 1 if a Tx IRQ is pending, 0 otherwise.
- */
- static int cdc_acm_irq_tx_ready(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- if (dev_data->tx_ready) {
- dev_data->tx_ready = 0;
- return 1;
- }
- return 0;
- }
- /**
- * @brief Enable RX interrupt
- *
- * @param dev CDC ACM device struct.
- *
- * @return N/A
- */
- static void cdc_acm_irq_rx_enable(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->rx_irq_ena = 1;
- }
- /**
- * @brief Disable RX interrupt
- *
- * @param dev CDC ACM device struct.
- *
- * @return N/A.
- */
- static void cdc_acm_irq_rx_disable(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->rx_irq_ena = 0;
- }
- /**
- * @brief Check if Rx IRQ has been raised
- *
- * @param dev CDC ACM device struct.
- *
- * @return 1 if an IRQ is ready, 0 otherwise.
- */
- static int cdc_acm_irq_rx_ready(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- if (dev_data->rx_ready) {
- dev_data->rx_ready = 0;
- return 1;
- }
- return 0;
- }
- /**
- * @brief Check if Tx or Rx IRQ is pending
- *
- * @param dev CDC ACM device struct.
- *
- * @return 1 if a Tx or Rx IRQ is pending, 0 otherwise.
- */
- static int cdc_acm_irq_is_pending(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- if (dev_data->tx_ready && dev_data->tx_irq_ena) {
- return 1;
- } else if (dev_data->rx_ready && dev_data->rx_irq_ena) {
- return 1;
- } else {
- return 0;
- }
- }
- /**
- * @brief Update IRQ status
- *
- * @param dev CDC ACM device struct.
- *
- * @return Always 1
- */
- static int cdc_acm_irq_update(const struct device *dev)
- {
- ARG_UNUSED(dev);
- return 1;
- }
- /**
- * @brief Set the callback function pointer for IRQ.
- *
- * @param dev CDC ACM device struct.
- * @param cb Callback function pointer.
- *
- * @return N/A
- */
- static void cdc_acm_irq_callback_set(const struct device *dev,
- uart_irq_callback_user_data_t cb, void *user_data)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- dev_data->cb = cb;
- dev_data->cb_data = user_data;
- }
- #ifdef CONFIG_UART_LINE_CTRL
- /**
- * @brief Send serial line state notification to the Host
- *
- * This routine sends asynchronous notification of UART status
- * on the interrupt endpoint
- *
- * @param dev CDC ACM device struct.
- * @param ep_status Endpoint status code.
- *
- * @return N/A.
- */
- static int cdc_acm_send_notification(const struct device *dev, u16_t serial_state)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- struct cdc_acm_notification notification;
- u32_t cnt = 0;
- notification.bmRequestType = 0xA1;
- notification.bNotificationType = 0x20;
- notification.wValue = 0;
- notification.wIndex = 0;
- notification.wLength = sys_cpu_to_le16(sizeof(serial_state));
- notification.data = sys_cpu_to_le16(serial_state);
- dev_data->notification_sent = 0;
- usb_write(cdc_acm_ep_data[ACM_INT_EP_IDX].ep_addr,
- (const u8_t *)¬ification, sizeof(notification), NULL);
- /* Wait for notification to be sent */
- while (!((volatile u8_t)dev_data->notification_sent)) {
- k_busy_wait(1);
- if (++cnt > CDC_CONTROL_SERIAL_STATE_TIMEOUT_US) {
- LOG_DBG("CDC ACM notification timeout!");
- return -EIO;
- }
- }
- return 0;
- }
- /**
- * @brief Manipulate line control for UART.
- *
- * @param dev CDC ACM device struct
- * @param ctrl The line control to be manipulated
- * @param val Value to set the line control
- *
- * @return 0 if successful, failed otherwise.
- */
- static int cdc_acm_line_ctrl_set(const struct device *dev,
- u32_t ctrl, u32_t val)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- if (dev_data->usb_status != USB_DC_CONFIGURED) {
- return -ENODEV;
- }
- switch (ctrl) {
- case UART_LINE_CTRL_BAUD_RATE:
- cdc_acm_baudrate_set(dev, val);
- return 0;
- case UART_LINE_CTRL_DCD:
- dev_data->serial_state &= ~SERIAL_STATE_RX_CARRIER;
- if (val) {
- dev_data->serial_state |= SERIAL_STATE_RX_CARRIER;
- }
- cdc_acm_send_notification(dev, SERIAL_STATE_RX_CARRIER);
- return 0;
- case UART_LINE_CTRL_DSR:
- dev_data->serial_state &= ~SERIAL_STATE_TX_CARRIER;
- if (val) {
- dev_data->serial_state |= SERIAL_STATE_TX_CARRIER;
- }
- cdc_acm_send_notification(dev, dev_data->serial_state);
- return 0;
- default:
- return -ENODEV;
- }
- return -ENOTSUP;
- }
- /**
- * @brief Manipulate line control for UART.
- *
- * @param dev CDC ACM device struct
- * @param ctrl The line control to be manipulated
- * @param val Value to set the line control
- *
- * @return 0 if successful, failed otherwise.
- */
- static int cdc_acm_line_ctrl_get(const struct device *dev,
- u32_t ctrl, u32_t *val)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- switch (ctrl) {
- case UART_LINE_CTRL_BAUD_RATE:
- *val = sys_le32_to_cpu(dev_data->line_coding.dwDTERate);
- return 0;
- case UART_LINE_CTRL_RTS:
- *val = (dev_data->line_state &
- SET_CONTROL_LINE_STATE_RTS) ? 1 : 0;
- return 0;
- case UART_LINE_CTRL_DTR:
- *val = (dev_data->line_state &
- SET_CONTROL_LINE_STATE_DTR) ? 1 : 0;
- return 0;
- }
- return -ENOTSUP;
- }
- #endif /* CONFIG_UART_LINE_CTRL */
- /*
- * @brief Poll the device for input.
- *
- * @return -ENOTSUP Since underlying USB device controller always uses
- * interrupts, polled mode UART APIs are not implemented for the UART interface
- * exported by CDC ACM driver. Apps should use fifo_read API instead.
- */
- static int cdc_acm_poll_in(const struct device *dev, unsigned char *c)
- {
- ARG_UNUSED(dev);
- ARG_UNUSED(c);
- return -ENOTSUP;
- }
- /*
- * @brief Output a character in polled mode.
- *
- * The UART poll method for USB UART is simulated by waiting till
- * we get the next BULK In upcall from the USB device controller or 100 ms.
- *
- * @return the same character which is sent
- */
- static void cdc_acm_poll_out(const struct device *dev,
- unsigned char c)
- {
- cdc_acm_fifo_fill(dev, &c, 1);
- k_sem_take(&poll_wait_sem, K_MSEC(100));
- }
- static const struct uart_driver_api cdc_acm_driver_api = {
- .poll_in = cdc_acm_poll_in,
- .poll_out = cdc_acm_poll_out,
- .fifo_fill = cdc_acm_fifo_fill,
- .fifo_read = cdc_acm_fifo_read,
- .irq_tx_enable = cdc_acm_irq_tx_enable,
- .irq_tx_disable = cdc_acm_irq_tx_disable,
- .irq_tx_ready = cdc_acm_irq_tx_ready,
- .irq_rx_enable = cdc_acm_irq_rx_enable,
- .irq_rx_disable = cdc_acm_irq_rx_disable,
- .irq_rx_ready = cdc_acm_irq_rx_ready,
- .irq_is_pending = cdc_acm_irq_is_pending,
- .irq_update = cdc_acm_irq_update,
- .irq_callback_set = cdc_acm_irq_callback_set,
- #ifdef CONFIG_UART_LINE_CTRL
- .line_ctrl_set = cdc_acm_line_ctrl_set,
- .line_ctrl_get = cdc_acm_line_ctrl_get,
- #endif /* CONFIG_UART_LINE_CTRL */
- };
- static struct cdc_acm_dev_data_t cdc_acm_dev_data = {
- .usb_status = USB_DC_UNKNOWN,
- .line_coding = CDC_ACM_DEFAUL_BAUDRATE,
- };
- /*
- * API: initialize USB CDC ACM
- */
- int usb_cdc_acm_init(const struct device *dev)
- {
- struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev);
- int ret;
- if (cdc_acm_dev != NULL) {
- LOG_WRN("Already");
- return -EALREADY;
- }
- /* initialize states */
- dev_data->usb_status = USB_DC_UNKNOWN;
- dev_data->tx_ready = 1;
- dev_data->rx_ready = 0;
- dev_data->rx_buf_head = 0;
- dev_data->rx_buf_tail = 0;
- dev_data->line_coding.dwDTERate = 0;
- dev_data->line_coding.bCharFormat = 0;
- dev_data->line_coding.bParityType = 0;
- dev_data->line_coding.bDataBits = 0;
- dev_data->serial_state = 0;
- dev_data->line_state = 0;
- dev_data->tx_irq_ena = 0;
- dev_data->rx_irq_ena = 0;
- ret = cdc_acm_init(dev);
- if (ret) {
- cdc_acm_dev = NULL;
- }
- return ret;
- }
- /*
- * API: deinitialize USB CDC ACM
- */
- int usb_cdc_acm_exit(void)
- {
- int ret;
- if (cdc_acm_dev == NULL) {
- LOG_WRN("Already");
- return -EALREADY;
- }
- ret = usb_disable();
- if (ret) {
- LOG_ERR("Failed to disable USB: %d", ret);
- return ret;
- }
- usb_deconfig();
- cdc_acm_dev = NULL;
- cdc_acm_dev_data.usb_status = USB_DC_UNKNOWN;
- LOG_INF("done");
- return 0;
- }
- static int cdc_acm_init_dummy(const struct device *dev)
- {
- ARG_UNUSED(dev);
- return 0;
- }
- DEVICE_DEFINE(cdc_acm, CONFIG_CDC_ACM_PORT_NAME, cdc_acm_init_dummy,
- NULL, &cdc_acm_dev_data, NULL,
- APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
- &cdc_acm_driver_api);
|