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 <adrian.bradianu@windriver.com> Signed-off-by: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com> Signed-off-by: Jithu Joseph <jithu.joseph@intel.com>
This commit is contained in:
parent
7a16ade6d1
commit
3d8b28b358
17 changed files with 1985 additions and 0 deletions
|
@ -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
|
||||
|
|
5
samples/usb/cdc_acm/Makefile
Normal file
5
samples/usb/cdc_acm/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
BOARD ?= quark_se_devboard
|
||||
KERNEL_TYPE ?= nano
|
||||
CONF_FILE = prj.conf
|
||||
|
||||
include ${ZEPHYR_BASE}/Makefile.inc
|
12
samples/usb/cdc_acm/prj.conf
Normal file
12
samples/usb/cdc_acm/prj.conf
Normal file
|
@ -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
|
1
samples/usb/cdc_acm/src/Makefile
Normal file
1
samples/usb/cdc_acm/src/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y += main.o
|
132
samples/usb/cdc_acm/src/main.c
Normal file
132
samples/usb/cdc_acm/src/main.c
Normal file
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <device.h>
|
||||
#include <uart.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||
#include <stdio.h>
|
||||
#define PRINT printf
|
||||
#else
|
||||
#include <misc/printk.h>
|
||||
#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);
|
||||
}
|
||||
}
|
5
samples/usb/dfu/Makefile
Normal file
5
samples/usb/dfu/Makefile
Normal file
|
@ -0,0 +1,5 @@
|
|||
BOARD ?= quark_se_devboard
|
||||
KERNEL_TYPE ?= nano
|
||||
CONF_FILE = prj.conf
|
||||
|
||||
include ${ZEPHYR_BASE}/Makefile.inc
|
10
samples/usb/dfu/prj.conf
Normal file
10
samples/usb/dfu/prj.conf
Normal file
|
@ -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
|
3
samples/usb/dfu/src/Makefile
Normal file
3
samples/usb/dfu/src/Makefile
Normal file
|
@ -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
|
68
samples/usb/dfu/src/main.c
Normal file
68
samples/usb/dfu/src/main.c
Normal file
|
@ -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 <string.h>
|
||||
#include <zephyr.h>
|
||||
#include "usb_dfu.h"
|
||||
|
||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||
#include <stdio.h>
|
||||
#define PRINT printf
|
||||
#else
|
||||
#include <misc/printk.h>
|
||||
#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 */
|
||||
}
|
||||
}
|
584
samples/usb/dfu/src/usb_dfu.c
Normal file
584
samples/usb/dfu/src/usb_dfu.c
Normal file
|
@ -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 <nanokernel.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <flash.h>
|
||||
#include "usb_device.h"
|
||||
#include "usb_dfu.h"
|
||||
|
||||
|
||||
#ifndef CONFIG_USB_DFU_DEBUG
|
||||
#define DBG(...) { ; }
|
||||
#else
|
||||
#if defined(CONFIG_STDOUT_CONSOLE)
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
130
samples/usb/dfu/src/usb_dfu.h
Normal file
130
samples/usb/dfu/src/usb_dfu.h
Normal file
|
@ -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 <device.h>
|
||||
#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__ */
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
43
usb/class/Kconfig
Normal file
43
usb/class/Kconfig
Normal file
|
@ -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
|
3
usb/class/Makefile
Normal file
3
usb/class/Makefile
Normal file
|
@ -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
|
830
usb/class/cdc_acm.c
Normal file
830
usb/class/cdc_acm.c
Normal file
|
@ -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 <nanokernel.h>
|
||||
#include <init.h>
|
||||
#include <uart.h>
|
||||
#include <string.h>
|
||||
#include <misc/byteorder.h>
|
||||
#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 <stdio.h>
|
||||
#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);
|
129
usb/class/cdc_acm.h
Normal file
129
usb/class/cdc_acm.h
Normal file
|
@ -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__ */
|
Loading…
Add table
Add a link
Reference in a new issue