You've already forked ivan-torvalds-GuitarPedal
forked from AllSpiceMirrors/torvalds-GuitarPedal
Two bugs going opposite ways through the hardware MIDI port, both from the same missing piece: nothing worked out what a USB-MIDI Code Index Number should be, so each direction guessed separately. Coming in, uart_midi_read() set packet[0] = 0 and uart_midi_poll() forwarded that to the host. CIN 0 is reserved in USB-MIDI 1.0, so every message arriving at the TRS jack and passed through to USB was malformed and a host was within its rights to drop the lot. handle_midi_packet() coped, because it reads the status byte rather than the CIN, which is why this could sit there looking like it worked. Going out, uart_midi_write() had its own switch listing 0x8, 0x9, 0xB, 0xC, 0xD and 0xE, and threw away anything else. 0xA - poly key pressure - is a perfectly ordinary channel voice message and was simply dropped, as was every real-time byte, so MIDI clock arriving over USB never reached the TRS out. Song position and SysEx went the same way, though nothing generates those down this path today. So write the two halves once, next to each other where they can be checked against one another: what CIN a status byte belongs in, and how many bytes a CIN carries. Note that none of this is built by default - it is all behind MIDI_HW - which is precisely why a table of sixteen cases could be wrong for as long as it liked. So there is a host-side test for it in Validation/ alongside the FFT one, checking the mapping and the round trip for every channel-voice status byte. 'make check' there runs it. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
128 lines
3.2 KiB
C
128 lines
3.2 KiB
C
#if MIDI_HW
|
|
#define UART_TX_BUF_SIZE 512
|
|
static uint8_t uart_tx_buf[UART_TX_BUF_SIZE];
|
|
static volatile unsigned uart_tx_head;
|
|
static volatile unsigned uart_tx_tail;
|
|
|
|
#define UART_RX_BUF_SIZE 256
|
|
static uint8_t uart_rx_buf[UART_RX_BUF_SIZE];
|
|
static volatile unsigned uart_rx_head;
|
|
static volatile unsigned uart_rx_tail;
|
|
#endif
|
|
|
|
void uart_midi_write(const uint8_t packet[4])
|
|
{
|
|
#if MIDI_HW
|
|
int len = midi_cin_length(packet[0] & 0x0F);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
unsigned head = uart_tx_head;
|
|
unsigned next_head = (head + 1) % UART_TX_BUF_SIZE;
|
|
if (next_head != uart_tx_tail) {
|
|
uart_tx_buf[head] = packet[1 + i];
|
|
uart_tx_head = next_head;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool uart_midi_read(uint8_t packet[4])
|
|
{
|
|
#if MIDI_HW
|
|
static int expected_bytes = 0;
|
|
static uint8_t parser_packet[4];
|
|
static int parser_idx = 0;
|
|
|
|
while (uart_rx_head != uart_rx_tail) {
|
|
uint8_t b = uart_rx_buf[uart_rx_tail];
|
|
uart_rx_tail = (uart_rx_tail + 1) % UART_RX_BUF_SIZE;
|
|
|
|
if (b >= 0xF8) {
|
|
// Real-time message
|
|
continue;
|
|
} else if (b >= 0x80) {
|
|
parser_packet[1] = b;
|
|
parser_idx = 2;
|
|
if ((b & 0xF0) == 0xC0 || (b & 0xF0) == 0xD0) {
|
|
expected_bytes = 1;
|
|
} else if (b < 0xF0) {
|
|
expected_bytes = 2;
|
|
} else {
|
|
expected_bytes = 0;
|
|
}
|
|
} else if (expected_bytes > 0 && parser_idx > 0) {
|
|
parser_packet[parser_idx++] = b;
|
|
if (parser_idx - 2 == expected_bytes) {
|
|
// CIN 0 is reserved: a host is entitled to
|
|
// ignore it, and ours was emitting nothing
|
|
// else on this path.
|
|
packet[0] = midi_status_cin(parser_packet[1]);
|
|
packet[1] = parser_packet[1];
|
|
packet[2] = parser_packet[2];
|
|
packet[3] = parser_packet[3];
|
|
parser_idx = 2;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void uart_midi_poll(void)
|
|
{
|
|
#if MIDI_HW
|
|
while (uart_is_readable(MIDI_UART)) {
|
|
unsigned head = uart_rx_head;
|
|
unsigned next_head = (head + 1) % UART_RX_BUF_SIZE;
|
|
if (next_head != uart_rx_tail) {
|
|
uart_rx_buf[head] = uart_getc(MIDI_UART);
|
|
uart_rx_head = next_head;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (uart_tx_head != uart_tx_tail && uart_is_writable(MIDI_UART)) {
|
|
uart_putc_raw(MIDI_UART, uart_tx_buf[uart_tx_tail]);
|
|
uart_tx_tail = (uart_tx_tail + 1) % UART_TX_BUF_SIZE;
|
|
}
|
|
|
|
uint8_t packet[4];
|
|
while (uart_midi_read(packet)) {
|
|
if (!handle_midi_packet(packet)) {
|
|
usb_midi_write(packet); // MIDI Thru: Echo to USB if not for us
|
|
uart_midi_write(packet); // MIDI Thru: Echo to UART if not for us
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void uart_midi_init(void)
|
|
{
|
|
#if MIDI_HW
|
|
// On RP2350, function 11 maps GPIO 26/27 to UART1 TX/RX
|
|
// The normal UART_FUNCSEL_NUM macro can't deal with that
|
|
//
|
|
// Don't even ask how long it took to debug this: I had
|
|
// read the datasheet when setting this all up, but I
|
|
// hadn't connected the dots on UART_FUNCSEL_NUM() not
|
|
// doing the right thing.
|
|
//
|
|
// There is probably some proper way to do this in the
|
|
// SDK, but whatever.
|
|
gpio_set_function(MIDI_OUT, 11);
|
|
gpio_set_function(MIDI_IN, 11);
|
|
|
|
// MIDI idle is +5V, but that is "no current": LED is off,
|
|
// and the TLP2310 drives the MIDI_IN pin low.
|
|
//
|
|
// Standard UART idle is high, but that is easily dealt
|
|
// with by just inverting the GPIO pin
|
|
gpio_set_inover(MIDI_IN, GPIO_OVERRIDE_INVERT);
|
|
|
|
// Let it rip!
|
|
uart_init(MIDI_UART, 31250);
|
|
#endif
|
|
}
|