Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ALSA] usb-audio: work around broken M-Audio MidiSport Uno firmware

The firmware of the M-Audio USB Uno MIDI Interface has, at least in
hardware revision 1.25, a bug that garbles its USB output. When it
receives a Note On MIDI message that uses running status, the resulting
USB MIDI packet has a wrong CIN (4 instead of 9) and a wrong length
(2 bytes, the status byte is still missing).
This patch adds a workaround to track the CINs and the MIDI messages of
received USB MIDI packets to detect whether a packet with CIN 4 is a
correct SysEx packet or a buggy running status packet.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>

authored by

Clemens Ladisch and committed by
Jaroslav Kysela
d05cc104 a9121458

+51 -1
+51 -1
sound/usb/usbmidi.c
··· 1 1 /* 2 2 * usbmidi.c - ALSA USB MIDI driver 3 3 * 4 - * Copyright (c) 2002-2005 Clemens Ladisch 4 + * Copyright (c) 2002-2007 Clemens Ladisch 5 5 * All rights reserved. 6 6 * 7 7 * Based on the OSS usb-midi driver by NAGANO Daisuke, ··· 145 145 struct urb* urb; 146 146 struct usbmidi_in_port { 147 147 struct snd_rawmidi_substream *substream; 148 + u8 running_status_length; 148 149 } ports[0x10]; 149 150 u8 seen_f5; 150 151 u8 error_resubmit; ··· 367 366 } 368 367 369 368 /* 369 + * Buggy M-Audio device: running status on input results in a packet that has 370 + * the data bytes but not the status byte and that is marked with CIN 4. 371 + */ 372 + static void snd_usbmidi_maudio_broken_running_status_input( 373 + struct snd_usb_midi_in_endpoint* ep, 374 + uint8_t* buffer, int buffer_length) 375 + { 376 + int i; 377 + 378 + for (i = 0; i + 3 < buffer_length; i += 4) 379 + if (buffer[i] != 0) { 380 + int cable = buffer[i] >> 4; 381 + u8 cin = buffer[i] & 0x0f; 382 + struct usbmidi_in_port *port = &ep->ports[cable]; 383 + int length; 384 + 385 + length = snd_usbmidi_cin_length[cin]; 386 + if (cin == 0xf && buffer[i + 1] >= 0xf8) 387 + ; /* realtime msg: no running status change */ 388 + else if (cin >= 0x8 && cin <= 0xe) 389 + /* channel msg */ 390 + port->running_status_length = length - 1; 391 + else if (cin == 0x4 && 392 + port->running_status_length != 0 && 393 + buffer[i + 1] < 0x80) 394 + /* CIN 4 that is not a SysEx */ 395 + length = port->running_status_length; 396 + else 397 + /* 398 + * All other msgs cannot begin running status. 399 + * (A channel msg sent as two or three CIN 0xF 400 + * packets could in theory, but this device 401 + * doesn't use this format.) 402 + */ 403 + port->running_status_length = 0; 404 + snd_usbmidi_input_data(ep, cable, &buffer[i + 1], length); 405 + } 406 + } 407 + 408 + /* 370 409 * Adds one USB MIDI packet to the output buffer. 371 410 */ 372 411 static void snd_usbmidi_output_standard_packet(struct urb* urb, uint8_t p0, ··· 564 523 .input = snd_usbmidi_midiman_input, 565 524 .output = snd_usbmidi_standard_output, 566 525 .output_packet = snd_usbmidi_output_midiman_packet, 526 + }; 527 + 528 + static struct usb_protocol_ops snd_usbmidi_maudio_broken_running_status_ops = { 529 + .input = snd_usbmidi_maudio_broken_running_status_input, 530 + .output = snd_usbmidi_standard_output, 531 + .output_packet = snd_usbmidi_output_standard_packet, 567 532 }; 568 533 569 534 /* ··· 1653 1606 switch (quirk ? quirk->type : QUIRK_MIDI_STANDARD_INTERFACE) { 1654 1607 case QUIRK_MIDI_STANDARD_INTERFACE: 1655 1608 err = snd_usbmidi_get_ms_info(umidi, endpoints); 1609 + if (chip->usb_id == USB_ID(0x0763, 0x0150)) /* M-Audio Uno */ 1610 + umidi->usb_protocol_ops = 1611 + &snd_usbmidi_maudio_broken_running_status_ops; 1656 1612 break; 1657 1613 case QUIRK_MIDI_FIXED_ENDPOINT: 1658 1614 memcpy(&endpoints[0], quirk->data,