[PATCH 10/11] digi00x: improve MIDI capture/playback

Takashi Sakamoto
2015-03-15T16:01:21Z

Digi 002/003 family are not conformant to IEC 61883-6:2005 or MMA/AMEI
RP-027. In fact, they uses AM824 format data, but data channel includes
port number in its LSB. The MSB is always 0x80, even if the data channel
includes any MIDI messages. As a result, every MIDI conformant data
channel can transfer maximum 2 bytes.

This commit adds own callback functions to handle this quirk.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
sound/firewire/digi00x/digi00x-protocol.c | 48 +++++++++++++++++++++++++++++++
sound/firewire/digi00x/digi00x-stream.c | 3 ++
sound/firewire/digi00x/digi00x.h | 4 +++
3 files changed, 55 insertions(+)

diff --git a/sound/firewire/digi00x/digi00x-protocol.c b/sound/firewire/digi00x/digi00x-protocol.c
index 3e5b3bec..4dd373d 100644
--- a/sound/firewire/digi00x/digi00x-protocol.c
+++ b/sound/firewire/digi00x/digi00x-protocol.c
@@ -8,6 +8,54 @@

#include "digi00x.h"

+void snd_dg00x_protocol_fill_midi(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ unsigned int f, port;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ port = (s->data_block_counter + f) % 4;
+ b = (u8 *)&buffer[s->midi_position];
+
+ /*
+ * The device allows to transfer MIDI messages by maximum two
+ * bytes per data channel. But this module transfers one byte
+ * one time because MIDI data rate is quite lower than IEEE
+ * 1394 bus data rate.
+ */
+ if (amdtp_midi_ratelimit_per_packet(s, port) &&
+ s->midi[port] != NULL &&
+ snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
+ amdtp_midi_rate_use_one_byte(s, port);
+ b[3] = 0x01 | (0x10 << port);
+ } else {
+ b[1] = 0;
+ b[3] = 0;
+ }
+ b[0] = 0x80;
+ b[2] = 0;
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+void snd_dg00x_protocol_pull_midi(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames)
+{
+ unsigned int f;
+ u8 *b;
+
+ for (f = 0; f < frames; f++) {
+ b = (u8 *)&buffer[s->midi_position];
+
+ if (s->midi[0] && (b[3] > 0))
+ snd_rawmidi_receive(s->midi[0], b + 1, b[3]);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
struct workqueue_struct *midi_wq;

static void send_midi_control(struct work_struct *work)
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
index 92eb86d..410b971 100644
--- a/sound/firewire/digi00x/digi00x-stream.c
+++ b/sound/firewire/digi00x/digi00x-stream.c
@@ -204,6 +204,9 @@ static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
dg00x->rx_stream.midi_position = 0;
dg00x->tx_stream.midi_position = 0;

+ dg00x->rx_stream.transfer_midi = snd_dg00x_protocol_fill_midi;
+ dg00x->tx_stream.transfer_midi = snd_dg00x_protocol_pull_midi;
+
return 0;
error:
release_resources(dg00x);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 20b178f..a658f44 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -84,6 +84,10 @@ enum snd_dg00x_optical_mode {
SND_DG00X_OPTICAL_MODE_SPDIF,
};

+void snd_dg00x_protocol_fill_midi(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames);
+void snd_dg00x_protocol_pull_midi(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int frames);
void snd_dg00x_protocol_queue_midi_message(struct snd_dg00x *dg00x);
int snd_dg00x_protocol_add_instance(struct snd_dg00x *dg00x);
void snd_dg00x_protocol_remove_instance(struct snd_dg00x *dg00x);
--
2.1.0