Krzysztof Foltman wrote: > On 16/09/10 10:02, Clemens Ladisch wrote: >>> ii) the driver is switched into config mode by sending a special SysEx >>> message to the output port (which is intercepted by the driver and used >>> to set a flag). This is to prevent programs that open all the MIDI ports >>> in the system (JACK daemon with ALSA MIDI driver, a2jmidid etc.) from >>> starting unwanted communication with configuration endpoints. >> >> This can be considered a bug in those programs. > > Yes. Well, it's the case of what's right vs. what's convenient. I think > the assumption that opening a MIDI port (sequencer or raw) won't cause > disastrous results (like increased latency of some other port) is a fair > assumption. It is possible that an opened MIDI port uses resources that would be needed by other devices. >> Since this device uses a completely vendor-dependent protocol, your >> changes result in a driver that uses practically none of the common >> code for the MPD16. > > Well, it _could_ be used for some other USB MIDI devices (the ones using > bulk mode on input endpoints) to reduce data churn on USB ports when > devices are physically connected but their MIDI ports are not used. > Probably isn't worth it though: the power and bandwidth use of the > unnecessary polling is probably minimal. I'd estimate that the power usage is measurable on laptops. Changing the driver to submit URBs only when a port is open is one of those changes that I want to do whenever I have time to do unimportant changes. >> I'll see if I can write it until Monday; you'll just have to test it >> and remove my bugs. :) > > That would be great - if you have time for it. I definitely can take > care of testing/debugging in that case. Well, it turned out bigger than I would have liked. Compile-tested. Handle with care. > To clarify: in this solution, to enable control input, you need to send > a fake sysex on control output. I've changed it so that you can send anything to enable the input. -- Regards, Clemens --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -45,6 +45,15 @@ config SND_USB_USX2Y To compile this driver as a module, choose M here: the module will be called snd-usb-usx2y. +config SND_USB_MPD16 + tristate "Akai MPD16 driver" + select SND_RAWMIDI + help + Say Y here to include support for the Akai MPD16 MIDI controller. + + To compile this driver as a module, choose M here: the module + will be called snd-mpd16. + config SND_USB_CAIAQ tristate "Native Instruments USB audio devices" select SND_HWDEP --- a/sound/usb/misc/Makefile +++ b/sound/usb/misc/Makefile @@ -1,2 +1,4 @@ +snd-mpd16-objs := mpd16.o snd-ua101-objs := ua101.o +obj-$(CONFIG_SND_USB_MPD16) += snd-mpd16.o obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o --- /dev/null +++ b/sound/usb/misc/mpd16.c @@ -0,0 +1,672 @@ +/* + * Akai MPD16 driver + * Copyright Clemens Ladisch <clemens@xxxxxxxxxx> + * Copyright Krzysztof Foltman <wdev@xxxxxxxxxxx> + * + * + * This driver is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this driver; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/rawmidi.h> +#include <sound/asequencer.h> + +MODULE_DESCRIPTION("Akai MPD16 driver"); +MODULE_AUTHOR("Clemens Ladisch <clemens@xxxxxxxxxx>"); +MODULE_AUTHOR("Krzysztof Foltman <wdev@xxxxxxxxxxx>"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("{{Akai,MPD16}}"); + +/* + * AKAI MPD16 protocol: + * + * For control port (endpoint 1): + * ============================== + * One or more chunks consisting of first byte of (0x10 | msg_len) and then a + * SysEx message (msg_len=9 bytes long). + * + * For data port (endpoint 2): + * =========================== + * One or more chunks consisting of first byte of (0x20 | msg_len) and then a + * MIDI message (msg_len bytes long) + * + * Messages sent: Active Sense, Note On, Poly Pressure, Control Change. + */ + +#define CONTROL_OUT_EP 1 +#define CONTROL_IN_EP 1 +#define MIDI_IN_EP 2 + +#define MAX_AKAI_SYSEX_LEN 9 + +struct mpd16 { + struct usb_device *dev; + + spinlock_t lock; + struct mutex mutex; + + struct snd_rawmidi_substream *control_output; + struct snd_rawmidi_substream *control_input; + struct snd_rawmidi_substream *midi_input; + + struct urb *control_output_urb; + struct urb *control_input_urb; + struct urb *midi_input_urbs[2]; + + unsigned int control_output_packet_size; + + bool control_output_was_triggered; + bool control_input_open; + bool control_input_allowed; + bool disconnected; +}; + +static struct usb_device_id id_table[] = { + { USB_DEVICE(0x09e8, 0x0062) }, + { } +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static const char *usb_error_string(int err) +{ + switch (err) { + case -ENODEV: + return "no device"; + case -ENOENT: + return "endpoint not enabled"; + case -EPIPE: + return "endpoint stalled"; + case -ENOSPC: + return "not enough bandwidth"; + case -ESHUTDOWN: + return "device disabled"; + case -EHOSTUNREACH: + return "device suspended"; + case -EINVAL: + case -EAGAIN: + case -EFBIG: + case -EMSGSIZE: + return "internal error"; + default: + return "unknown error"; + } +} + +static void do_control_output(struct mpd16 *mpd16) +{ + struct urb *urb; + u8 *msg; + unsigned int buf_end, count, pos, end; + int err; + + if (!mpd16->control_output) + return; + + urb = mpd16->control_output_urb; + msg = urb->transfer_buffer + urb->transfer_buffer_length; + buf_end = mpd16->control_output_packet_size - MAX_AKAI_SYSEX_LEN - 1; + + /* only try adding more data when there's space for at least 1 SysEx */ + while (urb->transfer_buffer_length < buf_end) { + count = snd_rawmidi_transmit_peek(mpd16->control_output, + msg + 1, MAX_AKAI_SYSEX_LEN); + + /* try to skip non-SysEx data */ + for (pos = 0; pos < count && msg[pos + 1] != 0xf0; pos++) + ; + if (pos > 0) { + snd_rawmidi_transmit_ack(mpd16->control_output, pos); + continue; + } + + /* look for the end of the SysEx data */ + for (end = 1; end < count && msg[end + 1] < 0x80; end++) + ; + /* next command started before the end of current one */ + if (end < count && msg[end + 1] != 0xf7) { + /* it's incomplete - drop it */ + snd_rawmidi_transmit_ack(mpd16->control_output, end); + continue; + } + + /* SysEx complete */ + if (end < count && msg[end + 1] == 0xf7) { + /* queue it, ack it, and get the next one */ + count = end + 1; + msg[0] = 0x10 | count; + snd_rawmidi_transmit_ack(mpd16->control_output, count); + urb->transfer_buffer_length += 1 + count; + msg += 1 + count; + continue; + } + + /* less than 9 bytes and no end byte - wait for more */ + if (count < MAX_AKAI_SYSEX_LEN) { + mpd16->control_output = NULL; + return; + } + + /* 9 bytes and no end marker in sight - malformed, skip it */ + snd_rawmidi_transmit_ack(mpd16->control_output, count); + } + + if (urb->transfer_buffer_length) { + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) + dev_err(&mpd16->dev->dev, "cannot send data (%d): %s\n", + err, usb_error_string(err)); + } +} + +static bool fatal_error_status(struct urb *urb) +{ + return urb->status == -ENOENT || /* killed */ + urb->status == -ENODEV || /* device removed */ + urb->status == -ECONNRESET || /* unlinked */ + urb->status == -ESHUTDOWN; /* device disabled */ +} + +static void control_output_complete(struct urb *urb) +{ + struct mpd16 *mpd16 = urb->context; + unsigned long flags; + + if (unlikely(fatal_error_status(urb))) + return; + + mpd16->control_output_urb->transfer_buffer_length = 0; + + spin_lock_irqsave(&mpd16->lock, flags); + do_control_output(mpd16); + spin_unlock_irqrestore(&mpd16->lock, flags); +} + +static void parse_input(const u8 *buffer, unsigned int length, + struct snd_rawmidi_substream *subs, unsigned int port) +{ + unsigned int pos = 0, received_port, msg_len; + + while (pos < length) { + received_port = buffer[pos] >> 4; + msg_len = buffer[pos] & 0x0f; + pos++; + if (pos + msg_len <= length && received_port == port) + snd_rawmidi_receive(subs, &buffer[pos], msg_len); + pos += msg_len; + } +} + +static void input_urb_complete(struct urb *urb, unsigned int port) +{ + struct mpd16 *mpd16 = urb->context; + unsigned long flags; + struct snd_rawmidi_substream *subs; + int err; + + if (unlikely(fatal_error_status(urb))) + return; + + spin_lock_irqsave(&mpd16->lock, flags); + + switch (port) { + case 1: + subs = mpd16->control_input; + break; + case 2: + subs = mpd16->midi_input; + break; + default: + subs = NULL; + break; + } + + if (subs) { + if (urb->status == 0) + parse_input(urb->transfer_buffer, urb->actual_length, + subs, port); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) + dev_err(&mpd16->dev->dev, "cannot submit URB (%d): %s\n", + err, usb_error_string(err)); + } + + spin_unlock_irqrestore(&mpd16->lock, flags); +} + +static void control_input_complete(struct urb *urb) +{ + input_urb_complete(urb, 1); +} + +static void midi_input_complete(struct urb *urb) +{ + input_urb_complete(urb, 2); +} + +static struct urb *alloc_urb(struct mpd16 *mpd16, unsigned int pipe, + void (*complete)(struct urb *)) +{ + unsigned int packet_size; + struct urb *urb; + + packet_size = usb_maxpacket(mpd16->dev, pipe, usb_pipeout(pipe)); + if (!packet_size) { + dev_err(&mpd16->dev->dev, "endpoint %u(%c) not found\n", + usb_pipeendpoint(pipe), usb_pipeout(pipe) ? 'O' : 'I'); + return NULL; + } + if (packet_size < MAX_AKAI_SYSEX_LEN + 1) { + dev_err(&mpd16->dev->dev, "packet size too small\n"); + return NULL; + } + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + + urb->transfer_buffer = + usb_alloc_coherent(mpd16->dev, packet_size, + GFP_KERNEL, &urb->transfer_dma); + if (!urb->transfer_buffer) { + usb_free_urb(urb); + return NULL; + } + + urb->dev = mpd16->dev; + urb->pipe = pipe; + urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + urb->transfer_buffer_length = packet_size; + urb->context = mpd16; + urb->complete = complete; + + return urb; +} + +static void kill_and_free_urb(struct mpd16 *mpd16, struct urb **urb) +{ + if (*urb) { + usb_kill_urb(*urb); + usb_free_coherent(mpd16->dev, + (*urb)->transfer_buffer_length, + (*urb)->transfer_buffer, + (*urb)->transfer_dma); + usb_free_urb(*urb); + *urb = NULL; + } +} + +static int control_output_open(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + + mutex_lock(&mpd16->mutex); + + if (mpd16->disconnected) { + mutex_unlock(&mpd16->mutex); + return -ENODEV; + } + + mpd16->control_output_urb = + alloc_urb(mpd16, usb_sndbulkpipe(mpd16->dev, CONTROL_OUT_EP), + control_output_complete); + if (!mpd16->control_output_urb) { + mutex_unlock(&mpd16->mutex); + return -ENOMEM; + } + + mpd16->control_output_packet_size = + mpd16->control_output_urb->transfer_buffer_length; + mpd16->control_output_urb->transfer_buffer_length = 0; + + mutex_unlock(&mpd16->mutex); + + return 0; +} + +static int control_output_close(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + + mutex_lock(&mpd16->mutex); + kill_and_free_urb(mpd16, &mpd16->control_output_urb); + mutex_unlock(&mpd16->mutex); + + mpd16->control_output_was_triggered = false; + + return 0; +} + +static void submit_control_input_urb(struct mpd16 *mpd16) +{ + int err; + + err = usb_submit_urb(mpd16->control_input_urb, GFP_ATOMIC); + if (err < 0) + dev_err(&mpd16->dev->dev, "cannot submit URB (%d): %s\n", + err, usb_error_string(err)); +} + +static void control_output_trigger(struct snd_rawmidi_substream *subs, int up) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&mpd16->lock, flags); + + if (up) { + if (!mpd16->control_output && !mpd16->disconnected) { + mpd16->control_output = subs; + mpd16->control_output_was_triggered = true; + + if (mpd16->control_input_open && + !mpd16->control_input_allowed) { + mpd16->control_input_allowed = true; + submit_control_input_urb(mpd16); + } + + do_control_output(mpd16); + } + } else { + mpd16->control_output = NULL; + } + + spin_unlock_irqrestore(&mpd16->lock, flags); +} + +static int control_input_open(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + + mutex_unlock(&mpd16->mutex); + + if (mpd16->disconnected) { + mutex_unlock(&mpd16->mutex); + return -ENODEV; + } + + mpd16->control_input_urb = + alloc_urb(mpd16, usb_rcvbulkpipe(mpd16->dev, CONTROL_IN_EP), + control_input_complete); + if (!mpd16->control_input_urb) { + mutex_unlock(&mpd16->mutex); + return -ENOMEM; + } + mpd16->control_input_urb->transfer_flags |= URB_NO_FSBR; + mpd16->control_input_open = true; + + if (mpd16->control_output_was_triggered) { + mpd16->control_input_allowed = true; + submit_control_input_urb(mpd16); + } + + mutex_unlock(&mpd16->mutex); + + return 0; +} + +static int control_input_close(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + + mutex_lock(&mpd16->mutex); + kill_and_free_urb(mpd16, &mpd16->control_input_urb); + mutex_unlock(&mpd16->mutex); + + mpd16->control_input_open = false; + mpd16->control_input_allowed = false; + + return 0; +} + +static void control_input_trigger(struct snd_rawmidi_substream *subs, int up) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&mpd16->lock, flags); + mpd16->control_input = up ? subs : NULL; + spin_unlock_irqrestore(&mpd16->lock, flags); +} + +static int midi_input_open(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + unsigned int i; + int err; + + mutex_lock(&mpd16->mutex); + + if (mpd16->disconnected) { + mutex_unlock(&mpd16->mutex); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(mpd16->midi_input_urbs); ++i) { + mpd16->midi_input_urbs[i] = + alloc_urb(mpd16, + usb_rcvbulkpipe(mpd16->dev, MIDI_IN_EP), + midi_input_complete); + if (!mpd16->midi_input_urbs[i]) { + err = -ENOMEM; + goto error; + } + } + + for (i = 0; i < ARRAY_SIZE(mpd16->midi_input_urbs); ++i) { + err = usb_submit_urb(mpd16->midi_input_urbs[i], GFP_KERNEL); + if (err < 0) { + dev_err(&mpd16->dev->dev, "cannot submit URB (%d): %s\n", + err, usb_error_string(err)); + goto error; + } + } + + mutex_unlock(&mpd16->mutex); + + return 0; + +error: + while (i > 0) { + --i; + kill_and_free_urb(mpd16, &mpd16->midi_input_urbs[i]); + } + + mutex_unlock(&mpd16->mutex); + + return err; +} + +static int midi_input_close(struct snd_rawmidi_substream *subs) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + unsigned int i; + + mutex_lock(&mpd16->mutex); + for (i = 0; i < ARRAY_SIZE(mpd16->midi_input_urbs); ++i) + kill_and_free_urb(mpd16, &mpd16->midi_input_urbs[i]); + mutex_unlock(&mpd16->mutex); + + return 0; +} + +static void midi_input_trigger(struct snd_rawmidi_substream *subs, int up) +{ + struct mpd16 *mpd16 = subs->rmidi->private_data; + unsigned long flags; + + spin_lock_irqsave(&mpd16->lock, flags); + mpd16->midi_input = up ? subs : NULL; + spin_unlock_irqrestore(&mpd16->lock, flags); +} + +static struct snd_rawmidi_ops control_output_ops = { + .open = control_output_open, + .close = control_output_close, + .trigger = control_output_trigger, +}; + +static struct snd_rawmidi_ops control_input_ops = { + .open = control_input_open, + .close = control_input_close, + .trigger = control_input_trigger, +}; + +static struct snd_rawmidi_ops midi_input_ops = { + .open = midi_input_open, + .close = midi_input_close, + .trigger = midi_input_trigger, +}; + +static void get_port_info(struct snd_rawmidi *rmidi, int number, + struct snd_seq_port_info *seq_port_info) +{ + seq_port_info->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | + SNDRV_SEQ_PORT_TYPE_HARDWARE; +} + +static struct snd_rawmidi_global_ops global_ops = { + .get_port_info = get_port_info, +}; + +static void mpd16_free(struct snd_card *card) +{ + struct mpd16 *mpd16 = card->private_data; + + mutex_destroy(&mpd16->mutex); +} + +static int probe(struct usb_interface *interface, + const struct usb_device_id *usb_id) +{ + struct snd_card *card; + struct mpd16 *mpd16; + struct snd_rawmidi *rmidi; + char usb_path[32]; + int err; + + if (interface->altsetting->desc.bInterfaceNumber != 0) + return -ENODEV; + + err = snd_card_create(-1, NULL, THIS_MODULE, sizeof(*mpd16), &card); + if (err < 0) + return err; + card->private_free = mpd16_free; + mpd16 = card->private_data; + mpd16->dev = interface_to_usbdev(interface); + + spin_lock_init(&mpd16->lock); + mutex_init(&mpd16->mutex); + + snd_card_set_dev(card, &interface->dev); + + strcpy(card->driver, "MPD16"); + strcpy(card->shortname, "MPD16"); + usb_make_path(mpd16->dev, usb_path, sizeof(usb_path)); + snprintf(card->longname, sizeof(card->longname), + "AKAI MPD16 at %s", usb_path); + + err = snd_rawmidi_new(card, "MPD16 Control", 0, 1, 1, &rmidi); + if (err < 0) + goto probe_error; + strcpy(rmidi->name, "MPD16 Control"); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->ops = &global_ops; + rmidi->private_data = mpd16; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &control_output_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &control_input_ops); + + err = snd_rawmidi_new(card, "MPD16 MIDI", 1, 0, 1, &rmidi); + if (err < 0) + goto probe_error; + strcpy(rmidi->name, "MPD16 MIDI"); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT; + rmidi->ops = &global_ops; + rmidi->private_data = mpd16; + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &midi_input_ops); + + err = snd_card_register(card); + if (err < 0) + goto probe_error; + + usb_set_intfdata(interface, card); + + return 0; + +probe_error: + snd_card_free(card); + return err; +} + +static void disconnect(struct usb_interface *interface) +{ + struct snd_card *card = usb_get_intfdata(interface); + struct mpd16 *mpd16; + unsigned int i; + + if (!card) + return; + mpd16 = card->private_data; + + /* make sure that userspace cannot create new requests */ + snd_card_disconnect(card); + + /* make sure that there are no pending USB requests */ + mutex_lock(&mpd16->mutex); + spin_lock_irq(&mpd16->lock); + mpd16->disconnected = true; + mpd16->control_output = NULL; + mpd16->control_input = NULL; + mpd16->midi_input = NULL; + spin_unlock_irq(&mpd16->lock); + kill_and_free_urb(mpd16, &mpd16->control_output_urb); + kill_and_free_urb(mpd16, &mpd16->control_input_urb); + for (i = 0; i < ARRAY_SIZE(mpd16->midi_input_urbs); ++i) + kill_and_free_urb(mpd16, &mpd16->midi_input_urbs[i]); + mutex_unlock(&mpd16->mutex); + + snd_card_free_when_closed(card); +} + +static struct usb_driver driver = { + .name = "snd-mpd16", + .id_table = id_table, + .probe = probe, + .disconnect = disconnect, +#if 0 + .suspend = suspend, + .resume = resume, +#endif +}; + +static int __init alsa_card_mpd16_init(void) +{ + return usb_register(&driver); +} + +static void __exit alsa_card_mpd16_exit(void) +{ + usb_deregister(&driver); +} + +module_init(alsa_card_mpd16_init); +module_exit(alsa_card_mpd16_exit); --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -645,105 +645,6 @@ static struct usb_protocol_ops snd_usbmi }; /* - * AKAI MPD16 protocol: - * - * For control port (endpoint 1): - * ============================== - * One or more chunks consisting of first byte of (0x10 | msg_len) and then a - * SysEx message (msg_len=9 bytes long). - * - * For data port (endpoint 2): - * =========================== - * One or more chunks consisting of first byte of (0x20 | msg_len) and then a - * MIDI message (msg_len bytes long) - * - * Messages sent: Active Sense, Note On, Poly Pressure, Control Change. - */ -static void snd_usbmidi_akai_input(struct snd_usb_midi_in_endpoint *ep, - uint8_t *buffer, int buffer_length) -{ - unsigned int pos = 0; - unsigned int len = (unsigned int)buffer_length; - while (pos < len) { - unsigned int port = (buffer[pos] >> 4) - 1; - unsigned int msg_len = buffer[pos] & 0x0f; - pos++; - if (pos + msg_len <= len && port < 2) - snd_usbmidi_input_data(ep, 0, &buffer[pos], msg_len); - pos += msg_len; - } -} - -#define MAX_AKAI_SYSEX_LEN 9 - -static void snd_usbmidi_akai_output(struct snd_usb_midi_out_endpoint *ep, - struct urb *urb) -{ - uint8_t *msg; - int pos, end, count, buf_end; - uint8_t tmp[MAX_AKAI_SYSEX_LEN]; - struct snd_rawmidi_substream *substream = ep->ports[0].substream; - - if (!ep->ports[0].active) - return; - - msg = urb->transfer_buffer + urb->transfer_buffer_length; - buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; - - /* only try adding more data when there's space for at least 1 SysEx */ - while (urb->transfer_buffer_length < buf_end) { - count = snd_rawmidi_transmit_peek(substream, - tmp, MAX_AKAI_SYSEX_LEN); - if (!count) { - ep->ports[0].active = 0; - return; - } - /* try to skip non-SysEx data */ - for (pos = 0; pos < count && tmp[pos] != 0xF0; pos++) - ; - - if (pos > 0) { - snd_rawmidi_transmit_ack(substream, pos); - continue; - } - - /* look for the start or end marker */ - for (end = 1; end < count && tmp[end] < 0xF0; end++) - ; - - /* next SysEx started before the end of current one */ - if (end < count && tmp[end] == 0xF0) { - /* it's incomplete - drop it */ - snd_rawmidi_transmit_ack(substream, end); - continue; - } - /* SysEx complete */ - if (end < count && tmp[end] == 0xF7) { - /* queue it, ack it, and get the next one */ - count = end + 1; - msg[0] = 0x10 | count; - memcpy(&msg[1], tmp, count); - snd_rawmidi_transmit_ack(substream, count); - urb->transfer_buffer_length += count + 1; - msg += count + 1; - continue; - } - /* less than 9 bytes and no end byte - wait for more */ - if (count < MAX_AKAI_SYSEX_LEN) { - ep->ports[0].active = 0; - return; - } - /* 9 bytes and no end marker in sight - malformed, skip it */ - snd_rawmidi_transmit_ack(substream, count); - } -} - -static struct usb_protocol_ops snd_usbmidi_akai_ops = { - .input = snd_usbmidi_akai_input, - .output = snd_usbmidi_akai_output, -}; - -/* * Novation USB MIDI protocol: number of data bytes is in the first byte * (when receiving) (+1!) or in the second byte (when sending); data begins * at the third byte. @@ -1533,11 +1434,6 @@ static struct port_info { EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"), EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"), EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"), - /* Akai MPD16 */ - CONTROL_PORT(0x09e8, 0x0062, 0, "%s Control"), - PORT_INFO(0x09e8, 0x0062, 1, "%s MIDI", 0, - SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | - SNDRV_SEQ_PORT_TYPE_HARDWARE), /* Access Music Virus TI */ EXTERNAL_PORT(0x133e, 0x0815, 0, "%s MIDI"), PORT_INFO(0x133e, 0x0815, 1, "%s Synth", 0, @@ -2139,12 +2035,6 @@ int snd_usbmidi_create(struct snd_card * umidi->usb_protocol_ops = &snd_usbmidi_cme_ops; err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); break; - case QUIRK_MIDI_AKAI: - umidi->usb_protocol_ops = &snd_usbmidi_akai_ops; - err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints); - /* endpoint 1 is input-only */ - endpoints[1].out_cables = 0; - break; default: snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type); err = -ENXIO; --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -2120,17 +2120,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -/* AKAI devices */ -{ - USB_DEVICE(0x09e8, 0x0062), - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "AKAI", - .product_name = "MPD16", - .ifnum = 0, - .type = QUIRK_MIDI_AKAI, - } -}, - /* TerraTec devices */ { USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0012), --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -290,7 +290,6 @@ int snd_usb_create_quirk(struct snd_usb_ [QUIRK_MIDI_FASTLANE] = create_any_midi_quirk, [QUIRK_MIDI_EMAGIC] = create_any_midi_quirk, [QUIRK_MIDI_CME] = create_any_midi_quirk, - [QUIRK_MIDI_AKAI] = create_any_midi_quirk, [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk, [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk, [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk, --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -73,7 +73,6 @@ enum quirk_type { QUIRK_MIDI_FASTLANE, QUIRK_MIDI_EMAGIC, QUIRK_MIDI_CME, - QUIRK_MIDI_AKAI, QUIRK_MIDI_US122L, QUIRK_AUDIO_STANDARD_INTERFACE, QUIRK_AUDIO_FIXED_ENDPOINT, _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel