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
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. This commit uses kernel monotonic time service to calculate the
time of next transaction.
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
if (rcode == RCODE_COMPLETE && substream != NULL)
snd_rawmidi_transmit_ack(substream, port->consume_bytes);
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_ktime = ktime_set(0, 0);
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return;
if (substream == NULL || snd_rawmidi_transmit_empty(substream))
return;
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
/*
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
* Later, snd_rawmidi_transmit_ack() is called.
/*
* Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
* Later, snd_rawmidi_transmit_ack() is called.
port->consume_bytes = port->fill(substream, port->buf);
if (port->consume_bytes <= 0) {
/* Do it in next chance, immediately. */
port->consume_bytes = port->fill(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_ktime = ktime_set(0, 0);
schedule_work(&port->work);
schedule_work(&port->work);
else
type = TCODE_WRITE_BLOCK_REQUEST;
else
type = TCODE_WRITE_BLOCK_REQUEST;
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
/* Start this transaction. */
port->idling = false;
/* Start this transaction. */
port->idling = false;
port->addr = addr;
port->fill = fill;
port->idling = true;
port->addr = addr;
port->fill = fill;
port->idling = true;
+ port->next_ktime = ktime_set(0, 0);
INIT_WORK(&port->work, midi_port_work);
INIT_WORK(&port->work, midi_port_work);
struct fw_device *parent;
struct work_struct work;
bool idling;
struct fw_device *parent;
struct work_struct work;
bool idling;
u64 addr;
struct fw_transaction transaction;
u64 addr;
struct fw_transaction transaction;