All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400
@ 2023-01-12 12:09 Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 1/6] ALSA: fireface: rename callback functions Takashi Sakamoto
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

Hi,

Fireface 400 uses asynchronous transaction mechanism to deliver event of
hardware knob control as well as received MIDI messages. Current
implementation doesn't distinguish them, thus all arrived messages are
processed for MIDI message.

This patchset adds the parser to distinguish them, and deliver knob
control event to user space via ALSA hwdep character device. The
implementation works well as long as I tested with the patches for
libhitaki library:

https://github.com/alsa-project/libhitaki/tree/topic/ff/400-msg

I note that Fireface 400 transmits no asynchronous transaction when it
is not configured by block write transaction to offset 0x000080100514,
which turn off HOST led from red.

Takashi Sakamoto (6):
  ALSA: fireface: rename callback functions
  ALSA: fireface: pick up time stamp for request subaction of asynchronous
    transaction
  ALSA: fireface: add helper function to parse MIDI messages transmitted by
    Fireface 400
  ALSA: fireface: update UAPI for data of knob control
  ALSA: fireface: add local framework to message parser
  ALSA: fireface: implement message parser for Fireface 400

 include/uapi/sound/firewire.h                |  24 +++
 sound/firewire/fireface/ff-hwdep.c           |  41 ++--
 sound/firewire/fireface/ff-protocol-former.c | 186 +++++++++++++++----
 sound/firewire/fireface/ff-protocol-latter.c |   6 +-
 sound/firewire/fireface/ff-transaction.c     |  17 +-
 sound/firewire/fireface/ff.c                 |  10 +
 sound/firewire/fireface/ff.h                 |   9 +-
 7 files changed, 239 insertions(+), 54 deletions(-)

-- 
2.37.2


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] ALSA: fireface: rename callback functions
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 2/6] ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction Takashi Sakamoto
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

It's cleared that Fireface 400 transmits quadlet message for two purposes
at least; received MIDI messages and notification of knob control
operation. Nevertheless current implementation uses callback function name
just for MIDI messages.

This commit renames the callback functions.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireface/ff-protocol-former.c | 12 ++++++------
 sound/firewire/fireface/ff-protocol-latter.c |  6 +++---
 sound/firewire/fireface/ff-transaction.c     | 12 +++++-------
 sound/firewire/fireface/ff.h                 |  4 ++--
 4 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
index 8900ffe517ed..16afcb334e3c 100644
--- a/sound/firewire/fireface/ff-protocol-former.c
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff)
 // address.
 // A write transaction to clear registered higher 4 bytes of destination address
 // has an effect to suppress asynchronous transaction from device.
-static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
-				  __le32 *buf, size_t length)
+static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+				  size_t length)
 {
 	int i;
 
@@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
 }
 
 const struct snd_ff_protocol snd_ff_protocol_ff800 = {
-	.handle_midi_msg	= ff800_handle_midi_msg,
+	.handle_msg		= ff800_handle_midi_msg,
 	.fill_midi_msg		= former_fill_midi_msg,
 	.get_clock		= former_get_clock,
 	.switch_fetching_mode	= former_switch_fetching_mode,
@@ -553,8 +553,8 @@ static void ff400_finish_session(struct snd_ff *ff)
 // input attenuation. This driver allocates destination address with '0000'0000
 // in its lower offset and expects userspace application to configure the
 // register for it.
-static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
-				  __le32 *buf, size_t length)
+static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+			     size_t length)
 {
 	int i;
 
@@ -592,7 +592,7 @@ static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
 }
 
 const struct snd_ff_protocol snd_ff_protocol_ff400 = {
-	.handle_midi_msg	= ff400_handle_midi_msg,
+	.handle_msg		= ff400_handle_msg,
 	.fill_midi_msg		= former_fill_midi_msg,
 	.get_clock		= former_get_clock,
 	.switch_fetching_mode	= former_switch_fetching_mode,
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
index 76c3eab36d4e..e7a066fb1ead 100644
--- a/sound/firewire/fireface/ff-protocol-latter.c
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer
 // input attenuation. This driver allocates for the first option
 // (0x'....'....'0000'0000) and expects userspace application to configure the
 // register for it.
-static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
-				   __le32 *buf, size_t length)
+static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+				   size_t length)
 {
 	u32 data = le32_to_cpu(*buf);
 	unsigned int index = (data & 0x000000f0) >> 4;
@@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff,
 }
 
 const struct snd_ff_protocol snd_ff_protocol_latter = {
-	.handle_midi_msg	= latter_handle_midi_msg,
+	.handle_msg		= latter_handle_midi_msg,
 	.fill_midi_msg		= latter_fill_midi_msg,
 	.get_clock		= latter_get_clock,
 	.switch_fetching_mode	= latter_switch_fetching_mode,
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
index ee7122c461d4..764c772a0b1e 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -125,10 +125,9 @@ static void transmit_midi1_msg(struct work_struct *work)
 	transmit_midi_msg(ff, 1);
 }
 
-static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
-			    int tcode, int destination, int source,
-			    int generation, unsigned long long offset,
-			    void *data, size_t length, void *callback_data)
+static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode,
+		       int destination, int source, int generation, unsigned long long offset,
+		       void *data, size_t length, void *callback_data)
 {
 	struct snd_ff *ff = callback_data;
 	__le32 *buf = data;
@@ -136,8 +135,7 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
 	fw_send_response(card, request, RCODE_COMPLETE);
 
 	offset -= ff->async_handler.offset;
-	ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf,
-					    length);
+	ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length);
 }
 
 static int allocate_own_address(struct snd_ff *ff, int i)
@@ -146,7 +144,7 @@ static int allocate_own_address(struct snd_ff *ff, int i)
 	int err;
 
 	ff->async_handler.length = ff->spec->midi_addr_range;
-	ff->async_handler.address_callback = handle_midi_msg;
+	ff->async_handler.address_callback = handle_msg;
 	ff->async_handler.callback_data = ff;
 
 	midi_msg_region.start = 0x000100000000ull * i;
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index 0535f0b58b67..0358b444bd01 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -110,8 +110,8 @@ enum snd_ff_clock_src {
 };
 
 struct snd_ff_protocol {
-	void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset,
-				__le32 *buf, size_t length);
+	void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+			   size_t length);
 	int (*fill_midi_msg)(struct snd_ff *ff,
 			     struct snd_rawmidi_substream *substream,
 			     unsigned int port);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/6] ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 1/6] ALSA: fireface: rename callback functions Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 3/6] ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400 Takashi Sakamoto
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

The time stamp of isochronous cycle at which asynchronous transaction is
sent is perhaps useful somehow. A commit b2405aa948b9 ("firewire: add
kernel API to access packet structure in request structure for AR context")
adds kernel API to retrieve the time stamp in inner structure of request
subaction.

This commit changes local framework to handle message delivered by the
asynchronous transaction so that time stamp is picked up by the kernel API.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireface/ff-protocol-former.c | 4 ++--
 sound/firewire/fireface/ff-protocol-latter.c | 2 +-
 sound/firewire/fireface/ff-transaction.c     | 3 ++-
 sound/firewire/fireface/ff.h                 | 2 +-
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
index 16afcb334e3c..22d6aee52de3 100644
--- a/sound/firewire/fireface/ff-protocol-former.c
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -403,7 +403,7 @@ static void ff800_finish_session(struct snd_ff *ff)
 // A write transaction to clear registered higher 4 bytes of destination address
 // has an effect to suppress asynchronous transaction from device.
 static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
-				  size_t length)
+				  size_t length, u32 tstamp)
 {
 	int i;
 
@@ -554,7 +554,7 @@ static void ff400_finish_session(struct snd_ff *ff)
 // in its lower offset and expects userspace application to configure the
 // register for it.
 static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
-			     size_t length)
+			     size_t length, u32 tstamp)
 {
 	int i;
 
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
index e7a066fb1ead..9947e0c2e0aa 100644
--- a/sound/firewire/fireface/ff-protocol-latter.c
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -394,7 +394,7 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer
 // (0x'....'....'0000'0000) and expects userspace application to configure the
 // register for it.
 static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
-				   size_t length)
+				   size_t length, u32 tstamp)
 {
 	u32 data = le32_to_cpu(*buf);
 	unsigned int index = (data & 0x000000f0) >> 4;
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
index 764c772a0b1e..79f733d8c98b 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -131,11 +131,12 @@ static void handle_msg(struct fw_card *card, struct fw_request *request, int tco
 {
 	struct snd_ff *ff = callback_data;
 	__le32 *buf = data;
+	u32 tstamp = fw_request_get_timestamp(request);
 
 	fw_send_response(card, request, RCODE_COMPLETE);
 
 	offset -= ff->async_handler.offset;
-	ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length);
+	ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
 }
 
 static int allocate_own_address(struct snd_ff *ff, int i)
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index 0358b444bd01..f430ebe157b3 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -111,7 +111,7 @@ enum snd_ff_clock_src {
 
 struct snd_ff_protocol {
 	void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
-			   size_t length);
+			   size_t length, u32 tstamp);
 	int (*fill_midi_msg)(struct snd_ff *ff,
 			     struct snd_rawmidi_substream *substream,
 			     unsigned int port);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/6] ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 1/6] ALSA: fireface: rename callback functions Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 2/6] ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 4/6] ALSA: fireface: update UAPI for data of knob control Takashi Sakamoto
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

This is minor code refactoring to add helper function to parse MIDI message
bytes in quadlet message.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireface/ff-protocol-former.c | 47 +++++++++-----------
 1 file changed, 20 insertions(+), 27 deletions(-)

diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
index 22d6aee52de3..d2cc9961b973 100644
--- a/sound/firewire/fireface/ff-protocol-former.c
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -534,6 +534,22 @@ static void ff400_finish_session(struct snd_ff *ff)
 			   FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
 }
 
+static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
+{
+	struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]);
+
+	if (substream != NULL) {
+		u8 byte = (quad >> (16 * port)) & 0x000000ff;
+
+		snd_rawmidi_receive(substream, &byte, 1);
+	}
+}
+
+#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
+
 // 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
 // select one of 4 options:
@@ -560,34 +576,11 @@ static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le3
 
 	for (i = 0; i < length / 4; i++) {
 		u32 quad = le32_to_cpu(buf[i]);
-		u8 byte;
-		unsigned int index;
-		struct snd_rawmidi_substream *substream;
 
-		/* Message in first port. */
-		/*
-		 * This value may represent the index of this unit when the same
-		 * units are on the same IEEE 1394 bus. This driver doesn't use
-		 * it.
-		 */
-		index = (quad >> 8) & 0xff;
-		if (index > 0) {
-			substream = READ_ONCE(ff->tx_midi_substreams[0]);
-			if (substream != NULL) {
-				byte = quad & 0xff;
-				snd_rawmidi_receive(substream, &byte, 1);
-			}
-		}
-
-		/* Message in second port. */
-		index = (quad >> 24) & 0xff;
-		if (index > 0) {
-			substream = READ_ONCE(ff->tx_midi_substreams[1]);
-			if (substream != NULL) {
-				byte = (quad >> 16) & 0xff;
-				snd_rawmidi_receive(substream, &byte, 1);
-			}
-		}
+		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)
+			parse_midi_msg(ff, quad, 1);
 	}
 }
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/6] ALSA: fireface: update UAPI for data of knob control
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
                   ` (2 preceding siblings ...)
  2023-01-12 12:09 ` [PATCH 3/6] ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400 Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 5/6] ALSA: fireface: add local framework to message parser Takashi Sakamoto
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

This commit adds a new event of knob control specific to RME Fireface 400.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/firewire.h | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 3532ac7046d7..50917581dd2b 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -14,6 +14,7 @@
 #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION	0x64776479
 #define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL	0x7473636d
 #define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE	0x4d545244
+#define SNDRV_FIREWIRE_EVENT_FF400_MESSAGE	0x4f6c6761
 
 struct snd_firewire_event_common {
 	unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -72,6 +73,28 @@ struct snd_firewire_event_motu_register_dsp_change {
 	__u32 changes[];	/* Encoded event for change of register DSP. */
 };
 
+/**
+ * struct snd_firewire_event_ff400_message - the container for message from Fireface 400 when
+ *					     operating hardware knob.
+ *
+ * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE.
+ * @messages.message: The messages expressing hardware knob operation.
+ * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous
+ *		     transaction was sent to deliver the message. It has 16 bit unsigned integer
+ *		     value. The higher 3 bits of value expresses the lower three bits of second
+ *		     field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle
+ *		     field up to 7999.
+ *
+ * The structure expresses message transmitted by Fireface 400 when operating hardware knob.
+ */
+struct snd_firewire_event_ff400_message {
+	unsigned int type;
+	struct {
+		__u32 message;
+		__u32 tstamp;
+	} messages[];
+};
+
 union snd_firewire_event {
 	struct snd_firewire_event_common            common;
 	struct snd_firewire_event_lock_status       lock_status;
@@ -81,6 +104,7 @@ union snd_firewire_event {
 	struct snd_firewire_event_tascam_control    tascam_control;
 	struct snd_firewire_event_motu_notification motu_notification;
 	struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change;
+	struct snd_firewire_event_ff400_message	    ff400_message;
 };
 
 
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/6] ALSA: fireface: add local framework to message parser
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
                   ` (3 preceding siblings ...)
  2023-01-12 12:09 ` [PATCH 4/6] ALSA: fireface: update UAPI for data of knob control Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-12 12:09 ` [PATCH 6/6] ALSA: fireface: implement message parser for Fireface 400 Takashi Sakamoto
  2023-01-13  8:57 ` [PATCH 0/6] ALSA: fireface: support knob control event " Takashi Iwai
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

This commit adds local framework to message parser. This is preparation
for future work to pass event of knob control for Fireface 400 to user
space.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireface/ff-hwdep.c       | 41 +++++++++++++++++-------
 sound/firewire/fireface/ff-transaction.c |  4 +++
 sound/firewire/fireface/ff.c             | 10 ++++++
 sound/firewire/fireface/ff.h             |  5 +++
 4 files changed, 49 insertions(+), 11 deletions(-)

diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c
index ea64a2a41eea..8a741b3b0436 100644
--- a/sound/firewire/fireface/ff-hwdep.c
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -15,16 +15,23 @@
 
 #include "ff.h"
 
+static bool has_msg(struct snd_ff *ff)
+{
+	if (ff->spec->protocol->has_msg)
+		return ff->spec->protocol->has_msg(ff);
+	else
+		return 0;
+}
+
 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
 		       loff_t *offset)
 {
 	struct snd_ff *ff = hwdep->private_data;
 	DEFINE_WAIT(wait);
-	union snd_firewire_event event;
 
 	spin_lock_irq(&ff->lock);
 
-	while (!ff->dev_lock_changed) {
+	while (!ff->dev_lock_changed && !has_msg(ff)) {
 		prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 		spin_unlock_irq(&ff->lock);
 		schedule();
@@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
 		spin_lock_irq(&ff->lock);
 	}
 
-	memset(&event, 0, sizeof(event));
-	event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
-	event.lock_status.status = (ff->dev_lock_count > 0);
-	ff->dev_lock_changed = false;
+	if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
+		struct snd_firewire_event_lock_status ev = {
+			.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+			.status = (ff->dev_lock_count > 0),
+		};
 
-	count = min_t(long, count, sizeof(event.lock_status));
+		ff->dev_lock_changed = false;
 
-	spin_unlock_irq(&ff->lock);
+		spin_unlock_irq(&ff->lock);
 
-	if (copy_to_user(buf, &event, count))
-		return -EFAULT;
+		if (copy_to_user(buf, &ev, sizeof(ev)))
+			return -EFAULT;
+		count = sizeof(ev);
+	} else if (has_msg(ff)) {
+		// NOTE: Acquired spin lock should be released before accessing to user space in the
+		// callback since the access can cause page fault.
+		count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
+		spin_unlock_irq(&ff->lock);
+	} else {
+		spin_unlock_irq(&ff->lock);
+
+		count = 0;
+	}
 
 	return count;
 }
@@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
 	poll_wait(file, &ff->hwdep_wait, wait);
 
 	spin_lock_irq(&ff->lock);
-	if (ff->dev_lock_changed)
+	if (ff->dev_lock_changed || has_msg(ff))
 		events = EPOLLIN | EPOLLRDNORM;
 	else
 		events = 0;
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
index 79f733d8c98b..6b89e39f4a43 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -132,11 +132,15 @@ static void handle_msg(struct fw_card *card, struct fw_request *request, int tco
 	struct snd_ff *ff = callback_data;
 	__le32 *buf = data;
 	u32 tstamp = fw_request_get_timestamp(request);
+	unsigned long flag;
 
 	fw_send_response(card, request, RCODE_COMPLETE);
 
 	offset -= ff->async_handler.offset;
+
+	spin_lock_irqsave(&ff->lock, flag);
 	ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
+	spin_unlock_irqrestore(&ff->lock, flag);
 }
 
 static int allocate_own_address(struct snd_ff *ff, int i)
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
index 7bf51d062021..448e972028d9 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card)
 	snd_ff_stream_destroy_duplex(ff);
 	snd_ff_transaction_unregister(ff);
 
+	kfree(ff->msg_parser);
+
 	mutex_destroy(&ff->mutex);
 	fw_unit_put(ff->unit);
 }
@@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e
 	if (err < 0)
 		goto error;
 
+	if (ff->spec->protocol->msg_parser_size > 0) {
+		ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL);
+		if (!ff->msg_parser) {
+			err = -ENOMEM;
+			goto error;
+		}
+	}
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index f430ebe157b3..7e42f5778a8a 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -97,6 +97,8 @@ struct snd_ff {
 	wait_queue_head_t hwdep_wait;
 
 	struct amdtp_domain domain;
+
+	void *msg_parser;
 };
 
 enum snd_ff_clock_src {
@@ -110,6 +112,9 @@ enum snd_ff_clock_src {
 };
 
 struct snd_ff_protocol {
+	size_t msg_parser_size;
+	bool (*has_msg)(struct snd_ff *ff);
+	long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count);
 	void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
 			   size_t length, u32 tstamp);
 	int (*fill_midi_msg)(struct snd_ff *ff,
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/6] ALSA: fireface: implement message parser for Fireface 400
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
                   ` (4 preceding siblings ...)
  2023-01-12 12:09 ` [PATCH 5/6] ALSA: fireface: add local framework to message parser Takashi Sakamoto
@ 2023-01-12 12:09 ` Takashi Sakamoto
  2023-01-13  8:57 ` [PATCH 0/6] ALSA: fireface: support knob control event " Takashi Iwai
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Sakamoto @ 2023-01-12 12:09 UTC (permalink / raw
  To: tiwai; +Cc: alsa-devel, clemens

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 --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,
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400
  2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
                   ` (5 preceding siblings ...)
  2023-01-12 12:09 ` [PATCH 6/6] ALSA: fireface: implement message parser for Fireface 400 Takashi Sakamoto
@ 2023-01-13  8:57 ` Takashi Iwai
  6 siblings, 0 replies; 8+ messages in thread
From: Takashi Iwai @ 2023-01-13  8:57 UTC (permalink / raw
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens

On Thu, 12 Jan 2023 13:09:48 +0100,
Takashi Sakamoto wrote:
> 
> Hi,
> 
> Fireface 400 uses asynchronous transaction mechanism to deliver event of
> hardware knob control as well as received MIDI messages. Current
> implementation doesn't distinguish them, thus all arrived messages are
> processed for MIDI message.
> 
> This patchset adds the parser to distinguish them, and deliver knob
> control event to user space via ALSA hwdep character device. The
> implementation works well as long as I tested with the patches for
> libhitaki library:
> 
> https://github.com/alsa-project/libhitaki/tree/topic/ff/400-msg
> 
> I note that Fireface 400 transmits no asynchronous transaction when it
> is not configured by block write transaction to offset 0x000080100514,
> which turn off HOST led from red.
> 
> Takashi Sakamoto (6):
>   ALSA: fireface: rename callback functions
>   ALSA: fireface: pick up time stamp for request subaction of asynchronous
>     transaction
>   ALSA: fireface: add helper function to parse MIDI messages transmitted by
>     Fireface 400
>   ALSA: fireface: update UAPI for data of knob control
>   ALSA: fireface: add local framework to message parser
>   ALSA: fireface: implement message parser for Fireface 400

Applied now to for-next branch.  Thanks.


Takashi

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-01-13  8:58 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-01-12 12:09 [PATCH 0/6] ALSA: fireface: support knob control event for Fireface 400 Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 1/6] ALSA: fireface: rename callback functions Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 2/6] ALSA: fireface: pick up time stamp for request subaction of asynchronous transaction Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 3/6] ALSA: fireface: add helper function to parse MIDI messages transmitted by Fireface 400 Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 4/6] ALSA: fireface: update UAPI for data of knob control Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 5/6] ALSA: fireface: add local framework to message parser Takashi Sakamoto
2023-01-12 12:09 ` [PATCH 6/6] ALSA: fireface: implement message parser for Fireface 400 Takashi Sakamoto
2023-01-13  8:57 ` [PATCH 0/6] ALSA: fireface: support knob control event " Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.