Skip to content

feat(midi2): derive function blocks from the GTB descriptor#3738

Open
sauloverissimo wants to merge 2 commits into
hathach:masterfrom
sauloverissimo:fix/midi2-fb-direction
Open

feat(midi2): derive function blocks from the GTB descriptor#3738
sauloverissimo wants to merge 2 commits into
hathach:masterfrom
sauloverissimo:fix/midi2-fb-direction

Conversation

@sauloverissimo

@sauloverissimo sauloverissimo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

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:

  • tud_midi2_fb_name_cb: per-block names.
  • tud_midi2_stream_msg_cb: see a UMP Stream message before the built-in responder
    (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

Copilot AI review requested due to automatic review settings June 27, 2026 03:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 bDirection reporting 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.

Comment thread src/class/midi/midi2_device.c Outdated
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Hardware-in-the-loop (HIL) Test Report

hfp.json

✅ 52 passed · ❌ 0 failed · ⚪ 0 skipped · blank not run

Board cdc_dual_ports cdc_msc dfu cdc_msc_throughput audio_test_freertos dfu_runtime cdc_msc_freertos hid_boot_interface msc_dual_lun hid_generic_inout printer_to_cdc midi_test mtp
stm32l412nucleo ✅ CDC 627k/392k MSC 822k/758k
stm32f746disco ✅ CDC 13.6M/9.3M MSC 15.4M/28.3M
stm32f746disco-DMA ✅ CDC 13.3M/10.3M MSC 14.2M/31.2M
lpcxpresso43s67 ✅ CDC 11.6M/11.6M MSC 32.8M/35.9M

tinyusb.json

✅ 335 passed · ❌ 3 failed · ⚪ 12 skipped · blank not run

Board cdc_dual_ports cdc_msc dfu cdc_msc_throughput audio_test_freertos dfu_runtime cdc_msc_freertos hid_boot_interface msc_dual_lun hid_generic_inout printer_to_cdc midi_test mtp host_info_to_device_cdc cdc_msc_hid msc_file_explorer msc_file_explorer_freertos device_info hid_composite_freertos
ek_tm4c123gxl ✅ CDC 752k/1M MSC 857k/996k
espressif_p4_function_ev rd 409KB/s
espressif_p4_function_ev-DMA rd 409KB/s
espressif_s3_devkitm rd 409KB/s
espressif_s3_devkitm-DMA
feather_nrf52840_express ✅ CDC 349k/467k MSC 543k/541k
max32666fthr ✅ CDC 7M/14.3M MSC 8.4M/16.3M
metro_m4_express ✅ CDC 473k/376k MSC 453k/534k
mimxrt1015_evk ✅ CDC 12.9M/6.8M MSC 24.9M/21M
mimxrt1064_evk ✅ CDC 15.9M/12.5M MSC 24.6M/18.6M
lpcxpresso11u37 ✅ CDC 331k/329k MSC 551k/544k
ra4m1_ek ✅ CDC 395k/408k MSC 555k/546k
raspberry_pi_pico ✅ CDC 508k/511k MSC 546k/499k rd 62KB/s
raspberry_pi_pico_w rd 1108KB/s rd 1022KB/s
raspberry_pi_pico2 rd 1108KB/s rd 1022KB/s
adafruit_fruit_jam ✅ CDC 538k/512k MSC 530k/578k rd 62KB/s rd 62KB/s
stm32f072disco ✅ CDC 454k/350k MSC 489k/479k
stm32f407disco ✅ CDC 927k/504k MSC 871k/948k
stm32f723disco ✅ CDC 668k/877k MSC 695k/796k rd 18078KB/s rd 4096KB/s
stm32f723disco-DMA ✅ CDC 623k/889k MSC 758k/953k rd 19418KB/s rd 4096KB/s
stm32h743nucleo ✅ CDC 862k/941k MSC 869k/1M
stm32h743nucleo-DMA ✅ CDC 811k/935k MSC 662k/918k
stm32g0b1nucleo ✅ CDC 417k/496k MSC 623k/534k
stm32l476disco ✅ CDC 582k/497k MSC 699k/790k
stm32u083nucleo ✅ CDC 456k/479k MSC 503k/581k
nanoch32v203-fsdev ✅ CDC 607k/601k MSC 664k/988k
nanoch32v203-usbfs ✅ CDC 500k/497k MSC 795k/1M
ch32v103r_r1_1v0 ✅ CDC 140k/502k MSC 696k/553k
ch582m_evt ✅ CDC 229k/222k MSC 393k/488k

@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

Top 10 targets by memory change (%) (out of 2404 targets) View Project Dashboard →

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%

@sauloverissimo sauloverissimo force-pushed the fix/midi2-fb-direction branch from 0ed10cf to 1866321 Compare June 27, 2026 04:31
@sauloverissimo

Copy link
Copy Markdown
Contributor Author

Addressed the readability note: replaced the 0x33 literal with the explicit bitfield composition (UI hint sender/receiver + bidirectional direction). Same value, now self-documenting.

@HiFiPhile

Copy link
Copy Markdown
Collaborator

Do you think it's useful to add a weak callback to define direction per instance ?

@sauloverissimo

Copy link
Copy Markdown
Contributor Author

Hi @HiFiPhile! Makes sense, and it lines up with the existing callbacks (num_groups, num_function_blocks, ep_name, product_id). Direction is the one FB attribute that isn't overridable yet. We could add a tud_midi2_..._cb(itf) returning the direction, with bidirectional as the default to keep current behavior, and have the driver apply it to both the GTB bGrpTrmBlkType and the FB Info. I can include it in this PR. Were you picturing it per instance (itf), like the other callbacks, or per function block? Either way, I'll prototype it here and bring something to show.

@sauloverissimo

sauloverissimo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Hi @HiFiPhile! Prototyped it per instance: tud_midi2_direction_cb(itf), following the existing callbacks, returning bidirectional (default) / input-only / output-only, driving both the GTB bGrpTrmBlkType and the FB Info direction + UI hint.

Validated on hardware with an RP2350 and an STM32F411 (dwc2). On Linux I confirmed the GTB on the wire (the bGrpTrmBlkType changes with the callback), and on Windows MIDI Services the ports line up: output-only shows up as MIDI In only, bidirectional as In + Out.

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? 2350_fb

@HiFiPhile

Copy link
Copy Markdown
Collaborator

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.
@sauloverissimo sauloverissimo force-pushed the fix/midi2-fb-direction branch from 1866321 to 516fb06 Compare June 29, 2026 13:26
@sauloverissimo sauloverissimo changed the title fix(midi2): report bidirectional function block direction feat(midi2): derive function blocks from the GTB descriptor Jun 29, 2026
@sauloverissimo

sauloverissimo commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

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.

image image

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +219 to +223
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);
}
Comment on lines +398 to +401
uint16_t gtb_len = 0;
const uint8_t* gtb = tud_midi2_gtb_desc_cb(_itf_idx(p_midi), &gtb_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);
Comment on lines +802 to +809
uint16_t gtb_len = 0;
const uint8_t* gtb = tud_midi2_gtb_desc_cb(idx, &gtb_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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants