Skip to content

Commit 41e667e

Browse files
pi-anlclaude
andcommitted
shared/tinyusb: Add per-driver runtime USB class control.
This change allows USB class drivers (CDC, MSC, NCM) to be individually enabled/disabled at runtime without requiring TinyUSB submodule changes. Key features: - Classes are always compiled in but only enabled drivers appear in USB descriptors - When MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE disabled: all compiled classes auto-enabled (backward compatibility) - When MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE enabled: only CDC enabled by default, others require explicit activation - New Python API: usb.enable_cdc(), usb.enable_msc(), usb.enable_ncm() - Dynamic descriptor generation based on enabled classes - Proper cleanup of disabled drivers (e.g. NCM networking) Usage: usb = machine.USBDevice() usb.active(False) usb.enable_msc(True) # Enable mass storage usb.enable_ncm(True) # Enable USB networking usb.active(True) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Andrew Leech <[email protected]>
1 parent bcf63fd commit 41e667e

File tree

5 files changed

+367
-11
lines changed

5 files changed

+367
-11
lines changed

extmod/machine_usb_device.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,58 @@ static mp_obj_t usb_device_config(size_t n_args, const mp_obj_t *pos_args, mp_ma
229229
}
230230
static MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_config_obj, 1, usb_device_config);
231231

232+
// Per-class control methods
233+
static mp_obj_t usb_device_enable_cdc(size_t n_args, const mp_obj_t *args) {
234+
mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]);
235+
236+
if (self->active) {
237+
mp_raise_OSError(MP_EINVAL);
238+
}
239+
240+
if (n_args == 1) {
241+
return mp_obj_new_bool(mp_usbd_class_state.cdc_enabled);
242+
} else {
243+
bool enable = mp_obj_is_true(args[1]);
244+
mp_usbd_enable_class_cdc(enable);
245+
return mp_const_none;
246+
}
247+
}
248+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_cdc_obj, 1, 2, usb_device_enable_cdc);
249+
250+
static mp_obj_t usb_device_enable_msc(size_t n_args, const mp_obj_t *args) {
251+
mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]);
252+
253+
if (self->active) {
254+
mp_raise_OSError(MP_EINVAL);
255+
}
256+
257+
if (n_args == 1) {
258+
return mp_obj_new_bool(mp_usbd_class_state.msc_enabled);
259+
} else {
260+
bool enable = mp_obj_is_true(args[1]);
261+
mp_usbd_enable_class_msc(enable);
262+
return mp_const_none;
263+
}
264+
}
265+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_msc_obj, 1, 2, usb_device_enable_msc);
266+
267+
static mp_obj_t usb_device_enable_ncm(size_t n_args, const mp_obj_t *args) {
268+
mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]);
269+
270+
if (self->active) {
271+
mp_raise_OSError(MP_EINVAL);
272+
}
273+
274+
if (n_args == 1) {
275+
return mp_obj_new_bool(mp_usbd_class_state.ncm_enabled);
276+
} else {
277+
bool enable = mp_obj_is_true(args[1]);
278+
mp_usbd_enable_class_ncm(enable);
279+
return mp_const_none;
280+
}
281+
}
282+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_ncm_obj, 1, 2, usb_device_enable_ncm);
283+
232284
static const MP_DEFINE_BYTES_OBJ(builtin_default_desc_dev_obj,
233285
&mp_usbd_builtin_desc_dev, sizeof(tusb_desc_device_t));
234286

@@ -278,6 +330,11 @@ static const mp_rom_map_elem_t usb_device_locals_dict_table[] = {
278330
{ MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&usb_device_active_obj) },
279331
{ MP_ROM_QSTR(MP_QSTR_stall), MP_ROM_PTR(&usb_device_stall_obj) },
280332
{ MP_ROM_QSTR(MP_QSTR_remote_wakeup), MP_ROM_PTR(&usb_remote_wakeup_obj) },
333+
334+
// Per-class control methods
335+
{ MP_ROM_QSTR(MP_QSTR_enable_cdc), MP_ROM_PTR(&usb_device_enable_cdc_obj) },
336+
{ MP_ROM_QSTR(MP_QSTR_enable_msc), MP_ROM_PTR(&usb_device_enable_msc_obj) },
337+
{ MP_ROM_QSTR(MP_QSTR_enable_ncm), MP_ROM_PTR(&usb_device_enable_ncm_obj) },
281338

282339
// Built-in driver constants
283340
{ MP_ROM_QSTR(MP_QSTR_BUILTIN_NONE), MP_ROM_PTR(&mp_type_usb_device_builtin_none) },

shared/tinyusb/mp_usbd.h

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,35 @@ extern void mp_usbd_port_get_serial_number(char *buf);
8080
// length (2 * bytes_len + 1) (including NUL terminator).
8181
void mp_usbd_hex_str(char *out_str, const uint8_t *bytes, size_t bytes_len);
8282

83+
// Per-class runtime enable/disable state
84+
typedef struct {
85+
bool cdc_enabled;
86+
bool msc_enabled;
87+
bool ncm_enabled;
88+
} mp_usbd_class_state_t;
89+
90+
// Global class enable state
91+
extern mp_usbd_class_state_t mp_usbd_class_state;
92+
93+
// Functions to control USB classes via bitfield flags
94+
void mp_usbd_update_class_state(uint8_t flags);
95+
void mp_usbd_init_class_state(void);
96+
97+
// Initialise TinyUSB device.
98+
static inline void mp_usbd_init_tud(void) {
99+
// Initialize class state before TinyUSB init
100+
mp_usbd_init_class_state();
101+
102+
tusb_init();
103+
#if MICROPY_HW_USB_CDC
104+
tud_cdc_configure_fifo_t cfg = { .rx_persistent = 0, .tx_persistent = 1 };
105+
tud_cdc_configure_fifo(&cfg);
106+
#endif
107+
}
108+
109+
// Get dynamic descriptor length based on enabled classes
110+
size_t mp_usbd_get_descriptor_cfg_len(void);
111+
83112
// Length of built-in configuration descriptor
84113
#define MP_USBD_BUILTIN_DESC_CFG_LEN ( \
85114
(CFG_TUD_CDC ? (TUD_CDC_DESC_LEN) : 0) + \
@@ -102,6 +131,7 @@ const char *mp_usbd_runtime_string_cb(uint8_t index);
102131
// Maximum number of pending exceptions per single TinyUSB task execution
103132
#define MP_USBD_MAX_PEND_EXCS 2
104133

134+
// Full runtime USB device structure
105135
typedef struct {
106136
mp_obj_base_t base;
107137

@@ -136,6 +166,23 @@ typedef struct {
136166
mp_obj_t pend_excs[MP_USBD_MAX_PEND_EXCS];
137167
} mp_obj_usb_device_t;
138168

169+
#else // Static USBD drivers only
170+
171+
// Minimal USB device structure for static mode (builtin_driver control only)
172+
typedef struct {
173+
mp_obj_base_t base;
174+
mp_obj_t builtin_driver; // Points to one of mp_type_usb_device_builtin_nnn
175+
bool active; // Has the user set the USB device active?
176+
} mp_obj_usb_device_t;
177+
178+
static inline void mp_usbd_init(void) {
179+
// Without runtime USB support, this can be a thin wrapper wrapper around tusb_init()
180+
// which is called in the below helper function.
181+
mp_usbd_init_tud();
182+
}
183+
184+
#endif
185+
139186
// Built-in constant objects, possible values of builtin_driver
140187
//
141188
// (Currently not possible to change built-in drivers at runtime, just enable/disable.)
@@ -147,16 +194,6 @@ inline static bool mp_usb_device_builtin_enabled(const mp_obj_usb_device_t *usbd
147194
return usbd->builtin_driver != MP_OBJ_FROM_PTR(&mp_type_usb_device_builtin_none);
148195
}
149196

150-
#else // Static USBD drivers only
151-
152-
static inline void mp_usbd_init(void) {
153-
// Without runtime USB support, this can be a thin wrapper wrapper around tusb_init()
154-
// which is called in the below helper function.
155-
mp_usbd_init_tud();
156-
}
157-
158-
#endif
159-
160197
#endif // MICROPY_HW_ENABLE_USBDEV
161198

162199
#endif // MICROPY_INCLUDED_SHARED_TINYUSB_USBD_H

0 commit comments

Comments
 (0)