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>
75 lines
2.5 KiB
C
75 lines
2.5 KiB
C
//
|
|
// Check the USB-MIDI Code Index Number tables in Software/midi.h.
|
|
//
|
|
// A lookup table is exactly the sort of thing that compiles cleanly and
|
|
// is wrong, and this one is only compiled at all when MIDI_HW is on -
|
|
// which it is not by default - so a mistake here would sit unnoticed.
|
|
//
|
|
// Build and run with 'make test-midi-cin && ./test-midi-cin'.
|
|
//
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include "midi.h"
|
|
|
|
static int fails;
|
|
static void chk(const char *what, int got, int want)
|
|
{
|
|
if (got != want) {
|
|
printf("FAIL %-28s got %d want %d\n", what, got, want);
|
|
fails++;
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
// Channel voice: CIN is the top nibble, and the lengths are the
|
|
// real MIDI data lengths plus the status byte.
|
|
struct { uint8_t status; int cin, len; } v[] = {
|
|
{ 0x80, 0x8, 3 }, // note off
|
|
{ 0x90, 0x9, 3 }, // note on
|
|
{ 0xA0, 0xA, 3 }, // poly key pressure - used to be dropped
|
|
{ 0xB0, 0xB, 3 }, // control change
|
|
{ 0xC0, 0xC, 2 }, // program change
|
|
{ 0xD0, 0xD, 2 }, // channel pressure
|
|
{ 0xE0, 0xE, 3 }, // pitch bend
|
|
{ 0xF1, 0x2, 2 }, // MIDI time code
|
|
{ 0xF2, 0x3, 3 }, // song position
|
|
{ 0xF3, 0x2, 2 }, // song select
|
|
{ 0xF6, 0x5, 1 }, // tune request
|
|
{ 0xF8, 0xF, 1 }, // clock - used to be dropped
|
|
{ 0xFA, 0xF, 1 }, // start
|
|
{ 0xFC, 0xF, 1 }, // stop
|
|
{ 0xFF, 0xF, 1 }, // reset
|
|
};
|
|
for (unsigned i = 0; i < sizeof(v)/sizeof(v[0]); i++) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof buf, "status %02x cin", v[i].status);
|
|
chk(buf, midi_status_cin(v[i].status), v[i].cin);
|
|
snprintf(buf, sizeof buf, "status %02x len", v[i].status);
|
|
chk(buf, midi_cin_length(midi_status_cin(v[i].status)), v[i].len);
|
|
}
|
|
|
|
// Every channel-voice status must round-trip to a usable length,
|
|
// regardless of channel nibble.
|
|
for (int s = 0x80; s <= 0xEF; s++) {
|
|
uint8_t cin = midi_status_cin(s);
|
|
if (cin != (s >> 4)) { printf("FAIL cin nibble %02x\n", s); fails++; }
|
|
if (midi_cin_length(cin) == 0) { printf("FAIL zero len %02x\n", s); fails++; }
|
|
}
|
|
|
|
// SysEx CINs carry data even though no single status byte maps to
|
|
// them - this is what uart_midi_write() used to throw away.
|
|
chk("sysex start/cont", midi_cin_length(0x4), 3);
|
|
chk("sysex end 1", midi_cin_length(0x5), 1);
|
|
chk("sysex end 2", midi_cin_length(0x6), 2);
|
|
chk("sysex end 3", midi_cin_length(0x7), 3);
|
|
|
|
// Reserved CINs carry nothing
|
|
chk("reserved 0", midi_cin_length(0x0), 0);
|
|
chk("reserved 1", midi_cin_length(0x1), 0);
|
|
|
|
printf(fails ? "%d failures\n" : "all CIN checks pass\n", fails);
|
|
return !!fails;
|
|
}
|