From 3d8b28b358ee4761ed06440c41cd87840afe863f Mon Sep 17 00:00:00 2001 From: Adrian Bradianu Date: Wed, 1 Jun 2016 14:21:11 +0300 Subject: [PATCH] usb: Add CDC ACM and DFU class examples CDC ACM sample class driver implements a virtual UART port. SET_LINE_CODING, GET_LINE_CODING and SET_CONTROL_LINE_STATE class requests are supported. DFU class example does not perform an actual firmware upgrade, instead it allows the user to upload a file at a predetermined flash address or to download the content from that flash address. Change-Id: I702e6727db15ef360d110a70a979c1e4bd4ee1bb Signed-off-by: Adrian Bradianu Signed-off-by: Jesus Sanchez-Palencia Signed-off-by: Jithu Joseph --- include/uart.h | 27 + samples/usb/cdc_acm/Makefile | 5 + samples/usb/cdc_acm/prj.conf | 12 + samples/usb/cdc_acm/src/Makefile | 1 + samples/usb/cdc_acm/src/main.c | 132 +++++ samples/usb/dfu/Makefile | 5 + samples/usb/dfu/prj.conf | 10 + samples/usb/dfu/src/Makefile | 3 + samples/usb/dfu/src/main.c | 68 +++ samples/usb/dfu/src/usb_dfu.c | 584 ++++++++++++++++++++++ samples/usb/dfu/src/usb_dfu.h | 130 +++++ usb/Kconfig | 2 + usb/Makefile | 1 + usb/class/Kconfig | 43 ++ usb/class/Makefile | 3 + usb/class/cdc_acm.c | 830 +++++++++++++++++++++++++++++++ usb/class/cdc_acm.h | 129 +++++ 17 files changed, 1985 insertions(+) create mode 100644 samples/usb/cdc_acm/Makefile create mode 100644 samples/usb/cdc_acm/prj.conf create mode 100644 samples/usb/cdc_acm/src/Makefile create mode 100644 samples/usb/cdc_acm/src/main.c create mode 100644 samples/usb/dfu/Makefile create mode 100644 samples/usb/dfu/prj.conf create mode 100644 samples/usb/dfu/src/Makefile create mode 100644 samples/usb/dfu/src/main.c create mode 100644 samples/usb/dfu/src/usb_dfu.c create mode 100644 samples/usb/dfu/src/usb_dfu.h create mode 100644 usb/class/Kconfig create mode 100644 usb/class/Makefile create mode 100644 usb/class/cdc_acm.c create mode 100644 usb/class/cdc_acm.h diff --git a/include/uart.h b/include/uart.h index a30b2115daa..226bee9c511 100644 --- a/include/uart.h +++ b/include/uart.h @@ -51,6 +51,8 @@ extern "C" { #define LINE_CTRL_BAUD_RATE (1 << 0) #define LINE_CTRL_RTS (1 << 1) #define LINE_CTRL_DTR (1 << 2) +#define LINE_CTRL_DCD (1 << 3) +#define LINE_CTRL_DSR (1 << 4) /* Common communication errors for UART.*/ @@ -174,6 +176,7 @@ struct uart_driver_api { #ifdef CONFIG_UART_LINE_CTRL int (*line_ctrl_set)(struct device *dev, uint32_t ctrl, uint32_t val); + int (*line_ctrl_get)(struct device *dev, uint32_t ctrl, uint32_t *val); #endif #ifdef CONFIG_UART_DRV_CMD @@ -593,6 +596,30 @@ static inline int uart_line_ctrl_set(struct device *dev, return -ENOTSUP; } +/** + * @brief Retrieve line control for UART. + * + * @param dev UART device structure. + * @param ctrl The line control to manipulate. + * @param val Value to get for the line control. + * + * @retval 0 If successful. + * @retval failed Otherwise. + */ +static inline int uart_line_ctrl_get(struct device *dev, + uint32_t ctrl, uint32_t *val) +{ + struct uart_driver_api *api; + + api = (struct uart_driver_api *)dev->driver_api; + + if (api && api->line_ctrl_get) { + return api->line_ctrl_get(dev, ctrl, val); + } + + return -ENOTSUP; +} + #endif /* CONFIG_UART_LINE_CTRL */ #ifdef CONFIG_UART_DRV_CMD diff --git a/samples/usb/cdc_acm/Makefile b/samples/usb/cdc_acm/Makefile new file mode 100644 index 00000000000..de3ed278161 --- /dev/null +++ b/samples/usb/cdc_acm/Makefile @@ -0,0 +1,5 @@ +BOARD ?= quark_se_devboard +KERNEL_TYPE ?= nano +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/samples/usb/cdc_acm/prj.conf b/samples/usb/cdc_acm/prj.conf new file mode 100644 index 00000000000..9371f064c3c --- /dev/null +++ b/samples/usb/cdc_acm/prj.conf @@ -0,0 +1,12 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_GPIO=y +CONFIG_ARC_INIT=n +CONFIG_USB=y +CONFIG_USB_DW=y +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DW_DEBUG=n +CONFIG_USB_CDC_ACM=y +CONFIG_USB_DEBUG=n +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y diff --git a/samples/usb/cdc_acm/src/Makefile b/samples/usb/cdc_acm/src/Makefile new file mode 100644 index 00000000000..b666967fd57 --- /dev/null +++ b/samples/usb/cdc_acm/src/Makefile @@ -0,0 +1 @@ +obj-y += main.o diff --git a/samples/usb/cdc_acm/src/main.c b/samples/usb/cdc_acm/src/main.c new file mode 100644 index 00000000000..3d99aa038c6 --- /dev/null +++ b/samples/usb/cdc_acm/src/main.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief Sample app for CDC ACM class driver + * + * Sample app for USB CDC ACM class driver. The received data is echoed back + * to the serial port. + */ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#define PRINT printf +#else +#include +#define PRINT printk +#endif + +static const char *banner1 = "Send characters to the UART device\r\n"; +static const char *banner2 = "Characters read:\r\n"; + +static volatile bool data_transmitted; +static volatile bool data_arrived; +static char data_buf[64]; + +static void interrupt_handler(struct device *dev) +{ + uart_irq_update(dev); + + if (uart_irq_tx_ready(dev)) { + data_transmitted = true; + } + + if (uart_irq_rx_ready(dev)) { + data_arrived = true; + } +} + +static void read_data(struct device *dev, int *bytes_read) +{ + uart_irq_rx_enable(dev); + + data_arrived = false; + while (data_arrived == false) + ; + + /* Read all data */ + *bytes_read = uart_fifo_read(dev, data_buf, sizeof(data_buf)); + + uart_irq_rx_disable(dev); +} + +static void write_data(struct device *dev, const char *buf, int len) +{ + uart_irq_tx_enable(dev); + + data_transmitted = false; + uart_fifo_fill(dev, buf, len); + while (data_transmitted == false) + ; + + uart_irq_tx_disable(dev); +} + +void main(void) +{ + struct device *dev; + uint32_t baudrate, bytes_read, dtr = 0; + int ret; + + dev = device_get_binding(CONFIG_CDC_ACM_PORT_NAME); + if (!dev) { + PRINT("CDC ACM device not found\n"); + return; + } + + PRINT("Wait for DTR\n"); + while (1) { + uart_line_ctrl_get(dev, LINE_CTRL_DTR, &dtr); + if (dtr) + break; + } + PRINT("DTR set, start test\n"); + + /* They are optional, we use them to test the interrupt endpoint */ + ret = uart_line_ctrl_set(dev, LINE_CTRL_DCD, 1); + if (ret) + PRINT("Failed to set DCD, ret code %d\n", ret); + + ret = uart_line_ctrl_set(dev, LINE_CTRL_DSR, 1); + if (ret) + PRINT("Failed to set DSR, ret code %d\n", ret); + + /* Wait 1 sec for the host to do all settings */ + sys_thread_busy_wait(1000000); + + ret = uart_line_ctrl_get(dev, LINE_CTRL_BAUD_RATE, &baudrate); + if (ret) + PRINT("Failed to get baudrate, ret code %d\n", ret); + else + PRINT("Baudrate detected: %d\n", baudrate); + + uart_irq_callback_set(dev, interrupt_handler); + write_data(dev, banner1, strlen(banner1)); + write_data(dev, banner2, strlen(banner2)); + + /* Echo the received data */ + while (1) { + read_data(dev, &bytes_read); + write_data(dev, data_buf, bytes_read); + } +} diff --git a/samples/usb/dfu/Makefile b/samples/usb/dfu/Makefile new file mode 100644 index 00000000000..de3ed278161 --- /dev/null +++ b/samples/usb/dfu/Makefile @@ -0,0 +1,5 @@ +BOARD ?= quark_se_devboard +KERNEL_TYPE ?= nano +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/samples/usb/dfu/prj.conf b/samples/usb/dfu/prj.conf new file mode 100644 index 00000000000..03867f4ca25 --- /dev/null +++ b/samples/usb/dfu/prj.conf @@ -0,0 +1,10 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_GPIO=y +CONFIG_ARC_INIT=n +CONFIG_USB=y +CONFIG_USB_DW=y +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DW_DEBUG=n +CONFIG_USB_DEBUG=n +CONFIG_FLASH=y +CONFIG_SOC_FLASH_QMSI=y diff --git a/samples/usb/dfu/src/Makefile b/samples/usb/dfu/src/Makefile new file mode 100644 index 00000000000..f5c44c638a3 --- /dev/null +++ b/samples/usb/dfu/src/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I${srctree}/include/drivers/usb -I${srctree}/include/usb/ -I${srctree}/usb/include + +obj-y += usb_dfu.o main.o diff --git a/samples/usb/dfu/src/main.c b/samples/usb/dfu/src/main.c new file mode 100644 index 00000000000..4be63c8d3c1 --- /dev/null +++ b/samples/usb/dfu/src/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * @brief Sample app for DFU class driver + * + * This sample app implements a DFU class driver. It does not perform an actual + * firmware upgrade instead it allows user to upload a file at a predetermined + * flash address or to download the content from that flash address. + */ + +#include +#include +#include "usb_dfu.h" + +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#define PRINT printf +#else +#include +#define PRINT printk +#endif + +#ifdef CONFIG_SOC_QUARK_SE +#define DFU_FLASH_DEVICE "QUARK_FLASH" +/* Unused flash area to test DFU class driver */ +#define DFU_FLASH_TEST_ADDR (0x40030000 + 0x10000) +#define DFU_FLASH_PAGE_SIZE (2048) +#define DFU_FLASH_UPLOAD_SIZE (0x6000) +#else +#error "Unsupported board" +#endif + +void main(void) +{ + struct device *dev = NULL; + + PRINT("DFU Test Application\n"); + + dev = device_get_binding(DFU_FLASH_DEVICE); + if (!dev) { + printf("Flash device not found\n"); + return; + } + + dfu_start(dev, + DFU_FLASH_TEST_ADDR, + DFU_FLASH_PAGE_SIZE, + DFU_FLASH_UPLOAD_SIZE); + + while (1) { + /* do nothing */ + } +} diff --git a/samples/usb/dfu/src/usb_dfu.c b/samples/usb/dfu/src/usb_dfu.c new file mode 100644 index 00000000000..c317a579d17 --- /dev/null +++ b/samples/usb/dfu/src/usb_dfu.c @@ -0,0 +1,584 @@ +/******************************************************************************* + * + * 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. + * + ******************************************************************************/ + +/** + * @brief DFU class driver + * + * USB DFU device class driver + * + */ + +#include +#include +#include +#include +#include "usb_device.h" +#include "usb_dfu.h" + + +#ifndef CONFIG_USB_DFU_DEBUG +#define DBG(...) { ; } +#else +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#define DBG printf +#else +#define DBG printk +#endif /* CONFIG_STDOUT_CONSOLE */ +#endif /* CONFIG_USB_DFU_DEBUG */ + +static struct usb_cfg_data dfu_config; + +/* Device data structure */ +struct dfu_data_t { + /* Flash device to read/write data from/to */ + struct device *flash_dev; + /* Flash base address to write/read to/from */ + uint32_t flash_base_addr; + /* Flash page size */ + uint32_t flash_page_size; + /* Size of the upload transfer */ + uint32_t flash_upload_size; + /* Number of bytes sent during upload */ + uint32_t bytes_sent; + /* Number of bytes received during download */ + uint32_t bytes_rcvd; + uint8_t buffer[DFU_MAX_XFER_SIZE]; /* DFU data buffer */ + enum dfu_state state; /* State of the DFU device */ + enum dfu_status status; /* Status of the DFU device */ + uint16_t block_nr; /* DFU block number */ +}; + +static struct dfu_data_t dfu_data = { + .state = appIDLE, + .status = statusOK, +}; + +/* Structure representing the global USB description */ +static const uint8_t dfu_runtime_usb_description[] = { + /* Device descriptor */ + USB_DEVICE_DESC_SIZE, /* Descriptor size */ + USB_DEVICE_DESC, /* Descriptor type */ + LOW_BYTE(USB_1_1), + HIGH_BYTE(USB_1_1), /* USB version in BCD format */ + 0x00, /* Class - Interface specific */ + 0x00, /* SubClass - Interface specific */ + 0x00, /* Protocol - Interface specific */ + MAX_PACKET_SIZE0, /* EP0 Max Packet Size */ + LOW_BYTE(DFU_VENDOR_ID), + HIGH_BYTE(DFU_VENDOR_ID), /* Vendor Id */ + LOW_BYTE(DFU_PRODUCT_ID), + HIGH_BYTE(DFU_PRODUCT_ID), /* Product Id */ + LOW_BYTE(BCDDEVICE_RELNUM), + HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */ + /* Index of Manufacturer String Descriptor */ + 0x00, + /* Index of Product String Descriptor */ + 0x00, + /* Index of Serial Number String Descriptor */ + 0x00, + DFU_NUM_CONF, /* Number of Possible Configuration */ + + /* Configuration descriptor */ + USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */ + USB_CONFIGURATION_DESC, /* Descriptor type */ + /* Total length in bytes of data returned */ + LOW_BYTE(DFU_CONF_SIZE), + HIGH_BYTE(DFU_CONF_SIZE), + DFU_NUM_ITF, /* Number of interfaces */ + 0x01, /* Configuration value */ + 0x00, /* Index of the Configuration string */ + USB_CONFIGURATION_ATTRIBUTES, /* Attributes */ + MAX_LOW_POWER, /* Max power consumption */ + + /* Interface descriptor */ + USB_INTERFACE_DESC_SIZE, /* Descriptor size */ + USB_INTERFACE_DESC, /* Descriptor type */ + 0x00, /* Interface index */ + 0x00, /* Alternate setting */ + DFU_NUM_EP, /* Number of Endpoints */ + DFU_CLASS, /* Class */ + DFU_INTERFACE_SUBCLASS, /* SubClass */ + DFU_RUNTIME_PROTOCOL, /* DFU Runtime Protocol */ + /* Index of the Interface String Descriptor */ + 0x00, + + /* DFU descriptor */ + USB_DFU_DESC_SIZE, /* Descriptor size */ + USB_DFU_FUNCTIONAL_DESC, /* Descriptor type DFU: Functional */ + DFU_ATTR_CAN_DNLOAD | + DFU_ATTR_CAN_UPLOAD | + DFU_ATTR_MANIFESTATION_TOLERANT,/* DFU attributes */ + LOW_BYTE(DFU_DETACH_TIMEOUT), + HIGH_BYTE(DFU_DETACH_TIMEOUT), /* wDetachTimeOut */ + LOW_BYTE(DFU_MAX_XFER_SIZE), + HIGH_BYTE(DFU_MAX_XFER_SIZE), /* wXferSize - 512bytes */ + 0x11, 0, /* DFU Version */ + + /* String descriptor language, only one, so min size 4 bytes. + * 0x0409 English(US) language code used. + */ + USB_STRING_DESC_SIZE, /* Descriptor size */ + USB_STRING_DESC, /* Descriptor type */ + 0x09, + 0x04, + + /* Manufacturer String Descriptor "Intel" */ + 0x0C, + USB_STRING_DESC, + 'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0, + + /* Product String Descriptor "DFU-Dev" */ + 0x10, + USB_STRING_DESC, + 'D', 0, 'F', 0, 'U', 0, '-', 0, 'D', 0, 'e', 0, 'v', 0, + + /* Serial Number String Descriptor "00.01" */ + 0x0C, + USB_STRING_DESC, + '0', 0, '0', 0, '.', 0, '0', 0, '1', 0 +}; + +/* Structure representing the global USB description */ +static const uint8_t dfu_mode_usb_description[] = { + /* Device descriptor */ + USB_DEVICE_DESC_SIZE, /* Descriptor size */ + USB_DEVICE_DESC, /* Descriptor type */ + LOW_BYTE(USB_1_1), + HIGH_BYTE(USB_1_1), /* USB version in BCD format */ + 0x00, /* Class - Interface specific */ + 0x00, /* SubClass - Interface specific */ + 0x00, /* Protocol - Interface specific */ + MAX_PACKET_SIZE0, /* EP0 Max Packet Size */ + LOW_BYTE(DFU_VENDOR_ID), + HIGH_BYTE(DFU_VENDOR_ID), /* Vendor Id */ + LOW_BYTE(DFU_PRODUCT_ID), + HIGH_BYTE(DFU_PRODUCT_ID), /* Product Id */ + LOW_BYTE(BCDDEVICE_RELNUM), + HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */ + /* Index of Manufacturer String Descriptor */ + 0x00, + /* Index of Product String Descriptor */ + 0x00, + /* Index of Serial Number String Descriptor */ + 0x00, + DFU_NUM_CONF, /* Number of Possible Configuration */ + + /* Configuration descriptor */ + USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */ + USB_CONFIGURATION_DESC, /* Descriptor type */ + /* Total length in bytes of data returned */ + LOW_BYTE(DFU_CONF_SIZE), + HIGH_BYTE(DFU_CONF_SIZE), + DFU_NUM_ITF, /* Number of interfaces */ + 0x01, /* Configuration value */ + 0x00, /* Index of the Configuration string */ + USB_CONFIGURATION_ATTRIBUTES, /* Attributes */ + MAX_LOW_POWER, /* Max power consumption */ + + /* Interface descriptor */ + USB_INTERFACE_DESC_SIZE, /* Descriptor size */ + USB_INTERFACE_DESC, /* Descriptor type */ + 0x00, /* Interface index */ + 0x00, /* Alternate setting */ + DFU_NUM_EP, /* Number of Endpoints */ + DFU_CLASS, /* Class */ + DFU_INTERFACE_SUBCLASS, /* SubClass */ + DFU_MODE_PROTOCOL, /* DFU Runtime Protocol */ + /* Index of the Interface String Descriptor */ + 0x00, + + /* DFU descriptor */ + USB_DFU_DESC_SIZE, /* Descriptor size */ + USB_DFU_FUNCTIONAL_DESC, /* Descriptor type DFU: Functional */ + DFU_ATTR_CAN_DNLOAD | + DFU_ATTR_CAN_UPLOAD | + DFU_ATTR_MANIFESTATION_TOLERANT,/* DFU attributes */ + LOW_BYTE(DFU_DETACH_TIMEOUT), + HIGH_BYTE(DFU_DETACH_TIMEOUT), /* wDetachTimeOut */ + LOW_BYTE(DFU_MAX_XFER_SIZE), + HIGH_BYTE(DFU_MAX_XFER_SIZE), /* wXferSize - 512bytes */ + 0x11, 0, /* DFU Version */ + + /* String descriptor language, only one, so min size 4 bytes. + * 0x0409 English(US) language code used. + */ + USB_STRING_DESC_SIZE, /* Descriptor size */ + USB_STRING_DESC, /* Descriptor type */ + 0x09, + 0x04, + + /* Manufacturer String Descriptor "Intel" */ + 0x0C, + USB_STRING_DESC, + 'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0, + + /* Product String Descriptor "DFU-Dev" */ + 0x10, + USB_STRING_DESC, + 'D', 0, 'F', 0, 'U', 0, '-', 0, 'D', 0, 'e', 0, 'v', 0, + + /* Serial Number String Descriptor "00.01" */ + 0x0C, + USB_STRING_DESC, + '0', 0, '0', 0, '.', 0, '0', 0, '1', 0 +}; + +/** + * @brief Helper function to check if in DFU app state. + * + * @return true if app state, false otherwise. + */ +static bool dfu_check_app_state(void) +{ + if (dfu_data.state == appIDLE || + dfu_data.state == appDETACH) { + dfu_data.state = appIDLE; + return true; + } + + return false; +} + +/** + * @brief Helper function to reset DFU internal counters. + */ +static void dfu_reset_counters(void) +{ + dfu_data.bytes_sent = 0; + dfu_data.bytes_rcvd = 0; + dfu_data.block_nr = 0; +} + +/** + * @brief Handler called for DFU 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 dfu_class_handle_req(struct usb_setup_packet *pSetup, + int32_t *data_len, uint8_t **data) +{ + int ret; + uint32_t len, bytes_left; + + switch (pSetup->bRequest) { + case DFU_GETSTATUS: + DBG("DFU_GETSTATUS: status %d, state %d\n", + dfu_data.status, dfu_data.state); + + if (dfu_data.state == dfuMANIFEST_SYNC) + dfu_data.state = dfuIDLE; + + (*data)[0] = dfu_data.status; + (*data)[1] = 0; + (*data)[2] = 1; + (*data)[3] = 0; + (*data)[4] = dfu_data.state; + (*data)[5] = 0; + *data_len = 6; + break; + + case DFU_GETSTATE: + DBG("DFU_GETSTATE\n"); + (*data)[0] = dfu_data.state; + *data_len = 1; + break; + + case DFU_ABORT: + DBG("DFU_ABORT\n"); + + if (dfu_check_app_state()) + return -EINVAL; + + dfu_reset_counters(); + dfu_data.state = dfuIDLE; + dfu_data.status = statusOK; + break; + + case DFU_CLRSTATUS: + DBG("DFU_CLRSTATUS\n"); + + if (dfu_check_app_state()) + return -EINVAL; + + dfu_data.state = dfuIDLE; + dfu_data.status = statusOK; + break; + + case DFU_DNLOAD: + DBG("DFU_DNLOAD block %d, len %d, state %d\n", + pSetup->wValue, pSetup->wLength, dfu_data.state); + + if (dfu_check_app_state()) + return -EINVAL; + + switch (dfu_data.state) { + case dfuIDLE: + dfu_reset_counters(); + DBG("DFU_DNLOAD start\n"); + case dfuDNLOAD_IDLE: + if (pSetup->wLength != 0) { + /* Download has started */ + dfu_data.state = dfuDNLOAD_IDLE; + + if (!(dfu_data.bytes_rcvd % + dfu_data.flash_page_size)) { + ret = flash_erase(dfu_data.flash_dev, + dfu_data.flash_base_addr + + dfu_data.bytes_rcvd, + dfu_data.flash_page_size); + DBG("Flash erase\n"); + if (ret) { + dfu_data.state = dfuERROR; + dfu_data.status = errERASE; + DBG("DFU flash erase error, " + "ret %d\n", ret); + } + } + + /* Flash write len should be multiple of 4 */ + len = (pSetup->wLength + 3) & (~3); + ret = flash_write(dfu_data.flash_dev, + dfu_data.flash_base_addr + + dfu_data.bytes_rcvd, + *data, len); + if (ret) { + dfu_data.state = dfuERROR; + dfu_data.status = errWRITE; + DBG("DFU flash write error, ret %d\n", + ret); + } else + dfu_data.bytes_rcvd += pSetup->wLength; + } else { + /* Download completed */ + dfu_data.state = dfuMANIFEST_SYNC; + dfu_reset_counters(); + } + break; + default: + DBG("DFU_DNLOAD wrong state %d\n", dfu_data.state); + dfu_data.state = dfuERROR; + dfu_data.status = errUNKNOWN; + dfu_reset_counters(); + return -EINVAL; + } + break; + case DFU_UPLOAD: + DBG("DFU_UPLOAD block %d, len %d, state %d\n", + pSetup->wValue, pSetup->wLength, dfu_data.state); + + if (dfu_check_app_state()) + return -EINVAL; + + switch (dfu_data.state) { + case dfuIDLE: + dfu_reset_counters(); + DBG("DFU_UPLOAD start\n"); + case dfuUPLOAD_IDLE: + if (!pSetup->wLength || + dfu_data.block_nr != pSetup->wValue) { + DBG("DFU_UPLOAD block %d, expected %d, " + "len %d\n", pSetup->wValue, + dfu_data.block_nr, pSetup->wLength); + dfu_data.state = dfuERROR; + dfu_data.status = errUNKNOWN; + break; + } + /* Upload in progress */ + bytes_left = dfu_data.flash_upload_size - + dfu_data.bytes_sent; + if (bytes_left < pSetup->wLength) + len = bytes_left; + else + len = pSetup->wLength; + + if (len) { + ret = flash_read(dfu_data.flash_dev, + dfu_data.flash_base_addr + + dfu_data.bytes_sent, + dfu_data.buffer, len); + if (ret) { + dfu_data.state = dfuERROR; + dfu_data.status = errFILE; + break; + } + } + *data_len = len; + *data = dfu_data.buffer; + + dfu_data.bytes_sent += len; + dfu_data.block_nr++; + + if (dfu_data.bytes_sent == dfu_data.flash_upload_size && + len < pSetup->wLength) { + /* Upload completed when a + * short packet is received + */ + *data_len = 0; + dfu_data.state = dfuIDLE; + } else + dfu_data.state = dfuUPLOAD_IDLE; + + break; + default: + DBG("DFU_UPLOAD wrong state %d\n", dfu_data.state); + dfu_data.state = dfuERROR; + dfu_data.status = errUNKNOWN; + dfu_reset_counters(); + return -EINVAL; + } + break; + case DFU_DETACH: + DBG("DFU_DETACH timeout %d, state %d\n", + pSetup->wValue, dfu_data.state); + + if (dfu_data.state != appIDLE) { + dfu_data.state = appIDLE; + return -EINVAL; + } + /* Move to appDETACH state */ + dfu_data.state = appDETACH; + + /* We should start a timer here but in order to + * keep things simple and do not increase the size + * we rely on the host to get us out of the appATTACHED + * state if needed. + */ + + /* Set the DFU mode descriptors to be used after reset */ + dfu_config.usb_device_description = dfu_mode_usb_description; + usb_set_config(&dfu_config); + break; + default: + DBG("DFU UNKNOWN STATE: %d\n", pSetup->bRequest); + return -EINVAL; + } + + return 0; +} + +/** + * @brief Callback used to know the USB connection status + * + * @param status USB device status code. + * + * @return N/A. + */ +static void dfu_status_cb(enum usb_dc_status_code status) +{ + /* Check the USB status and do needed action if required */ + switch (status) { + case USB_DC_ERROR: + DBG("USB device error\n"); + break; + case USB_DC_RESET: + DBG("USB device reset detected, state %d\n", dfu_data.state); + if (dfu_data.state == appDETACH) { + dfu_data.state = dfuIDLE; + } + break; + case USB_DC_CONNECTED: + DBG("USB device connected\n"); + break; + case USB_DC_CONFIGURED: + DBG("USB device configured\n"); + break; + case USB_DC_DISCONNECTED: + DBG("USB device disconnected\n"); + break; + case USB_DC_SUSPEND: + DBG("USB device supended\n"); + break; + case USB_DC_RESUME: + DBG("USB device resumed\n"); + break; + case USB_DC_UNKNOWN: + default: + DBG("USB unknown state\n"); + break; + } +} + +/* Configuration of the DFU Device send to the USB Driver */ +static struct usb_cfg_data dfu_config = { + .usb_device_description = dfu_runtime_usb_description, + .cb_usb_status = dfu_status_cb, + .interface = { + .class_handler = dfu_class_handle_req, + .payload_data = dfu_data.buffer, + }, + .num_endpoints = DFU_NUM_EP, +}; + +/** + * @brief DFU class driver start routine + * + * @param flash_dev Flash device driver to be used. + * @param flash_base_addr Flash address to write/read to/from. + * @param flash_page_size Flash page size. + * @param flash_upload_size Flash upload data size. + * + * @return N/A. + */ +int dfu_start(struct device *flash_dev, uint32_t flash_base_addr, + uint32_t flash_page_size, uint32_t flash_upload_size) +{ + int ret; + + if (!flash_dev) + return -EINVAL; + + dfu_data.flash_dev = flash_dev; + dfu_data.flash_base_addr = flash_base_addr; + dfu_data.flash_page_size = flash_page_size; + dfu_data.flash_upload_size = flash_upload_size; + + /* Initialize the USB driver with the right configuration */ + ret = usb_set_config(&dfu_config); + if (ret < 0) { + DBG("Failed to config USB\n"); + return ret; + } + + /* Enable USB driver */ + ret = usb_enable(&dfu_config); + if (ret < 0) { + DBG("Failed to enable USB\n"); + return ret; + } + + return 0; +} diff --git a/samples/usb/dfu/src/usb_dfu.h b/samples/usb/dfu/src/usb_dfu.h new file mode 100644 index 00000000000..9d6272b8f92 --- /dev/null +++ b/samples/usb/dfu/src/usb_dfu.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * + * 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 USB DFU device class driver header file + * + * Header file for USB DFU device class driver + */ + +#ifndef __USB_DFU_H__ +#define __USB_DFU_H__ + +#include +#include "usb_common.h" + +/* Intel vendor ID */ +#define DFU_VENDOR_ID 0x8086 + +/* Product Id, random value */ +#define DFU_PRODUCT_ID 0x48FC + +#define DFU_MAX_XFER_SIZE 256 + +#define DFU_NUM_CONF 0x01 /* Number of configurations for the USB Device */ +#define DFU_NUM_ITF 0x01 /* Number of interfaces in the configuration */ +#define DFU_NUM_EP 0x00 /* Number of Endpoints in the interface */ + +/* Class specific request */ +#define DFU_DETACH 0x00 +#define DFU_DNLOAD 0x01 +#define DFU_UPLOAD 0x02 +#define DFU_GETSTATUS 0x03 +#define DFU_CLRSTATUS 0x04 +#define DFU_GETSTATE 0x05 +#define DFU_ABORT 0x06 + + +/* DFU attributes */ +#define DFU_ATTR_CAN_DNLOAD 0x01 +#define DFU_ATTR_CAN_UPLOAD 0x02 +#define DFU_ATTR_MANIFESTATION_TOLERANT 0x4 + +#define DFU_DETACH_TIMEOUT 1000 + +/***** STATUS CODE ****/ +enum dfu_status { + statusOK, + errTARGET, + errFILE, + errWRITE, + errERASE, + errCHECK_ERASED, + errPROG, + errVERIFY, + errADDRESS, + errNOTDONE, + errFIRMWARE, + errVENDOR, + errUSB, + errPOR, + errUNKNOWN, + errSTALLEDPKT +}; + +/**** STATES ****/ +enum dfu_state { + appIDLE, + appDETACH, + dfuIDLE, + dfuDNLOAD_SYNC, + dfuDNBUSY, + dfuDNLOAD_IDLE, + dfuMANIFEST_SYNC, + dfuMANIFEST, + dfuMANIFEST_WAIT_RST, + dfuUPLOAD_IDLE, + dfuERROR, +}; + +/* Size in bytes of the configuration sent to the Host on + * GetConfiguration() request + * For DFU: CONF + ITF + DFU) -> 27 bytes + */ +#define DFU_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \ + USB_INTERFACE_DESC_SIZE + USB_DFU_DESC_SIZE) + +/** + * @brief DFU class driver start routine + * + * @param flash_dev Flash device driver to be used. + * @param flash_base_addr Flash address to write/read to/from. + * @param flash_page_size Flash page size. + * @param flash_upload_size Flash upload data size. + * + * @return N/A. + */ +int dfu_start(struct device *flash_dev, uint32_t flash_base_addr, + uint32_t flash_page_size, uint32_t flash_upload_size); + +#endif /* __USB_DFU_H__ */ diff --git a/usb/Kconfig b/usb/Kconfig index 6fe1ff5fdad..9be08445606 100644 --- a/usb/Kconfig +++ b/usb/Kconfig @@ -33,4 +33,6 @@ config USB_DEBUG help This option enables the debug features for the USB device stack. +source "usb/class/Kconfig" + endif # USB_DEVICE_STACK diff --git a/usb/Makefile b/usb/Makefile index f4ad7c5dc29..92c6be21bab 100644 --- a/usb/Makefile +++ b/usb/Makefile @@ -1,3 +1,4 @@ ccflags-y += -I${srctree}/include/drivers/usb -I${srctree}/usb/include obj-$(CONFIG_USB_DEVICE_STACK) += usb_device.o +obj-$(CONFIG_USB_DEVICE_STACK) += class/ diff --git a/usb/class/Kconfig b/usb/class/Kconfig new file mode 100644 index 00000000000..783d3b617c3 --- /dev/null +++ b/usb/class/Kconfig @@ -0,0 +1,43 @@ +# Kconfig - USB class drivers configuration options + +# +# Copyright (c) 2016 Wind River Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +if USB_DEVICE_STACK + +config USB_CDC_ACM + bool + prompt "USB CDC ACM Device Class Driver" + default n + help + USB CDC ACM device class driver + +config CDC_ACM_PORT_NAME + string "CDC ACM class device driver port name" + depends on USB_CDC_ACM + default "CDC_ACM" + help + Port name through which CDC ACM class device driver is accessed + +config USB_CDC_ACM_DEBUG + bool + prompt "Enable debug options for USB CDC ACM device class driver" + default n + depends on USB_CDC_ACM + help + This option enables the debug features for USB CDC ACM device class driver + +endif # CONFIG_USB_DEVICE_STACK diff --git a/usb/class/Makefile b/usb/class/Makefile new file mode 100644 index 00000000000..e46bbb98de4 --- /dev/null +++ b/usb/class/Makefile @@ -0,0 +1,3 @@ +ccflags-y += -I${srctree}/include/drivers/usb -I${srctree}/include/usb/ -I${srctree}/usb/include/ + +obj-$(CONFIG_USB_CDC_ACM) += cdc_acm.o diff --git a/usb/class/cdc_acm.c b/usb/class/cdc_acm.c new file mode 100644 index 00000000000..bf6526d2479 --- /dev/null +++ b/usb/class/cdc_acm.c @@ -0,0 +1,830 @@ +/******************************************************************************* + * + * 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 +#include +#include +#include +#include +#include "cdc_acm.h" +#include "usb_device.h" +#include "usb_common.h" + +#ifndef CONFIG_UART_INTERRUPT_DRIVEN +#error "CONFIG_UART_INTERRUPT_DRIVEN must be set for CDC ACM driver" +#endif + +/* definitions */ + +#ifndef CONFIG_USB_CDC_ACM_DEBUG +#define DBG(...) { ; } +#else +#if defined(CONFIG_STDOUT_CONSOLE) +#include +#define DBG printf +#else +#define DBG printk +#endif /* CONFIG_STDOUT_CONSOLE */ +#endif /* CONFIG_USB_CDC_ACM_DEBUG */ + +#define DEV_DATA(dev) \ + ((struct cdc_acm_dev_data_t * const)(dev)->driver_data) + +static struct uart_driver_api cdc_acm_driver_api; + +/* 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 * CDC_BULK_EP_MPS) + +struct device *cdc_acm_dev; + +/* 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_t cb; + /* Tx ready status. Signals when */ + uint8_t tx_ready; + uint8_t rx_ready; /* Rx ready status */ + uint8_t tx_irq_ena; /* Tx interrupt enable status */ + uint8_t rx_irq_ena; /* Rx interrupt enable status */ + uint8_t rx_buf[CDC_ACM_BUFFER_SIZE];/* Internal Rx buffer */ + uint32_t rx_buf_head; /* Head of the internal Rx buffer */ + uint32_t rx_buf_tail; /* Tail of the internal Rx buffer */ + /* Interface data buffer */ + uint8_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 */ + uint8_t line_state; + /* CDC ACM serial state bitmap, DCE side */ + uint8_t serial_state; + /* CDC ACM notification sent status */ + uint8_t notification_sent; +}; + +/* Structure representing the global USB description */ +static const uint8_t cdc_acm_usb_description[] = { + /* Device descriptor */ + USB_DEVICE_DESC_SIZE, /* Descriptor size */ + USB_DEVICE_DESC, /* Descriptor type */ + LOW_BYTE(USB_1_1), + HIGH_BYTE(USB_1_1), /* USB version in BCD format */ + COMMUNICATION_DEVICE_CLASS, /* Class */ + 0x00, /* SubClass - Interface specific */ + 0x00, /* Protocol - Interface specific */ + MAX_PACKET_SIZE0, /* Max Packet Size */ + LOW_BYTE(CDC_VENDOR_ID), + HIGH_BYTE(CDC_VENDOR_ID), /* Vendor Id */ + LOW_BYTE(CDC_PRODUCT_ID), + HIGH_BYTE(CDC_PRODUCT_ID), /* Product Id */ + LOW_BYTE(BCDDEVICE_RELNUM), + HIGH_BYTE(BCDDEVICE_RELNUM), /* Device Release Number */ + /* Index of Manufacturer String Descriptor */ + 0x01, + /* Index of Product String Descriptor */ + 0x02, + /* Index of Serial Number String Descriptor */ + 0x03, + CDC_NUM_CONF, /* Number of Possible Configuration */ + + /* Configuration descriptor */ + USB_CONFIGURATION_DESC_SIZE, /* Descriptor size */ + USB_CONFIGURATION_DESC, /* Descriptor type */ + /* Total length in bytes of data returned */ + LOW_BYTE(CDC_CONF_SIZE), + HIGH_BYTE(CDC_CONF_SIZE), + CDC_NUM_ITF, /* Number of interfaces */ + 0x01, /* Configuration value */ + 0x00, /* Index of the Configuration string */ + USB_CONFIGURATION_ATTRIBUTES, /* Attributes */ + MAX_LOW_POWER, /* Max power consumption */ + + /* Interface descriptor */ + USB_INTERFACE_DESC_SIZE, /* Descriptor size */ + USB_INTERFACE_DESC, /* Descriptor type */ + 0x00, /* Interface index */ + 0x00, /* Alternate setting */ + CDC1_NUM_EP, /* Number of Endpoints */ + COMMUNICATION_DEVICE_CLASS, /* Class */ + ACM_SUBCLASS, /* SubClass */ + V25TER_PROTOCOL, /* Protocol */ + /* Index of the Interface String Descriptor */ + 0x00, + + /* Header Functional Descriptor */ + USB_HFUNC_DESC_SIZE, /* Descriptor size */ + CS_INTERFACE, /* Descriptor type */ + USB_HFUNC_SUBDESC, /* Descriptor SubType */ + LOW_BYTE(USB_1_1), + HIGH_BYTE(USB_1_1), /* CDC Device Release Number */ + + /* Call Management Functional Descriptor */ + USB_CMFUNC_DESC_SIZE, /* Descriptor size */ + CS_INTERFACE, /* Descriptor type */ + USB_CMFUNC_SUBDESC, /* Descriptor SubType */ + 0x00, /* Capabilities */ + 0x01, /* Data Interface */ + + /* ACM Functional Descriptor */ + USB_ACMFUNC_DESC_SIZE, /* Descriptor size */ + CS_INTERFACE, /* Descriptor type */ + USB_ACMFUNC_SUBDESC, /* Descriptor SubType */ + /* Capabilities - Device supports the request combination of: + * Set_Line_Coding, + * Set_Control_Line_State, + * Get_Line_Coding + * and the notification Serial_State + */ + 0x02, + + /* Union Functional Descriptor */ + USB_UFUNC_DESC_SIZE, /* Descriptor size */ + CS_INTERFACE, /* Descriptor type */ + USB_UFUNC_SUBDESC, /* Descriptor SubType */ + 0x00, /* Master Interface */ + 0x01, /* Slave Interface */ + + /* Endpoint INT */ + USB_ENDPOINT_DESC_SIZE, /* Descriptor size */ + USB_ENDPOINT_DESC, /* Descriptor type */ + CDC_ENDP_INT, /* Endpoint address */ + USB_DC_EP_INTERRUPT, /* Attributes */ + LOW_BYTE(CDC_INTERRUPT_EP_MPS), + HIGH_BYTE(CDC_INTERRUPT_EP_MPS),/* Max packet size */ + 0x0A, /* Interval */ + + /* Interface descriptor */ + USB_INTERFACE_DESC_SIZE, /* Descriptor size */ + USB_INTERFACE_DESC, /* Descriptor type */ + 0x01, /* Interface index */ + 0x00, /* Alternate setting */ + CDC2_NUM_EP, /* Number of Endpoints */ + COMMUNICATION_DEVICE_CLASS_DATA,/* Class */ + 0x00, /* SubClass */ + 0x00, /* Protocol */ + /* Index of the Interface String Descriptor */ + 0x00, + + /* First Endpoint IN */ + USB_ENDPOINT_DESC_SIZE, /* Descriptor size */ + USB_ENDPOINT_DESC, /* Descriptor type */ + CDC_ENDP_IN, /* Endpoint address */ + USB_DC_EP_BULK, /* Attributes */ + LOW_BYTE(CDC_BULK_EP_MPS), + HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */ + 0x00, /* Interval */ + + /* Second Endpoint OUT */ + USB_ENDPOINT_DESC_SIZE, /* Descriptor size */ + USB_ENDPOINT_DESC, /* Descriptor type */ + CDC_ENDP_OUT, /* Endpoint address */ + USB_DC_EP_BULK, /* Attributes */ + LOW_BYTE(CDC_BULK_EP_MPS), + HIGH_BYTE(CDC_BULK_EP_MPS), /* Max packet size */ + 0x00, /* Interval */ + + /* String descriptor language, only one, so min size 4 bytes. + * 0x0409 English(US) language code used + */ + USB_STRING_DESC_SIZE, /* Descriptor size */ + USB_STRING_DESC, /* Descriptor type */ + 0x09, + 0x04, + + /* Manufacturer String Descriptor "Intel" */ + 0x0C, + USB_STRING_DESC, + 'I', 0, 'n', 0, 't', 0, 'e', 0, 'l', 0, + + /* Product String Descriptor "CDC-ACM" */ + 0x10, + USB_STRING_DESC, + 'C', 0, 'D', 0, 'C', 0, '-', 0, 'A', 0, 'C', 0, 'M', 0, + + /* Serial Number String Descriptor "00.01" */ + 0x0C, + USB_STRING_DESC, + '0', 0, '0', 0, '.', 0, '0', 0, '1', 0, +}; + +/** + * @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. + */ +int cdc_acm_class_handle_req(struct usb_setup_packet *pSetup, + int32_t *len, uint8_t **data) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev); + + switch (pSetup->bRequest) { + case CDC_SET_LINE_CODING: + memcpy(&dev_data->line_coding, + *data, sizeof(dev_data->line_coding)); + DBG("\nCDC_SET_LINE_CODING %d %d %d %d\n", + 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 CDC_SET_CONTROL_LINE_STATE: + dev_data->line_state = (uint8_t)sys_le16_to_cpu(pSetup->wValue); + DBG("CDC_SET_CONTROL_LINE_STATE 0x%x\n", dev_data->line_state); + break; + + case CDC_GET_LINE_CODING: + *data = (uint8_t *)(&dev_data->line_coding); + *len = sizeof(dev_data->line_coding); + DBG("\nCDC_GET_LINE_CODING %d %d %d %d\n", + 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: + DBG("CDC ACM request 0x%x, value 0x%x\n", + 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(uint8_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); + + dev_data->tx_ready = 1; + /* Call callback only if tx irq ena */ + if (dev_data->cb && dev_data->tx_irq_ena) + dev_data->cb(cdc_acm_dev); +} + +/** + * @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(uint8_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); + uint32_t bytes_to_read, i, buf_tail; + + /* Check how many bytes were received */ + usb_read(ep, NULL, 0, &bytes_to_read); + + for (i = 0; i < bytes_to_read; i++) { + buf_tail = (dev_data->rx_buf_tail + i) % CDC_ACM_BUFFER_SIZE; + usb_read(ep, &dev_data->rx_buf[buf_tail], 1, NULL); + } + + dev_data->rx_buf_tail = (dev_data->rx_buf_tail + bytes_to_read) % + CDC_ACM_BUFFER_SIZE; + + 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); +} + +/** + * @brief EP Interrupt handler + * + * @param ep Endpoint address. + * @param ep_status Endpoint status code. + * + * @return N/A. + */ +static void cdc_acm_int_in(uint8_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); + + dev_data->notification_sent = 1; + DBG("CDC_IntIN EP[%x]\r\n", ep); +} + +/** + * @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) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(cdc_acm_dev); + + /* 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: + DBG("USB device error\n"); + break; + case USB_DC_RESET: + DBG("USB device reset detected\n"); + break; + case USB_DC_CONNECTED: + DBG("USB device connected\n"); + break; + case USB_DC_CONFIGURED: + DBG("USB device configured\n"); + break; + case USB_DC_DISCONNECTED: + DBG("USB device disconnected\n"); + break; + case USB_DC_SUSPEND: + DBG("USB device supended\n"); + break; + case USB_DC_RESUME: + DBG("USB device resumed\n"); + break; + case USB_DC_UNKNOWN: + default: + DBG("USB unknown state\n"); + break; + } +} + +/* Describe EndPoints configuration */ +static struct usb_ep_cfg_data cdc_acm_ep_data[] = { + { + .ep_cb = cdc_acm_int_in, + .ep_addr = CDC_ENDP_INT + }, + { + .ep_cb = cdc_acm_bulk_out, + .ep_addr = CDC_ENDP_OUT + }, + { + .ep_cb = cdc_acm_bulk_in, + .ep_addr = CDC_ENDP_IN + } +}; + +/* Configuration of the CDC-ACM Device send to the USB Driver */ +static struct usb_cfg_data cdc_acm_config = { + .usb_device_description = cdc_acm_usb_description, + .cb_usb_status = cdc_acm_dev_status_cb, + .interface = { + .class_handler = cdc_acm_class_handle_req, + .custom_handler = NULL, + .payload_data = NULL, + }, + .num_endpoints = CDC1_NUM_EP + CDC2_NUM_EP, + .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(struct device *dev, uint32_t baudrate) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + dev_data->line_coding.dwDTERate = sys_cpu_to_le32(baudrate); +} + +/** + * @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 allways. + */ +static int cdc_acm_init(struct device *dev) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + int ret; + + cdc_acm_config.interface.payload_data = dev_data->interface_data; + cdc_acm_dev = dev; + + /* Initialize the USB driver with the right configuration */ + ret = usb_set_config(&cdc_acm_config); + if (ret < 0) { + DBG("Failed to config USB\n"); + return ret; + } + + /* Enable USB driver */ + ret = usb_enable(&cdc_acm_config); + if (ret < 0) { + DBG("Failed to enable USB\n"); + return ret; + } + + dev->driver_api = &cdc_acm_driver_api; + + 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(struct device *dev, + const uint8_t *tx_data, int len) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + uint32_t bytes_written = 0; + + if (dev_data->usb_status != USB_DC_CONFIGURED) + return 0; + + dev_data->tx_ready = 0; + usb_write(CDC_ENDP_IN, tx_data, len, &bytes_written); + + return bytes_written; +} + +/** + * @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(struct device *dev, uint8_t *rx_data, + const int size) +{ + uint32_t avail_data, bytes_read, i; + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + avail_data = (CDC_ACM_BUFFER_SIZE + dev_data->rx_buf_tail - + dev_data->rx_buf_head) % CDC_ACM_BUFFER_SIZE; + if (avail_data > size) + bytes_read = size; + else + bytes_read = avail_data; + + for (i = 0; i < bytes_read; i++) + rx_data[i] = dev_data->rx_buf[(dev_data->rx_buf_head + i) % + CDC_ACM_BUFFER_SIZE]; + + dev_data->rx_buf_head = (dev_data->rx_buf_head + bytes_read) % + CDC_ACM_BUFFER_SIZE; + + if (dev_data->rx_buf_head == dev_data->rx_buf_tail) { + /* Buffer empty */ + dev_data->rx_ready = 0; + } + + return bytes_read; +} + +/** + * @brief Enable TX interrupt + * + * @param dev CDC ACM device struct. + * + * @return N/A. + */ +static void cdc_acm_irq_tx_enable(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(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(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(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(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(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(struct device *dev) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + if (dev_data->tx_ready || dev_data->rx_ready) + return 1; + + return 0; +} + +/** + * @brief Update IRQ status + * + * @param dev CDC ACM device struct. + * + * @return Always 1 + */ +static int cdc_acm_irq_update(struct device *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(struct device *dev, + uart_irq_callback_t cb) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + dev_data->cb = cb; +} + +#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(struct device *dev, uint16_t serial_state) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + struct cdc_acm_notification notification; + uint32_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_ENDP_INT, (const uint8_t *)¬ification, + sizeof(notification), NULL); + + /* Wait for notification to be sent */ + while (!((volatile uint8_t)dev_data->notification_sent)) { + sys_thread_busy_wait(1); + if (++cnt > CDC_CONTROL_SERIAL_STATE_TIMEOUT_US) { + DBG("CDC ACM notification timeout!\n"); + 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(struct device *dev, + uint32_t ctrl, uint32_t val) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + switch (ctrl) { + case LINE_CTRL_BAUD_RATE: + cdc_acm_baudrate_set(dev, val); + return 0; + case LINE_CTRL_DCD: + dev_data->serial_state &= ~CDC_CONTROL_SERIAL_STATE_DCD; + if (val) + dev_data->serial_state |= CDC_CONTROL_SERIAL_STATE_DCD; + + cdc_acm_send_notification(dev, CDC_CONTROL_SERIAL_STATE_DCD); + return 0; + case LINE_CTRL_DSR: + dev_data->serial_state &= ~CDC_CONTROL_SERIAL_STATE_DSR; + if (val) + dev_data->serial_state |= CDC_CONTROL_SERIAL_STATE_DSR; + + 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(struct device *dev, + uint32_t ctrl, uint32_t *val) +{ + struct cdc_acm_dev_data_t * const dev_data = DEV_DATA(dev); + + switch (ctrl) { + case LINE_CTRL_BAUD_RATE: + *val = sys_le32_to_cpu(dev_data->line_coding.dwDTERate); + return 0; + case LINE_CTRL_RTS: + *val = + (dev_data->line_state & CDC_CONTROL_LINE_STATE_RTS) ? 1 : 0; + return 0; + case LINE_CTRL_DTR: + *val = + (dev_data->line_state & CDC_CONTROL_LINE_STATE_DTR) ? 1 : 0; + return 0; + } + + return -ENOTSUP; +} + +#endif /* CONFIG_UART_LINE_CTRL */ + +static struct uart_driver_api cdc_acm_driver_api = { + .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_CTR */ +}; + +static struct cdc_acm_dev_data_t cdc_acm_dev_data = { + .usb_status = USB_DC_UNKNOWN, + .line_coding = CDC_ACM_DEFAUL_BAUDRATE, +}; + +DEVICE_INIT(cdc_acm, CONFIG_CDC_ACM_PORT_NAME, &cdc_acm_init, + &cdc_acm_dev_data, NULL, + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); diff --git a/usb/class/cdc_acm.h b/usb/class/cdc_acm.h new file mode 100644 index 00000000000..d179c7315d5 --- /dev/null +++ b/usb/class/cdc_acm.h @@ -0,0 +1,129 @@ +/*************************************************************************** + * + * 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 header file + * + * Header file for USB CDC ACM device class driver + */ + +#ifndef __CDC_ACM_H__ +#define __CDC_ACM_H__ + +/* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */ +struct cdc_acm_line_coding { + uint32_t dwDTERate; + uint8_t bCharFormat; + uint8_t bParityType; + uint8_t bDataBits; +} __packed; + +struct cdc_acm_notification { + uint8_t bmRequestType; + uint8_t bNotificationType; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + uint16_t data; +} __packed; + +/* Intel vendor ID */ +#define CDC_VENDOR_ID 0x8086 + +/* Product Id, random value */ +#define CDC_PRODUCT_ID 0xF8A1 + + +/* Max packet size for Bulk endpoints */ +#define CDC_BULK_EP_MPS 64 + +/* Max packet size for Interrupt endpoints */ +#define CDC_INTERRUPT_EP_MPS 16 + +/* Max CDC ACM class request max data size */ +#define CDC_CLASS_REQ_MAX_DATA_SIZE 8 + +/* Number of configurations for the USB Device */ +#define CDC_NUM_CONF 0x01 +/* Number of interfaces in the configuration */ +#define CDC_NUM_ITF 0x02 +/* Number of Endpoints in the first interface */ +#define CDC1_NUM_EP 0x01 +/* Number of Endpoints in the second interface */ +#define CDC2_NUM_EP 0x02 + +#define CDC_ENDP_INT 0x81 +#define CDC_ENDP_OUT 0x03 +#define CDC_ENDP_IN 0x84 + +/* Decriptor size in bytes */ +#define USB_HFUNC_DESC_SIZE 5 /* Header Functional Descriptor */ +#define USB_CMFUNC_DESC_SIZE 5 /* Call Management Functional Descriptor */ +#define USB_ACMFUNC_DESC_SIZE 4 /* ACM Functional Descriptor */ +#define USB_UFUNC_DESC_SIZE 5 /* Union Functional Descriptor */ + +/* Descriptor type */ +#define CS_INTERFACE 0x24 +#define CS_ENDPOINT 0x25 + +/* Descriptor subtype */ +#define USB_HFUNC_SUBDESC 0x00 +#define USB_CMFUNC_SUBDESC 0x01 +#define USB_ACMFUNC_SUBDESC 0x02 +#define USB_UFUNC_SUBDESC 0x06 + +/* Class specific request */ +#define CDC_SET_LINE_CODING 0x20 +#define CDC_GET_LINE_CODING 0x21 +#define CDC_SET_CONTROL_LINE_STATE 0x22 + +/* Control line state signal bitmap */ +#define CDC_CONTROL_LINE_STATE_DTR 0x1 +#define CDC_CONTROL_LINE_STATE_RTS 0x2 + +/* Serial state notification bitmap*/ +#define CDC_CONTROL_SERIAL_STATE_DCD 0x1 +#define CDC_CONTROL_SERIAL_STATE_DSR 0x2 + +/* Serial state notification timeout */ +#define CDC_CONTROL_SERIAL_STATE_TIMEOUT_US 100000 + +/* Size in bytes of the configuration sent to + * the Host on GetConfiguration() request + * For Communication Device: CONF + (2 x ITF) + + * (3 x EP) + HF + CMF + ACMF + UF -> 67 bytes + */ +#define CDC_CONF_SIZE (USB_CONFIGURATION_DESC_SIZE + \ + (2 * USB_INTERFACE_DESC_SIZE) + (3 * USB_ENDPOINT_DESC_SIZE) + 19) + +#endif /* __CDC_ACM_H__ */