Skip to content

Commit 9250bb5

Browse files
committed
shared/tinyusb: Optimize descriptor generation with compile-time conditionals.
Replaces runtime loop-based descriptor size calculations with compile-time #if CFG_TUD_* conditionals. Descriptor lengths (TUD_CDC_DESC_LEN, etc.) are compile-time constants, so runtime loops are unnecessary overhead. Functions moved from mp_usbd_descriptor.c to mp_usbd.h as static inline to eliminate call overhead while using preprocessor conditionals to prevent code duplication when classes aren't compiled in. This maintains zero overhead for static mode while minimizing overhead in runtime mode compared to previous loop-based implementation. Signed-off-by: Andrew Leech <[email protected]>
1 parent 6ea7550 commit 9250bb5

File tree

4 files changed

+121
-87
lines changed

4 files changed

+121
-87
lines changed

extmod/machine_usb_device.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,9 @@ static uint8_t mp_usbd_get_str_max(uint8_t flags) {
429429
}
430430

431431
static const uint8_t *mp_usbd_get_builtin_desc_cfg(uint8_t flags) {
432-
extern const uint8_t *mp_usbd_generate_desc_cfg_from_flags(uint8_t flags);
433-
return mp_usbd_generate_desc_cfg_from_flags(flags);
432+
extern uint8_t mp_usbd_desc_cfg_buffer[];
433+
extern const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *buffer);
434+
return mp_usbd_generate_desc_cfg_unified(flags, mp_usbd_desc_cfg_buffer);
434435
}
435436

436437
static size_t mp_usbd_get_desc_cfg_len(uint8_t flags) {

shared/tinyusb/mp_usbd.h

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,32 +65,25 @@ typedef struct {
6565
// Global class enable state
6666
extern mp_usbd_class_state_t mp_usbd_class_state;
6767

68-
// Functions to control USB classes via bitfield flags
69-
void mp_usbd_update_class_state(uint8_t flags);
70-
void mp_usbd_init_class_state(void);
68+
// Descriptor template entry for lookup table
69+
typedef struct {
70+
uint8_t flag_mask; // USB_BUILTIN_FLAG_* to check
71+
uint8_t desc_len; // Length of descriptor in bytes
72+
uint8_t itf_count; // Number of interfaces used
73+
const uint8_t *template; // Descriptor bytes (0xFF = interface number placeholder)
74+
} usb_desc_entry_t;
7175

72-
// Initialise TinyUSB device.
73-
static inline void mp_usbd_init_tud(void) {
74-
// Initialize class state before TinyUSB init
75-
mp_usbd_init_class_state();
76+
// External reference to descriptor lookup table for inline functions
77+
extern const usb_desc_entry_t usb_desc_table[];
7678

77-
tusb_init();
78-
#if MICROPY_HW_USB_CDC
79-
tud_cdc_configure_fifo_t cfg = { .rx_persistent = 0,
80-
.tx_persistent = 1,
79+
// Number of entries in usb_desc_table
80+
#define USB_DESC_TABLE_SIZE ((CFG_TUD_CDC ? 1 : 0) + (CFG_TUD_MSC ? 1 : 0))
8181

82-
// This config flag is unreleased in TinyUSB >v0.18.0
83-
// but included in Espressif's TinyUSB component since v0.18.0~3
84-
//
85-
// Versioning issue reported as
86-
// https://github.com/espressif/esp-usb/issues/236
87-
#if TUSB_VERSION_NUMBER > 1800 || defined(ESP_PLATFORM)
88-
.tx_overwritabe_if_not_connected = 1,
89-
#endif
90-
};
91-
tud_cdc_configure_fifo(&cfg);
92-
#endif
93-
}
82+
// Descriptor info structure for combined length/count calculation
83+
typedef struct {
84+
size_t length;
85+
uint8_t interface_count;
86+
} usb_desc_info_t;
9487

9588
// Allow runtime override of VID/PID defaults
9689
#ifndef MICROPY_HW_USB_RUNTIME_VID
@@ -110,6 +103,97 @@ extern const mp_obj_type_t mp_type_usb_builtin;
110103
#define USB_BUILTIN_FLAG_MSC 0x02
111104
#define USB_BUILTIN_FLAG_NCM 0x04
112105

106+
// Initialize class state based on compile-time configuration and runtime mode
107+
static inline void mp_usbd_init_class_state(void) {
108+
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
109+
// In runtime mode, only CDC enabled by default
110+
mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1);
111+
mp_usbd_class_state.msc_enabled = false;
112+
#else
113+
// In static mode, enable all compiled classes
114+
mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1);
115+
mp_usbd_class_state.msc_enabled = (CFG_TUD_MSC == 1);
116+
#endif
117+
}
118+
119+
// Update class state based on bitfield flags
120+
static inline void mp_usbd_update_class_state(uint8_t flags) {
121+
mp_usbd_class_state.cdc_enabled = (flags & USB_BUILTIN_FLAG_CDC) && (CFG_TUD_CDC == 1);
122+
mp_usbd_class_state.msc_enabled = (flags & USB_BUILTIN_FLAG_MSC) && (CFG_TUD_MSC == 1);
123+
}
124+
125+
// Calculate descriptor length from flags using compile-time conditionals
126+
static inline size_t mp_usbd_get_descriptor_cfg_len_from_flags(uint8_t flags) {
127+
size_t len = TUD_CONFIG_DESC_LEN;
128+
#if CFG_TUD_CDC
129+
if (flags & USB_BUILTIN_FLAG_CDC) {
130+
len += TUD_CDC_DESC_LEN;
131+
}
132+
#endif
133+
#if CFG_TUD_MSC
134+
if (flags & USB_BUILTIN_FLAG_MSC) {
135+
len += TUD_MSC_DESC_LEN;
136+
}
137+
#endif
138+
return len;
139+
}
140+
141+
// Calculate interface count from flags using compile-time conditionals
142+
static inline uint8_t mp_usbd_get_interface_count_from_flags(uint8_t flags) {
143+
uint8_t count = 0;
144+
#if CFG_TUD_CDC
145+
if (flags & USB_BUILTIN_FLAG_CDC) {
146+
count += 2; // CDC uses 2 interfaces
147+
}
148+
#endif
149+
#if CFG_TUD_MSC
150+
if (flags & USB_BUILTIN_FLAG_MSC) {
151+
count += 1; // MSC uses 1 interface
152+
}
153+
#endif
154+
return count;
155+
}
156+
157+
// Combined descriptor info calculation using compile-time conditionals
158+
static inline usb_desc_info_t mp_usbd_get_desc_info_from_flags(uint8_t flags) {
159+
usb_desc_info_t info = { .length = TUD_CONFIG_DESC_LEN, .interface_count = 0 };
160+
#if CFG_TUD_CDC
161+
if (flags & USB_BUILTIN_FLAG_CDC) {
162+
info.length += TUD_CDC_DESC_LEN;
163+
info.interface_count += 2;
164+
}
165+
#endif
166+
#if CFG_TUD_MSC
167+
if (flags & USB_BUILTIN_FLAG_MSC) {
168+
info.length += TUD_MSC_DESC_LEN;
169+
info.interface_count += 1;
170+
}
171+
#endif
172+
return info;
173+
}
174+
175+
// Initialise TinyUSB device
176+
static inline void mp_usbd_init_tud(void) {
177+
// Initialize class state before TinyUSB init
178+
mp_usbd_init_class_state();
179+
180+
tusb_init();
181+
#if MICROPY_HW_USB_CDC
182+
tud_cdc_configure_fifo_t cfg = { .rx_persistent = 0,
183+
.tx_persistent = 1,
184+
185+
// This config flag is unreleased in TinyUSB >v0.18.0
186+
// but included in Espressif's TinyUSB component since v0.18.0~3
187+
//
188+
// Versioning issue reported as
189+
// https://github.com/espressif/esp-usb/issues/236
190+
#if TUSB_VERSION_NUMBER > 1800 || defined(ESP_PLATFORM)
191+
.tx_overwritabe_if_not_connected = 1,
192+
#endif
193+
};
194+
tud_cdc_configure_fifo(&cfg);
195+
#endif
196+
}
113197

114198
// Get dynamic descriptor length based on enabled classes
115199
size_t mp_usbd_get_descriptor_cfg_len(void);

shared/tinyusb/mp_usbd_descriptor.c

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -58,33 +58,6 @@ const tusb_desc_device_t mp_usbd_builtin_desc_dev = {
5858
// Global class enable state
5959
mp_usbd_class_state_t mp_usbd_class_state;
6060

61-
// Initialize class state based on compile-time configuration and runtime mode
62-
void mp_usbd_init_class_state(void) {
63-
#if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
64-
// In runtime mode, only CDC enabled by default
65-
mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1);
66-
mp_usbd_class_state.msc_enabled = false;
67-
#else
68-
// In static mode, enable all compiled classes
69-
mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1);
70-
mp_usbd_class_state.msc_enabled = (CFG_TUD_MSC == 1);
71-
#endif
72-
}
73-
74-
// Update class state based on bitfield flags
75-
void mp_usbd_update_class_state(uint8_t flags) {
76-
mp_usbd_class_state.cdc_enabled = (flags & USB_BUILTIN_FLAG_CDC) && (CFG_TUD_CDC == 1);
77-
mp_usbd_class_state.msc_enabled = (flags & USB_BUILTIN_FLAG_MSC) && (CFG_TUD_MSC == 1);
78-
}
79-
80-
// Descriptor template entry for lookup table
81-
typedef struct {
82-
uint8_t flag_mask; // USB_BUILTIN_FLAG_* to check
83-
uint8_t desc_len; // Length of descriptor in bytes
84-
uint8_t itf_count; // Number of interfaces used
85-
const uint8_t *template; // Descriptor bytes (0xFF = interface number placeholder)
86-
} usb_desc_entry_t;
87-
8861
// CDC descriptor template with interface number placeholders (0xFF)
8962
// Expands TUD_CDC_DESCRIPTOR macro with 0xFF for interface numbers
9063
#if CFG_TUD_CDC
@@ -125,8 +98,8 @@ static const uint8_t msc_desc_template[] = {
12598
};
12699
#endif
127100

128-
// Lookup table for all USB class descriptors
129-
static const usb_desc_entry_t usb_desc_table[] = {
101+
// Lookup table for all USB class descriptors (extern for inlining in header)
102+
const usb_desc_entry_t usb_desc_table[] = {
130103
#if CFG_TUD_CDC
131104
{USB_BUILTIN_FLAG_CDC, TUD_CDC_DESC_LEN, 2, cdc_desc_template},
132105
#endif
@@ -135,33 +108,15 @@ static const usb_desc_entry_t usb_desc_table[] = {
135108
#endif
136109
};
137110

138-
// Calculate descriptor length from flags using lookup table
139-
size_t mp_usbd_get_descriptor_cfg_len_from_flags(uint8_t flags) {
140-
size_t len = TUD_CONFIG_DESC_LEN;
141-
for (size_t i = 0; i < MP_ARRAY_SIZE(usb_desc_table); i++) {
142-
if (flags & usb_desc_table[i].flag_mask) {
143-
len += usb_desc_table[i].desc_len;
144-
}
145-
}
146-
return len;
147-
}
148-
149-
// Calculate interface count from flags using lookup table
150-
static uint8_t mp_usbd_get_interface_count_from_flags(uint8_t flags) {
151-
uint8_t count = 0;
152-
for (size_t i = 0; i < MP_ARRAY_SIZE(usb_desc_table); i++) {
153-
if (flags & usb_desc_table[i].flag_mask) {
154-
count += usb_desc_table[i].itf_count;
155-
}
156-
}
157-
return count;
158-
}
111+
// Global descriptor buffer for runtime configuration (used by both runtime and static modes)
112+
uint8_t mp_usbd_desc_cfg_buffer[MP_USBD_BUILTIN_DESC_CFG_LEN];
159113

160114
// Unified descriptor generation function using lookup table
161-
static const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *buffer) {
115+
const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *buffer) {
162116
uint8_t *desc = buffer;
163-
uint8_t interface_count = mp_usbd_get_interface_count_from_flags(flags);
164-
size_t total_len = mp_usbd_get_descriptor_cfg_len_from_flags(flags);
117+
usb_desc_info_t info = mp_usbd_get_desc_info_from_flags(flags);
118+
uint8_t interface_count = info.interface_count;
119+
size_t total_len = info.length;
165120

166121
// Configuration descriptor header (unrolled for size optimization)
167122
*desc++ = 9; // bLength
@@ -203,12 +158,6 @@ static const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *
203158
return buffer;
204159
}
205160

206-
// Wrapper for bitfield API
207-
const uint8_t *mp_usbd_generate_desc_cfg_from_flags(uint8_t flags) {
208-
static uint8_t desc_cfg_buffer[MP_USBD_BUILTIN_DESC_CFG_LEN];
209-
return mp_usbd_generate_desc_cfg_unified(flags, desc_cfg_buffer);
210-
}
211-
212161
#if !MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE
213162

214163

shared/tinyusb/mp_usbd_runtime.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,10 @@ const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
123123

124124
// If no runtime descriptor but builtin_driver is set, generate from flags
125125
if (!result && usbd->builtin_driver != USB_BUILTIN_FLAG_NONE) {
126-
extern void mp_usbd_update_class_state(uint8_t flags);
127-
extern const uint8_t *mp_usbd_generate_desc_cfg_from_flags(uint8_t flags);
126+
extern uint8_t mp_usbd_desc_cfg_buffer[];
127+
extern const uint8_t *mp_usbd_generate_desc_cfg_unified(uint8_t flags, uint8_t *buffer);
128128
mp_usbd_update_class_state(usbd->builtin_driver);
129-
result = mp_usbd_generate_desc_cfg_from_flags(usbd->builtin_driver);
129+
result = mp_usbd_generate_desc_cfg_unified(usbd->builtin_driver, mp_usbd_desc_cfg_buffer);
130130
}
131131
}
132132
// Fallback to static descriptor (needed for boot before Python initializes)

0 commit comments

Comments
 (0)