1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:12:57 +01:00

Merge pull request #11077 from bergzand/pr/usb/cdcecm

usbus: Add CDC-ECM (Ethernet Control Model) function
This commit is contained in:
Dylan Laduranty 2019-06-14 18:44:27 +02:00 committed by GitHub
commit ea36d68703
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1134 additions and 0 deletions

View File

@ -838,6 +838,14 @@ ifneq (,$(filter usbus,$(USEMODULE)))
USEMODULE += event
endif
ifneq (,$(filter usbus_cdc_ecm,$(USEMODULE)))
USEMODULE += iolist
USEMODULE += fmt
USEMODULE += usbus
USEMODULE += netdev_eth
USEMODULE += luid
endif
ifneq (,$(filter uuid,$(USEMODULE)))
USEMODULE += hashes
USEMODULE += random

View File

@ -261,6 +261,11 @@ void auto_init(void)
auto_init_kw2xrf();
#endif
#ifdef MODULE_USBUS_CDC_ECM
extern void auto_init_netdev_cdcecm(void);
auto_init_netdev_cdcecm();
#endif
#ifdef MODULE_NETDEV_TAP
extern void auto_init_netdev_tap(void);
auto_init_netdev_tap();

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 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_auto_init_gnrc_netif
* @{
*
* @file
* @brief Auto initialization for USB CDC ECM module
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifdef MODULE_USBUS_CDC_ECM
#include "log.h"
#include "usb/usbus/cdc/ecm.h"
#include "net/gnrc/netif/ethernet.h"
/**
* @brief global cdc ecm object, declared in the usb auto init file
*/
extern usbus_cdcecm_device_t cdcecm;
/**
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define CDCECM_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#ifndef CDCECM_MAC_PRIO
#define CDCECM_MAC_PRIO (GNRC_NETIF_PRIO)
#endif
/**
* @brief Stacks for the MAC layer threads
*/
static char _netdev_eth_stack[CDCECM_MAC_STACKSIZE];
extern void cdcecm_netdev_setup(usbus_cdcecm_device_t *cdcecm);
void auto_init_netdev_cdcecm(void)
{
LOG_DEBUG("[auto_init_netif] initializing cdc ecm #0\n");
cdcecm_netdev_setup(&cdcecm);
/* initialize netdev<->gnrc adapter state */
gnrc_netif_ethernet_create(_netdev_eth_stack, CDCECM_MAC_STACKSIZE,
CDCECM_MAC_PRIO, "cdcecm", &cdcecm.netdev);
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_CDC_ECM */
/** @} */

View File

@ -24,6 +24,11 @@
#include "usb/usbus.h"
#ifdef MODULE_USBUS_CDC_ECM
#include "usb/usbus/cdc/ecm.h"
usbus_cdcecm_device_t cdcecm;
#endif
static char _stack[USBUS_STACKSIZE];
static usbus_t usbus;
@ -36,6 +41,10 @@ void auto_init_usb(void)
/* Initialize basic usbus struct, don't start the thread yet */
usbus_init(&usbus, usbdev);
/* USBUS function handlers initialization */
#ifdef MODULE_USBUS_CDC_ECM
usbus_cdcecm_init(&usbus, &cdcecm);
#endif
/* Finally initialize USBUS thread */
usbus_create(_stack, USBUS_STACKSIZE, USBUS_PRIO, USBUS_TNAME, &usbus);

246
sys/include/usb/cdc.h Normal file
View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2018 Dylan Laduranty <dylan.laduranty@mesotic.com>
*
* 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 usb_cdc CDC - USB communications device class
* @ingroup usb
* @brief Generic USB CDC defines and helpers
*
* @{
*
* @file
* @brief Definition for USB CDC interfaces
*
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef USB_CDC_H
#define USB_CDC_H
#include <stdint.h>
#include "usb.h"
#include "usb/descriptor.h"
#ifdef __cplusplus
extern "C" {
#endif
#define USB_TYPE_DESCRIPTOR_CDC 0x24 /**< USB CDC type descriptor*/
#define USB_CDC_VERSION_BCD 0x0120 /**< USB CDC version in BCD */
/**
* @name USB CDC subclass types
* @anchor usb_cdc_subtype
* @{
*/
#define USB_CDC_SUBCLASS_NONE 0x00 /**< No subclass */
#define USB_CDC_SUBCLASS_DLCM 0x01 /**< Direct Line Control Model */
#define USB_CDC_SUBCLASS_ACM 0x02 /**< Abstract Control Model */
#define USB_CDC_SUBCLASS_TCM 0x03 /**< Telephone Control Model */
#define USB_CDC_SUBCLASS_MCCM 0x04 /**< Multi-Channel Control Model */
#define USB_CDC_SUBCLASS_CCM 0x05 /**< CAPI Control Mode */
#define USB_CDC_SUBCLASS_ENCM 0x06 /**< Eth Networking Control Model */
#define USB_CDC_SUBCLASS_ANCM 0x07 /**< ATM Networking Control Model */
#define USB_CDC_SUBCLASS_WHCM 0x08 /**< Wireless Handset Control Model */
#define USB_CDC_SUBCLASS_DM 0x09 /**< Device Management */
#define USB_CDC_SUBCLASS_MDLM 0x0A /**< Mobile Direct Line Model */
#define USB_CDC_SUBCLASS_OBEX 0x0B /**< OBEX */
#define USB_CDC_SUBCLASS_EEM 0x0C /**< Ethernet Emulation Model */
#define USB_CDC_SUBCLASS_NCM 0x0D /**< Network Control Model */
/** @} */
/**
* @name USB CDC protocol types
* @{
*/
#define USB_CDC_PROTOCOL_NONE 0x00 /**< No protocol required */
#define USB_CDC_PROTOCOL_ITU 0x01 /**< AT Commands: V.250 etc */
#define USB_CDC_PROTOCOL_PCCA 0x02 /**< AT Commands defined by PCCA-101 */
#define USB_CDC_PROTOCOL_PCCA_A 0x03 /**< AT Commands defined by PCCA-101 & Annex O */
#define USB_CDC_PROTOCOL_GSM 0x04 /**< AT Commands defined by GSM 07.07 */
#define USB_CDC_PROTOCOL_3GPP 0x05 /**< AT Commands defined by 3GPP 27.007 */
#define USB_CDC_PROTOCOL_CS 0x06 /**< AT Commands defined by TIA for CDMA */
#define USB_CDC_PROTOCOL_EEM 0x07 /**< Ethernet Emulation Model */
#define USB_CDC_PROTOCOL_EXT 0xFE /**< External Protocol */
#define USB_CDC_PROTOCOL_VENDOR 0xFF /**< Vendor-specific */
/** @} */
/**
* @name USB CDC descriptor subtypes
*/
#define USB_CDC_DESCR_SUBTYPE_FUNCTIONAL 0x00 /**< Header functional
* descriptor */
#define USB_CDC_DESCR_SUBTYPE_CALL_MGMT 0x01 /**< Call management
descriptor */
#define USB_CDC_DESCR_SUBTYPE_ACM 0x02 /**< Abstract control
management descriptor */
#define USB_CDC_DESCR_SUBTYPE_UNION 0x06 /**< Union descriptor */
#define USB_CDC_DESCR_SUBTYPE_ETH_NET 0x0f /**< Ethernet descriptor */
/** @} */
/**
* @name USB CDC management requests
* @{
*/
/**
* @brief Set ethernet multicast filter request
*/
#define USB_CDC_MGNT_REQUEST_SET_ETH_MULTICAST_FILTER 0x40
/**
* @brief Set ethernet power management pattern filter
*/
#define USB_CDC_MGNT_REQUEST_SET_ETH_PM_PATTERN_FILTER 0x41
/**
* @brief Get ethernet power management pattern filter
*/
#define USB_CDC_MGNT_REQUEST_GET_ETH_PM_PATTERN_FILTER 0x42
/**
* @brief Set ethernet packet filter
*/
#define USB_CDC_MGNT_REQUEST_SET_ETH_PACKET_FILTER 0x43
/**
* @brief Get ethernet statistics
*/
#define USB_CDC_MGNT_REQUEST_GET_ETH_STATISTICS 0x44
/** @} */
/**
* @name USB CDC management notifications
* @{
*/
/**
* @brief Network connection status notification
*/
#define USB_CDC_MGNT_NOTIF_NETWORK_CONNECTION 0x00
/**
* @brief Response available notification
*/
#define USB_CDC_MGNT_NOTIF_RESPONSE_AVAILABLE 0x01
/**
* @brief Hook on the auxiliary phone changed notification
*/
#define USB_CDC_MGNT_NOTIF_AUX_JACK_HOOK_STATE 0x08
/**
* @brief Ring voltage on the POTS line interface notification
*/
#define USB_CDC_MGNT_NOTIF_RING_DETECT 0x09
/**
* @brief Asynchronous UART status notification
*/
#define USB_CDC_MGNT_NOTIF_SERIAL_STATE 0x20
/**
* @brief Call state change notification
*/
#define USB_CDC_MGNT_NOTIF_CALL_STATE_CHANGE 0x28
/**
* @brief Line state change notification
*/
#define USB_CDC_MGNT_NOTIF_LINE_STATE_CHANGE 0x29
/**
* @brief Throughput change notification
*/
#define USB_CDC_MGNT_NOTIF_CONN_SPEED_CHANGE 0x2A
/** @} */
/**
* @brief USB CDC ECM descriptor
*
* @see USB CDC 1.2 ECM spec table 3
*/
typedef struct __attribute__((packed)) {
uint8_t length; /**< Size of this descriptor */
uint8_t type; /**< Descriptor type (@ref USB_TYPE_DESCRIPTOR_CDC) */
uint8_t subtype; /**< Descriptor subtype (@ref USB_CDC_DESCR_SUBTYPE_ETH_NET) */
uint8_t macaddress; /**< Index of the string containing the ethernet MAC address */
uint32_t ethernetstatistics; /**< Bitmap indicating the statistics caps */
uint16_t maxsegmentsize; /**< Maximum segment size of the interface */
uint16_t numbermcfilters; /**< Number of configurable multicast filters */
uint8_t numberpowerfilters; /**< Number of pattern filters for host wake-up */
} usb_desc_ecm_t;
/**
* @brief USB CDC ACM descriptor
*
* @see USB CDC 1.2 PTSN spec table 4
*/
typedef struct __attribute__((packed)) {
uint8_t length; /**< Size of this descriptor */
uint8_t type; /**< Descriptor type (@ref USB_TYPE_DESCRIPTOR_CDC) */
uint8_t subtype; /**< Descriptor subtype (@ref USB_CDC_DESCR_SUBTYPE_ACM) */
uint8_t capabalities; /**< Bitmap indicating the capabilities */
} usb_desc_acm_t;
/**
* @brief Generic USB CDC descriptor
*
* @see USB CDC 1.2 spec table 15
*/
typedef struct __attribute__((packed)) {
uint8_t length; /**< Size of this descriptor */
uint8_t type; /**< Descriptor type (@ref USB_TYPE_DESCRIPTOR_CDC) */
uint8_t subtype; /**< Descriptor subtype (@ref usb_cdc_subtype) */
uint16_t bcd_cdc; /**< CDC release number in bcd (@ref USB_CDC_VERSION_BCD) */
} usb_desc_cdc_t;
/**
* @brief USB union descriptor
*/
typedef struct __attribute__((packed)) {
uint8_t length; /**< Size of this descriptor */
uint8_t type; /**< Descriptor type (@ref USB_TYPE_DESCRIPTOR_CDC) */
uint8_t subtype; /**< Descriptor subtype (@ref USB_CDC_DESCR_SUBTYPE_UNION) */
uint8_t master_if; /**< Master/controlling interface number */
uint8_t slave_if; /**< Slave/subordinate interface number */
} usb_desc_union_t;
/**
* @brief USB CDC call management functional descriptor
*
* @see USB CDC 1.2 PSTN spec table 13
*/
typedef struct __attribute__((packed)) {
uint8_t length; /**< Size of this descriptor */
uint8_t type; /**< Descriptor type (@ref USB_TYPE_DESCRIPTOR_CDC) */
uint8_t subtype; /**< Descriptor subtype (@ref USB_CDC_DESCR_SUBTYPE_CALL_MGMT) */
uint8_t capabalities; /**< Supported capabilities */
uint8_t data_if; /**< Interface number used for the call management */
} usb_desc_call_mngt_t;
/**
* @brief USB CDC ECM connection speed change notification
*
* @see USB CDC 1.2 spec section 6.3.3
*/
typedef struct __attribute__((packed)) {
usb_setup_t setup; /**< Setup request header for the notification */
uint32_t down; /**< Downlink bit rate */
uint32_t up; /**< Uplink bit rate */
} usb_desc_cdcecm_speed_t;
#ifdef __cplusplus
}
#endif
#endif /* USB_CDC_H */
/** @} */

View File

@ -40,6 +40,7 @@ extern "C" {
#define USB_SETUP_REQ_SET_CONFIGURATION 0x09 /**< Set configuration */
#define USB_SETUP_REQ_GET_INTERFACE 0x0a /**< Get interface */
#define USB_SETUP_REQ_SET_INTERFACE 0x0b /**< Set interface */
#define USB_SETUP_REQ_SYNCH_FRAME 0x0c /**< Synch frame */
/** @} */
/**

View File

@ -0,0 +1,129 @@
/*
* Copyright (C) 2019 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.
*/
/**
* @defgroup usbus_cdc_ecm USBUS CDC ECM - USBUS CDC ethernet control model
* @ingroup usb
* @brief USBUS CDC ECM interface module
*
* @{
*
* @file
* @brief Interface and definitions for USB CDC ECM type interfaces
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef USB_USBUS_CDC_ECM_H
#define USB_USBUS_CDC_ECM_H
#include <stdint.h>
#include <stdlib.h>
#include "net/ethernet.h"
#include "net/ethernet/hdr.h"
#include "usb/descriptor.h"
#include "usb/usbus.h"
#include "net/netdev.h"
#include "mutex.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Link throughput as reported by the peripheral
*
* This defines a common up and down link throughput in bits/second. The USB
* peripheral will report this to the host. This doesn't affect the actual
* throughput, only what the peripheral reports to the host.
*/
#ifndef USBUS_CDC_ECM_CONFIG_SPEED
#define USBUS_CDC_ECM_CONFIG_SPEED 1000000
#endif
/**
* @brief Link download speed as reported by the peripheral
*/
#ifndef USBUS_CDC_ECM_CONFIG_SPEED_DOWNSTREAM
#define USBUS_CDC_ECM_CONFIG_SPEED_DOWNSTREAM USBUS_CDC_ECM_CONFIG_SPEED
#endif
/**
* @brief Link upload speed as reported by the peripheral
*/
#ifndef USBUS_CDC_ECM_CONFIG_SPEED_UPSTREAM
#define USBUS_CDC_ECM_CONFIG_SPEED_UPSTREAM USBUS_CDC_ECM_CONFIG_SPEED
#endif
/**
* @brief CDC ECM interrupt endpoint size.
*
* Used by the device to report events to the host.
*
* @note Must be at least 16B to allow for reporting the link throughput
*/
#define USBUS_CDCECM_EP_CTRL_SIZE 16
/**
* @brief CDC ECM bulk data endpoint size.
*
* Used for the transfer of network frames.
*/
#define USBUS_CDCECM_EP_DATA_SIZE 64
/**
* @brief notification state, used to track which information must be send to
* the host
*/
typedef enum {
USBUS_CDCECM_NOTIF_NONE, /**< Nothing notified so far */
USBUS_CDCECM_NOTIF_LINK_UP, /**< Link status is notified */
USBUS_CDCECM_NOTIF_SPEED, /**< Link speed is notified */
} usbus_cdcecm_notif_t;
/**
* @brief USBUS CDC ECM device interface context
*/
typedef struct usbus_cdcecm_device {
usbus_handler_t handler_ctrl; /**< Control interface handler */
usbus_interface_t iface_data; /**< Data interface */
usbus_interface_t iface_ctrl; /**< Control interface */
usbus_interface_alt_t iface_data_alt; /**< Data alternative (active) interface */
usbus_endpoint_t *ep_in; /**< Data endpoint in */
usbus_endpoint_t *ep_out; /**< Data endpoint out */
usbus_endpoint_t *ep_ctrl; /**< Control endpoint */
usbus_hdr_gen_t ecm_hdr; /**< ECM header generator */
event_t rx_flush; /**< Receive flush event */
event_t tx_xmit; /**< Transmit ready event */
netdev_t netdev; /**< Netdev context struct */
uint8_t mac_netdev[ETHERNET_ADDR_LEN]; /**< this device's MAC address */
char mac_host[13]; /**< host side's MAC address as string */
usbus_string_t mac_str; /**< String context for the host side mac address */
usbus_t *usbus; /**< Ptr to the USBUS context */
mutex_t out_lock; /**< mutex used for locking netif/USBUS send */
size_t tx_len; /**< Length of the current tx frame */
uint8_t in_buf[ETHERNET_FRAME_LEN]; /**< Buffer for the received frames */
size_t len; /**< Length of the current rx frame */
usbus_cdcecm_notif_t notif; /**< Startup message notification tracker */
unsigned active_iface; /**< Current active data interface */
} usbus_cdcecm_device_t;
/**
* @brief CDC ECM initialization function
*
* @param usbus USBUS thread to use
* @param handler CDCECM device struct
*/
void usbus_cdcecm_init(usbus_t *usbus, usbus_cdcecm_device_t *handler);
#ifdef __cplusplus
}
#endif
#endif /* USB_USBUS_CDC_ECM_H */
/** @} */

View File

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

View File

@ -0,0 +1,3 @@
MODULE = usbus_cdc_ecm
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,373 @@
/*
* Copyright (C) 2019 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 usbus_cdc_ecm
* @{
* @file USBUS implementation for ethernet control model
*
* @author Koen Zandberg <koen@bergzand.net>
* @}
*/
#include "event.h"
#include "fmt.h"
#include "kernel_defines.h"
#include "luid.h"
#include "net/ethernet.h"
#include "net/eui48.h"
#include "usb/cdc.h"
#include "usb/descriptor.h"
#include "usb/usbus.h"
#include "usb/usbus/control.h"
#include "usb/usbus/cdc/ecm.h"
#include <string.h>
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_event_usb_t event);
static int _setup_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_setuprq_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 _init(usbus_t *usbus, usbus_handler_t *handler);
static void _handle_rx_flush_ev(event_t *ev);
static void _handle_tx_xmit(event_t *ev);
static size_t _gen_full_ecm_descriptor(usbus_t *usbus, void *arg);
static const usbus_hdr_gen_funcs_t _ecm_descriptor = {
.get_header = _gen_full_ecm_descriptor,
.len = {
.fixed_len = sizeof(usb_desc_cdc_t) +
sizeof(usb_desc_union_t) +
sizeof(usb_desc_ecm_t),
},
.len_type = USBUS_HDR_LEN_FIXED,
};
static size_t _gen_union_descriptor(usbus_t *usbus, usbus_cdcecm_device_t *cdcecm)
{
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 = cdcecm->iface_ctrl.idx;
uni.slave_if = cdcecm->iface_data.idx;
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&uni, sizeof(uni));
return sizeof(usb_desc_union_t);
}
static size_t _gen_ecm_descriptor(usbus_t *usbus, usbus_cdcecm_device_t *cdcecm)
{
usb_desc_ecm_t ecm;
/* functional cdc ecm descriptor */
ecm.length = sizeof(usb_desc_ecm_t);
ecm.type = USB_TYPE_DESCRIPTOR_CDC;
ecm.subtype = USB_CDC_DESCR_SUBTYPE_ETH_NET;
ecm.macaddress = cdcecm->mac_str.idx;
ecm.ethernetstatistics = 0;
ecm.maxsegmentsize = ETHERNET_FRAME_LEN;
ecm.numbermcfilters = 0x0000; /* No filtering */
ecm.numberpowerfilters = 0;
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&ecm, sizeof(ecm));
return sizeof(usb_desc_ecm_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 = 0x00;
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&cdc, sizeof(cdc));
return sizeof(usb_desc_cdc_t);
}
static size_t _gen_full_ecm_descriptor(usbus_t *usbus, void *arg)
{
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)arg;
size_t total_size = 0;
total_size += _gen_cdc_descriptor(usbus);
total_size += _gen_union_descriptor(usbus, cdcecm);
total_size += _gen_ecm_descriptor(usbus, cdcecm);
return total_size;
}
static void _notify_link_speed(usbus_cdcecm_device_t *cdcecm)
{
DEBUG("CDC ECM: sending link speed indication\n");
usb_desc_cdcecm_speed_t *notification =
(usb_desc_cdcecm_speed_t *)cdcecm->ep_ctrl->ep->buf;
notification->setup.type = USB_SETUP_REQUEST_DEVICE2HOST |
USB_SETUP_REQUEST_TYPE_CLASS |
USB_SETUP_REQUEST_RECIPIENT_INTERFACE;
notification->setup.request = USB_CDC_MGNT_NOTIF_CONN_SPEED_CHANGE;
notification->setup.value = 0;
notification->setup.index = cdcecm->iface_ctrl.idx;
notification->setup.length = 8;
notification->down = USBUS_CDC_ECM_CONFIG_SPEED_DOWNSTREAM;
notification->up = USBUS_CDC_ECM_CONFIG_SPEED_UPSTREAM;
usbdev_ep_ready(cdcecm->ep_ctrl->ep,
sizeof(usb_desc_cdcecm_speed_t));
cdcecm->notif = USBUS_CDCECM_NOTIF_SPEED;
}
static void _notify_link_up(usbus_cdcecm_device_t *cdcecm)
{
DEBUG("CDC ECM: sending link up indication\n");
usb_setup_t *notification = (usb_setup_t *)cdcecm->ep_ctrl->ep->buf;
notification->type = USB_SETUP_REQUEST_DEVICE2HOST |
USB_SETUP_REQUEST_TYPE_CLASS |
USB_SETUP_REQUEST_RECIPIENT_INTERFACE;
notification->request = USB_CDC_MGNT_NOTIF_NETWORK_CONNECTION;
notification->value = 1;
notification->index = cdcecm->iface_ctrl.idx;
notification->length = 0;
usbdev_ep_ready(cdcecm->ep_ctrl->ep, sizeof(usb_setup_t));
cdcecm->notif = USBUS_CDCECM_NOTIF_LINK_UP;
}
static const usbus_handler_driver_t cdcecm_driver = {
.init = _init,
.event_handler = _event_handler,
.transfer_handler = _transfer_handler,
.setup_handler = _setup_handler,
};
static void _fill_ethernet(usbus_cdcecm_device_t *cdcecm)
{
uint8_t ethernet[ETHERNET_ADDR_LEN];
luid_get(ethernet, ETHERNET_ADDR_LEN);
eui48_set_local((eui48_t*)ethernet);
eui48_clear_group((eui48_t*)ethernet);
fmt_bytes_hex(cdcecm->mac_host, ethernet, sizeof(ethernet));
}
void usbus_cdcecm_init(usbus_t *usbus, usbus_cdcecm_device_t *handler)
{
assert(usbus);
assert(handler);
memset(handler, 0, sizeof(usbus_cdcecm_device_t));
mutex_init(&handler->out_lock);
_fill_ethernet(handler);
handler->usbus = usbus;
handler->handler_ctrl.driver = &cdcecm_driver;
usbus_register_event_handler(usbus, (usbus_handler_t *)handler);
}
static void _init(usbus_t *usbus, usbus_handler_t *handler)
{
DEBUG("CDC ECM: intialization\n");
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)handler;
/* Add event handlers */
cdcecm->tx_xmit.handler = _handle_tx_xmit;
cdcecm->rx_flush.handler = _handle_rx_flush_ev;
/* Set up header generators */
cdcecm->ecm_hdr.next = NULL;
cdcecm->ecm_hdr.funcs = &_ecm_descriptor;
cdcecm->ecm_hdr.arg = cdcecm;
/* Configure Interface 0 as control interface */
cdcecm->iface_ctrl.class = USB_CLASS_CDC_CONTROL;
cdcecm->iface_ctrl.subclass = USB_CDC_SUBCLASS_ENCM;
cdcecm->iface_ctrl.protocol = USB_CDC_PROTOCOL_NONE;
cdcecm->iface_ctrl.hdr_gen = &cdcecm->ecm_hdr;
cdcecm->iface_ctrl.handler = handler;
/* Configure second interface to handle data endpoint */
cdcecm->iface_data.class = USB_CLASS_CDC_DATA;
cdcecm->iface_data.subclass = USB_CDC_SUBCLASS_NONE;
cdcecm->iface_data.protocol = USB_CDC_PROTOCOL_NONE;
cdcecm->iface_data.hdr_gen = NULL;
cdcecm->iface_data.handler = handler;
/* Add string descriptor for the host mac */
usbus_add_string_descriptor(usbus, &cdcecm->mac_str, cdcecm->mac_host);
/* Create required endpoints */
cdcecm->ep_ctrl = usbus_add_endpoint(usbus, &cdcecm->iface_ctrl,
USB_EP_TYPE_INTERRUPT,
USB_EP_DIR_IN,
USBUS_CDCECM_EP_CTRL_SIZE);
cdcecm->ep_ctrl->interval = 0x10;
cdcecm->ep_out = usbus_add_endpoint(usbus,
(usbus_interface_t *)&cdcecm->iface_data_alt,
USB_EP_TYPE_BULK,
USB_EP_DIR_OUT,
USBUS_CDCECM_EP_DATA_SIZE);
cdcecm->ep_out->interval = 0; /* Must be 0 for bulk endpoints */
cdcecm->ep_in = usbus_add_endpoint(usbus,
(usbus_interface_t *)&cdcecm->iface_data_alt,
USB_EP_TYPE_BULK,
USB_EP_DIR_IN,
USBUS_CDCECM_EP_DATA_SIZE);
cdcecm->ep_in->interval = 0; /* Must be 0 for bulk endpoints */
/* Add interfaces to the stack */
usbus_add_interface(usbus, &cdcecm->iface_ctrl);
usbus_add_interface(usbus, &cdcecm->iface_data);
cdcecm->iface_data.alts = &cdcecm->iface_data_alt;
usbus_enable_endpoint(cdcecm->ep_out);
usbus_enable_endpoint(cdcecm->ep_in);
usbus_enable_endpoint(cdcecm->ep_ctrl);
usbdev_ep_ready(cdcecm->ep_out->ep, 0);
usbus_handler_set_flag(handler, USBUS_HANDLER_FLAG_RESET);
}
static int _setup_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_setuprq_state_t state, usb_setup_t *setup)
{
(void)usbus;
(void)state;
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)handler;
DEBUG("CDC ECM: Request: 0x%x\n", setup->request);
switch (setup->request) {
case USB_SETUP_REQ_SET_INTERFACE:
DEBUG("CDC ECM: Changing active interface to alt %d\n",
setup->value);
cdcecm->active_iface = (uint8_t)setup->value;
if (cdcecm->active_iface == 1) {
_notify_link_up(cdcecm);
}
break;
case USB_CDC_MGNT_REQUEST_SET_ETH_PACKET_FILTER:
/* While we do answer the request, CDC ECM filters are not really
* implemented */
DEBUG("CDC ECM: Not modifying filter to 0x%x\n", setup->value);
break;
default:
return -1;
}
return 1;
}
static int _handle_in_complete(usbus_t *usbus, usbus_handler_t *handler)
{
(void)usbus;
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)handler;
mutex_unlock(&cdcecm->out_lock);
return 0;
}
static void _handle_tx_xmit(event_t *ev)
{
usbus_cdcecm_device_t *cdcecm = container_of(ev, usbus_cdcecm_device_t,
tx_xmit);
usbus_t *usbus = cdcecm->usbus;
DEBUG("CDC_ECM: Handling TX xmit from netdev\n");
if (usbus->state != USBUS_STATE_CONFIGURED || cdcecm->active_iface == 0) {
DEBUG("CDC ECM: not configured, unlocking\n");
mutex_unlock(&cdcecm->out_lock);
}
/* Data prepared by netdev_send, signal ready to usbus */
usbdev_ep_ready(cdcecm->ep_in->ep, cdcecm->tx_len);
}
static void _handle_rx_flush(usbus_cdcecm_device_t *cdcecm)
{
cdcecm->len = 0;
usbdev_ep_ready(cdcecm->ep_out->ep, 0);
}
static void _handle_rx_flush_ev(event_t *ev)
{
usbus_cdcecm_device_t *cdcecm = container_of(ev, usbus_cdcecm_device_t,
rx_flush);
_handle_rx_flush(cdcecm);
}
static void _store_frame_chunk(usbus_cdcecm_device_t *cdcecm)
{
uint8_t *buf = cdcecm->ep_out->ep->buf;
size_t len = 0;
usbdev_ep_get(cdcecm->ep_out->ep, USBOPT_EP_AVAILABLE, &len,
sizeof(size_t));
memcpy(cdcecm->in_buf + cdcecm->len, buf, len);
cdcecm->len += len;
if (len < USBUS_CDCECM_EP_DATA_SIZE && cdcecm->netdev.event_callback) {
cdcecm->netdev.event_callback(&cdcecm->netdev, NETDEV_EVENT_ISR);
}
}
static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event)
{
(void)event; /* Only receives TR_COMPLETE events */
(void)usbus;
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)handler;
if (ep == cdcecm->ep_out->ep) {
/* Retrieve incoming data */
if (cdcecm->notif == USBUS_CDCECM_NOTIF_NONE) {
_notify_link_up(cdcecm);
}
size_t len = 0;
usbdev_ep_get(ep, USBOPT_EP_AVAILABLE, &len, sizeof(size_t));
_store_frame_chunk(cdcecm);
if (len == USBUS_CDCECM_EP_DATA_SIZE) {
usbdev_ep_ready(ep, 0);
}
}
else if (ep == cdcecm->ep_in->ep) {
_handle_in_complete(usbus, handler);
}
else if (ep == cdcecm->ep_ctrl->ep &&
cdcecm->notif == USBUS_CDCECM_NOTIF_LINK_UP) {
_notify_link_speed(cdcecm);
}
}
static void _handle_reset(usbus_t *usbus, usbus_handler_t *handler)
{
usbus_cdcecm_device_t *cdcecm = (usbus_cdcecm_device_t *)handler;
DEBUG("CDC ECM: Reset\n");
_handle_rx_flush(cdcecm);
_handle_in_complete(usbus, handler);
cdcecm->notif = USBUS_CDCECM_NOTIF_NONE;
cdcecm->active_iface = 0;
mutex_unlock(&cdcecm->out_lock);
}
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_event_usb_t event)
{
switch (event) {
case USBUS_EVENT_USB_RESET:
_handle_reset(usbus, handler);
break;
default:
DEBUG("Unhandled event :0x%x\n", event);
break;
}
}

View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2019 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 usbus_cdc_ecm
* @{
* @file Netdev implementation for ethernet control model
*
* @author Koen Zandberg <koen@bergzand.net>
* @}
*/
#include <string.h>
#include "kernel_defines.h"
#include "iolist.h"
#include "luid.h"
#include "mutex.h"
#include "net/ethernet.h"
#include "net/eui48.h"
#include "net/netdev.h"
#include "net/netdev/eth.h"
#include "usb/usbus/cdc/ecm.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static const netdev_driver_t netdev_driver_cdcecm;
static void _signal_rx_flush(usbus_cdcecm_device_t *cdcecm)
{
usbus_event_post(cdcecm->usbus, &cdcecm->rx_flush);
}
static void _signal_tx_xmit(usbus_cdcecm_device_t *cdcecm)
{
usbus_event_post(cdcecm->usbus, &cdcecm->tx_xmit);
}
static usbus_cdcecm_device_t *_netdev_to_cdcecm(netdev_t *netdev)
{
return container_of(netdev, usbus_cdcecm_device_t, netdev);
}
void cdcecm_netdev_setup(usbus_cdcecm_device_t *cdcecm)
{
cdcecm->netdev.driver = &netdev_driver_cdcecm;
}
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
assert(iolist);
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(netdev);
uint8_t *buf = cdcecm->ep_in->ep->buf;
const iolist_t *iolist_start = iolist;
size_t len = iolist_size(iolist);
DEBUG("CDC_ECM_netdev: sending %u bytes\n", len);
/* load packet data into FIFO */
size_t iol_offset = 0;
size_t usb_offset = 0;
size_t usb_remain = cdcecm->ep_in->ep->len;
DEBUG("CDC_ECM_netdev: cur iol: %d\n", iolist->iol_len);
while (len) {
mutex_lock(&cdcecm->out_lock);
if (iolist->iol_len - iol_offset > usb_remain) {
/* Only part of the iolist can be copied, usb_remain bytes */
memcpy(buf + usb_offset, (uint8_t *)iolist->iol_base + iol_offset,
usb_remain);
usb_offset = cdcecm->ep_in->maxpacketsize;
len -= usb_remain;
iol_offset += usb_remain;
usb_remain = 0;
}
else {
size_t bytes_copied = iolist->iol_len - iol_offset;
/* Full iolist can be copied */
memcpy(buf + usb_offset, (uint8_t *)iolist->iol_base + iol_offset,
bytes_copied);
len -= bytes_copied;
usb_offset += bytes_copied;
usb_remain -= bytes_copied;
iol_offset = iolist->iol_len;
}
if (iol_offset == iolist->iol_len) {
/* Current iolist exhausted */
iolist = iolist->iol_next;
if (iolist) {
DEBUG("CDC_ECM: cur iol: %d\n", iolist->iol_len);
}
iol_offset = 0;
}
if (usb_remain == 0 || !len) {
cdcecm->tx_len = usb_offset;
/* USB frame full or last frame, flush! */
DEBUG("CDC_ECM_NETDEV: triggering xmit with len %d\n",
cdcecm->tx_len);
_signal_tx_xmit(cdcecm);
usb_remain = cdcecm->ep_in->maxpacketsize;
usb_offset = 0;
}
else {
mutex_unlock(&cdcecm->out_lock);
}
}
/* Zero length USB packet required */
if ((iolist_size(iolist_start) % cdcecm->ep_in->maxpacketsize) == 0) {
mutex_lock(&cdcecm->out_lock);
DEBUG("CDC ECM netdev: Zero length USB packet required\n");
cdcecm->tx_len = 0;
_signal_tx_xmit(cdcecm);
}
return len;
}
static int _recv(netdev_t *netdev, void *buf, size_t max_len, void *info)
{
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(netdev);
(void)info;
if (max_len == 0 && buf == NULL) {
return cdcecm->len;
}
if (max_len && buf == NULL) {
_signal_rx_flush(cdcecm);
return cdcecm->len;
}
memcpy(buf, cdcecm->in_buf, max_len);
_signal_rx_flush(cdcecm);
return max_len;
}
static int _init(netdev_t *netdev)
{
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(netdev);
luid_get(cdcecm->mac_netdev, ETHERNET_ADDR_LEN);
eui48_set_local((eui48_t*)cdcecm->mac_netdev);
eui48_clear_group((eui48_t*)cdcecm->mac_netdev);
return 0;
}
static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
{
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(netdev);
(void)max_len;
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len >= ETHERNET_ADDR_LEN);
memcpy(value, cdcecm->mac_netdev, ETHERNET_ADDR_LEN);
return ETHERNET_ADDR_LEN;
default:
return netdev_eth_get(netdev, opt, value, max_len);
}
}
static int _set(netdev_t *netdev, netopt_t opt, const void *value,
size_t value_len)
{
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(netdev);
(void)cdcecm;
switch (opt) {
case NETOPT_ADDRESS:
assert(value_len == ETHERNET_ADDR_LEN);
memcpy(cdcecm->mac_netdev, value, ETHERNET_ADDR_LEN);
return ETHERNET_ADDR_LEN;
default:
return netdev_eth_set(netdev, opt, value, value_len);
}
}
static void _isr(netdev_t *dev)
{
usbus_cdcecm_device_t *cdcecm = _netdev_to_cdcecm(dev);
if (cdcecm->len) {
cdcecm->netdev.event_callback(&cdcecm->netdev,
NETDEV_EVENT_RX_COMPLETE);
}
}
static const netdev_driver_t netdev_driver_cdcecm = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};

View File

@ -0,0 +1,28 @@
BOARD ?= samr21-xpro
include ../Makefile.tests_common
USEMODULE += auto_init_gnrc_netif
USEMODULE += auto_init_usbus
USEMODULE += gnrc_ipv6_router_default
USEMODULE += gnrc_icmpv6_echo
USEMODULE += usbus_cdc_ecm
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
# Increase the number of network interfaces in case the board under test also provides a network interface
CFLAGS += -DGNRC_NETIF_NUMOF=2
# USB device vendor and product ID
# pid.codes test VID/PID, not globally unique
USB_VID ?= 1209
USB_PID ?= 0001
CFLAGS += -DUSB_CONFIG_VID=0x$(USB_VID) -DUSB_CONFIG_PID=0x$(USB_PID)
include $(RIOTBASE)/Makefile.include
ifeq ($(USB_VID):$(USB_PID), 1209:0001)
$(shell $(COLOR_ECHO) "$(COLOR_RED)Private testing pid.codes USB VID/PID used!, do not use it outside of test environments!$(COLOR_RESET)" 1>&2)
$(shell $(COLOR_ECHO) "$(COLOR_RED)MUST NOT be used on any device redistributed, sold or manufactured, VID/PID is not unique!$(COLOR_RESET)" 1>&2)
endif

View File

@ -0,0 +1,25 @@
Expected result
===============
Use the network related shell commands to verify the network link between the
board under test and the host computer. Ping to the link local address from and
to the host computer must work.
On the host computer, using tools such as `ethtool` must show the USB CDC ECM
interface as link dectected:
```
# ethtool enp0s20u9u4
Settings for enp0s20u9u4:
Current message level: 0x00000007 (7)
drv probe link
Link detected: yes
```
Background
==========
This test application can be used to verify the USBUS CDC ECM implementation.
Assuming drivers available, the board under test should show up on the host
computer as an USB network interface. Drivers are available for both Linux and
macOS.

View File

@ -0,0 +1,46 @@
/*
* 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.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the USBUS CDC ECM interface
*
* @author Koen Zandberg <koen@bergzand.net>
*
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#define MAIN_QUEUE_SIZE (8U)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];
int main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("Test application for the USBUS CDC ECM interface\n");
puts("This test pulls in parts of the GNRC network stack, use the\n"
"provided shell commands (i.e. ifconfig, ping6) to interact with\n"
"the CDC ECM based network interface.\n");
/* start shell */
puts("Starting the shell now...");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
/* should be never reached */
return 0;
}