diff mbox

[06/25] ALSA: firewire-lib: add throttle for MIDI data rate

Message ID 1440235181-18181-7-git-send-email-o-takashi@sakamocchi.jp (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Sakamoto Aug. 22, 2015, 9:19 a.m. UTC
Typically, the target devices have internal buffer to adjust output of
received MIDI messages for MIDI serial bus, while the capacity of the
buffer is limited. IEEE 1394 transactions can transfer more MIDI messages
than MIDI serial bus can. This can cause buffer over flow in device side.

This commit adds throttle to limit MIDI data rate by counting intervals
by jiffies between two MIDI messages. Usual MIDI messages consists of two
or three bytes. This requires 1.302 to 1.953 mili-seconds interval between
these messages. Using jiffies for this purpose is not perfect idea.
Further work is needed for better implementation.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/lib.c | 26 +++++++++++++++++++++++++-
 sound/firewire/lib.h |  1 +
 2 files changed, 26 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 83d20b6..5d04632 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -76,6 +76,9 @@  static void async_midi_port_callback(struct fw_card *card, int rcode,
 
 	if (rcode == RCODE_COMPLETE && substream != NULL)
 		snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+	else if (!rcode_is_permanent_error(rcode))
+		/* To start next transaction immediately for recovery. */
+		port->next_tick = 0;
 
 	port->idling = true;
 
@@ -99,6 +102,12 @@  static void midi_port_tasklet(unsigned long data)
 	if (substream == NULL || snd_rawmidi_transmit_empty(substream))
 		return;
 
+	/* Do it in next chance. */
+	if (time_before64(get_jiffies_64(), port->next_tick)) {
+		tasklet_schedule(&port->tasklet);
+		return;
+	}
+
 	/*
 	 * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
 	 * Later, snd_rawmidi_transmit_ack() is called.
@@ -107,8 +116,10 @@  static void midi_port_tasklet(unsigned long data)
 	port->consume_bytes = port->packetize(substream, port->buf);
 	if (port->consume_bytes <= 0) {
 		/* Do it in next chance, immediately. */
-		if (port->consume_bytes == 0)
+		if (port->consume_bytes == 0) {
+			port->next_tick = 0;
 			tasklet_schedule(&port->tasklet);
+		}
 		return;
 	}
 
@@ -118,6 +129,18 @@  static void midi_port_tasklet(unsigned long data)
 	else
 		type = TCODE_WRITE_BLOCK_REQUEST;
 
+	/*
+	 * Set interval to next transaction.
+	 *
+	 * TODO: Using jiffies to count 31,250 bits per second may be a bad
+	 * idea. The jiffies is assumed to count up constantly according to HZ,
+	 * while recent Linux goes to dynamic ticks or tickless system. Further
+	 * work is required and the interval should be set with the other
+	 * services, i.e. by using monotonic time with higher resolution.
+	 */
+	port->next_tick = jiffies_64 +
+		msecs_to_jiffies(port->consume_bytes * 8 * 1000 / 31250);
+
 	/* Start this transaction. */
 	port->idling = false;
 
@@ -163,6 +186,7 @@  int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
 	port->addr = addr;
 	port->packetize = packetize;
 	port->idling = true;
+	port->next_tick = 0;
 
 	tasklet_init(&port->tasklet, midi_port_tasklet, (unsigned long)port);
 
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index 9d76f5c..bfc353b 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -26,6 +26,7 @@  struct snd_fw_async_midi_port {
 	struct fw_device *parent;
 	struct tasklet_struct tasklet;
 	bool idling;
+	__u64 next_tick;
 
 	__u64 addr;
 	struct fw_transaction transaction;