summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSaulo VerĂ­ssimo <[email protected]>2026-04-22 18:04:56 -0300
committerSaulo VerĂ­ssimo <[email protected]>2026-05-12 11:07:45 -0300
commit97852816e873bf7f91f3a81f093ad08f96179656 (patch)
tree2674623ed18af99f131b31f03f713705e5fd5bd1 /src
parent0c68ca8c1de45e24ae022ab58934c6930bad7b4b (diff)
midi2: align descriptors with USB-MIDI 2.0 spec
Brings the MIDI 2.0 device driver into full conformance with USB Device Class Definition for MIDI Devices v2.0 (USB-IF, May 2020). - Alt 1 MS Interface Header wTotalLength now reports 0x0007 per Table 5-2 ("set to match bLength"), replacing the prior 0x0011 carried over from USB-MIDI 1.0 conventions. - GET_DESCRIPTOR class request now validates bmRequestType direction, type and recipient plus wIndex and wValue high byte per Section 6. - iBlockItem in the default Group Terminal Block is driven by CFG_TUD_MIDI2_BLOCK_STRIDX so applications can attach a UI string descriptor to the block per Table 5-6. - UMP word byte order assumption (little-endian host per Section 3.2.2) is documented inline so future big-endian ports know where to wrap access with tu_htole32 / tu_le32toh. Validated on RP2040 and ESP32-P4 under Linux kernel 6.17: lsusb -v reports wTotalLength = 0x0007 on Alt 1 MS Header (raw bytes 07 24 01 00 02 07 00). amidi -l enumerates Group Terminals exposed via the class-specific GET_DESCRIPTOR response.
Diffstat (limited to 'src')
-rw-r--r--src/class/midi/midi2_device.c45
-rw-r--r--src/device/usbd.h12
-rw-r--r--src/tusb_option.h6
3 files changed, 45 insertions, 18 deletions
diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c
index 34e467fc6..15daad096 100644
--- a/src/class/midi/midi2_device.c
+++ b/src/class/midi/midi2_device.c
@@ -44,6 +44,16 @@ TU_ATTR_WEAK bool tud_midi2_get_req_itf_cb(uint8_t rhport, const tusb_control_re
}
//--------------------------------------------------------------------+
+// Byte order note
+//--------------------------------------------------------------------+
+// Per USB-MIDI 2.0 Section 3.2.2, each 32-bit UMP word is transmitted with the
+// least significant byte first. This driver reads and writes UMP words as
+// native uint32_t through tu_edpt_stream_read/write. All TinyUSB targets are
+// little-endian, so the in-memory layout already matches the wire order and no
+// swap is needed. If a big-endian target is ever supported, wrap access with
+// tu_htole32 / tu_le32toh at the buffer boundary.
+
+//--------------------------------------------------------------------+
// UMP Stream Message Constants
//--------------------------------------------------------------------+
// UMP Message Type for Stream messages (bits 31:28)
@@ -133,7 +143,7 @@ static const uint8_t _default_gtb_desc[] = {
0x00, // bGrpTrmBlkType: bidirectional
0x00, // nGroupTrm: first group (0)
CFG_TUD_MIDI2_NUM_GROUPS, // nNumGroupTrm
- 0, // iBlockItem: no string
+ CFG_TUD_MIDI2_BLOCK_STRIDX, // iBlockItem: string descriptor index (0 = none)
0x00, // bMIDIProtocol: unknown/not fixed
0, 0, // wMaxInputBandwidth: unknown
0, 0 // wMaxOutputBandwidth: unknown
@@ -554,19 +564,30 @@ bool midi2d_control_xfer_cb(uint8_t rhport, uint8_t stage, const tusb_control_re
}
case TUSB_REQ_GET_DESCRIPTOR: {
- // wValue: descriptor type (high) | index (low)
- // 0x26 = CS_GRP_TRM_BLOCK, index 0x01
- if (request->wValue == ((uint16_t)MIDI2_CS_GRP_TRM_BLOCK << 8 | 0x01)) {
- if (tud_midi2_get_req_itf_cb(rhport, request)) return true;
+ // USB-MIDI 2.0 Section 6: GTB descriptor retrieval
+ // bmRequestType = 0x81 (Device-to-Host, Standard, Interface)
+ // wValue = CS_GR_TRM_BLOCK (0x26) in high byte, alt setting in low byte
+ // wIndex = interface number
+ if (request->bmRequestType_bit.direction != TUSB_DIR_IN) return false;
+ if (request->bmRequestType_bit.type != TUSB_REQ_TYPE_STANDARD) return false;
+ if (request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE) return false;
+ if (tu_u16_high(request->wValue) != MIDI2_CS_GRP_TRM_BLOCK) return false;
- uint16_t len = request->wLength;
- if (len > sizeof(_default_gtb_desc)) {
- len = sizeof(_default_gtb_desc);
- }
- tud_control_xfer(rhport, request, (void*)(uintptr_t) _default_gtb_desc, len);
- return true;
+ uint8_t itf_num = tu_u16_low(request->wIndex);
+ uint8_t idx = find_midi2_itf_by_num(itf_num);
+ if (idx >= CFG_TUD_MIDI2) return false;
+
+ // Only Alt Setting 1 exposes Group Terminal Block descriptors.
+ if (tu_u16_low(request->wValue) != 0x01) return false;
+
+ if (tud_midi2_get_req_itf_cb(rhport, request)) return true;
+
+ uint16_t len = request->wLength;
+ if (len > sizeof(_default_gtb_desc)) {
+ len = sizeof(_default_gtb_desc);
}
- return false;
+ tud_control_xfer(rhport, request, (void*)(uintptr_t) _default_gtb_desc, len);
+ return true;
}
default:
diff --git a/src/device/usbd.h b/src/device/usbd.h
index a9f4c5f08..abce5a887 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -429,14 +429,14 @@ 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)
+// Per USB-MIDI 2.0 Table 5-2: wTotalLength in the MS Header is not used in 2.0
+// and shall be set to match bLength (= 0x0007) for conformity with USB-MIDI 1.0.
#define TUD_MIDI2_DESC_ALT1_HEAD_LEN (9 + 7)
-#define TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx, _numgtbs) \
+#define TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx) \
/* 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, 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))
+ /* MS Header (MIDI 2.0): wTotalLength = bLength per spec */\
+ 7, TUSB_DESC_CS_INTERFACE, MIDI_CS_INTERFACE_HEADER, U16_TO_U8S_LE(0x0200), U16_TO_U8S_LE(0x0007)
// Alt Setting 1: Standard USB Endpoint (7 bytes) + CS Endpoint General 2.0
#define TUD_MIDI2_DESC_ALT1_EP_LEN(_numgtbs) (7 + 4 + (_numgtbs))
@@ -457,7 +457,7 @@ 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, 1),\
+ TUD_MIDI2_DESC_ALT1_HEAD(_itfnum, _stridx),\
TUD_MIDI2_DESC_ALT1_EP(_epout, _epsize, 1, 1 /* bAssoGrpTrmBlkID */),\
TUD_MIDI2_DESC_ALT1_EP(_epin, _epsize, 1, 1 /* bAssoGrpTrmBlkID */)
diff --git a/src/tusb_option.h b/src/tusb_option.h
index 4483c2200..2614110fc 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -682,6 +682,12 @@
#define CFG_TUD_MIDI2_PRODUCT_ID "TinyUSB-MIDI2"
#endif
+// String descriptor index for the Group Terminal Block (iBlockItem, Table 5-6).
+// 0 = no string descriptor (default, spec-allowed).
+#ifndef CFG_TUD_MIDI2_BLOCK_STRIDX
+ #define CFG_TUD_MIDI2_BLOCK_STRIDX 0
+#endif
+
#ifndef CFG_TUD_VENDOR
#define CFG_TUD_VENDOR 0
#endif