Ethernet Communication Device Solution

Overview

The Ethernet Control Model (ECM) within the USB Communication Device Class (CDC) defines a standard protocol for transmitting Ethernet frames over a USB interface. In Device mode, Ameba emulates a standard USB network adapter. When connected, the USB host (another Ameba board, a PC, or any host supporting CDC ECM) loads the driver and recognizes it as a local network interface.

The Ameba platform integrates a USB-IF compliant CDC ECM Device protocol stack, enabling seamless integration with the system’s built-in LwIP network stack. In this configuration, Ameba operates with a static IP and includes a built-in DHCP Server that automatically assigns an IP address to the connected USB host, providing plug-and-play functionality.

Ameba USB CDC ECM Device

Features

  • Emulates a standard USB Ethernet adapter with native host-side support (Linux/macOS require no additional drivers, Windows 10+ has built-in support)

  • Built-in DHCP Server automatically assigns an IP address to the CDC ECM host

  • Device uses a static IP, ensuring stable network addressing for debugging and management scenarios

  • Deep integration with the LwIP protocol stack, abstracted as a standard network interface (Netif)

  • Supports USB hot plugging – automatic re-enumeration and DHCP reassignment after disconnection and reconnection

  • Fully customizable descriptors, including device MAC address, VID/PID, etc.

Application Scenarios

As a USB Device, Ameba presents a virtual Ethernet interface to the connected host and acts as the default gateway, providing network services:

  • Embedded Network Debug Terminal: The CDC ECM host establishes a TCP/IP channel over USB without requiring a dedicated Ethernet port, enabling log monitoring, parameter configuration, and OTA firmware updates.

  • USB Wireless Gateway: Leveraging Ameba’s built-in Wi-Fi module, the USB host accesses the wireless network through Ameba, which also serves as a DHCP Server and network routing gateway.

  • Inter-board High-speed Communication: Two Ameba boards connected directly via USB – one running CDC ECM Device (this solution) and the other running CDC ECM Host – establish a high-speed, low-latency Ethernet channel for multi-module coordination or data passthrough.

Protocol Introduction

The Communication Device Class (CDC) is a general standard defined by the USB specification for communication devices. As a subclass, the Ethernet Control Model (ECM) specifically defines how to encapsulate and transmit Ethernet data frames over a USB interface, enabling a USB device to be recognized by the operating system as a standard Network Interface Controller (NIC).

Protocol Documentation

The USB-IF officially publishes the base CDC class protocol and the ECM subclass specification. Please refer to the following core documents during development:

Specification Type

Document

CDC 1.2 (Communication Class Base Protocol)

Download

ECM 1.2 (Ethernet Control Model)

Included as ECM120.pdf within the CDC 1.2 zip archive above.

Terminology

Common CDC ECM technical terms used in this document are defined as follows:

Term

Description

CCI (Communication Class Interface)

The interface used for managing device connection status, sending control commands, and receiving network status notifications (e.g., cable plug/unplug). It typically utilizes Control and Interrupt endpoints.

DCI (Data Class Interface)

The interface used for the actual transmission of Ethernet packets. It typically utilizes Bulk IN and Bulk OUT endpoints.

Ethernet Frame

The data payload transmitted by CDC ECM, usually following the IEEE 802.3 standard (containing Destination MAC, Source MAC, EtherType, and Payload).

Notification

Asynchronous events actively reported by the device to the host via the Interrupt endpoint, such as NETWORK_CONNECTION (link status change) or CONNECTION_SPEED_CHANGE.

Functional Descriptor

CDC-specific descriptors embedded within the standard configuration descriptor. They describe the specific capabilities of the network device (e.g., MAC address, maximum segment size).

Protocol Framework

The CDC ECM Host protocol stack adopts a layered architecture designed to decouple the USB transport layer from the LwIP network protocol stack.

../../../_images/usb_cdc_ecm_spec_arch.svg

Component Responsibilities

  • Network Application Layer

    Located at the top of the architecture; represents specific network applications.

  • LwIP/Network Stack

    The network stack is unaware of underlying USB details. It operates on an abstract standard network interface (Netif) to provide network services to upper layers, handling TCP/IP, UDP, DHCP, and other network layer logic.

  • CDC ECM Class Driver

    The core middleware implementing the behavior defined by the ECM specification:

    • Enumeration Parsing: Parses CDC functional descriptors to retrieve the MAC address.

    • Control Management: Configures the Packet Filter and handles network status interrupt notifications.

    • Data Tx/Rx: Encapsulates pbufs from the network stack into USB transfer requests (URB/Transfer) and restores received USB packets into Ethernet frames for LwIP.

  • USB Core & HCD (Host Controller Driver)

    The low-level driver responsible for standard USB enumeration, endpoint management, and physical data transmission scheduling (e.g., DMA operations), abstracting hardware controller differences from the class driver.

Communication Mechanism

A standard CDC ECM device typically consists of two USB Interfaces, associated via a Union Functional Descriptor:

Communication Interface (CCI) - Control & Notification

  • Control Transfer

    • Mapped Endpoint: Default Control Endpoint 0.

    • Enumeration & Configuration: Transmits standard USB descriptors and CDC-specific functional descriptors.

    • Class-Specific Requests: Handles ECM protocol control commands. The most critical is SetEthernetPacketFilter, used to configure the device to receive broadcast, multicast, or unicast packets.

  • Interrupt Transfer

    • Interrupt IN: Mandatory. Used by the device to send Notifications to the host.

    • Typical Applications:

      NETWORK_CONNECTION: Reports cable insertion (Link Up) or removal (Link Down) status. CONNECTION_SPEED_CHANGE: Reports current upstream and downstream connection speeds.

Data Interface (DCI) - Bulk Pipes

The Data Interface typically contains two Alternate Settings, which is a key design point of the ECM protocol:

  • Alt Setting 0: Idle Mode. Contains no endpoints. When the network is disconnected or the driver is not loaded, the host should switch the interface to this mode to save bus bandwidth.

  • Alt Setting 1: Active Mode**. Contains a pair of Bulk endpoints (Bulk IN / Bulk OUT).

    • Bulk IN: Device -> Host. Uploads received network data packets.

    • Bulk OUT: Host -> Device. Sends Ethernet frames to be forwarded to the network.

Descriptor Structure

In the standard configuration descriptor, CDC ECM devices use Class-Specific Interface Descriptors to define network capabilities in detail. These are collectively referred to as Functional Descriptors.

CDC ECM Descriptor Topology

Device Descriptor
└── Identifies basic device information (USB Version 2.00)

Configuration Descriptor
├── Contains total length of the entire configuration, power supply information, etc.
│
├── CDC Control (CDC Control) Interface Descriptor (Interface 0)
│       ├── Standard Interface Descriptor (AlternateSetting 0, Control Class)
│       ├── Header Functional Descriptor
│       ├── Union Functional Descriptor
│       ├── Ethernet Networking Functional Descriptor
│       └── Endpoint Descriptor(Interrupt IN)
│
└── CDC Data (CDC-Data) Interface Descriptor (Interface 1)
        ├── Alternate Setting 0: Control transfer active state (control transfer only)
        │
        ├── Alternate Setting 1: Data transfer active state (with data endpoint)
        ├── Endpoint Descriptor(Bulk In)
        └── Endpoint Descriptor(Bulk Out)

Device Qualifier Descriptor
└── Device information while running in another speed mode

Other Speed Configuration Descriptor
├── Configuration information while running in another speed mode.
│
├── CDC Control (CDC Control) Interface Descriptor (Interface 0)
│       ├── Standard Interface Descriptor (AlternateSetting 0, Control Class)
│       ├── Header Functional Descriptor
│       ├── Union Functional Descriptor
│       ├── Ethernet Networking Functional Descriptor
│       └── Endpoint Descriptor(Interrupt IN)
│
└── CDC Data (CDC-Data) Interface Descriptor (Interface 1)
        ├── Alternate Setting 0: Control transfer active state (control transfer only)
        │
        ├── Alternate Setting 1: Data transfer active state (with data endpoint)
        ├── Endpoint Descriptor(Bulk In)
        └── Endpoint Descriptor(Bulk Out)

Functional Descriptors Details

  • Header Functional Descriptor

Header Functional Descriptor
├── bLength (1 byte): Fixed 0x05
├── bDescriptorType (1 byte): 0x24 (CS_INTERFACE)
├── bDescriptorSubtype (1 byte): 0x00 (HEADER)
└── bcdCDC (2 bytes): CDC specification version
  • Union Functional Descriptor

Union Functional Descriptor
├── bLength (1 byte): 0x05 + n (n = number of subordinate interfaces)
├── bDescriptorType (1 byte): 0x24 (CS_INTERFACE)
├── bDescriptorSubtype (1 byte): 0x06 (UNION)
├── bControlInterface (1 byte): Master interface number
└── bSubordinateInterface[n] (n bytes): One or more slave interface numbers
  • Ethernet Networking Functional Descriptor

Ethernet Networking Functional Descriptor
├── bLength (1 byte): Total descriptor length
├── bDescriptorType (1 byte): 0x24 (CS_INTERFACE)
├── bDescriptorSubtype (1 byte): 0x0F
├── iMACAddress (1 byte): MAC address string index
├── bmEthernetStatistics (4 bytes): Supported statistics counters
├── wMaxSegmentSize (2 bytes): Maximum frame size (e.g., 1518)
├── wNumberMCFilters (2 bytes): Multicast filtering capability
└── bNumberPowerFilters (1 byte): Number of wake patterns

Class-Specific Requests

The CDC ECM Host driver controls device behavior by sending the following requests via Control Endpoint 0.

Request

Requirement

Description

SendEncapsulatedCommand

Optional

Sends an encapsulated command.

GetEncapsulatedResponse

Optional

Retrieves an encapsulated response.

SetEthernetMulticastFilters

Optional

Sets the multicast address filter list.

SetEthernetPowerManagementPatternFilter

Optional

Configures power management patterns (e.g., Wake-on-LAN/WoL).

GetEthernetPowerManagementPatternFilter

Optional

Retrieves the power management pattern filter.

SetEthernetPacketFilter

Mandatory

Sets the packet filter.

The host uses this command to notify the device which types of packets to receive (Unicast, Broadcast, Multicast, Promiscuous mode, etc.).

GetEthernetStatistic

Optional

Retrieves device transmission statistics (e.g., dropped packets, error frames).

Data Transmission Format

Data transmission in CDC ECM is straightforward. The USB Payload is the raw Ethernet Frame, without additional header encapsulation (unlike RNDIS or NCM). The figure below shows a full Ethernet frame (1514 bytes) being split into 3 USB packets for transmission.

../../../_images/usb_cdc_ecm_spec_split_ethernet_packet.svg

Note

ZLP (Zero Length Packet)

If the length of the transmitted Ethernet frame is exactly a multiple of the endpoint’s wMaxPacketSize (e.g., 512 bytes in High Speed), the host or device must send a Zero Length Packet (ZLP) to indicate the end of the transfer.

Class Driver

Driver Descriptor Structure

This section describes the CDC ECM class-specific descriptor structures defined in the driver layer, corresponding to the standard descriptor definitions in the USB CDC 1.2 specification.

Device Descriptor
└─ USB Version 2.00 (CDC ECM)

Configuration Descriptor (Interfaces 2)
│
├─ Communication Interface (IF 0, CCI)
│  ├─ Header Functional (CDC 1.20)
│  ├─ Union Functional (Master=0, Slave=1)
│  ├─ Ethernet Networking Functional (MAC Address String Index)
│  └─ Endpoint: INTR IN, maxpkt=16, Interval=8
│
└─ Data Interface (IF 1, DCI)
   ├─ Endpoint: BULK OUT, maxpkt=0x0200 (512 bytes)
   └─ Endpoint: BULK IN,  maxpkt=0x0200 (512 bytes)

Device Qualifier Descriptor
└─ USB 2.0

Other Speed Configuration Descriptor (Interfaces 2)
│
├─ Communication Interface (IF 0, CCI)
│  ├─ Header Functional (CDC 1.20)
│  ├─ Union Functional (Master=0, Slave=1)
│  ├─ Ethernet Networking Functional (MAC Address String Index)
│  └─ Endpoint: INTR IN, maxpkt=16, Interval=8
│
└─ Data Interface (IF 1, DCI)
   ├─ Endpoint: BULK OUT, maxpkt=0x0040 (64 bytes)
   └─ Endpoint: BULK IN,  maxpkt=0x0040 (64 bytes)

Endpoint Configuration

Endpoint

Count

Description

Control IN/OUT

1

EP0, used for handling standard requests and CDC class-specific requests from the host.

Interrupt IN

1

Belongs to the Communication Interface (CCI), used for actively reporting network notifications to the host (e.g., NetworkConnection, SpeedChange).

Bulk OUT

1

Belongs to the Data Interface (DCI), used for receiving Ethernet frames from the host (downstream).

Bulk IN

1

Belongs to the Data Interface (DCI), used for sending Ethernet frames from Ameba to the host (upstream).

Key Data Structures

The driver exposes two main structures:

usbd_cdc_ecm_priv_data_t

Contains device-specific private configuration. The current version includes the following field:

  • mac_value: Pointer to a 6-byte MAC address buffer. The Device side provides this MAC address to the Host via the iMACAddress string index in the descriptor. The Host sets it as the MAC of its local LwIP network interface.

usbd_cdc_ecm_cb_t

Application callback collection. Contains a priv private data pointer and the following event callbacks:

  • init / deinit: Called during driver initialization and deinitialization.

  • setup: Handles class-specific SETUP requests (called in ISR context; do not perform time-consuming operations).

  • received: Called when Bulk OUT data arrives (dispatched from the dedicated RX thread context).

  • status_changed: Called when the USB connection status changes (called in ISR context; do not perform time-consuming operations).

API Reference

Driver API

Application Example

Application Design

This section outlines the complete workflow for the CDC ECM driver, covering driver loading, hotplug management, network link state monitoring, Ethernet frame transmission/reception, and driver unloading.

Loading the Driver

Define the configuration structure, MAC address private data, and callbacks, then call the initialization interfaces in sequence to load the USB Device core driver and the CDC ECM class driver.

Step-by-Step Description:

  • MAC Configuration: Provide a 6-byte device MAC address via usbd_cdc_ecm_priv_data_t.mac_value. This MAC address is advertised to the host through the descriptor and synchronized to the LwIP network interface.

  • Load Core Driver: Call usbd_init() to initialize the USB Device core driver.

  • Load Class Driver: Call usbd_cdc_ecm_init() to register callbacks and start the class driver.

// USB speed
#ifdef CONFIG_SUPPORT_USB_FS_ONLY
#define CONFIG_USBD_CDC_ECM_SPEED   USB_SPEED_FULL
#else
#define CONFIG_USBD_CDC_ECM_SPEED   USB_SPEED_HIGH
#endif

/*
 * Configure hardware-specific FIFO depth and interrupt settings.
 * Values vary by SoC; refer to SDK defaults for your target chip.
 */
static usbd_config_t cdc_ecm_cfg = {
    .speed = CONFIG_USBD_CDC_ECM_SPEED,
    .isr_priority = INT_PRI_MIDDLE,
    .ext_intr_enable = USBD_SOF_INTR,
};

/* Device MAC address (6 bytes). */
static u8 dongle_mac[6] = {0x02, 0x11, 0x22, 0x33, 0x44, 0x55};

/*
 * Private data: supplies the device MAC address to the class driver.
 * The MAC is also exported to the host via the Ethernet Networking
 * Functional descriptor's iMACAddress string.
 */
static usbd_cdc_ecm_priv_data_t ecm_priv = {
    .mac_value = dongle_mac,
};

static usbd_cdc_ecm_cb_t cdc_ecm_cb = {
    .priv = &ecm_priv,
    .init = cdc_ecm_cb_init,                       /* Initialization callback */
    .deinit = cdc_ecm_cb_deinit,                   /* De-initialization callback */
    .setup = cdc_ecm_cb_setup,                     /* Class SETUP request callback */
    .received = cdc_ecm_cb_received,               /* Bulk OUT data received callback */
    .status_changed = cdc_ecm_cb_status_changed,   /* USB attach status change callback */
};

int ret = 0;

/**
 * Initialize USB device core driver.
 * param[in] cfg: USB device hardware configuration.
 * return 0 on success, non-zero on failure.
 */
ret = usbd_init(&cdc_ecm_cfg);
if (ret != HAL_OK) {
    return;
}

/**
 * Initialize CDC ECM class driver with application callbacks.
 * param[in] cb: Pointer to user callback structure (includes priv MAC config).
 * return 0 on success, non-zero on failure.
 */
ret = usbd_cdc_ecm_init(&cdc_ecm_cb);
if (ret != HAL_OK) {
    usbd_deinit();
    return;
}

Hotplug Event Handling

Monitor USB connection status changes (attach/detach) by registering the status_changed callback. It is recommended to use a semaphore to notify a dedicated task thread for processing, avoiding time-consuming operations in interrupt context.

Refer to Device Connection Status Detection for more details. The following is example code:

/* USB attach status change callback — runs in ISR context */
static void cdc_ecm_cb_status_changed(u8 old_status, u8 status)
{
    /*
     * State transitions:
     *   INIT(0) -> ATTACHED(1): cold boot, device enumerated for the first time.
     *   ATTACHED(1) -> DETACHED(2): host disconnects, suspends, or system sleeps.
     *   DETACHED(2) -> ATTACHED(1): hot-plug in, remote wakeup, or host resumes.
     */
    cdc_ecm_attach_status = status;
    cdc_ecm_attach_old_status = old_status;

    if (cdc_ecm_attach_status_changed_sema != NULL) {
        rtos_sema_give(cdc_ecm_attach_status_changed_sema);
    }

    /* Mark link as down immediately on detach so the link-state thread
     * can stop the DHCP server without waiting for the next poll. */
    if (status == USBD_ATTACH_STATUS_DETACHED) {
        cdc_ecm_link_disconnected = 1;
    }
}

/* Hotplug detection thread — runs in task context */
static void cdc_ecm_hotplug_thread(void *param)
{
    int ret = 0;
    UNUSED(param);

    cdc_ecm_hotplug_thread_running = 1;

    while (cdc_ecm_hotplug_thread_running) {
        if (rtos_sema_take(cdc_ecm_attach_status_changed_sema, RTOS_SEMA_MAX_COUNT) != RTK_SUCCESS) {
            continue;
        }

        RTK_LOGS(TAG, RTK_LOG_INFO, "Status change %d -> %d\n", cdc_ecm_attach_old_status, cdc_ecm_attach_status);

        if (cdc_ecm_attach_status == USBD_ATTACH_STATUS_DETACHED) {
            RTK_LOGS(TAG, RTK_LOG_INFO, "DETACHED\n");

            /* Tear down the stack in reverse init order */
            usbd_cdc_ecm_deinit();

            ret = usbd_deinit();
            if (ret != HAL_OK) {
                RTK_LOGS(TAG, RTK_LOG_ERROR, "Deinit core fail %d\n", ret);
                break;
            }

            rtos_time_delay_ms(100);
            RTK_LOGS(TAG, RTK_LOG_INFO, "Free heap 0x%x\n", rtos_mem_get_free_heap_size());

            /* Re-initialize for the next connection */
            ret = usbd_init(&cdc_ecm_cfg);
            if (ret != HAL_OK) {
                break;
            }

            ret = usbd_cdc_ecm_init(&cdc_ecm_cb);
            if (ret != HAL_OK) {
                usbd_deinit();
                break;
            }

            RTK_LOGS(TAG, RTK_LOG_INFO, "Reinit done\n");
        } else if (cdc_ecm_attach_status == USBD_ATTACH_STATUS_ATTACHED) {
            RTK_LOGS(TAG, RTK_LOG_INFO, "Attached\n");
        }
    }

    cdc_ecm_hotplug_thread_running = 0;
    rtos_task_delete(NULL);
}

Ethernet Input Process (RX)

When the host sends Ethernet frames to Ameba, the data arrives through the Bulk OUT endpoint. The class driver’s dedicated RX thread triggers the received callback.

Process Description:

  • USB Reception: The class driver uses a ping-pong buffer (ping-pong rx_buf) mechanism. The ISR places the frame into an idle buffer, and the RX thread passes the buffer pointer to the callback, avoiding memory operations inside the ISR.

  • Deliver to Stack: The callback calls netif_adapter_usb_eth_recv(), which handles pbuf allocation and netif->input() invocation, delivering the Ethernet frame to the LwIP protocol stack for processing.

/*
 * Bulk OUT received callback — called by the class driver when the
 * host sends an ethernet frame to Ameba.
 * Delegates directly to the USB Ethernet netif adapter which allocates
 * a pbuf and hands the frame up to LwIP for IP/ARP processing.
 */
static int cdc_ecm_cb_received(u8 *buf, u32 length)
{
    if (buf == NULL || length == 0) {
        return HAL_ERR_PARA;
    }

    /* netif_adapter_usb_eth_recv() wraps pbuf allocation + netif->input().
     * It must NOT be called from an ISR context. The class driver
     * dispatches this callback from its dedicated RX thread. */
    netif_adapter_usb_eth_recv(buf, length);

    return HAL_OK;
}

Ethernet Output Process (TX)

When the LwIP protocol stack has packets to send to the USB host, it calls the registered linkoutput function, which ultimately invokes usbd_cdc_ecm_transmit().

Process Description:

  • When the block parameter is 1, the function blocks, waiting for the Bulk IN transfer completion semaphore before returning; when 0, it returns immediately.

  • If USB is not connected (bulk_tx_block is set), the function returns safely to prevent deadlock.

/*
 * Transmit an ethernet frame to the host via Bulk IN endpoint.
 * This wrapper is registered as the LwIP netif's linkoutput function
 * by the USB Ethernet adapter layer (rltk_usb_eth_init).
 *
 * param[in] buf:   Pointer to the raw ethernet frame buffer.
 * param[in] len:   Frame length in bytes.
 * param[in] block: 1 = blocking (waits for transfer completion semaphore);
 *                  0 = non-blocking (returns immediately after submit).
 */
int usb_ethernet_transmit(u8 *buf, u32 len, u8 block)
{
    return usbd_cdc_ecm_transmit(buf, len, block);
}

Note

For the complete transmission and reception logic, please refer to the SDK example code: {SDK}/example/usb/usbd_cdc_ecm/example_usbd_cdc_ecm.c.

Driver Unloading

When CDC ECM functionality is no longer needed or during system shutdown, release resources in the reverse order of driver loading.

/* Deinitialize CDC ECM class driver. */
usbd_cdc_ecm_deinit();

/* Deinitialize USB device core driver. */
usbd_deinit();

Usage Example

This section demonstrates Ameba acting as a USB CDC ECM Device connected to another Ameba board, showing how to configure Ameba as a USB Ethernet adapter. The peer device (CDC ECM Host) automatically obtains an IP address and completes network connectivity tests.

The peer device is another Ameba board running the CDC ECM Host example (see CDC ECM Host Solution ).

Example Path: {SDK}/example/usb/usbd_cdc_ecm

Configuration & Compilation

  • Compile and Flash

    Execute the following commands in the SDK root directory to configure the environment, select the target SoC, compile the project, and flash the generated Image file to the development board:

    # Initialize environment (required for every new terminal)
    source env.sh or env.bat(Windows system)
    
    # Select Target SoC (replace xxx with your specific SoC)
    ameba.py soc xxx
    
    ameba.py build -a usbd_cdc_ecm -p
    
  • Menuconfig Configuration

    If compilation fails, run ameba.py menuconfig and confirm that USBD CDC ECM is selected:

    - Choose `CONFIG USB --->`:
    
        [*] Enable USB
            USB Mode (Device)  --->
        [*] CDC ECM
    

Result Verification

  • Start Device

    Reboot the development board. The serial log should display the driver startup information:

    [ECM-I] ECM demo start
    [ECM-I] Enter link status task!
    [ECM-I] Attached
    [ECM-I] USB ECM MAC: 00:11:22:33:44:55
    [ECM-I] Device IP: 192.168.45.1
    [ECM-I] DHCP Server started
    

    Note

    The Attached, Device IP, and DHCP Server started logs appear only after the USB cable is connected.

  • Connect the Peer Device

    Connect the development board’s USB port to the peer host (another Ameba board) using a USB cable. The hardware topology is as follows:

    [Ameba (CDC ECM Device)] ---USB Cable--- [Ameba (CDC ECM Host)]
    
  • Functional Testing

    After successful USB enumeration, the host side automatically obtains an IP address from Ameba’s built-in DHCP Server (subnet 192.168.45.0/24).

    • Ping Test (AT+PING)

      Execute the Ping command on the Ameba serial terminal to send packets to the host IP and verify bidirectional connectivity. See Ping Test for details.

    • Network Bandwidth Test (AT+IPERF)

      Use the iPerf tool to test USB Ethernet channel throughput. See Network Bandwidth Test for details.