This is an automated email from the ASF dual-hosted git repository.

kasjer pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 74806b919f4d3a93088f811ef3119c9c1f272bb5
Author: Jerzy Kasenberg <[email protected]>
AuthorDate: Tue Oct 13 12:45:45 2026 +0200

    tinyusb/std_descriptors: Add CDC-NCM, CDC-ECM and RNDIS function
    
    Add USB network function to standard descriptors, selected with
    USBD_NET choice:
    
     - NCM: CDC Network Control Model (Linux, macOS, Windows 10+)
     - ECM: CDC Ethernet Control Model (Linux, macOS)
     - RNDIS: Remote NDIS (Windows, Linux)
    
    TinyUSB supports only one network function, so the setting is a
    single choice.  Function is placed right after BTH so it is the first
    function on most devices, as Windows expects for RNDIS.  For NCM and
    ECM the host MAC address string is generated from
    tud_network_mac_address, which together with tud_network_* callbacks
    has to be provided by the application.
    
    Endpoints come from port tusb_hw.h unless set in syscfg, and build
    fails when they collide with endpoints of another enabled class.
    Configuration descriptor length is now checked for all configurations,
    not only for audio.
    
    Assisted-by: Claude:claude-opus-5-5
    Signed-off-by: Jerzy Kasenberg <[email protected]>
---
 .../tinyusb/std_descriptors/include/tusb_config.h  | 94 ++++++++++++++++++++++
 .../tinyusb/std_descriptors/src/std_descriptors.c  | 75 +++++++++++++++--
 hw/usb/tinyusb/std_descriptors/syscfg.yml          | 52 ++++++++++++
 3 files changed, 216 insertions(+), 5 deletions(-)

diff --git a/hw/usb/tinyusb/std_descriptors/include/tusb_config.h 
b/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
index 6df605298..ece2b6f8c 100755
--- a/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
+++ b/hw/usb/tinyusb/std_descriptors/include/tusb_config.h
@@ -324,6 +324,100 @@ extern "C" {
 #endif
 #endif /* CFG_TUD_AUDIO */
 
+/* ------------- Network (CDC-NCM, CDC-ECM, RNDIS) ------------- */
+/*
+ * TinyUSB has one driver for ECM and RNDIS and another one for NCM, only
+ * one of them can be enabled.  Note: CFG_TUD_NET is a deprecated TinyUSB
+ * name, USBD_NET_ENABLED is used instead.
+ */
+#if MYNEWT_VAL_CHOICE(USBD_NET, NCM)
+#define CFG_TUD_NCM                     1
+#define CFG_TUD_ECM_RNDIS               0
+#elif MYNEWT_VAL_CHOICE(USBD_NET, ECM) || MYNEWT_VAL_CHOICE(USBD_NET, RNDIS)
+#define CFG_TUD_NCM                     0
+#define CFG_TUD_ECM_RNDIS               1
+#else
+#define CFG_TUD_NCM                     0
+#define CFG_TUD_ECM_RNDIS               0
+#endif
+#define USBD_NET_ENABLED                (CFG_TUD_NCM || CFG_TUD_ECM_RNDIS)
+/* NCM and ECM report host side MAC address in a string descriptor */
+#define USBD_NET_MAC_STRING \
+    (CFG_TUD_NCM || MYNEWT_VAL_CHOICE(USBD_NET, ECM))
+
+#if USBD_NET_ENABLED
+/* Ethernet frame without FCS */
+#define CFG_TUD_NET_MTU                 1514
+
+#if !defined(USBD_NET_NOTIFY_EP) || !defined(USBD_NET_DATA_OUT_EP) || \
+    !defined(USBD_NET_DATA_IN_EP)
+#error "USB port tusb_hw.h does not define network endpoints"
+#endif
+#define USBD_NET_NOTIFY_EP_SIZE         MYNEWT_VAL(USBD_NET_NOTIFY_EP_SIZE)
+
+#if CFG_TUD_NCM
+#if MYNEWT_VAL(USBD_NET_NCM_NTB_SIZE) < 2048
+#error "USBD_NET_NCM_NTB_SIZE must be at least 2048"
+#endif
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE     MYNEWT_VAL(USBD_NET_NCM_NTB_SIZE)
+#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE    MYNEWT_VAL(USBD_NET_NCM_NTB_SIZE)
+#define CFG_TUD_NCM_IN_NTB_N            MYNEWT_VAL(USBD_NET_NCM_NTB_COUNT)
+#define CFG_TUD_NCM_OUT_NTB_N           MYNEWT_VAL(USBD_NET_NCM_NTB_COUNT)
+#endif
+
+/*
+ * Network endpoints must not be used by any other enabled class.  Port
+ * defaults avoid CDC where hardware allows, other combinations may need
+ * USBD_NET_*_EP settings.
+ */
+#define USBD_NET_EP_CLASH(_ep) \
+    ((_ep) == USBD_NET_NOTIFY_EP || (_ep) == USBD_NET_DATA_OUT_EP || \
+     (_ep) == USBD_NET_DATA_IN_EP)
+#if CFG_CDC && (USBD_NET_EP_CLASH(USBD_CDC_NOTIFY_EP) || \
+                USBD_NET_EP_CLASH(USBD_CDC_DATA_OUT_EP) || \
+                USBD_NET_EP_CLASH(USBD_CDC_DATA_IN_EP))
+#error "USBD_NET endpoints collide with CDC endpoints"
+#endif
+#if CFG_CDC_CONSOLE && (USBD_NET_EP_CLASH(USBD_CDC_CONSOLE_NOTIFY_EP) || \
+                        USBD_NET_EP_CLASH(USBD_CDC_CONSOLE_DATA_OUT_EP) || \
+                        USBD_NET_EP_CLASH(USBD_CDC_CONSOLE_DATA_IN_EP))
+#error "USBD_NET endpoints collide with CDC console endpoints"
+#endif
+#if CFG_CDC_HCI && (USBD_NET_EP_CLASH(USBD_CDC_HCI_NOTIFY_EP) || \
+                    USBD_NET_EP_CLASH(USBD_CDC_HCI_DATA_OUT_EP) || \
+                    USBD_NET_EP_CLASH(USBD_CDC_HCI_DATA_IN_EP))
+#error "USBD_NET endpoints collide with CDC HCI endpoints"
+#endif
+#if CFG_TUD_HID && USBD_NET_EP_CLASH(USBD_HID_REPORT_EP)
+#error "USBD_NET endpoints collide with HID endpoint"
+#endif
+#if CFG_TUD_MSC && (USBD_NET_EP_CLASH(USBD_MSC_DATA_OUT_EP) || \
+                    USBD_NET_EP_CLASH(USBD_MSC_DATA_IN_EP))
+#error "USBD_NET endpoints collide with MSC endpoints"
+#endif
+#if CFG_TUD_BTH && (USBD_NET_EP_CLASH(USBD_BTH_EVENT_EP) || \
+                    USBD_NET_EP_CLASH(USBD_BTH_DATA_OUT_EP) || \
+                    USBD_NET_EP_CLASH(USBD_BTH_DATA_IN_EP) || \
+                    USBD_NET_EP_CLASH(USBD_BTH_DATA_OUT_EP + 1) || \
+                    USBD_NET_EP_CLASH(USBD_BTH_DATA_IN_EP + 1))
+#error "USBD_NET endpoints collide with BTH endpoints"
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT && USBD_NET_EP_CLASH(USBD_AUDIO_OUT_EP)
+#error "USBD_NET endpoints collide with audio OUT endpoint"
+#endif
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && USBD_NET_EP_CLASH(USBD_AUDIO_IN_EP)
+#error "USBD_NET endpoints collide with audio IN endpoint"
+#endif
+#if CFG_TUD_AUDIO && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP && \
+    USBD_NET_EP_CLASH(USBD_AUDIO_FEEDBACK_EP)
+#error "USBD_NET endpoints collide with audio feedback endpoint"
+#endif
+#if CFG_TUD_AUDIO && CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP && \
+    USBD_NET_EP_CLASH(USBD_AUDIO_INT_EP)
+#error "USBD_NET endpoints collide with audio interrupt endpoint"
+#endif
+#endif /* USBD_NET_ENABLED */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c 
b/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
index 0d1f4eeb7..793d57cef 100755
--- a/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
+++ b/hw/usb/tinyusb/std_descriptors/src/std_descriptors.c
@@ -79,9 +79,20 @@ enum usb_desc_ix {
 #else
 #define AUDIO_IF_STR_IX 0
 #endif
+#if defined MYNEWT_VAL_USBD_NET_DESCRIPTOR_STRING
+    NET_IF_STR_IX,
+#else
+#define NET_IF_STR_IX   0
+#endif
 #if defined MYNEWT_VAL_USBD_DFU_SLOT_NAME
     DFU_SLOT_NAME_IF_STR_IX,
 #endif
+#if USBD_NET_MAC_STRING
+    /* Generated from tud_network_mac_address, not in string_desc_arr */
+    NET_MAC_STR_IX,
+#else
+#define NET_MAC_STR_IX  0
+#endif
 };
 
 #if MYNEWT_VAL(USBD_CONFIGURATION_SELF_POWERED)
@@ -214,9 +225,9 @@ tusb_desc_device_qualifier_t const desc_device_qualifier = {
     .bDeviceClass       = TUD_BT_APP_CLASS,
     .bDeviceSubClass    = TUD_BT_APP_SUBCLASS,
     .bDeviceProtocol    = TUD_BT_PROTOCOL_PRIMARY_CONTROLLER,
-#elif CFG_TUD_CDC || CFG_TUD_AUDIO
+#elif CFG_TUD_CDC || CFG_TUD_AUDIO || USBD_NET_ENABLED
     /*
-     * Use Interface Association Descriptor (IAD) for CDC and Audio
+     * Use Interface Association Descriptor (IAD) for CDC, Audio and Network
      * As required by USB Specs IAD's subclass must be common class (2) and 
protocol must be IAD (1)
      */
     .bDeviceClass       = TUSB_CLASS_MISC,
@@ -269,6 +280,12 @@ enum {
 #endif
 #endif
 
+#if USBD_NET_ENABLED
+    /* Windows expects RNDIS to be the first function */
+    ITF_NUM_NET,
+    ITF_NUM_NET_DATA,
+#endif
+
 #if CFG_CDC
     ITF_NUM_CDC,
     ITF_NUM_CDC_DATA,
@@ -313,7 +330,24 @@ enum {
     ITF_NUM_TOTAL
 };
 
+#if CFG_TUD_NCM
+#define USBD_NET_DESC_LEN   TUD_CDC_NCM_DESC_LEN
+#elif MYNEWT_VAL_CHOICE(USBD_NET, ECM)
+#define USBD_NET_DESC_LEN   TUD_CDC_ECM_DESC_LEN
+#elif MYNEWT_VAL_CHOICE(USBD_NET, RNDIS)
+#define USBD_NET_DESC_LEN   TUD_RNDIS_DESC_LEN
+#else
+#define USBD_NET_DESC_LEN   0
+#endif
+
+#define USBD_NET_DATA_EP_SIZE \
+    ((USBD_RHPORT_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64)
+/* NCM notification interval: 32 ms at high speed (2^(9-1) * 125 us) */
+#define USBD_NET_NOTIFY_INTERVAL \
+    ((USBD_RHPORT_MODE & OPT_MODE_HIGH_SPEED) ? 9 : 50)
+
 #define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + \
+                             USBD_NET_DESC_LEN + \
                              CFG_CDC * TUD_CDC_DESC_LEN + \
                              CFG_CDC_CONSOLE * TUD_CDC_DESC_LEN + \
                              CFG_CDC_HCI * TUD_CDC_DESC_LEN + \
@@ -336,6 +370,25 @@ const uint8_t desc_configuration[] = {
                        0, 9, 17, 25, 33, 49),
 #endif
 
+#if CFG_TUD_NCM
+    TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_NET, NET_IF_STR_IX, NET_MAC_STR_IX,
+                           USBD_NET_NOTIFY_EP, USBD_NET_NOTIFY_EP_SIZE,
+                           USBD_NET_DATA_OUT_EP, USBD_NET_DATA_IN_EP,
+                           USBD_NET_DATA_EP_SIZE, CFG_TUD_NET_MTU,
+                           USBD_NET_NOTIFY_INTERVAL,
+                           NCM_NETWORK_CAPS_ETH_FILTER |
+                           NCM_NETWORK_CAPS_NTB_INPUT_SIZE),
+#elif MYNEWT_VAL_CHOICE(USBD_NET, ECM)
+    TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_NET, NET_IF_STR_IX, NET_MAC_STR_IX,
+                           USBD_NET_NOTIFY_EP, USBD_NET_NOTIFY_EP_SIZE,
+                           USBD_NET_DATA_OUT_EP, USBD_NET_DATA_IN_EP,
+                           USBD_NET_DATA_EP_SIZE, CFG_TUD_NET_MTU),
+#elif MYNEWT_VAL_CHOICE(USBD_NET, RNDIS)
+    TUD_RNDIS_DESCRIPTOR(ITF_NUM_NET, NET_IF_STR_IX, USBD_NET_NOTIFY_EP,
+                         USBD_NET_NOTIFY_EP_SIZE, USBD_NET_DATA_OUT_EP,
+                         USBD_NET_DATA_IN_EP, USBD_NET_DATA_EP_SIZE),
+#endif
+
 #if CFG_CDC_CONSOLE
     TUD_CDC_DESCRIPTOR(ITF_NUM_CDC_CONSOLE, CDC_CONSOLE_IF_STR_IX, 
USBD_CDC_CONSOLE_NOTIFY_EP,
                        USBD_CDC_CONSOLE_NOTIFY_EP_SIZE, 
USBD_CDC_CONSOLE_DATA_OUT_EP, USBD_CDC_CONSOLE_DATA_IN_EP,
@@ -380,11 +433,9 @@ const uint8_t desc_configuration[] = {
 #endif
 };
 
-#if CFG_TUD_AUDIO
-/* Audio descriptor has many parts, make sure declared length matches. */
+/* Descriptor is composed from many parts, check declared length. */
 TU_VERIFY_STATIC(sizeof(desc_configuration) == CONFIG_TOTAL_LEN,
                  "Configuration descriptor length mismatch");
-#endif
 
 /**
  * Invoked when received GET CONFIGURATION DESCRIPTOR
@@ -423,6 +474,9 @@ const char *string_desc_arr[] = {
 #if defined MYNEWT_VAL_USBD_AUDIO_DESCRIPTOR_STRING
     MYNEWT_VAL(USBD_AUDIO_DESCRIPTOR_STRING),
 #endif
+#if defined MYNEWT_VAL_USBD_NET_DESCRIPTOR_STRING
+    MYNEWT_VAL(USBD_NET_DESCRIPTOR_STRING),
+#endif
 #if defined MYNEWT_VAL_USBD_DFU_SLOT_NAME
     MYNEWT_VAL(USBD_DFU_SLOT_NAME),
 #endif
@@ -557,6 +611,17 @@ tud_descriptor_string_cb(uint8_t index, uint16_t langid)
         /* TODO: Add function call to get serial number */
         desc_string[1] = '1';
         char_num = 1;
+#if USBD_NET_MAC_STRING
+    } else if (index == NET_MAC_STR_IX) {
+        /* Host side MAC address as 12 hex digits, e.g. 02A1B2C3D4E5 */
+        for (i = 0; i < 6; ++i) {
+            desc_string[1 + 2 * i] =
+                "0123456789ABCDEF"[tud_network_mac_address[i] >> 4];
+            desc_string[2 + 2 * i] =
+                "0123456789ABCDEF"[tud_network_mac_address[i] & 0x0F];
+        }
+        char_num = 12;
+#endif
     } else if (index - 2 < ARRAY_SIZE(string_desc_arr)) {
         str = string_desc_arr[index - 2];
 
diff --git a/hw/usb/tinyusb/std_descriptors/syscfg.yml 
b/hw/usb/tinyusb/std_descriptors/syscfg.yml
index bd8ec07b3..0ad95a3fa 100644
--- a/hw/usb/tinyusb/std_descriptors/syscfg.yml
+++ b/hw/usb/tinyusb/std_descriptors/syscfg.yml
@@ -337,6 +337,56 @@ syscfg.defs:
             Size of TinyUSB software FIFO for speaker data in 1 ms packets.
         value: 4
 
+    USBD_NET:
+        description: >
+            Enable USB network function in TinyUSB stack.
+            NCM - CDC Network Control Model (Linux, macOS, Windows 10+),
+            ECM - CDC Ethernet Control Model (Linux, macOS),
+            RNDIS - Remote NDIS (Windows, Linux),
+            empty or NONE - no network function.
+            Only one network function can be present.  The function uses
+            Interface Association Descriptor so USBD_COMPOSITE_DEVICE has
+            to be set.  Application (e.g. hw/usb/tinyusb/net_lwip) provides
+            TinyUSB network callbacks (tud_network_*) and
+            tud_network_mac_address.
+        value:
+        choices:
+            - NONE
+            - NCM
+            - ECM
+            - RNDIS
+    USBD_NET_DESCRIPTOR_STRING:
+        description: String for network control interface
+        value:
+    USBD_NET_NOTIFY_EP:
+        description: >
+            Network notification (interrupt IN) endpoint address
+            (e.g. 0x85).  Empty for USB port default from tusb_hw.h.
+        value:
+    USBD_NET_NOTIFY_EP_SIZE:
+        description: Network notification endpoint size
+        value: 16
+    USBD_NET_DATA_OUT_EP:
+        description: >
+            Network data (bulk OUT) endpoint address (e.g. 0x07).
+            Empty for USB port default from tusb_hw.h.
+        value:
+    USBD_NET_DATA_IN_EP:
+        description: >
+            Network data (bulk IN) endpoint address (e.g. 0x87).
+            Empty for USB port default from tusb_hw.h.
+        value:
+    USBD_NET_NCM_NTB_SIZE:
+        description: >
+            Size of NCM transfer block buffers (NTB) in both directions.
+            CDC-NCM requires at least 2048.
+        value: 2048
+    USBD_NET_NCM_NTB_COUNT:
+        description: >
+            Number of NCM transfer block buffers in each direction.  On full
+            speed 2 can improve throughput at the cost of RAM.
+        value: 1
+
     USBD_WINDOWS_COMP_ID:
         description: >
             Add Windows compatibility descriptor.
@@ -359,6 +409,8 @@ syscfg.defs:
 
 syscfg.restrictions:
     - '(CFG_TUSB_DEBUG == 0) || (USBD_STACK_SIZE >= 400)'
+    - '((USBD_NET != "NCM") && (USBD_NET != "ECM") && (USBD_NET != "RNDIS")) ||
+       USBD_COMPOSITE_DEVICE'
 
 syscfg.vals.USBD_COMPOSITE_DEVICE:
     USBD_DEVICE_CLASS: TUSB_CLASS_MISC

Reply via email to