Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/class/midi/midi2_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ static uint32_t _tx_ump_write(midi2d_interface_t* p_midi, const uint32_t* words,
//--------------------------------------------------------------------+
// Protocol Negotiation
//--------------------------------------------------------------------+
#if !CFG_TUD_MIDI2_USER_RESPONDER
static void _nego_send_ump(midi2d_interface_t* p_midi, const uint32_t* words, uint8_t count) {
if (!_tx_opened(p_midi)) return;
if (tu_fifo_remaining(&p_midi->ep_stream.tx.ff) < (uint32_t) count * 4) return;
Expand Down Expand Up @@ -383,8 +384,16 @@ static void _nego_handle_stream_msg(midi2d_interface_t* p_midi, const uint32_t*
break;
}
}
#endif // !CFG_TUD_MIDI2_USER_RESPONDER

static void _nego_process_rx(midi2d_interface_t* p_midi) {
#if CFG_TUD_MIDI2_USER_RESPONDER
// User responder owns UMP Stream discovery: MT 0xF messages stay in the
// RX FIFO and surface through tud_midi2_n_ump_read so the application can
// answer Endpoint / Function Block discovery itself. The app must drain
// them (e.g. in tud_midi2_rx_cb) to avoid RX FIFO backpressure.
(void)p_midi;
Comment on lines +390 to +395

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve negotiation state in user responder mode

With CFG_TUD_MIDI2_USER_RESPONDER=1, this branch returns before the only code path that handles STREAM_CONFIG_REQUEST; since there is also no public setter for p_midi->protocol or p_midi->negotiated, an application that accepts a host's MIDI 1.0 Config Request and sends its own Config Notify will still see tud_midi2_n_protocol() report the reset default MIDI2 and tud_midi2_n_negotiated() stay false. Please either keep parsing config requests for state or provide a way for the user responder to update these getters.

Useful? React with 👍 / 👎.

#else
tu_edpt_stream_t* ep_rx = &p_midi->ep_stream.rx;
uint8_t word_bytes[4];

Expand All @@ -402,6 +411,7 @@ static void _nego_process_rx(midi2d_interface_t* p_midi) {
tu_edpt_stream_read(ep_rx, buf, pkt_bytes);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm thinking about adding a weak callback here, with the return value we can differentiate between Handled, Negotiated to V1 , Negotiated to V2 , etc.

Maybe the user only want to override a one specific response instead of implement full negotiation in app.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hi @HiFiPhile! Are you thinking of a per-message hook at the stream dispatch, where the return value decides fallthrough vs handled? So the app could intercept just FB Discovery, say, and let the built-in responder take the rest.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yes exactely.

_nego_handle_stream_msg(p_midi, buf);
}
#endif
}

//--------------------------------------------------------------------+
Expand Down Expand Up @@ -510,6 +520,12 @@ uint8_t tud_midi2_n_protocol(uint8_t itf) {
return _midi2d_itf[itf].protocol;
}

void tud_midi2_n_set_protocol(uint8_t itf, uint8_t protocol) {
TU_VERIFY(itf < CFG_TUD_MIDI2, );
_midi2d_itf[itf].protocol = protocol;
_midi2d_itf[itf].negotiated = true;
}

//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
Expand Down
18 changes: 18 additions & 0 deletions src/class/midi/midi2_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ extern "C" {
#define CFG_TUD_MIDI2_BLOCK_STRIDX 0
#endif

// When set to 1, the built-in UMP Stream responder is disabled and MT 0xF
// (Stream) messages are left in the RX FIFO for the application to handle
// (Endpoint / Function Block discovery, Stream Configuration). The app must
// drain them (e.g. in tud_midi2_rx_cb) to avoid RX FIFO backpressure, and
// call tud_midi2_n_set_protocol() after accepting a Stream Configuration
// Request to keep tud_midi2_n_protocol / tud_midi2_n_negotiated accurate.
#ifndef CFG_TUD_MIDI2_USER_RESPONDER
#define CFG_TUD_MIDI2_USER_RESPONDER 0
#endif

//--------------------------------------------------------------------+
// MIDI Protocol Values (returned by tud_midi2_n_protocol)
//--------------------------------------------------------------------+
Expand Down Expand Up @@ -119,6 +129,10 @@ uint8_t tud_midi2_n_alt_setting(uint8_t itf);
bool tud_midi2_n_negotiated(uint8_t itf);
uint8_t tud_midi2_n_protocol(uint8_t itf);

// Record the negotiated protocol when the application owns UMP Stream
// negotiation (CFG_TUD_MIDI2_USER_RESPONDER). Marks the endpoint negotiated.
void tud_midi2_n_set_protocol(uint8_t itf, uint8_t protocol);

// Read up to max_words UMP words from the RX FIFO. Returns the number of
// words actually read (0 if FIFO is empty).
//
Expand Down Expand Up @@ -156,6 +170,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_midi2_protocol(void) {
return tud_midi2_n_protocol(0);
}

TU_ATTR_ALWAYS_INLINE static inline void tud_midi2_set_protocol(uint8_t protocol) {
tud_midi2_n_set_protocol(0, protocol);
}

TU_ATTR_ALWAYS_INLINE static inline uint32_t
tud_midi2_ump_read(uint32_t* words, uint32_t max_words) {
return tud_midi2_n_ump_read(0, words, max_words);
Expand Down
Loading