i’ve been working on adding support for MIDI to teletype, and i’ve got it working for incoming MIDI events, but having issues with sending events. any help will be much appreciated! i’ve been debugging for a few evenings now and here are my findings so far.
polling for incoming MIDI events is done this way:
-
midi_read
(frommidi.c
) is called from a timer callback (ansible polls every 8ms) - it returns if no MIDI connected or if there is already a read in progress (
rxBusy
is set) - otherwise it calls
uhi_midi_in_run
which callsuhd_ep_run
on the in endpoint
once uhd_ep_run
is called here is what happens next (all functions are in usbb_host.c
):
-
uhd_ep_run
sets up a job for the in endpoint -
uhd_pipe_trans_complet
executes and exits here
at this point if there are no incoming MIDI events nothing happens until it times out (the timeout is 20s):
-
uhd_sof_interrupt
detects there is a job that timed out -
uhd_ep_abort_pipe
is called -
uhd_pipe_finish_job
is called, and the job is complete
if a MIDI event arrives before the timeout, the following happens:
- DMA interrupt results in
uhd_interrupt
getting called which callsuhd_pipe_interrupt_dma
-
uhd_pipe_interrupt_dma
executes and callsuhd_pipe_trans_complet
-
uhd_pipe_trans_complet
completes the job and executes the callback which executes the appropriate MIDI processing functions
so, basically, polling for MIDI events creates a job that either gets complete once a MIDI event is received, or once it times out.
now, if i disable midi_read
and send events using midi_write
it works, MIDI events get sent successfully. here is what happens exactly:
-
uhd_ep_run
sets up a job for the out endpoint -
uhd_pipe_trans_complet
executes - DMA interrupt,
uhd_interrupt
executes and callsuhd_pipe_interrupt_dma
-
uhd_pipe_interrupt_dma
executes and callsuhd_enable_bank_interrupt
- DMA interrupt,
uhd_interrupt
executes and callsuhd_pipe_interrupt
-
uhd_pipe_interrupt
executes and callsuhd_pipe_finish_job
-
uhd_pipe_finish_job
completes the job and calls the callback.
here is the problem: if midi_read
is called, it creates a job on the in endpoint, and this somehow blocks jobs on the out endpoint. any subsequent calls to send MIDI fail because there is already a job scheduled. once the in job completes (either because of an incoming MIDI event or a timeout), it unblocks the out job and it completes normally.
i tried setting the MIDI timeout to a much shorter value (using the same value as the timer that calls midi_read
). it unblocked sending MIDI but it stopped receiving MIDI events. another thing i could try is checking if there is anything waiting to be sent and doing that first before calling midi_read
. but this feels like a hack - it should just work, from what i understand - FTDI uses the same USB functions and it works fine. this also implies the USB implementation itself is fine. it must be something specifically with the MIDI code. i have a feeling this is some misconfiguration issue, maybe something to do with interrupt masks? but i’m not entirely sure where to check and what to look for.