Skip to content

shared/tinyusb, extmod: Add USB Network (NCM) with runtime class selection.#31

Draft
andrewleech wants to merge 12 commits intoreview/stm32_tinyusb_ncmfrom
stm32_tinyusb_ncm
Draft

shared/tinyusb, extmod: Add USB Network (NCM) with runtime class selection.#31
andrewleech wants to merge 12 commits intoreview/stm32_tinyusb_ncmfrom
stm32_tinyusb_ncm

Conversation

@andrewleech
Copy link
Owner

Summary

This is a rewrite of micropython#16459 on a current master base. The STM32 TinyUSB enablement commits from that branch have since landed, so this one focuses on the NCM networking layer and the runtime USB class selection mechanism.

The core addition is network.USB_NET - an NCM network interface that lets a host computer see the MicroPython device as a USB Ethernet adapter. The device runs a DHCP server and optionally mDNS, so the host gets an IP and can reach the device immediately after plug-in. The driver integrates with lwIP via the standard netif interface.

USB class selection is handled through integer flag constants (BUILTIN_FLAG_CDC, BUILTIN_FLAG_MSC, BUILTIN_FLAG_NCM) that can be OR'd together and assigned to machine.USBDevice.builtin_driver in boot.py. A dynamic configuration descriptor is built at runtime containing only the selected classes, with compact interface/endpoint numbering. The PID is varied per combination so the host re-enumerates correctly. BUILTIN_DEFAULT is now CDC-only - MSC and NCM are opt-in.

Port Makefiles for alif, mimxrt, renesas-ra, and samd are refactored to use TinyUSB's upstream tinyusb.mk instead of hand-maintained file lists. NCM is auto-enabled on any port with lwIP and TinyUSB.

Testing

Built for NUCLEO_F429ZI (stm32 with TinyUSB + lwIP). NCM interface enumerates on Linux and macOS hosts. DHCP assigns an address and the device is reachable over USB. Flag-mask class selection tested with CDC-only, CDC+NCM, and CDC+MSC+NCM combinations - host sees distinct devices and re-enumerates on each change.

Not tested: mimxrt, renesas-ra, samd, rp2 (build-tested only via the auto-enable logic). The descriptor patch offset tables for the dynamic builder have not been verified against TinyUSB versions other than the current submodule.

Trade-offs and Alternatives

The descriptor builder uses hardcoded byte-offset patch tables derived from TinyUSB's macro-generated descriptors. This is fragile across TinyUSB updates but unavoidable without TinyUSB exposing a programmatic descriptor API. The offset tables include a comment noting this dependency.

The previous usbd_net branch used an opaque C type with an internal flags byte for class selection, requiring casting hacks in the descriptor callback. The integer flag approach here is simpler at both the Python and C level.

Generative AI

I used generative AI tools when creating this PR, but a human has checked the code and is responsible for the description above.

@codecov-commenter
Copy link

codecov-commenter commented Mar 22, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.46%. Comparing base (5c00edc) to head (7d1a1ed).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@                    Coverage Diff                    @@
##           review/stm32_tinyusb_ncm      #31   +/-   ##
=========================================================
  Coverage                     98.46%   98.46%           
=========================================================
  Files                           176      176           
  Lines                         22784    22784           
=========================================================
  Hits                          22435    22435           
  Misses                          349      349           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

pi-anl added 3 commits March 23, 2026 09:39
Provides atoi() function implementation required by lwIP's netif_find()
function when using string-based network interface lookup.

Based on (MIT) implementation from https://github.com/littlekernel/lk

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Consolidates TinyUSB source includes across multiple ports (alif, mimxrt,
renesas-ra, samd) to use a common approach for TinyUSB integration.

This cleanup ensures consistent TinyUSB usage patterns across ports
and simplifies maintenance.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Adds TinyUSB configuration and descriptor support for USB Network
Control Model (NCM) interface. This provides the low-level USB
infrastructure needed for USB networking.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
@github-actions
Copy link

github-actions bot commented Mar 22, 2026

Code size report:

Reference:  tools/ci.sh: Increase qemu_arm test run timeout. [5c00edc]
Comparison: stm32: Fix mboot build with TinyUSB-enabled boards. [merge of 7d1a1ed]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2: +13668 +1.485% RPI_PICO_W[incl +10552(bss)]
       samd:  +504 +0.183% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@andrewleech andrewleech force-pushed the stm32_tinyusb_ncm branch 3 times, most recently from fb352c8 to f04861e Compare March 23, 2026 15:21
pi-anl added 9 commits March 24, 2026 09:19
When false, the DHCP server omits the Router option from DHCP
responses. This prevents point-to-point links (like USB NCM) from
advertising a default gateway that hijacks the host's internet route.

Defaults to true for backward compatibility with CYW43 AP mode.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Implements a complete USB Network Control Model (NCM) driver that
provides ethernet-over-USB functionality integrated with lwIP stack.

Features:
- Link-local IP address generation from MAC using CRC32
- DHCP server integration with connect callbacks
- Full lwIP network interface with IPv6 support
- USB NCM protocol handling via TinyUSB
- Python network.USB_NET class interface

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Automatically enable MICROPY_HW_NETWORK_USBNET when MICROPY_PY_LWIP
is enabled, following the same pattern as WEBSOCKET and WEBREPL.

This makes USB networking available by default on boards that support
both USB device mode and LWIP networking, while still allowing boards
to explicitly disable it by setting MICROPY_HW_NETWORK_USBNET=0.

Also fixes USB NCM build issues when LWIP is enabled for ethernet by
making MICROPY_HW_NETWORK_USBNET conditional on both LWIP and TinyUSB.

Signed-off-by: Andrew Leech <andrew@alelec.net>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Add builtin_driver flag constants (BUILTIN_FLAG_CDC, BUILTIN_FLAG_MSC,
BUILTIN_FLAG_NCM) that can be OR'd together to select USB classes at
runtime from boot.py. A dynamic configuration descriptor is built
containing only the selected classes with compact interface/endpoint
numbering.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
usbd_conf.c unconditionally includes shared/tinyusb/mp_usbd.h which
pulls in tusb.h when MICROPY_HW_ENABLE_USBDEV is set. The
BUILDING_MBOOT guard that disables TinyUSB comes after the include,
so mboot builds fail with missing tusb.h. Guard the include with
!BUILDING_MBOOT.

Also add forward declaration of mp_usbd_ll_init() next to the
MICROPY_HW_TINYUSB_LL_INIT macro in mpconfigboard_common.h, since
the function is used in an inline function in mp_usbd.h but only
declared in the port-specific usbd_conf.h.

Signed-off-by: Andrew Leech <andrew.leech@planetinnovation.com.au>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants