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:
commit
ea36d68703
@ -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
|
||||
|
@ -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();
|
||||
|
59
sys/auto_init/netif/auto_init_cdcecm.c
Normal file
59
sys/auto_init/netif/auto_init_cdcecm.c
Normal 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 */
|
||||
/** @} */
|
@ -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
246
sys/include/usb/cdc.h
Normal 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 */
|
||||
/** @} */
|
@ -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 */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
129
sys/include/usb/usbus/cdc/ecm.h
Normal file
129
sys/include/usb/usbus/cdc/ecm.h
Normal 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 */
|
||||
/** @} */
|
@ -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
|
||||
|
3
sys/usb/usbus/cdc/ecm/Makefile
Normal file
3
sys/usb/usbus/cdc/ecm/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE = usbus_cdc_ecm
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
373
sys/usb/usbus/cdc/ecm/cdc_ecm.c
Normal file
373
sys/usb/usbus/cdc/ecm/cdc_ecm.c
Normal 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;
|
||||
}
|
||||
}
|
199
sys/usb/usbus/cdc/ecm/cdc_ecm_netdev.c
Normal file
199
sys/usb/usbus/cdc/ecm/cdc_ecm_netdev.c
Normal 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,
|
||||
};
|
28
tests/usbus_cdc_ecm/Makefile
Normal file
28
tests/usbus_cdc_ecm/Makefile
Normal 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
|
25
tests/usbus_cdc_ecm/README.md
Normal file
25
tests/usbus_cdc_ecm/README.md
Normal 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.
|
46
tests/usbus_cdc_ecm/main.c
Normal file
46
tests/usbus_cdc_ecm/main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user