feat(midi2): derive function blocks from the GTB descriptor#3738
feat(midi2): derive function blocks from the GTB descriptor#3738sauloverissimo wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Updates the USB-MIDI 2.0 device negotiation response so Function Block Info Notification reports a bidirectional function block (and associated UI hints), allowing hosts to expose both MIDI IN and OUT ports.
Changes:
- Adjust Function Block Info Notification
bDirectionreporting from output-only to bidirectional. - Include sender/receiver UI-hint bits in the same field to improve host UI behavior.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Hardware-in-the-loop (HIL) Test Reporthfp.json✅ 52 passed · ❌ 0 failed · ⚪ 0 skipped · blank not run
tinyusb.json✅ 335 passed · ❌ 3 failed · ⚪ 12 skipped · blank not run
|
|
| target | .text | .rodata | .data | .bss | total | % diff |
|---|---|---|---|---|---|---|
| metro_m0_express/midi2_device | 16,876 → 17,244 (+368) | — | — | — | 17,004 → 17,372 (+368) | +2.2% |
| lpcxpresso11u37/midi2_device | 17,056 → 17,424 (+368) | — | — | — | 17,540 → 17,908 (+368) | +2.1% |
| msp_exp430f5529lp/midi2_device | 21,200 → 21,656 (+456) | 1,700 → 1,730 (+30) | — | — | 23,316 → 23,802 (+486) | +2.1% |
| lpcxpresso1347/midi2_device | 16,780 → 17,128 (+348) | — | — | — | 17,276 → 17,624 (+348) | +2.0% |
| metro_m4_express/midi2_device | 17,216 → 17,564 (+348) | — | — | — | 17,344 → 17,692 (+348) | +2.0% |
| samg55_xplained/midi2_device | 17,408 → 17,756 (+348) | — | — | — | 17,532 → 17,880 (+348) | +2.0% |
| lpcxpresso1769/midi2_device | 17,400 → 17,748 (+348) | — | — | — | 17,896 → 18,244 (+348) | +1.9% |
| lpcxpresso1549/midi2_device | 17,384 → 17,732 (+348) | — | — | — | 17,960 → 18,308 (+348) | +1.9% |
| lpcxpresso51u68/midi2_device | 19,096 → 19,464 (+368) | — | — | — | 19,224 → 19,592 (+368) | +1.9% |
| frdm_kl25z/midi2_device | 19,276 → 19,644 (+368) | — | — | — | 19,412 → 19,780 (+368) | +1.9% |
0ed10cf to
1866321
Compare
|
Addressed the readability note: replaced the |
|
Do you think it's useful to add a weak callback to define direction per instance ? |
|
Hi @HiFiPhile! Makes sense, and it lines up with the existing callbacks ( |
|
Hi @HiFiPhile! Prototyped it per instance: Validated on hardware with an RP2350 and an STM32F411 (dwc2). On Linux I confirmed the GTB on the wire (the Kept it per instance for now. Per function block would be the natural generalization, but that also pulls in giving each FB its own group span and multiple GTBs, which is a bigger change. Would you rather stay per-instance or aim for per-FB? |
Since you know MIDI2 better you decide it :) |
Function Block Info (direction, group span, name) is derived from the GTB descriptor as the single source of truth, so a device can expose multiple Group Terminal Blocks with independent directions. Adds Function Block Name notifications (tud_midi2_fb_name_cb) and an opt-in callback to answer UMP Stream messages from the application (tud_midi2_stream_msg_cb). Ref hathach#3571
Adds two Group Terminal Blocks (output and input) with names and per-direction endpoint association, plus an explicit endpoint name.
1866321 to
516fb06
Compare
|
I've been studying M2-104-UM more closely and testing on hardware over the past few days, and this part of the stack makes a lot more sense to me now. The more I worked on it the more the pieces overlapped, so I decided to bring everything together here and close #3739. It started as the function block direction fix, but it grew a little. The direction and group span for each function block now come straight from the Group Terminal Block descriptor, so what the device reports in FB Info always matches what it actually declares. The built-in UMP Stream responder answers Endpoint and Function Block Discovery from that same descriptor too, so a device with more than one block just works without extra code. For apps that need more control there are two optional weak callbacks: tud_midi2_fb_name_cb to name the blocks, and tud_midi2_stream_msg_cb to look at a Stream message before the built-in responder does, returning PASS to let it handle the message, HANDLED to answer it yourself, or NEGOTIATED_* to set the protocol. That last one is really why #3739 isn't needed anymore. It does what the CFG_TUD_MIDI2_USER_RESPONDER flag did, covering the dynamic case like a bridge whose blocks follow whatever is plugged into it, but per message and without a compile-time switch, which felt cleaner than handing the whole responder to the app. I tested it on an STM32F411 first, then an RP2350. Both Windows MIDI Services and ALSA now enumerate the device fully, each function block showing its name, direction (input, output or bidirectional) and group correctly, on Windows and Linux.
|
| static uint8_t _gtb_block_count(midi2d_interface_t* p_midi) { | ||
| uint16_t len = 0; | ||
| const uint8_t* gtb = tud_midi2_gtb_desc_cb(_itf_idx(p_midi), &len); | ||
| return _gtb_blocks(gtb, len, 0xFF, NULL, NULL, NULL); | ||
| } |
| uint16_t gtb_len = 0; | ||
| const uint8_t* gtb = tud_midi2_gtb_desc_cb(_itf_idx(p_midi), >b_len); | ||
| uint8_t type = 0x00, first_group = 0, num_groups = (uint8_t) CFG_TUD_MIDI2_NUM_GROUPS; | ||
| _gtb_blocks(gtb, gtb_len, fb_idx, &type, &first_group, &num_groups); |
| uint16_t gtb_len = 0; | ||
| const uint8_t* gtb = tud_midi2_gtb_desc_cb(idx, >b_len); | ||
|
|
||
| uint16_t len = request->wLength; | ||
| if (len > sizeof(_default_gtb_desc)) { | ||
| len = sizeof(_default_gtb_desc); | ||
| if (len > gtb_len) { | ||
| len = gtb_len; | ||
| } | ||
| tud_control_xfer(rhport, request, (void*)(uintptr_t) _default_gtb_desc, len); | ||
| tud_control_xfer(rhport, request, (void*)(uintptr_t) gtb, len); |



The device reports its function blocks straight from the Group Terminal Block
descriptor, so what it sends in UMP Stream FB Info (direction, group span) always
matches what it declares over USB. The built-in responder answers Endpoint and
Function Block Discovery from that descriptor, so a device with more than one
block works out of the box.
Two optional weak callbacks for apps that need more:
(PASS / HANDLED / NEGOTIATED_*), for endpoints with dynamic topology.
This started as the function block direction fix and grew to cover the related
pieces, so I'm closing #3739 and folding its intent into the stream callback here.
Tested on STM32F411 and RP2350 against Windows MIDI Services and ALSA: each block
shows the right name, direction and group on Windows and Linux. Device-only; the
MIDI 1.0 driver is untouched.
Ref #3571