All of lore.kernel.org
 help / color / mirror / Atom feed
* proposed MPD16 latency workaround
@ 2010-09-15 23:21 Krzysztof Foltman
  2010-09-16  9:02 ` Clemens Ladisch
  0 siblings, 1 reply; 6+ messages in thread
From: Krzysztof Foltman @ 2010-09-15 23:21 UTC (permalink / raw)
  To: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 1676 bytes --]

  Hi all,

I've played with USB MIDI driver and MPD16 a bit more. The problem is 
definitely triggered by submitting URBs on the configuration input 
endpoint - when these are killed, the latency disappears. Disabling the 
configuration port works, but is a bit heavy-handed, so I've implemented 
a workaround: initially, the URBs for the input configuration endpoint 
are not submitted until two conditions are met:

i) the control port is actually open (I check that using input_triggered 
bitmask)

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.

Any time one of these conditions is changed, the driver may either 
submit the URBs or kill them or do nothing - depending on the difference 
between expected and current "queuing state" of the URBs. This logic is 
only used for MPD16, for all the other devices the driver always submit 
their input URBs, no matter if any input is actually wanted or not.

The attached diff is relative to the Ubuntu 2.6.35 kernel, but I'm 
mostly interested in the review of the general approach - I have some 
doubts about thread-safety of calling snd_usbmidi_input_update_ep from 
send function and about the elegance of the SysEx approach (vs. ioctl or 
something else). If the approach is not too icky and doesn't have hidden 
flaws, I can try to create a diff relative to alsa-kernel or some other 
tree.

Any comments?
Krzysztof


[-- Attachment #2: mpd16-control-port.diff --]
[-- Type: text/x-diff, Size: 6729 bytes --]

--- linux-source-2.6.35/sound/usb/midi.c	2010-08-01 23:11:14.000000000 +0100
+++ /home/kfoltman/midi.c	2010-09-16 00:03:12.000000000 +0100
@@ -70,6 +70,9 @@
 #define OUTPUT_URBS 7
 #define INPUT_URBS 7
 
+#define EP_UPDATE_MODE_OFF 0
+#define EP_UPDATE_MODE_AUTO 1
+#define EP_UPDATE_MODE_ON 2
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("USB Audio/MIDI helper module");
@@ -102,6 +105,7 @@
 	void (*output_packet)(struct urb*, uint8_t, uint8_t, uint8_t, uint8_t);
 	void (*init_out_endpoint)(struct snd_usb_midi_out_endpoint*);
 	void (*finish_out_endpoint)(struct snd_usb_midi_out_endpoint*);
+	int (*input_wanted)(struct snd_usb_midi_in_endpoint *);
 };
 
 struct snd_usb_midi {
@@ -125,6 +129,7 @@
 	unsigned long input_triggered;
 	unsigned int opened;
 	unsigned char disconnected;
+	unsigned char akai_config_mode;
 
 	struct snd_kcontrol *roland_load_ctl;
 };
@@ -171,11 +176,16 @@
 	} ports[0x10];
 	u8 seen_f5;
 	u8 error_resubmit;
+	u8 enabled;
+	u8 running;
 	int current_port;
 };
 
 static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint* ep);
 
+static void snd_usbmidi_input_update_ep(struct snd_usb_midi_in_endpoint* ep,
+					int mode);
+
 static const uint8_t snd_usbmidi_cin_length[] = {
 	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
 };
@@ -266,8 +276,10 @@
 		}
 	}
 
-	urb->dev = ep->umidi->dev;
-	snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+	if (ep->enabled) {
+		urb->dev = ep->umidi->dev;
+		snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+	}
 }
 
 static void snd_usbmidi_out_urb_complete(struct urb* urb)
@@ -658,7 +670,30 @@
  * MIDI message (msg_len bytes long)
  *
  * Messages sent: Active Sense, Note On, Poly Pressure, Control Change.
+ *
+ * The control port should not be polled unless configuration operation is
+ * performed, as polling it tends to interfere with proper transfer on the
+ * data port (reducing polling rate to impractical values)
  */
+static int snd_usbmidi_akai_input_wanted(struct snd_usb_midi_in_endpoint* ep)
+{
+	/* only one port for an endpoint */
+	struct usbmidi_in_port* port = &ep->ports[0];
+
+	if (!port->substream) {
+		snd_printd("unexpected port %d!\n", portidx);
+		return 0;
+	}
+	if (port->substream->number > 0)
+		return 1;
+	/* do not poll the config endpoint until:
+	  1) config mode is switched on (via fake SysEx)
+	  2) the port is open
+	 */
+	return ep->umidi->akai_config_mode && 
+		test_bit(port->substream->number, &ep->umidi->input_triggered);
+}
+
 static void snd_usbmidi_akai_input(struct snd_usb_midi_in_endpoint *ep,
 				   uint8_t *buffer, int buffer_length)
 {
@@ -719,6 +754,14 @@
 		}
 		/* SysEx complete */
 		if (end < count && tmp[end] == 0xF7) {
+			/* Fake SysEx to enable/disable polling of the control port */
+			if (count == 9 &&
+			    !memcmp(tmp, "\xF0\x47\x62\x60\x10\x7F", 6)) {
+				ep->umidi->akai_config_mode = tmp[6];
+				snd_usbmidi_input_update_ep(ep->umidi->endpoints[0].in, EP_UPDATE_MODE_AUTO);
+				snd_rawmidi_transmit_ack(substream, end + 1);
+				continue;
+			}
 			/* queue it, ack it, and get the next one */
 			count = end + 1;
 			msg[0] = 0x10 | count;
@@ -741,6 +784,7 @@
 static struct usb_protocol_ops snd_usbmidi_akai_ops = {
 	.input = snd_usbmidi_akai_input,
 	.output = snd_usbmidi_akai_output,
+	.input_wanted = snd_usbmidi_akai_input_wanted
 };
 
 /*
@@ -1124,11 +1168,21 @@
 static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 {
 	struct snd_usb_midi* umidi = substream->rmidi->private_data;
-
+	int i;
+	unsigned long old_value = umidi->input_triggered;
+	
 	if (up)
 		set_bit(substream->number, &umidi->input_triggered);
 	else
 		clear_bit(substream->number, &umidi->input_triggered);
+
+	if (old_value != umidi->input_triggered) {
+		for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+			struct snd_usb_midi_in_endpoint* ep = umidi->endpoints[i].in;
+			if (ep)
+				snd_usbmidi_input_update_ep(ep, EP_UPDATE_MODE_AUTO);
+		}
+	}
 }
 
 static struct snd_rawmidi_ops snd_usbmidi_output_ops = {
@@ -2013,33 +2067,56 @@
 	return 0;
 }
 
+static void snd_usbmidi_input_update_ep(struct snd_usb_midi_in_endpoint* ep,
+					int mode)
+{
+	unsigned int i;
+	int (*input_wanted)(struct snd_usb_midi_in_endpoint *);
+	
+	if (!ep)
+		return;
+
+	input_wanted = ep->umidi->usb_protocol_ops->input_wanted;
+	if (mode == EP_UPDATE_MODE_AUTO && input_wanted)
+		ep->enabled = input_wanted(ep);
+	else
+		ep->enabled = (mode != EP_UPDATE_MODE_OFF);
+
+	snd_printk(KERN_ERR "Before EP %p Card %s Enabled %d running %d\n", ep, ep->umidi->card->shortname, (int)ep->enabled, (int)ep->running);
+	
+	/* if just switched off, kill the URBs */
+	if (ep->running && !ep->enabled) {
+		snd_printk(KERN_ERR "Killing URBs\n");
+		for (i = 0; i < INPUT_URBS; ++i)
+			usb_kill_urb(ep->urbs[i]);
+		ep->running = 0;
+	}
+	/* if just switched on, submit the URBs */
+	if (!ep->running && ep->enabled) {
+		snd_printk(KERN_ERR "Submitting URBs\n");
+		for (i = 0; i < INPUT_URBS; ++i) {
+			struct urb* urb = ep->urbs[i];
+			urb->dev = ep->umidi->dev;
+			snd_usbmidi_submit_urb(urb, GFP_KERNEL);
+		}
+		ep->running = 1;
+	}
+	snd_printk(KERN_ERR "After EP %p Card %s Enabled %d running %d\n", ep, ep->umidi->card->shortname, (int)ep->enabled, (int)ep->running);
+}
+
 /*
  * Temporarily stop input.
  */
 void snd_usbmidi_input_stop(struct list_head* p)
 {
 	struct snd_usb_midi* umidi;
-	unsigned int i, j;
+	unsigned int i;
 
 	umidi = list_entry(p, struct snd_usb_midi, list);
 	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
-		struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i];
-		if (ep->in)
-			for (j = 0; j < INPUT_URBS; ++j)
-				usb_kill_urb(ep->in->urbs[j]);
-	}
-}
-
-static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep)
-{
-	unsigned int i;
-
-	if (!ep)
-		return;
-	for (i = 0; i < INPUT_URBS; ++i) {
-		struct urb* urb = ep->urbs[i];
-		urb->dev = ep->umidi->dev;
-		snd_usbmidi_submit_urb(urb, GFP_KERNEL);
+		struct snd_usb_midi_in_endpoint* ep = umidi->endpoints[i].in;
+		if (ep)
+			snd_usbmidi_input_update_ep(ep, EP_UPDATE_MODE_OFF);
 	}
 }
 
@@ -2052,8 +2129,11 @@
 	int i;
 
 	umidi = list_entry(p, struct snd_usb_midi, list);
-	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
-		snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+		struct snd_usb_midi_in_endpoint* ep = umidi->endpoints[i].in;
+		if (ep)
+			snd_usbmidi_input_update_ep(ep, EP_UPDATE_MODE_AUTO);
+	}
 }
 
 /*
@@ -2181,7 +2261,8 @@
 	list_add_tail(&umidi->list, midi_list);
 
 	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
-		snd_usbmidi_input_start_ep(umidi->endpoints[i].in);
+		snd_usbmidi_input_update_ep(umidi->endpoints[i].in,
+					    EP_UPDATE_MODE_AUTO);
 	return 0;
 }
 

[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: proposed MPD16 latency workaround
  2010-09-15 23:21 proposed MPD16 latency workaround Krzysztof Foltman
@ 2010-09-16  9:02 ` Clemens Ladisch
  2010-09-16 13:37   ` Krzysztof Foltman
  0 siblings, 1 reply; 6+ messages in thread
From: Clemens Ladisch @ 2010-09-16  9:02 UTC (permalink / raw)
  To: Krzysztof Foltman; +Cc: alsa-devel

Krzysztof Foltman wrote:
> I've played with USB MIDI driver and MPD16 a bit more. The problem is 
> definitely triggered by submitting URBs on the configuration input 
> endpoint - when these are killed, the latency disappears. Disabling the 
> configuration port works, but is a bit heavy-handed, so I've implemented 
> a workaround: initially, the URBs for the input configuration endpoint 
> are not submitted until two conditions are met:
> 
> i) the control port is actually open (I check that using input_triggered 
> bitmask)
> 
> 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.

> I'm mostly interested in the review of the general approach - I have
> some doubts about thread-safety of calling snd_usbmidi_input_update_ep
> from send function

I also have doubts, but I didn't look too much into this.

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.  Furthermore, your code makes it harder to maintain
the driver because these parts of the code cannot be tested without the
MPD16.  In other words, I think that writing a separate driver would be
a better idea.

I'll see if I can write it until Monday; you'll just have to test it
and remove my bugs.  :)

> and about the elegance of the SysEx approach (vs. ioctl or something
> else).

If the control port is used only by some specialized control
application, it doesn't matter too much which mechanism is used.
If the data port is already being used by it, the SysEx approach
would indeed be the simplest to use.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: proposed MPD16 latency workaround
  2010-09-16  9:02 ` Clemens Ladisch
@ 2010-09-16 13:37   ` Krzysztof Foltman
  2010-09-20  7:23     ` Clemens Ladisch
  0 siblings, 1 reply; 6+ messages in thread
From: Krzysztof Foltman @ 2010-09-16 13:37 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel

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.

Also, working around the problem in the central place (kernel driver) 
is, in my opinion, more convenient than fixing that otherwise-harmless 
behaviour in many other projects (jackd1, jackd2, a2jmidid, another JACK 
implementation by Torben Hohn that I don't know the name of). Especially 
given the minimal popularity of the MPD16.

> 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.

The MPD16 code *does* use some things in the generic driver, like most 
of the setup code. The only differences are: 1) I/O quirks (and many 
other devices have those), 2) conditional opening of endpoints 
(potentially reusable). I don't think the impact is very severe, 
comparing with the Roland stuff in the same file, for example.

> 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.

> If the control port is used only by some specialized control
> application, it doesn't matter too much which mechanism is used.
> If the data port is already being used by it, the SysEx approach
> would indeed be the simplest to use.

To clarify: in this solution, to enable control input, you need to send 
a fake sysex on control output. The device has no functional *data* 
output, as far as I can tell - there's only data input which sends 
events from the pads and the slider.

I thought of using alternatives like a hwdep device, a mixer control or 
an ioctl, but those were frighteningly hard to implement and not really 
much nicer for the user. And the control port talks MIDI (SysEx 
messages) anyway.

That specialized control application doesn't even exist at this point. 
So, a low-effort alternative option to consider would be to completely 
drop the ability to configure the device, as most of the configuration 
can be done from the device anyway (the inconvenient but working "hold 
some button, press some pads" type of interface).

If I had to choose between "enormous latency and automatic 
configuration... maybe some day" and "imperceptible latency, but have to 
set it up manually", I'd definitely choose the latter.

Krzysztof

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: proposed MPD16 latency workaround
  2010-09-16 13:37   ` Krzysztof Foltman
@ 2010-09-20  7:23     ` Clemens Ladisch
  2010-09-21 10:01       ` Krzysztof Foltman
  0 siblings, 1 reply; 6+ messages in thread
From: Clemens Ladisch @ 2010-09-20  7:23 UTC (permalink / raw)
  To: Krzysztof Foltman; +Cc: alsa-devel

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@ladisch.de>
+ * Copyright Krzysztof Foltman <wdev@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@ladisch.de>");
+MODULE_AUTHOR("Krzysztof Foltman <wdev@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,

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: proposed MPD16 latency workaround
  2010-09-20  7:23     ` Clemens Ladisch
@ 2010-09-21 10:01       ` Krzysztof Foltman
  2010-09-21 11:22         ` Clemens Ladisch
  0 siblings, 1 reply; 6+ messages in thread
From: Krzysztof Foltman @ 2010-09-21 10:01 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: ALSA Development Mailing List

On 20/09/10 08:23, Clemens Ladisch wrote:

> It is possible that an opened MIDI port uses resources that would be
> needed by other devices.

True.

> 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.

In that case, you can probably just use my changes as a starting point. 
They should be easily adaptable for at least some of the devices (I 
think only multiple cables per port are hard to support with current 
set-up). Some day, maybe :)

> Well, it turned out bigger than I would have liked.
> Compile-tested.  Handle with care.

Checked it yesterday. The data input works, however, the trying to 
output some sysex data with amidi prints the "bad dma" messages from the 
UHCI driver, and data don't end up on a device. Also, either on 
disconnection or rmmod it OOPSes (can't tell which of them, it came up 
in the dmesg output but I couldn't do proper tests yet).

I'll try to investigate it further when I have a couple of hours.

That aside, why do you need URB_NO_FSBR flag for the URBs? Because of 
prevalence of very short packets in the Akai MIDI protocol?

>> 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.

That might cause some problems if I (or anyone else) create the config 
program. They're avoidable (only run the config tool while JACK is not 
running), but still, it's not what I'd expect as a user.

I'll try to write a patch for all JACKs to blacklist the control device, 
that should take care of the JACK incompatibility.

Krzysztof

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: proposed MPD16 latency workaround
  2010-09-21 10:01       ` Krzysztof Foltman
@ 2010-09-21 11:22         ` Clemens Ladisch
  0 siblings, 0 replies; 6+ messages in thread
From: Clemens Ladisch @ 2010-09-21 11:22 UTC (permalink / raw)
  To: Krzysztof Foltman; +Cc: ALSA Development Mailing List

Krzysztof Foltman wrote:
> Checked it yesterday. The data input works, however, the trying to
> output some sysex data with amidi prints the "bad dma" messages from the
> UHCI driver,

Never heard of that message. 

> Also, either on disconnection or rmmod it OOPSes (can't tell which of
> them, it came up in the dmesg output but I couldn't do proper tests yet).

Might be the same problem with some DMA buffer ...

> That aside, why do you need URB_NO_FSBR flag for the URBs?

It's optional, so it isn't really needed.  I'm using it for control
input transfers because those aren't latency sensitive, and using less
polling might help with the latency problem on the data input endpoint.

> I'll try to write a patch for all JACKs to blacklist the control device,
> that should take care of the JACK incompatibility.

In theory, Jack should never open any MIDI port that isn't actually used.


Regards,
Clemens

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2010-09-21 11:21 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-15 23:21 proposed MPD16 latency workaround Krzysztof Foltman
2010-09-16  9:02 ` Clemens Ladisch
2010-09-16 13:37   ` Krzysztof Foltman
2010-09-20  7:23     ` Clemens Ladisch
2010-09-21 10:01       ` Krzysztof Foltman
2010-09-21 11:22         ` Clemens Ladisch

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.