|
@@ -174,6 +174,8 @@ struct snd_usb_midi_in_endpoint {
|
|
|
u8 running_status_length;
|
|
|
} ports[0x10];
|
|
|
u8 seen_f5;
|
|
|
+ bool in_sysex;
|
|
|
+ u8 last_cin;
|
|
|
u8 error_resubmit;
|
|
|
int current_port;
|
|
|
};
|
|
@@ -467,6 +469,39 @@ static void snd_usbmidi_maudio_broken_running_status_input(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * QinHeng CH345 is buggy: every second packet inside a SysEx has not CIN 4
|
|
|
+ * but the previously seen CIN, but still with three data bytes.
|
|
|
+ */
|
|
|
+static void ch345_broken_sysex_input(struct snd_usb_midi_in_endpoint *ep,
|
|
|
+ uint8_t *buffer, int buffer_length)
|
|
|
+{
|
|
|
+ unsigned int i, cin, length;
|
|
|
+
|
|
|
+ for (i = 0; i + 3 < buffer_length; i += 4) {
|
|
|
+ if (buffer[i] == 0 && i > 0)
|
|
|
+ break;
|
|
|
+ cin = buffer[i] & 0x0f;
|
|
|
+ if (ep->in_sysex &&
|
|
|
+ cin == ep->last_cin &&
|
|
|
+ (buffer[i + 1 + (cin == 0x6)] & 0x80) == 0)
|
|
|
+ cin = 0x4;
|
|
|
+#if 0
|
|
|
+ if (buffer[i + 1] == 0x90) {
|
|
|
+ /*
|
|
|
+ * Either a corrupted running status or a real note-on
|
|
|
+ * message; impossible to detect reliably.
|
|
|
+ */
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ length = snd_usbmidi_cin_length[cin];
|
|
|
+ snd_usbmidi_input_data(ep, 0, &buffer[i + 1], length);
|
|
|
+ ep->in_sysex = cin == 0x4;
|
|
|
+ if (!ep->in_sysex)
|
|
|
+ ep->last_cin = cin;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* CME protocol: like the standard protocol, but SysEx commands are sent as a
|
|
|
* single USB packet preceded by a 0x0F byte.
|
|
@@ -660,6 +695,12 @@ static struct usb_protocol_ops snd_usbmidi_cme_ops = {
|
|
|
.output_packet = snd_usbmidi_output_standard_packet,
|
|
|
};
|
|
|
|
|
|
+static struct usb_protocol_ops snd_usbmidi_ch345_broken_sysex_ops = {
|
|
|
+ .input = ch345_broken_sysex_input,
|
|
|
+ .output = snd_usbmidi_standard_output,
|
|
|
+ .output_packet = snd_usbmidi_output_standard_packet,
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* AKAI MPD16 protocol:
|
|
|
*
|
|
@@ -2380,6 +2421,7 @@ int snd_usbmidi_create(struct snd_card *card,
|
|
|
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
|
|
|
break;
|
|
|
case QUIRK_MIDI_CH345:
|
|
|
+ umidi->usb_protocol_ops = &snd_usbmidi_ch345_broken_sysex_ops;
|
|
|
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
|
|
|
break;
|
|
|
default:
|