diff mbox series

[6/6] ALSA: fireface: implement message parser for Fireface 400

Message ID 20230112120954.500692-7-o-takashi@sakamocchi.jp
State Accepted
Commit acdebd8b4c0c90ec3debe3def0460f07d27504bb
Headers show
Series ALSA: fireface: support knob control event for Fireface 400 | expand

Commit Message

Takashi Sakamoto Jan. 12, 2023, 12:09 p.m. UTC
This commit implements message parser for Fireface 400 to pass data of
knob control to user space. The parser has FIFO which can store maximum
32 events without no overrun detection since it doesn't matter to lose
the event.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireface/ff-protocol-former.c | 143 ++++++++++++++++++-
 1 file changed, 137 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
index d2cc9961b973..f58008762fe6 100644
--- a/sound/firewire/fireface/ff-protocol-former.c
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -545,10 +545,23 @@  static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
 	}
 }
 
-#define FF400_MSG_FLAG_IS_MIDI_PORT_0		0x00000100
-#define  FF400_MSG_MASK_MIDI_PORT_0		0x000000ff
-#define FF400_MSG_FLAG_IS_MIDI_PORT_1		0x01000000
-#define  FF400_MSG_MASK_MIDI_PORT_1		0x00ff0000
+#define FF400_QUEUE_SIZE	32
+
+struct ff400_msg_parser {
+	struct {
+		u32 msg;
+		u32 tstamp;
+	} msgs[FF400_QUEUE_SIZE];
+	size_t push_pos;
+	size_t pull_pos;
+};
+
+static bool ff400_has_msg(struct snd_ff *ff)
+{
+	struct ff400_msg_parser *parser = ff->msg_parser;
+
+	return (parser->push_pos != parser->pull_pos);
+}
 
 // For Fireface 400, lower 4 bytes of destination address is configured by bit
 // flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
@@ -569,22 +582,140 @@  static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
 // input attenuation. This driver allocates destination address with '0000'0000
 // in its lower offset and expects userspace application to configure the
 // register for it.
+
+// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of
+// stereo physical port.
+// - 0: Microphone input 0/1
+// - 1: Line input 0/1
+// - [2-4]: Line output 0-5
+// - 5: Headphone output 0/1
+// - 6: S/PDIF output 0/1
+// - [7-10]: ADAT output 0-7
+//
+// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone
+// input:
+//
+// - 0:    0.0 dB
+// - 10: +10.0 dB
+// - 11: +11.0 dB
+// - 12: +12.0 dB
+// - ...
+// - 63: +63.0 dB:
+// - 64: +64.0 dB:
+// - 65: +65.0 dB:
+//
+// For signal level of line input:
+//
+// - 0:  0.0 dB
+// - 1: +0.5 dB
+// - 2: +1.0 dB
+// - 3: +1.5 dB
+// - ...
+// - 34: +17.0 dB:
+// - 35: +17.5 dB:
+// - 36: +18.0 dB:
+//
+// For signal level of any type of output:
+//
+// - 63: -infinite
+// - 62: -58.0 dB
+// - 61: -56.0 dB
+// - 60: -54.0 dB
+// - 59: -53.0 dB
+// - 58: -52.0 dB
+// - ...
+// - 7: -1.0 dB
+// - 6:  0.0 dB
+// - 5: +1.0 dB
+// - ...
+// - 2: +4.0 dB
+// - 1: +5.0 dB
+// - 0: +6.0 dB
+//
+// When the message is not for signal level operation, it's for MIDI bytes. When matching to
+// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When
+// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000.
+#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL		0x04000000
+#define  FF400_MSG_FLAG_IS_RIGHT_CHANNEL	0x08000000
+#define  FF400_MSG_FLAG_IS_STEREO_PAIRED	0x02000000
+#define  FF400_MSG_MASK_STEREO_PAIR		0xf0000000
+#define  FF400_MSG_MASK_SIGNAL_LEVEL		0x00fffc00
+#define FF400_MSG_FLAG_IS_MIDI_PORT_0		0x00000100
+#define  FF400_MSG_MASK_MIDI_PORT_0		0x000000ff
+#define FF400_MSG_FLAG_IS_MIDI_PORT_1		0x01000000
+#define  FF400_MSG_MASK_MIDI_PORT_1		0x00ff0000
+
 static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
 			     size_t length, u32 tstamp)
 {
+	bool need_hwdep_wake_up = false;
 	int i;
 
 	for (i = 0; i < length / 4; i++) {
 		u32 quad = le32_to_cpu(buf[i]);
 
-		if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0)
+		if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) {
+			struct ff400_msg_parser *parser = ff->msg_parser;
+
+			parser->msgs[parser->push_pos].msg = quad;
+			parser->msgs[parser->push_pos].tstamp = tstamp;
+			++parser->push_pos;
+			if (parser->push_pos >= FF400_QUEUE_SIZE)
+				parser->push_pos = 0;
+
+			need_hwdep_wake_up = true;
+		} else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) {
 			parse_midi_msg(ff, quad, 0);
-		else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1)
+		} else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) {
 			parse_midi_msg(ff, quad, 1);
+		}
 	}
+
+	if (need_hwdep_wake_up)
+		wake_up(&ff->hwdep_wait);
+}
+
+static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count)
+{
+	struct ff400_msg_parser *parser = ff->msg_parser;
+	u32 type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE;
+	long consumed = 0;
+
+	if (count < 8)
+		return 0;
+
+	spin_unlock_irq(&ff->lock);
+
+	if (copy_to_user(buf, &type, sizeof(type)))
+		return -EFAULT;
+
+	spin_lock_irq(&ff->lock);
+
+	count -= sizeof(type);
+	consumed += sizeof(type);
+
+	while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) {
+		spin_unlock_irq(&ff->lock);
+
+		if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos,
+				 sizeof(*parser->msgs)))
+			return -EFAULT;
+
+		spin_lock_irq(&ff->lock);
+		++parser->pull_pos;
+		if (parser->pull_pos >= FF400_QUEUE_SIZE)
+			parser->pull_pos = 0;
+		count -= sizeof(*parser->msgs);
+		consumed += sizeof(*parser->msgs);
+	}
+
+	return consumed;
 }
 
 const struct snd_ff_protocol snd_ff_protocol_ff400 = {
+	.msg_parser_size	= sizeof(struct ff400_msg_parser),
+	.has_msg		= ff400_has_msg,
+	.copy_msg_to_user	= ff400_copy_msg_to_user,
 	.handle_msg		= ff400_handle_msg,
 	.fill_midi_msg		= former_fill_midi_msg,
 	.get_clock		= former_get_clock,