summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaulo VerĂ­ssimo <[email protected]>2026-03-25 07:05:56 -0300
committerSaulo VerĂ­ssimo <[email protected]>2026-05-12 11:07:44 -0300
commitfa9edeff9c00ee1fc4ee7ab9938b1a5955a6281a (patch)
tree4655c751abefbf5ebd65ad0265904754d5552a96
parentd5c5ac586bfdbf53e18a8cdbb11b978f53b6d059 (diff)
fix: address PR review feedback for MIDI 2.0 drivers
Host driver (midi2_host.c): - midih2_open() now returns actual parsed length instead of max_len, preventing composite device interface conflicts - Parsers (alt0/alt1) refactored to return const uint8_t* end pointer following midi_host.c switch/case pattern - Alt 1 CS Endpoint now parses MIDI 2.0 layout (bNumGrpTrmBlk at offset 3 with MIDI_CS_ENDPOINT_GENERAL_2_0 subtype check) instead of reusing MIDI 1.0 struct (bNumEmbMIDIJack) - midih2_set_config() now issues SET_INTERFACE control request via tuh_interface_set() before completing configuration. Falls back to alt 0 if SET_INTERFACE fails - Extracted midih2_set_config_complete() and midih2_set_interface_cb() for async SET_INTERFACE handling Device driver (midi2_device.c): - midi2d_open() skip loop now checks bInterfaceNumber, stopping at interfaces that belong to other functions in composite devices - SET_INTERFACE handler now rejects alt > 1 (returns false/stall) - Named constants for GTB descriptor types and MIDI protocol values Descriptor macros (usbd.h): - TUD_MIDI2_DESC_ALT1_HEAD: iInterface set to 0 (consistent with Alt 0), wTotalLength now uses TUD_MIDI2_DESC_ALT1_CS_LEN to cover all Alt 1 class-specific descriptors - TUD_MIDI2_DESC_ALT1_EP: now accepts GTB ID list via variadic args, emitting complete CS endpoint descriptor Host example: - CMakeLists.txt restricted to rp2040 family (display.c requires Pico SDK headers) - display.c: null terminator after strncpy in log scroll Documentation: - class_drivers.rst updated to reflect SET_INTERFACE behavior and auto-select with fallback Addresses: Codex P1 (#1, #2, #3), Copilot (#4-#9)
-rw-r--r--docs/reference/class_drivers.rst8
-rw-r--r--examples/host/midi2_host/CMakeLists.txt3
-rw-r--r--examples/host/midi2_host/src/display.c1
-rw-r--r--src/class/midi/midi2_device.c14
-rw-r--r--src/class/midi/midi2_host.c213
-rw-r--r--src/device/usbd.h24
6 files changed, 166 insertions, 97 deletions
diff --git a/docs/reference/class_drivers.rst b/docs/reference/class_drivers.rst
index 9ed332acb..3ac0d8d4e 100644
--- a/docs/reference/class_drivers.rst
+++ b/docs/reference/class_drivers.rst
@@ -87,7 +87,7 @@ The MIDI 2.0 Host driver enables TinyUSB to enumerate and communicate with USB M
**Key Features:**
- **Reactive Architecture**: Auto-detects Alt Setting 1 (MIDI 2.0) capability during enumeration
-- **Auto-Selection**: Automatically selects the highest available protocol (MIDI 2.0 preferred)
+- **Auto-Selection**: Automatically selects the highest available protocol and issues SET_INTERFACE to activate Alt Setting 1 when MIDI 2.0 is detected
- **Transparent Stream Messages**: All data (UMP packets + Stream Messages) flow through callbacks
- **Memory Safe**: No dynamic allocation, fixed-size instances per device
@@ -271,9 +271,9 @@ Architecture
The MIDI 2.0 Host driver uses a **reactive, callback-driven architecture** that mirrors the proven patterns in TinyUSB's existing device drivers (CDC, HID, etc.):
- **Auto-Detection**: Host automatically detects Alt Setting 1 capability
-- **Auto-Selection**: Selects highest protocol available (MIDI 2.0 preferred)
-- **Application Control**: App makes protocol behavior decisions via callbacks
-- **Transparent I/O**: Stream Messages and UMP packets flow transparently
+- **Auto-Selection**: Selects highest protocol available and issues SET_INTERFACE
+- **Transparent I/O**: Stream Messages and UMP packets flow through callbacks
+- **Callback-Driven**: App receives events via callbacks (descriptor, mount, rx, tx, unmount)
Differences from MIDI 1.0 Host
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/examples/host/midi2_host/CMakeLists.txt b/examples/host/midi2_host/CMakeLists.txt
index 221de7adf..cd70d122a 100644
--- a/examples/host/midi2_host/CMakeLists.txt
+++ b/examples/host/midi2_host/CMakeLists.txt
@@ -6,7 +6,8 @@ project(midi2_host C CXX ASM)
family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
-if(FAMILY STREQUAL "espressif")
+# This example requires PIO-USB and Pico SDK (I2C, SSD1306 display)
+if(NOT FAMILY STREQUAL "rp2040")
return()
endif()
diff --git a/examples/host/midi2_host/src/display.c b/examples/host/midi2_host/src/display.c
index 6d81bcd5c..4745c2961 100644
--- a/examples/host/midi2_host/src/display.c
+++ b/examples/host/midi2_host/src/display.c
@@ -190,6 +190,7 @@ void display_log(const char* text, uint16_t color) {
if (log_count >= LOG_LINES) {
for (int i = 0; i < LOG_LINES - 1; i++) {
strncpy(log_lines[i], log_lines[i + 1], CHARS_PER_LINE);
+ log_lines[i][CHARS_PER_LINE] = '\0';
}
log_count = LOG_LINES - 1;
}
diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c
index 9363aac11..aecbda4c5 100644
--- a/src/class/midi/midi2_device.c
+++ b/src/class/midi/midi2_device.c
@@ -505,12 +505,19 @@ uint16_t midi2d_open(uint8_t rhport, const tusb_desc_interface_t* desc_itf, uint
}
// Skip remaining descriptors (alt setting 1, CS endpoints, GTB)
+ // Stop at any interface descriptor that is not our MIDI Streaming alt setting
while (tu_desc_in_bounds(p_desc, desc_end)) {
uint8_t dtype = tu_desc_type(p_desc);
- if (dtype != TUSB_DESC_CS_INTERFACE && dtype != TUSB_DESC_CS_ENDPOINT &&
- dtype != TUSB_DESC_INTERFACE && dtype != TUSB_DESC_ENDPOINT) {
+
+ if (dtype == TUSB_DESC_INTERFACE) {
+ const tusb_desc_interface_t* next_itf = (const tusb_desc_interface_t*) p_desc;
+ // Continue only if this is an alternate setting of our own interface
+ if (next_itf->bInterfaceNumber != desc_midi->bInterfaceNumber) break;
+ } else if (dtype != TUSB_DESC_CS_INTERFACE && dtype != TUSB_DESC_CS_ENDPOINT &&
+ dtype != TUSB_DESC_ENDPOINT) {
break;
}
+
p_desc = tu_desc_next(p_desc);
}
@@ -528,6 +535,9 @@ bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_re
uint8_t itf_num = tu_u16_low(request->wIndex);
uint8_t alt = tu_u16_low(request->wValue);
+ // Only Alt Setting 0 (MIDI 1.0) and 1 (UMP) are valid
+ if (alt > 1) return false;
+
uint8_t idx = find_midi2_itf_by_num(itf_num);
if (idx >= CFG_TUD_MIDI2) return false;
diff --git a/src/class/midi/midi2_host.c b/src/class/midi/midi2_host.c
index beb441b56..5b9f98f8b 100644
--- a/src/class/midi/midi2_host.c
+++ b/src/class/midi/midi2_host.c
@@ -125,9 +125,10 @@ static inline uint8_t get_idx_by_ep_addr(uint8_t daddr, uint8_t ep_addr) {
// Descriptor parsing
//--------------------------------------------------------------------+
-static void midih2_parse_descriptors_alt0(midih2_interface_t *p_midi,
+// Parse Alt Setting 0 (MIDI 1.0) descriptors. Returns pointer past last consumed descriptor.
+static const uint8_t* midih2_parse_descriptors_alt0(midih2_interface_t *p_midi,
const tusb_desc_interface_t *desc_itf, const uint8_t *desc_end) {
- TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass,);
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, NULL);
p_midi->bInterfaceNumber = desc_itf->bInterfaceNumber;
@@ -136,93 +137,113 @@ static void midih2_parse_descriptors_alt0(midih2_interface_t *p_midi,
uint8_t rx_cable_count = 0;
uint8_t tx_cable_count = 0;
+ bool found_new_interface = false;
- while (tu_desc_in_bounds(p_desc, desc_end)) {
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) {
- break;
- }
-
- if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
- const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
-
- // Open endpoint and stream
- TU_ASSERT(tuh_edpt_open(p_midi->daddr, p_ep),);
- if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_IN) {
- tu_edpt_stream_open(&p_midi->ep_stream.rx, p_midi->daddr, p_ep, tu_edpt_packet_size(p_ep));
- tu_edpt_stream_clear(&p_midi->ep_stream.rx);
- } else {
- tu_edpt_stream_open(&p_midi->ep_stream.tx, p_midi->daddr, p_ep, tu_edpt_packet_size(p_ep));
- tu_edpt_stream_clear(&p_midi->ep_stream.tx);
- }
-
- p_desc = tu_desc_next(p_desc);
+ while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) {
+ switch (tu_desc_type(p_desc)) {
+ case TUSB_DESC_INTERFACE:
+ found_new_interface = true;
+ break;
- if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
- const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
+ case TUSB_DESC_ENDPOINT: {
+ const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
- if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
- tx_cable_count = p_csep->bNumEmbMIDIJack;
+ TU_ASSERT(tuh_edpt_open(p_midi->daddr, p_ep), NULL);
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_IN) {
+ tu_edpt_stream_open(&p_midi->ep_stream.rx, p_midi->daddr, p_ep, tu_edpt_packet_size(p_ep));
+ tu_edpt_stream_clear(&p_midi->ep_stream.rx);
} else {
- rx_cable_count = p_csep->bNumEmbMIDIJack;
+ tu_edpt_stream_open(&p_midi->ep_stream.tx, p_midi->daddr, p_ep, tu_edpt_packet_size(p_ep));
+ tu_edpt_stream_clear(&p_midi->ep_stream.tx);
+ }
+
+ p_desc = tu_desc_next(p_desc);
+ if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
+ const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
+ tx_cable_count = p_csep->bNumEmbMIDIJack;
+ } else {
+ rx_cable_count = p_csep->bNumEmbMIDIJack;
+ }
}
+ break;
}
+
+ default:
+ break;
}
- p_desc = tu_desc_next(p_desc);
+ if (!found_new_interface) {
+ p_desc = tu_desc_next(p_desc);
+ }
}
p_midi->rx_cable_count_alt0 = rx_cable_count;
p_midi->tx_cable_count_alt0 = tx_cable_count;
+ return p_desc;
}
-static void midih2_parse_descriptors_alt1(midih2_interface_t *p_midi,
+// Parse Alt Setting 1 (MIDI 2.0 UMP) descriptors. Returns pointer past last consumed descriptor.
+static const uint8_t* midih2_parse_descriptors_alt1(midih2_interface_t *p_midi,
const tusb_desc_interface_t *desc_itf, const uint8_t *desc_end) {
- TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass,);
- TU_VERIFY(desc_itf->bAlternateSetting == 1,);
+ TU_VERIFY(AUDIO_SUBCLASS_MIDI_STREAMING == desc_itf->bInterfaceSubClass, NULL);
+ TU_VERIFY(desc_itf->bAlternateSetting == 1, NULL);
const uint8_t *p_desc = (const uint8_t *) desc_itf;
p_desc = tu_desc_next(p_desc);
uint8_t rx_cable_count = 0;
uint8_t tx_cable_count = 0;
+ bool found_new_interface = false;
- while (tu_desc_in_bounds(p_desc, desc_end)) {
- if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) {
- break;
- }
-
- if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE) {
- if (tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_HEADER) {
- const uint8_t *bcd_ptr = p_desc + 3;
- p_midi->bcdMSC_lo = bcd_ptr[0];
- p_midi->bcdMSC_hi = bcd_ptr[1];
+ while (tu_desc_in_bounds(p_desc, desc_end) && !found_new_interface) {
+ switch (tu_desc_type(p_desc)) {
+ case TUSB_DESC_INTERFACE:
+ found_new_interface = true;
+ break;
- if (p_midi->bcdMSC_hi == 0x02) {
- p_midi->protocol_version = 1;
+ case TUSB_DESC_CS_INTERFACE:
+ if (tu_desc_subtype(p_desc) == MIDI_CS_INTERFACE_HEADER) {
+ // bcdMSC at offset 3-4 in CS Interface Header
+ const uint8_t *bcd_ptr = p_desc + 3;
+ p_midi->bcdMSC_lo = bcd_ptr[0];
+ p_midi->bcdMSC_hi = bcd_ptr[1];
+ if (p_midi->bcdMSC_hi == 0x02) { // bcdMSC 0x0200 = USB-MIDI 2.0
+ p_midi->protocol_version = 1;
+ }
}
- }
- }
-
- if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
- const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
- p_desc = tu_desc_next(p_desc);
+ break;
- if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
- const midi_desc_cs_endpoint_t *p_csep = (const midi_desc_cs_endpoint_t *) p_desc;
+ case TUSB_DESC_ENDPOINT: {
+ const tusb_desc_endpoint_t *p_ep = (const tusb_desc_endpoint_t *) p_desc;
+ p_desc = tu_desc_next(p_desc);
- if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
- tx_cable_count = p_csep->bNumEmbMIDIJack;
- } else {
- rx_cable_count = p_csep->bNumEmbMIDIJack;
+ if (tu_desc_in_bounds(p_desc, desc_end) && tu_desc_type(p_desc) == TUSB_DESC_CS_ENDPOINT) {
+ // MIDI 2.0 CS Endpoint General 2.0: bNumGrpTrmBlk at offset 3
+ if (p_desc[0] >= 4 && p_desc[2] == MIDI_CS_ENDPOINT_GENERAL_2_0) {
+ uint8_t num_grp_trm_blk = p_desc[3];
+ if (tu_edpt_dir(p_ep->bEndpointAddress) == TUSB_DIR_OUT) {
+ tx_cable_count = num_grp_trm_blk;
+ } else {
+ rx_cable_count = num_grp_trm_blk;
+ }
+ }
}
+ break;
}
+
+ default:
+ break;
}
- p_desc = tu_desc_next(p_desc);
+ if (!found_new_interface) {
+ p_desc = tu_desc_next(p_desc);
+ }
}
p_midi->rx_cable_count_alt1 = rx_cable_count;
p_midi->tx_cable_count_alt1 = tx_cable_count;
+ return p_desc;
}
//--------------------------------------------------------------------+
@@ -327,33 +348,20 @@ uint16_t midih2_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface
desc_itf->bInterfaceNumber, desc_itf->bAlternateSetting, dev_addr);
// Dispatch to appropriate parser based on Alt Setting
+ const uint8_t *p_end = NULL;
if (desc_itf->bAlternateSetting == 0) {
- midih2_parse_descriptors_alt0(p_midi, desc_itf, desc_end);
+ p_end = midih2_parse_descriptors_alt0(p_midi, desc_itf, desc_end);
} else if (desc_itf->bAlternateSetting == 1) {
- midih2_parse_descriptors_alt1(p_midi, desc_itf, desc_end);
+ p_end = midih2_parse_descriptors_alt1(p_midi, desc_itf, desc_end);
}
- return max_len;
+ // Return number of bytes consumed (following midi_host.c pattern)
+ uint16_t const parsed_len = (p_end != NULL) ? (uint16_t)(p_end - desc_start) : 0;
+ return parsed_len;
}
-bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num) {
- uint8_t idx = 0;
- for (idx = 0; idx < CFG_TUH_MIDI2; idx++) {
- if (_midi2_host[idx].daddr == dev_addr && _midi2_host[idx].bInterfaceNumber == itf_num) {
- break;
- }
- }
-
- if (idx >= CFG_TUH_MIDI2) {
- // Not our interface (e.g. Audio Control) - pass through to next
- usbh_driver_set_config_complete(dev_addr, itf_num);
- return true;
- }
-
- midih2_interface_t *p_midi = &_midi2_host[idx];
-
- // Auto-select alt setting
- midih2_auto_select_alt_setting(p_midi);
+static void midih2_set_config_complete(midih2_interface_t *p_midi, uint8_t idx) {
+ uint8_t dev_addr = p_midi->daddr;
// Invoke descriptor_cb
tuh_midi2_descriptor_cb_t desc_cb = {
@@ -374,7 +382,7 @@ bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num) {
// Invoke mount_cb
tuh_midi2_mount_cb_t mount_cb = {
- .daddr = p_midi->daddr,
+ .daddr = dev_addr,
.bInterfaceNumber = p_midi->bInterfaceNumber,
.protocol_version = p_midi->protocol_version,
.alt_setting_active = p_midi->alt_setting_current,
@@ -387,7 +395,56 @@ bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num) {
tu_edpt_stream_read_xfer(&p_midi->ep_stream.rx);
// Signal USBH that configuration is complete
- usbh_driver_set_config_complete(dev_addr, itf_num);
+ usbh_driver_set_config_complete(dev_addr, p_midi->bInterfaceNumber);
+}
+
+static void midih2_set_interface_cb(tuh_xfer_t *xfer) {
+ uint8_t const dev_addr = xfer->daddr;
+ uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+
+ // Find our interface
+ for (uint8_t idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ if (_midi2_host[idx].daddr == dev_addr && _midi2_host[idx].bInterfaceNumber == itf_num) {
+ if (xfer->result == XFER_RESULT_SUCCESS) {
+ midih2_set_config_complete(&_midi2_host[idx], idx);
+ } else {
+ // SET_INTERFACE failed, fall back to alt 0
+ TU_LOG_DRV("MIDI2 SET_INTERFACE failed, falling back to alt 0\r\n");
+ _midi2_host[idx].alt_setting_current = 0;
+ midih2_set_config_complete(&_midi2_host[idx], idx);
+ }
+ return;
+ }
+ }
+}
+
+bool midih2_set_config(uint8_t dev_addr, uint8_t itf_num) {
+ uint8_t idx = 0;
+ for (idx = 0; idx < CFG_TUH_MIDI2; idx++) {
+ if (_midi2_host[idx].daddr == dev_addr && _midi2_host[idx].bInterfaceNumber == itf_num) {
+ break;
+ }
+ }
+
+ if (idx >= CFG_TUH_MIDI2) {
+ // Not our interface (e.g. Audio Control) - pass through to next
+ usbh_driver_set_config_complete(dev_addr, itf_num);
+ return true;
+ }
+
+ midih2_interface_t *p_midi = &_midi2_host[idx];
+
+ // Auto-select alt setting
+ midih2_auto_select_alt_setting(p_midi);
+
+ // If MIDI 2.0 detected, issue SET_INTERFACE to activate Alt Setting 1
+ if (p_midi->alt_setting_current == 1) {
+ TU_LOG_DRV("MIDI2 requesting SET_INTERFACE alt 1 for itf %u\r\n", itf_num);
+ TU_ASSERT(tuh_interface_set(dev_addr, itf_num, 1, midih2_set_interface_cb, 0));
+ } else {
+ // MIDI 1.0 only, complete immediately
+ midih2_set_config_complete(p_midi, idx);
+ }
return true;
}
diff --git a/src/device/usbd.h b/src/device/usbd.h
index af37eff56..a9f4c5f08 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -429,18 +429,20 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
//--------------------------------------------------------------------+
// Alt Setting 1: MS Interface + MS Header (bcdMSC=0x0200)
+// wTotalLength covers MS Header + all CS Endpoint descriptors
+#define TUD_MIDI2_DESC_ALT1_CS_LEN(_numgtbs) (7 + (4 + (_numgtbs)) * 2)
#define TUD_MIDI2_DESC_ALT1_HEAD_LEN (9 + 7)
-#define TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx) \
+#define TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx, _numgtbs) \
/* MIDI Streaming Interface, Alt Setting 1 */\
- 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 1, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, _stridx,\
- /* MS Header (MIDI 2.0) */\
- 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0200), U16_TO_U8S_LE(7)
+ 9, TUSB_DESC_INTERFACE, (uint8_t)((_itfnum) + 1), 1, 2, TUSB_CLASS_AUDIO, AUDIO_SUBCLASS_MIDI_STREAMING, AUDIO_FUNC_PROTOCOL_CODE_UNDEF, 0,\
+ /* MS Header (MIDI 2.0): wTotalLength = header + 2x CS Endpoint */\
+ 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0200), U16_TO_U8S_LE(TUD_MIDI2_DESC_ALT1_CS_LEN(_numgtbs))
-// Alt Setting 1: Standard USB Endpoint (7 bytes) + CS Endpoint (subtype 0x02)
+// Alt Setting 1: Standard USB Endpoint (7 bytes) + CS Endpoint General 2.0
#define TUD_MIDI2_DESC_ALT1_EP_LEN(_numgtbs) (7 + 4 + (_numgtbs))
-#define TUD_MIDI2_DESC_ALT1_EP(_ep, _epsize, _numgtbs) \
+#define TUD_MIDI2_DESC_ALT1_EP(_ep, _epsize, _numgtbs, ...) \
7, TUSB_DESC_ENDPOINT, _ep, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0, \
- (uint8_t)(4 + (_numgtbs)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL_2_0, _numgtbs
+ (uint8_t)(4 + (_numgtbs)), TUSB_DESC_CS_ENDPOINT, MIDI_CS_ENDPOINT_GENERAL_2_0, _numgtbs, ## __VA_ARGS__
// Total length: Alt 0 (MIDI 1.0) + Alt 1 (UMP)
#define TUD_MIDI2_DESC_LEN (TUD_MIDI_DESC_LEN + TUD_MIDI2_DESC_ALT1_HEAD_LEN + TUD_MIDI2_DESC_ALT1_EP_LEN(1) * 2)
@@ -455,11 +457,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ
TUD_MIDI_DESC_EP(_epin, _epsize, 1),\
TUD_MIDI_JACKID_OUT_EMB(1),\
/* Alt Setting 1 (UMP) */\
- TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx),\
- TUD_MIDI2_DESC_ALT1_EP(_epout, _epsize, 1),\
- 1, /* bAssoGrpTrmBlkID = 1 */\
- TUD_MIDI2_DESC_ALT1_EP(_epin, _epsize, 1),\
- 1 /* bAssoGrpTrmBlkID = 1 */
+ TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx, 1),\
+ TUD_MIDI2_DESC_ALT1_EP(_epout, _epsize, 1, 1 /* bAssoGrpTrmBlkID */),\
+ TUD_MIDI2_DESC_ALT1_EP(_epin, _epsize, 1, 1 /* bAssoGrpTrmBlkID */)
//--------------------------------------------------------------------+
// Audio Descriptor Templates