[alsa-devel] proposed MPD16 latency workaround
Clemens Ladisch
clemens at ladisch.de
Mon Sep 20 09:23:41 CEST 2010
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 at ladisch.de>
+ * Copyright Krzysztof Foltman <wdev at foltman.com>
+ *
+ *
+ * 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 at ladisch.de>");
+MODULE_AUTHOR("Krzysztof Foltman <wdev at foltman.com>");
+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,
More information about the Alsa-devel
mailing list