summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaulo VerĂ­ssimo <[email protected]>2026-05-16 12:59:44 -0300
committerSaulo VerĂ­ssimo <[email protected]>2026-05-16 12:59:44 -0300
commit840f3e0a628e69a2b7fb4136b2dca8ab5b350f83 (patch)
treed1bd9c4b2d6fb8a8d54d630f7b613611ded70ca1
parent0af0665ed65ef9574a4c3c2da83c1c502cb713de (diff)
midi2: prevent UMP packet split across USB transfers
The edpt_stream auto-flush is byte-oriented and could cut an UMP message in half when the FIFO reached wMaxPacketSize, corrupting the peer's RX context. Pre-flush whole packets before writing one that would cross the boundary on both device (tud_midi2_n_ump_write) and host (tuh_midi2_ump_write) paths. Host write also becomes packet-aware instead of word-by-word. Ref #3571
-rw-r--r--src/class/midi/midi2_device.c13
-rw-r--r--src/class/midi/midi2_host.c27
2 files changed, 30 insertions, 10 deletions
diff --git a/src/class/midi/midi2_device.c b/src/class/midi/midi2_device.c
index f7d338ede..a005c8e51 100644
--- a/src/class/midi/midi2_device.c
+++ b/src/class/midi/midi2_device.c
@@ -347,11 +347,20 @@ uint32_t tud_midi2_n_ump_write(uint8_t itf, const uint32_t* words, uint32_t coun
while (written < count) {
uint8_t mt = (uint8_t)((words[written] >> 28) & 0x0F);
uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint32_t pkt_bytes = (uint32_t)pkt_words * 4;
if (written + pkt_words > count) break;
- if (tu_edpt_stream_write_available(ep_tx) < pkt_words * 4) break;
+ if (tu_edpt_stream_write_available(ep_tx) < pkt_bytes) break;
+
+ // Flush whole packets already queued before adding one that would cross
+ // the wMaxPacketSize boundary. Prevents an UMP message from being split
+ // across two USB transfers, which would corrupt the host RX context.
+ uint16_t ff_count = tu_fifo_count(&ep_tx->ff);
+ if (ff_count > 0 && ff_count + pkt_bytes > ep_tx->mps) {
+ tu_edpt_stream_write_xfer(ep_tx);
+ }
- tu_edpt_stream_write(ep_tx, &words[written], pkt_words * 4);
+ tu_edpt_stream_write(ep_tx, &words[written], pkt_bytes);
written += pkt_words;
}
diff --git a/src/class/midi/midi2_host.c b/src/class/midi/midi2_host.c
index 90d632a8e..2046fdb67 100644
--- a/src/class/midi/midi2_host.c
+++ b/src/class/midi/midi2_host.c
@@ -527,17 +527,28 @@ uint32_t tuh_midi2_ump_write(uint8_t idx, const uint32_t* words, uint32_t count)
midih2_interface_t *p_midi = &_midi2_host[idx];
tu_edpt_stream_t *ep_tx = &p_midi->ep_stream.tx;
- uint32_t n_words = 0;
- for (uint32_t i = 0; i < count; i++) {
- if (tu_edpt_stream_write_available(ep_tx) >= 4) {
- tu_edpt_stream_write(ep_tx, (const uint8_t *) &words[i], 4);
- n_words++;
- } else {
- break;
+ uint32_t written = 0;
+ while (written < count) {
+ uint8_t mt = (uint8_t)((words[written] >> 28) & 0x0F);
+ uint8_t pkt_words = midi2_ump_word_count(mt);
+ uint32_t pkt_bytes = (uint32_t)pkt_words * 4;
+
+ if (written + pkt_words > count) break;
+ if (tu_edpt_stream_write_available(ep_tx) < pkt_bytes) break;
+
+ // Flush whole packets already queued before adding one that would cross
+ // the wMaxPacketSize boundary. Prevents an UMP message from being split
+ // across two USB transfers, which would corrupt the peer RX context.
+ uint16_t ff_count = tu_fifo_count(&ep_tx->ff);
+ if (ff_count > 0 && ff_count + pkt_bytes > ep_tx->mps) {
+ tu_edpt_stream_write_xfer(ep_tx);
}
+
+ tu_edpt_stream_write(ep_tx, (const uint8_t *) &words[written], pkt_bytes);
+ written += pkt_words;
}
- return n_words;
+ return written;
}
uint32_t tuh_midi2_write_flush(uint8_t idx) {