1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #11085 from bergzand/pr/usb/cdcacm

usbus: Add CDC-ACM (Serial console) function
This commit is contained in:
benpicco 2019-10-01 11:22:54 +02:00 committed by GitHub
commit 3f0dfc14ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 764 additions and 1 deletions

View File

@ -401,7 +401,7 @@ ifneq (,$(filter newlib,$(USEMODULE)))
ifeq (,$(filter newlib_syscalls_%,$(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifeq (,$(filter stdio_rtt,$(USEMODULE)))
ifeq (,$(filter stdio_rtt stdio_cdc_acm,$(USEMODULE)))
USEMODULE += stdio_uart
endif
endif
@ -414,6 +414,11 @@ ifneq (,$(filter posix_sockets,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter stdio_cdc_acm,$(USEMODULE)))
USEMODULE += usbus_cdc_acm
USEMODULE += isrpipe
endif
ifneq (,$(filter stdio_rtt,$(USEMODULE)))
USEMODULE += xtimer
endif
@ -880,6 +885,11 @@ ifneq (,$(filter usbus,$(USEMODULE)))
USEMODULE += event
endif
ifneq (,$(filter usbus_cdc_acm,$(USEMODULE)))
USEMODULE += tsrb
USEMODULE += usbus
endif
ifneq (,$(filter usbus_cdc_ecm,$(USEMODULE)))
USEMODULE += iolist
USEMODULE += fmt

View File

@ -80,6 +80,7 @@ PSEUDOMODULES += sock_tcp
PSEUDOMODULES += sock_udp
PSEUDOMODULES += stdin
PSEUDOMODULES += stdio_ethos
PSEUDOMODULES += stdio_cdc_acm
PSEUDOMODULES += stdio_uart_rx
PSEUDOMODULES += sock_dtls

View File

@ -28,6 +28,9 @@
#include "usb/usbus/cdc/ecm.h"
usbus_cdcecm_device_t cdcecm;
#endif
#ifdef MODULE_USBUS_CDC_ACM
#include "usb/usbus/cdc/acm.h"
#endif
static char _stack[USBUS_STACKSIZE];
static usbus_t usbus;
@ -42,6 +45,11 @@ void auto_init_usb(void)
usbus_init(&usbus, usbdev);
/* USBUS function handlers initialization */
#ifdef MODULE_STDIO_CDC_ACM
void usb_cdc_acm_stdio_init(usbus_t *usbus);
usb_cdc_acm_stdio_init(&usbus);
#endif
#ifdef MODULE_USBUS_CDC_ECM
usbus_cdcecm_init(&usbus, &cdcecm);
#endif

View File

@ -90,6 +90,21 @@ extern "C" {
* @{
*/
/**
* @brief Set line character formatting properties
*/
#define USB_CDC_MGNT_REQUEST_SET_LINE_CODING (0x20)
/**
* @brief Request the currently configured line coding
*/
#define USB_CDC_MGNT_REQUEST_GET_LINE_CODING (0x21)
/**
* @brief Set the control line state
*/
#define USB_CDC_MGNT_REQUEST_SET_CONTROL_LINE_STATE (0x22)
/**
* @brief Set ethernet multicast filter request
*/
@ -116,6 +131,22 @@ extern "C" {
#define USB_CDC_MGNT_REQUEST_GET_ETH_STATISTICS 0x44
/** @} */
/**
* @name USB CDC ACM control line state flags
* @{
*/
/**
* @brief DTE (e.g. a PC) is present and listening
*/
#define USB_CDC_ACM_CONTROL_LINE_DTE (0x01)
/**
* @brief Activate carrier control for half duplex modems
*/
#define USB_CDC_ACM_CONTROL_LINE_CARRIER (0x02)
/** @} */
/**
* @name USB CDC management notifications
* @{
@ -238,6 +269,36 @@ typedef struct __attribute__((packed)) {
uint32_t up; /**< Uplink bit rate */
} usb_desc_cdcecm_speed_t;
/**
* @name USB CDC ACM line coding setup defines
* @{
*/
/**
* @brief USB CDC ACM line coding setup content
* @see USB CDC 1.2 PSTN subclass spec section 6.3.11
*/
typedef struct __attribute__((packed)) {
uint32_t baud; /**< Requested baud rate */
uint8_t format; /**< Stop bits settings */
uint8_t parity; /**< Parity settings */
uint8_t databits; /**< Number of data bits (5, 6, 7, 8 or 16) */
} usb_req_cdcacm_coding_t;
#define USB_CDC_ACM_CODING_STOP_BITS_1 0 /**< 1 stop bit */
#define USB_CDC_ACM_CODING_STOP_BITS_1_5 1 /**< 1.5 stop bits */
#define USB_CDC_ACM_CODING_STOP_BITS_2 2 /**< 2 stop bits */
#define USB_CDC_ACM_CODING_PARITY_NONE 0 /**< No parity bit */
#define USB_CDC_ACM_CODING_PARITY_ODD 1 /**< Odd parity */
#define USB_CDC_ACM_CODING_PARITY_EVEN 2 /**< Even parity */
#define USB_CDC_ACM_CODING_PARITY_MARK 3 /**< Mark parity */
#define USB_CDC_ACM_CODING_PARITY_SPACE 4 /**< Space parity */
/** @} */
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 2018 Mesotic SAS
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for
* more details.
*/
/**
* @defgroup usbus_cdc_acm USBUS CDC ACM - USBUS CDC abstract control model
* @ingroup usb
* @brief USBUS CDC ACM interface module
*
* @{
*
* @file
* @brief Interface and definitions for USB CDC ACM type interfaces in
* USBUS.
*
* The functionality provided here only implements the USB
* specific handling. A different module is required to provide
* functional handling of the data e.g. UART or STDIO integration.
*
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef USB_USBUS_CDC_ACM_H
#define USB_USBUS_CDC_ACM_H
#include <stdint.h>
#include "usb/cdc.h"
#include "usb/usbus.h"
#include "tsrb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Buffer size for STDIN and STDOUT data to and from USB when using
* the USBUS_CDC_ACM_STDIO module
*/
#ifndef USBUS_CDC_ACM_STDIO_BUF_SIZE
#define USBUS_CDC_ACM_STDIO_BUF_SIZE (128)
#endif
/**
* @brief USB CDC ACM bulk endpoint size
*/
#ifndef USBUS_CDC_ACM_BULK_EP_SIZE
#define USBUS_CDC_ACM_BULK_EP_SIZE (64)
#endif
/**
* @brief USBUS CDC ACM interrupt endpoint size.
*/
#define USBUS_CDC_ACM_INT_EP_SIZE (8)
/**
* @brief CDC ACM line state as reported by the host computer
*/
typedef enum {
/**
* @brief No DTE connected
*/
USBUS_CDC_ACM_LINE_STATE_DISCONNECTED,
/**
* @brief DTE (e.g. a personal computer) is present and connected
*/
USBUS_CDC_ACM_LINE_STATE_DTE
} usbus_cdcacm_line_state_t;
/**
* @brief USBUS CDC ACM context struct forward declaration
*/
typedef struct usbus_cdcacm_device usbus_cdcacm_device_t;
/**
* @brief CDC ACM data callback.
*
* Callback for received data from the USB host
*
* @param[in] cdcacm CDC ACM handler context
* @param[in] data ptr to the data
* @param[in] len Length of the received data
*/
typedef void (*usbus_cdcacm_cb_t)(usbus_cdcacm_device_t *cdcacm,
uint8_t *data, size_t len);
/**
* @brief CDC ACM line coding callback.
*
* Callback for received line coding request from the USB host
*
* @param[in] cdcacm CDC ACM handler context
* @param[in] baud requested baud rate
* @param[in] bits requested number of data bits
* @param[in] parity requested parity
* @param[in] stop requested number of stop bits
*
* @return 0 when the mode is available
* @return negative if the mode is not available
*/
typedef int (*usbus_cdcacm_coding_cb_t)(usbus_cdcacm_device_t *cdcacm,
uint32_t baud, uint8_t bits,
uint8_t parity, uint8_t stop);
/**
* @brief USBUS CDC ACM context struct
*/
struct usbus_cdcacm_device {
usbus_handler_t handler_ctrl; /**< control handler */
usbus_interface_t iface_ctrl; /**< CDC control interface */
usbus_interface_t iface_data; /**< CDC data interface */
usbus_hdr_gen_t cdcacm_hdr; /**< CDC header generator */
usbus_cdcacm_cb_t cb; /**< Callback for data handlers */
usbus_cdcacm_coding_cb_t coding_cb; /**< Callback for ACM coding changes */
tsrb_t tsrb; /**< TSRB for data to the host */
usbus_t *usbus; /**< USBUS reference */
size_t occupied; /**< Number of bytes for the host */
usbus_cdcacm_line_state_t state; /**< Current line state */
event_t flush; /**< device2host forced flush event */
usb_req_cdcacm_coding_t coding; /**< Current coding configuration */
};
/**
* @brief Initialize an USBUS CDC ACM interface
*
* @param[in] usbus USBUS context to register with
* @param[in] cdcacm USBUS CDC ACM handler
* @param[in] cb Callback for data from the USB interface
* @param[in] coding_cb Callback for control settings
* @param[in] buf Buffer for data to the USB interface
* @param[in] len Size in bytes of the buffer
*/
void usbus_cdc_acm_init(usbus_t *usbus, usbus_cdcacm_device_t *cdcacm,
usbus_cdcacm_cb_t cb,
usbus_cdcacm_coding_cb_t coding_cb,
uint8_t *buf, size_t len);
/**
* @brief Submit bytes to the CDC ACM handler
*
* @param[in] cdcacm USBUS CDC ACM handler context
* @param[in] buf buffer to submit
* @param[in] len length of the submitted buffer
*
* @return Number of bytes added to the CDC ACM ring buffer
*/
size_t usbus_cdc_acm_submit(usbus_cdcacm_device_t *cdcacm,
const uint8_t *buf, size_t len);
/**
* @brief Flush the buffer to the USB host
*
* @param[in] cdcacm USBUS CDC ACM handler context
*/
void usbus_cdc_acm_flush(usbus_cdcacm_device_t *cdcacm);
/**
* @brief Set the callback for control settings
*
* Interrupts are disabled during update to ensure thread safety
*
* @param[in] cdcacm USBUS CDC ACM handler context
* @param[in] coding_cb Callback for control settings
*/
void usbus_cdc_acm_set_coding_cb(usbus_cdcacm_device_t *cdcacm,
usbus_cdcacm_coding_cb_t coding_cb);
#ifdef __cplusplus
}
#endif
#endif /* USB_USBUS_CDC_ACM_H */
/** @} */

View File

@ -4,4 +4,7 @@ SRCS += usbus_hdrs.c
ifneq (,$(filter usbus_cdc_ecm,$(USEMODULE)))
DIRS += cdc/ecm
endif
ifneq (,$(filter usbus_cdc_acm,$(USEMODULE)))
DIRS += cdc/acm
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,7 @@
MODULE = usbus_cdc_acm
SRC = cdc_acm.c
ifneq (,$(filter stdio_cdc_acm,$(USEMODULE)))
SRC += cdc_acm_stdio.c
endif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,338 @@
/*
* Copyright (C) 2018 Dylan Laduranty
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup usbus_cdc_acm
* @{
* @file
*
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
* @author Koen Zandberg <koen@bergzand.net>
* @}
*/
#include <string.h>
#include "tsrb.h"
#include "usb/descriptor.h"
#include "usb/cdc.h"
#include "usb/descriptor.h"
#include "usb/usbus.h"
#include "usb/usbus/cdc/acm.h"
#include "usb/usbus/control.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _init(usbus_t *usbus, usbus_handler_t *handler);
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler, usbus_event_usb_t event);
static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_control_request_state_t state,
usb_setup_t *setup);
static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event);
static void _handle_flush(event_t *ev);
static const usbus_handler_driver_t cdc_driver = {
.init = _init,
.event_handler = _event_handler,
.control_handler = _control_handler,
.transfer_handler = _transfer_handler,
};
static size_t _gen_full_acm_descriptor(usbus_t *usbus, void *arg);
/* Descriptors */
static const usbus_hdr_gen_funcs_t _cdcacm_descriptor = {
.get_header = _gen_full_acm_descriptor,
.len = {
.fixed_len = sizeof(usb_desc_cdc_t) +
sizeof(usb_desc_acm_t) +
sizeof(usb_desc_union_t) +
sizeof(usb_desc_call_mngt_t),
},
.len_type = USBUS_HDR_LEN_FIXED,
};
static size_t _gen_mngt_descriptor(usbus_t *usbus, usbus_cdcacm_device_t *cdcacm)
{
usb_desc_call_mngt_t mngt;
/* functional call management descriptor */
mngt.length = sizeof(usb_desc_call_mngt_t);
mngt.type = USB_TYPE_DESCRIPTOR_CDC;
mngt.subtype = USB_CDC_DESCR_SUBTYPE_CALL_MGMT;
mngt.capabalities = 0;
mngt.data_if = cdcacm->iface_data.idx;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&mngt, sizeof(mngt));
return sizeof(usb_desc_call_mngt_t);
}
static size_t _gen_union_descriptor(usbus_t *usbus,
usbus_cdcacm_device_t *cdcacm)
{
usb_desc_union_t uni;
/* functional union descriptor */
uni.length = sizeof(usb_desc_union_t);
uni.type = USB_TYPE_DESCRIPTOR_CDC;
uni.subtype = USB_CDC_DESCR_SUBTYPE_UNION;
uni.master_if = cdcacm->iface_ctrl.idx;
uni.slave_if = cdcacm->iface_data.idx;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&uni, sizeof(uni));
return sizeof(usb_desc_union_t);
}
static size_t _gen_acm_descriptor(usbus_t *usbus)
{
usb_desc_acm_t acm;
/* functional cdc acm descriptor */
acm.length = sizeof(usb_desc_acm_t);
acm.type = USB_TYPE_DESCRIPTOR_CDC;
acm.subtype = USB_CDC_DESCR_SUBTYPE_ACM;
/* Support for Set/Get_Line_coding, Control_State, and Serial_State notif */
acm.capabalities = 0x02;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&acm, sizeof(acm));
return sizeof(usb_desc_acm_t);
}
static size_t _gen_cdc_descriptor(usbus_t *usbus)
{
usb_desc_cdc_t cdc;
/* functional cdc descriptor */
cdc.length = sizeof(usb_desc_cdc_t);
cdc.bcd_cdc = USB_CDC_VERSION_BCD;
cdc.type = USB_TYPE_DESCRIPTOR_CDC;
cdc.subtype = USB_CDC_DESCR_SUBTYPE_FUNCTIONAL;
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&cdc, sizeof(cdc));
return sizeof(usb_desc_cdc_t);
}
static size_t _gen_full_acm_descriptor(usbus_t *usbus, void *arg)
{
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t*)arg;
size_t total_len = 0;
total_len += _gen_cdc_descriptor(usbus);
total_len += _gen_acm_descriptor(usbus);
total_len += _gen_union_descriptor(usbus, cdcacm);
total_len += _gen_mngt_descriptor(usbus, cdcacm);
return total_len;
}
/* Submit (ACM interface in) */
size_t usbus_cdc_acm_submit(usbus_cdcacm_device_t *cdcacm, const uint8_t *buf, size_t len)
{
return tsrb_add(&cdcacm->tsrb, buf, len);
}
void usbus_cdc_acm_set_coding_cb(usbus_cdcacm_device_t *cdcacm,
usbus_cdcacm_coding_cb_t coding_cb)
{
irq_disable();
cdcacm->coding_cb = coding_cb;
irq_enable();
}
/* flush event */
void usbus_cdc_acm_flush(usbus_cdcacm_device_t *cdcacm)
{
if (cdcacm->usbus) {
usbus_event_post(cdcacm->usbus, &cdcacm->flush);
}
}
void usbus_cdc_acm_init(usbus_t *usbus, usbus_cdcacm_device_t *cdcacm,
usbus_cdcacm_cb_t cb, usbus_cdcacm_coding_cb_t coding_cb,
uint8_t *buf, size_t len)
{
memset(cdcacm, 0, sizeof(usbus_cdcacm_device_t));
cdcacm->usbus = usbus;
tsrb_init(&cdcacm->tsrb, buf, len);
cdcacm->handler_ctrl.driver = &cdc_driver;
cdcacm->cb = cb;
cdcacm->coding_cb = coding_cb;
cdcacm->state = USBUS_CDC_ACM_LINE_STATE_DISCONNECTED;
usbus_register_event_handler(usbus, &cdcacm->handler_ctrl);
}
static void _init(usbus_t *usbus, usbus_handler_t *handler)
{
DEBUG("CDC_ACM: intialization\n");
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t*)handler;
cdcacm->flush.handler = _handle_flush;
cdcacm->cdcacm_hdr.next = NULL;
cdcacm->cdcacm_hdr.funcs = &_cdcacm_descriptor;
cdcacm->cdcacm_hdr.arg = cdcacm;
/* Configure Interface 0 as control interface */
cdcacm->iface_ctrl.class = USB_CLASS_CDC_CONTROL ;
cdcacm->iface_ctrl.subclass = USB_CDC_SUBCLASS_ACM;
cdcacm->iface_ctrl.protocol = USB_CDC_PROTOCOL_NONE;
cdcacm->iface_ctrl.hdr_gen = &cdcacm->cdcacm_hdr;
cdcacm->iface_ctrl.handler = handler;
/* Configure second interface to handle data endpoint */
cdcacm->iface_data.class = USB_CLASS_CDC_DATA ;
cdcacm->iface_data.subclass = USB_CDC_SUBCLASS_NONE;
cdcacm->iface_data.protocol = USB_CDC_PROTOCOL_NONE;
cdcacm->iface_data.hdr_gen = NULL;
cdcacm->iface_data.handler = handler;
/* Create required endpoints */
usbus_endpoint_t *ep = usbus_add_endpoint(usbus, &cdcacm->iface_ctrl,
USB_EP_TYPE_INTERRUPT,
USB_EP_DIR_IN, 8);
ep->interval = 255; /* Max interval */
usbus_enable_endpoint(ep);
ep = usbus_add_endpoint(usbus, &cdcacm->iface_data,
USB_EP_TYPE_BULK, USB_EP_DIR_IN,
USBUS_CDC_ACM_BULK_EP_SIZE);
ep->interval = 0; /* Interval is not used with bulk endpoints */
usbus_enable_endpoint(ep);
ep = usbus_add_endpoint(usbus, &cdcacm->iface_data,
USB_EP_TYPE_BULK, USB_EP_DIR_OUT,
USBUS_CDC_ACM_BULK_EP_SIZE);
ep->interval = 0; /* Interval is not used with bulk endpoints */
usbus_enable_endpoint(ep);
usbdev_ep_ready(ep->ep, 0);
/* Add interfaces to the stack */
usbus_add_interface(usbus, &cdcacm->iface_ctrl);
usbus_add_interface(usbus, &cdcacm->iface_data);
usbus_handler_set_flag(handler, USBUS_HANDLER_FLAG_RESET);
}
static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_control_request_state_t state,
usb_setup_t *setup)
{
(void)state;
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t*)handler;
switch(setup->request) {
case USB_CDC_MGNT_REQUEST_SET_LINE_CODING:
if ((state == USBUS_CONTROL_REQUEST_STATE_OUTDATA) &&
(setup->length == sizeof(usb_req_cdcacm_coding_t))) {
size_t len = 0;
usb_req_cdcacm_coding_t *coding =
(usb_req_cdcacm_coding_t*)usbus_control_get_out_data(usbus,
&len);
if (len != sizeof(usb_req_cdcacm_coding_t)) {
DEBUG("CDCACM: incorrect length of LINE_CODING set request"
", expected: %u, got: %u",
sizeof(usb_req_cdcacm_coding_t), len);
return -1;
}
if (cdcacm->coding_cb) {
DEBUG("Setting line coding to baud rate %" PRIu32 ", "
"%u data bits, parity value %u, stop bit value %u\n",
coding->baud, coding->databits, coding->parity,
coding->format);
if (cdcacm->coding_cb(cdcacm, coding->baud,
coding->databits, coding->parity,
coding->format) < 0) {
return -1;
}
}
memcpy(&cdcacm->coding, coding,
sizeof(usb_req_cdcacm_coding_t));
}
break;
case USB_CDC_MGNT_REQUEST_GET_LINE_CODING:
usbus_control_slicer_put_bytes(usbus, (uint8_t*)&cdcacm->coding,
sizeof(usb_req_cdcacm_coding_t));
break;
case USB_CDC_MGNT_REQUEST_SET_CONTROL_LINE_STATE:
if (setup->value & USB_CDC_ACM_CONTROL_LINE_DTE) {
DEBUG("CDC ACM: DTE enabled on interface %u\n", setup->index);
cdcacm->state = USBUS_CDC_ACM_LINE_STATE_DTE;
usbus_cdc_acm_flush(cdcacm);
}
else {
cdcacm->state = USBUS_CDC_ACM_LINE_STATE_DISCONNECTED;
DEBUG("CDC ACM: DTE disabled on interface %u\n", setup->index);
}
break;
default:
DEBUG("unhandled USB setup request:0x%x\n", setup->request);
return -1;
}
return 1;
}
static void _handle_in(usbus_cdcacm_device_t *cdcacm,
usbdev_ep_t *ep)
{
if ((cdcacm->usbus->state != USBUS_STATE_CONFIGURED) ||
(cdcacm->state != USBUS_CDC_ACM_LINE_STATE_DTE)) {
return;
}
while (!tsrb_empty(&cdcacm->tsrb)) {
int c = tsrb_get_one(&cdcacm->tsrb);
ep->buf[cdcacm->occupied++] = (uint8_t)c;
if (cdcacm->occupied >= USBUS_CDC_ACM_BULK_EP_SIZE) {
break;
}
}
usbdev_ep_ready(ep, cdcacm->occupied);
}
static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event)
{
(void)usbus;
(void)event; /* Only receives TR_COMPLETE events */
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t*)handler;
if ((ep->dir == USB_EP_DIR_OUT) && (ep->type == USB_EP_TYPE_BULK)) {
size_t len;
/* Retrieve incoming data */
usbdev_ep_get(ep, USBOPT_EP_AVAILABLE, &len, sizeof(size_t));
if (len > 0) {
cdcacm->cb(cdcacm, ep->buf, len);
}
usbdev_ep_ready(ep, 0);
}
if ((ep->dir == USB_EP_DIR_IN) && (ep->type == USB_EP_TYPE_BULK)) {
cdcacm->occupied = 0;
if (!tsrb_empty(&cdcacm->tsrb)) {
return _handle_in(cdcacm, ep);
}
}
}
static void _handle_flush(event_t *ev)
{
usbus_cdcacm_device_t *cdcacm = container_of(ev, usbus_cdcacm_device_t,
flush);
if (cdcacm->occupied == 0) {
_handle_in(cdcacm, cdcacm->iface_data.ep->next->ep);
}
}
static void _handle_reset(usbus_handler_t *handler)
{
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t *)handler;
DEBUG("CDC ACM: Reset notification received\n");
cdcacm->state = USBUS_CDC_ACM_LINE_STATE_DISCONNECTED;
}
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler, usbus_event_usb_t event)
{
(void)usbus;
switch(event) {
case USBUS_EVENT_USB_RESET:
_handle_reset(handler);
break;
default:
DEBUG("Unhandled event :0x%x\n", event);
break;
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys
* @{
*
* @file
* @brief CDC ACM stdio implementation for USBUS CDC ACM
*
* This file implements a USB CDC ACM callback and read/write functions.
*
*
* @}
*/
#include <stdio.h>
#include "isrpipe.h"
#include "usb/usbus.h"
#include "usb/usbus/cdc/acm.h"
#if MODULE_VFS
#include "vfs.h"
#endif
static usbus_cdcacm_device_t cdcacm;
static uint8_t _cdc_tx_buf_mem[USBUS_CDC_ACM_STDIO_BUF_SIZE];
static uint8_t _cdc_rx_buf_mem[USBUS_CDC_ACM_STDIO_BUF_SIZE];
static isrpipe_t _cdc_stdio_isrpipe = ISRPIPE_INIT(_cdc_rx_buf_mem);
void stdio_init(void)
{
/* Initialize this side of the CDC ACM pipe */
#if MODULE_VFS
vfs_bind_stdio();
#endif
}
ssize_t stdio_read(void* buffer, size_t len)
{
(void)buffer;
(void)len;
return isrpipe_read(&_cdc_stdio_isrpipe, buffer, len);
}
ssize_t stdio_write(const void* buffer, size_t len)
{
usbus_cdc_acm_submit(&cdcacm, buffer, len);
usbus_cdc_acm_flush(&cdcacm);
/* Use tsrb and flush */
return len;
}
static void _cdc_acm_rx_pipe(usbus_cdcacm_device_t *cdcacm,
uint8_t *data, size_t len)
{
(void)cdcacm;
for (size_t i = 0; i < len; i++) {
isrpipe_write_one(&_cdc_stdio_isrpipe, data[i]);
}
}
void usb_cdc_acm_stdio_init(usbus_t *usbus)
{
usbus_cdc_acm_init(usbus, &cdcacm, _cdc_acm_rx_pipe, NULL,
_cdc_tx_buf_mem, sizeof(_cdc_tx_buf_mem));
}

View File

@ -0,0 +1,27 @@
BOARD ?= samr21-xpro
include ../Makefile.tests_common
USEMODULE += auto_init_usbus
USEMODULE += stdio_cdc_acm
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
# USB device vendor and product ID
DEFAULT_VID = 1209
DEFAULT_PID = 0001
USB_VID ?= $(DEFAULT_VID)
USB_PID ?= $(DEFAULT_PID)
CFLAGS += -DUSB_CONFIG_VID=0x$(USB_VID) -DUSB_CONFIG_PID=0x$(USB_PID)
include $(RIOTBASE)/Makefile.include
.PHONY: usb_id_check
usb_id_check:
@if [ $(USB_VID) = $(DEFAULT_VID) ] || [ $(USB_PID) = $(DEFAULT_PID) ] ; then \
$(COLOR_ECHO) "$(COLOR_RED)Private testing pid.codes USB VID/PID used!, do not use it outside of test environments!$(COLOR_RESET)" 1>&2 ; \
$(COLOR_ECHO) "$(COLOR_RED)MUST NOT be used on any device redistributed, sold or manufactured, VID/PID is not unique!$(COLOR_RESET)" 1>&2 ; \
fi
all: | usb_id_check

View File

@ -0,0 +1,23 @@
Expected result
===============
A second USB serial console (ttyACMx) appears when plugging the USB peripheral
into a host computer. When opening the serial device it should show the RIOT
shell. Basic command interaction must work.
The test should work on Linux, MacOS and Windows. Putty is known to work on
Windows.
Changing the baud rate, bit mode and parity mode is accepted by the device and
reflected back. However, changing these should not affect shell operation.
Note that when testing with this firmware, the regular USB serial console from
the attached debugger is not functional.
Background
==========
This test application can be used to verify the USBUS CDC ACM implementation.
Assuming drivers available, the board under test should show up on the host
computer as an USB CDC Abstract Control Management device (ttyACMx on Linux).
Drivers are available for Linux, macOS and Windows.

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 Koen Zandberg <koen@bergzand.net>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @file
* @brief Basic test for USB CDC ACM functionality. When plugged into a
* USB port, the peripheral should show up as a serial modem USB
* peripheral (/dev/ttyACMx on Linux) and should present the RIOT
* shell over this serial device.
*
* @author Koen Zandberg <koen@bergzand.net>
*
*/
#include <stdio.h>
#include "shell.h"
#include "shell_commands.h"
int main(void)
{
(void) puts("RIOT USB CDC ACM shell test");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}