Skip to content

Commit c282040

Browse files
Ricard Wanderloftiwai
authored andcommitted
ALSA: usb-audio: Fix CME quirk for UF series keyboards
Fix quirk for CME master keyboards so it not only handles sysex but also song position pointer, MIDI timing clock, start and stop messages, and active sensing. All of these can be output by the CME UF series master keyboards. Tested with a CME UF6 in a desktop Linux environment as well as on the Zynthian Raspberry Pi based platform. Signed-off-by: Ricard Wanderlof <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 9335a36 commit c282040

File tree

1 file changed

+74
-6
lines changed

1 file changed

+74
-6
lines changed

sound/usb/midi.c

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -489,16 +489,84 @@ static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
489489

490490
/*
491491
* CME protocol: like the standard protocol, but SysEx commands are sent as a
492-
* single USB packet preceded by a 0x0F byte.
492+
* single USB packet preceded by a 0x0F byte, as are system realtime
493+
* messages and MIDI Active Sensing.
494+
* Also, multiple messages can be sent in the same packet.
493495
*/
494496
static void snd_usbmidi_cme_input(struct snd_usb_midi_in_endpoint *ep,
495497
uint8_t *buffer, int buffer_length)
496498
{
497-
if (buffer_length < 2 || (buffer[0] & 0x0f) != 0x0f)
498-
snd_usbmidi_standard_input(ep, buffer, buffer_length);
499-
else
500-
snd_usbmidi_input_data(ep, buffer[0] >> 4,
501-
&buffer[1], buffer_length - 1);
499+
int remaining = buffer_length;
500+
501+
/*
502+
* CME send sysex, song position pointer, system realtime
503+
* and active sensing using CIN 0x0f, which in the standard
504+
* is only intended for single byte unparsed data.
505+
* So we need to interpret these here before sending them on.
506+
* By default, we assume single byte data, which is true
507+
* for system realtime (midi clock, start, stop and continue)
508+
* and active sensing, and handle the other (known) cases
509+
* separately.
510+
* In contrast to the standard, CME does not split sysex
511+
* into multiple 4-byte packets, but lumps everything together
512+
* into one. In addition, CME can string multiple messages
513+
* together in the same packet; pressing the Record button
514+
* on an UF6 sends a sysex message directly followed
515+
* by a song position pointer in the same packet.
516+
* For it to have any reasonable meaning, a sysex message
517+
* needs to be at least 3 bytes in length (0xf0, id, 0xf7),
518+
* corresponding to a packet size of 4 bytes, and the ones sent
519+
* by CME devices are 6 or 7 bytes, making the packet fragments
520+
* 7 or 8 bytes long (six or seven bytes plus preceding CN+CIN byte).
521+
* For the other types, the packet size is always 4 bytes,
522+
* as per the standard, with the data size being 3 for SPP
523+
* and 1 for the others.
524+
* Thus all packet fragments are at least 4 bytes long, so we can
525+
* skip anything that is shorter; this also conveniantly skips
526+
* packets with size 0, which CME devices continuously send when
527+
* they have nothing better to do.
528+
* Another quirk is that sometimes multiple messages are sent
529+
* in the same packet. This has been observed for midi clock
530+
* and active sensing i.e. 0x0f 0xf8 0x00 0x00 0x0f 0xfe 0x00 0x00,
531+
* but also multiple note ons/offs, and control change together
532+
* with MIDI clock. Similarly, some sysex messages are followed by
533+
* the song position pointer in the same packet, and occasionally
534+
* additionally by a midi clock or active sensing.
535+
* We handle this by looping over all data and parsing it along the way.
536+
*/
537+
while (remaining >= 4) {
538+
int source_length = 4; /* default */
539+
540+
if ((buffer[0] & 0x0f) == 0x0f) {
541+
int data_length = 1; /* default */
542+
543+
if (buffer[1] == 0xf0) {
544+
/* Sysex: Find EOX and send on whole message. */
545+
/* To kick off the search, skip the first
546+
* two bytes (CN+CIN and SYSEX (0xf0).
547+
*/
548+
uint8_t *tmp_buf = buffer + 2;
549+
int tmp_length = remaining - 2;
550+
551+
while (tmp_length > 1 && *tmp_buf != 0xf7) {
552+
tmp_buf++;
553+
tmp_length--;
554+
}
555+
data_length = tmp_buf - buffer;
556+
source_length = data_length + 1;
557+
} else if (buffer[1] == 0xf2) {
558+
/* Three byte song position pointer */
559+
data_length = 3;
560+
}
561+
snd_usbmidi_input_data(ep, buffer[0] >> 4,
562+
&buffer[1], data_length);
563+
} else {
564+
/* normal channel events */
565+
snd_usbmidi_standard_input(ep, buffer, source_length);
566+
}
567+
buffer += source_length;
568+
remaining -= source_length;
569+
}
502570
}
503571

504572
/*

0 commit comments

Comments
 (0)