[3/3] ALSA USB MIDI: Make amount of MIDI events per output URB selectable by user

Message ID 00d9b60b04efca71748acb006c05217ec8a28ef8.camel@domdv.de
State New
Headers show
  • [1/3] ALSA USB MIDI: Fix port starvation
Related show

Commit Message

Andreas Steinmetz March 14, 2020, 8:11 a.m.
This patch introduces a new module parameter "events" which allows
userspace to limit the amount of MIDI events per output URB. An
"events" value of 0 which is the default means "use wMaxPacketSize".
Out of range values for "events" are clamped to the nearest valid value.

It is assumed that the previous patches are applied and "outqueue=1" is
used. Looking then at a four port USB MIDI interface with a wMaxPacketSize
of 64 and assuming an optimal output rate of 320us/byte the following is

16 MIDI events PER URB
Up to 3 bytes per MIDI event (3 bytes are assumed below)
A driver queue of 1 URB which is completely filled.
Resulting latency is 16*3*1*320us=15.36ms which still is in the
audible range (a latency of less than 5ms is required, think playing
a keyboard to MIDI in and using MIDI out to control a piano module
and using headphones, while using another port for sysex transfers).

After applying this patch and selecting the valid minimum event value
of 4 (there must be at least space for one event per port to assert proper
output balancing) tests show that the throughput isn't really affected,
the worst case latency however drops to 3.84ms which is acceptable.

In case of the largest class compliant devices which do have 16 ports
the worst case latency is then 15.36ms which is tolerable. If this is
still to long it could be fixed by introducing additional complexity
to the balancing of snd_usbmidi_standard_output() at some later stage.

As the device default is used by default no implications are expected for
existing users. By making the parameter writable via sysfs it is possible
to do selective output URB event count management from userspace via a
script that manages USB authorize and the module option.

Signed-off-by: Andreas Steinmetz <ast@domdv.de>


--- a/sound/usb/midi.c	2020-03-13 20:18:33.443265194 +0100
+++ b/sound/usb/midi.c	2020-03-13 20:42:49.445546326 +0100
@@ -79,9 +79,12 @@ 
 static int outqueue = OUTPUT_URBS;
+static int events;
 module_param(outqueue, int, 0644);
-MODULE_PARM_DESC(outqueue, "Outgoing queue size, 1-7 (default: 7).");
+MODULE_PARM_DESC(outqueue, "Outgoing MIDI queue size, 1-7 (default: 7).");
+module_param(events, int, 0644);
+MODULE_PARM_DESC(events, "MIDI events per queue element, 0=device default (default: 0).");
 struct usb_ms_header_descriptor {
@@ -1426,6 +1429,17 @@ 
 	switch (umidi->usb_id) {
 		ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1);
+		if (events) {
+			int ev = 0;
+			for (i = 0; i < 0x10; ++i)
+				if (ep_info->out_cables & (1 << i))
+					ev++;
+			if (events > ev)
+				ev = events;
+			if (ev < ep->max_transfer >> 2)
+				ep->max_transfer = ev << 2;
+		}
 		 * Various chips declare a packet size larger than 4 bytes, but