All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin
@ 2018-10-03 23:45 Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton Andre Guedes
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

Hi all,

The AAF plugin RFC series v2 has been around on alsa-devel ML for more than two
weeks so I'm moving forward and sending it as PATCH series now.

The first 5 patches from this series are pretty much the same from the previous
version. The news are the top 2 patches which add more functionality to the
plugin. Patch 6/7 implements the dump() callback which has been useful for
debugging purposes and Patch 7/7 adds support for direct read/write transfers.

For general context information about the AAF plugin, I'm quoting below the
cover letter from the RFC series v1:

	"This patchset introduces the AAF plugin to the alsa-plugins project
	which enables TSN audio applications on Linux.

	For those not familiarized with TSN, it stands for Time-Sensitive
	Networking, and it's a set of IEEE technologies (enhancements to IEEE
	802.1Q and Ethernet protocols) that provide precise time
	synchronization, bounded latency and application interoperability to
	bridged networks. Those technologies enable time-sensitive applications
	such as audio, video and control loops to run on top of bridged
	networks, co-existing with regular applications. TSN technologies are a
	super set of Audio Video Bridging (AVB) technologies also developed by
	IEEE. AVTP is the protocol defined to transport data in a TSN system,
	and AAF is one of the formats defined by AVTP to encapsulate audio
	data. AAF is specified in Chapter 7 from the AVTP spec [1].

	This work is part of the effort to enable TSN technologies on upstream
	Linux ecosystem. All building-blocks required to enable TSN audio
	applications have been already developed and pushed to upstream
	projects. Time synchronization features are provided by linuxptp
	project [2], bounded latency features are provided by Linux Traffic
	Control subsystem since kernel version 4.15 [3], and AVTP packetization
	is provided by libavtp [4]. What is currently missing in the ecosystem
	to enable TSN audio applications is a piece of software that plumbs it
	all together and interfaces with Linux Audio system properly. That's
	the point of the AAF plugin introduced here.

	The AAF plugin is a PCM plugin that uses AVTP protocol to transmit /
	receive audio data through a TSN capable network.  When operating in
	playback mode, the plugin reads PCM samples from the audio buffer,
	encapsulates into AVTP packets and transmits to the network, mimicking
	a typical AVTP Talker. When operating in capture mode, the plugin
	receives AVTP packets from the network, retrieves the PCM samples, and
	present them to alsa-lib layer at the presentation time, mimicking a
	typical AVTP Listener."

For further information about what has been previously discussed, please refer
to RFC series v1 and v2 archives in [5] and [6]. Finally, all versions of this
series can be also found in my alsa-plugins tree in github [7].

Best regards,

Andre

[1] 1722-2016 - IEEE Standard for a Transport Protocol for Time-Sensitive
    Applications in Bridged Local Area Networks
[2] http://linuxptp.sourceforge.net
[3] https://patchwork.ozlabs.org/cover/826678/
[4] https://github.com/AVnu/libavtp
[5] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-August/139494.html
[6] http://mailman.alsa-project.org/pipermail/alsa-devel/2018-September/140290.html
[7] https://github.com/aguedes/alsa-plugins

Andre Guedes (7):
  aaf: Introduce plugin skeleton
  aaf: Load configuration parameters
  aaf: Implement Playback mode support
  aaf: Prepare for Capture mode support
  aaf: Implement Capture mode support
  aaf: Implement dump() ioplug callback
  aaf: Add support for direct read/write transfers

 Makefile.am     |    3 +
 aaf/Makefile.am |    9 +
 aaf/pcm_aaf.c   | 1197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure.ac    |    9 +
 doc/aaf.txt     |  142 +++++++
 5 files changed, 1360 insertions(+)
 create mode 100644 aaf/Makefile.am
 create mode 100644 aaf/pcm_aaf.c
 create mode 100644 doc/aaf.txt

-- 
2.14.4

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

* [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-10 10:27   ` Takashi Iwai
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters Andre Guedes
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

The patch introduces the skeleton code from the AAF plugin as well as
the buildsystem bits in order to get the plugin built. Following the
approach from other plugins, the AAF plugin is only built if its
dependency (libavtp) is detected by configure.

Follow-up patches implement support for both playback and capture modes.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 Makefile.am     |  3 ++
 aaf/Makefile.am |  9 ++++++
 aaf/pcm_aaf.c   | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure.ac    |  9 ++++++
 doc/aaf.txt     | 18 ++++++++++++
 5 files changed, 130 insertions(+)
 create mode 100644 aaf/Makefile.am
 create mode 100644 aaf/pcm_aaf.c
 create mode 100644 doc/aaf.txt

diff --git a/Makefile.am b/Makefile.am
index 27f61a4..af0e9c4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -35,6 +35,9 @@ endif
 if HAVE_SPEEXDSP
 SUBDIRS += speex
 endif
+if HAVE_AAF
+SUBDIRS += aaf
+endif
 
 EXTRA_DIST = gitcompile version COPYING.GPL m4/attributes.m4
 AUTOMAKE_OPTIONS = foreign
diff --git a/aaf/Makefile.am b/aaf/Makefile.am
new file mode 100644
index 0000000..492b883
--- /dev/null
+++ b/aaf/Makefile.am
@@ -0,0 +1,9 @@
+asound_module_pcm_aaf_LTLIBRARIES = libasound_module_pcm_aaf.la
+
+asound_module_pcm_aafdir = @ALSA_PLUGIN_DIR@
+
+AM_CFLAGS = @ALSA_CFLAGS@ @AVTP_CFLAGS@
+AM_LDFLAGS = -module -avoid-version -export-dynamic -no-undefined $(LDFLAGS_NOUNDEFINED)
+
+libasound_module_pcm_aaf_la_SOURCES = pcm_aaf.c
+libasound_module_pcm_aaf_la_LIBADD = @ALSA_LIBS@ @AVTP_LIBS@
diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
new file mode 100644
index 0000000..7890e10
--- /dev/null
+++ b/aaf/pcm_aaf.c
@@ -0,0 +1,91 @@
+/*
+ * AVTP Audio Format (AAF) PCM Plugin
+ *
+ * Copyright (c) 2018, Intel Corporation
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_external.h>
+
+typedef struct {
+	snd_pcm_ioplug_t io;
+} snd_pcm_aaf_t;
+
+static int aaf_close(snd_pcm_ioplug_t *io)
+{
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	if (!aaf)
+		return -EBADFD;
+
+	free(aaf);
+	aaf = NULL;
+	return 0;
+}
+
+static snd_pcm_sframes_t aaf_pointer(snd_pcm_ioplug_t *io)
+{
+	return 0;
+}
+
+static int aaf_start(snd_pcm_ioplug_t *io)
+{
+	return 0;
+}
+
+static int aaf_stop(snd_pcm_ioplug_t *io)
+{
+	return 0;
+}
+
+static const snd_pcm_ioplug_callback_t aaf_callback = {
+	.close = aaf_close,
+	.pointer = aaf_pointer,
+	.start = aaf_start,
+	.stop = aaf_stop,
+};
+
+SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
+{
+	snd_pcm_aaf_t *aaf;
+	int res;
+
+	aaf = calloc(1, sizeof(*aaf));
+	if (!aaf) {
+		SNDERR("Failed to allocate memory");
+		return -ENOMEM;
+	}
+
+	aaf->io.version = SND_PCM_IOPLUG_VERSION;
+	aaf->io.name = "AVTP Audio Format (AAF) Plugin";
+	aaf->io.callback = &aaf_callback;
+	aaf->io.private_data = aaf;
+	res = snd_pcm_ioplug_create(&aaf->io, name, stream, mode);
+	if (res < 0) {
+		SNDERR("Failed to create ioplug instance");
+		goto err;
+	}
+
+	*pcmp = aaf->io.pcm;
+	return 0;
+
+err:
+	free(aaf);
+	return res;
+}
+
+SND_PCM_PLUGIN_SYMBOL(aaf);
diff --git a/configure.ac b/configure.ac
index cc3740b..6d0e14b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -176,6 +176,14 @@ fi
 test "x$prefix" = xNONE && prefix=$ac_default_prefix
 test "x$exec_prefix" = xNONE && exec_prefix=$prefix
 
+AC_ARG_ENABLE([aaf],
+      AS_HELP_STRING([--disable-aaf], [Disable building of AAF plugin]))
+
+if test "x$enable_aaf" != "xno"; then
+  PKG_CHECK_MODULES(AVTP, avtp >= 0.1, [HAVE_AAF=yes], [HAVE_AAF=no])
+fi
+AM_CONDITIONAL(HAVE_AAF, test x$HAVE_AAF = xyes)
+
 dnl ALSA plugin directory
 AC_ARG_WITH(plugindir,
     AS_HELP_STRING([--with-plugindir=dir],
@@ -251,6 +259,7 @@ AC_OUTPUT([
 	usb_stream/Makefile
 	speex/Makefile
 	arcam-av/Makefile
+	aaf/Makefile
 ])
 
 dnl Show the build conditions
diff --git a/doc/aaf.txt b/doc/aaf.txt
new file mode 100644
index 0000000..b260a26
--- /dev/null
+++ b/doc/aaf.txt
@@ -0,0 +1,18 @@
+AVTP Audio Format (AAF) Plugin
+==============================
+
+Overview
+--------
+
+The AAF plugin is a PCM plugin that uses Audio Video Transport Protocol (AVTP)
+to transmit/receive audio samples through a Time-Sensitive Network (TSN)
+capable network. The plugin enables media applications to easily implement AVTP
+Talker and Listener functionalities.
+
+Plugin Dependencies
+-------------------
+
+The AAF plugin uses libavtp to handle AVTP packetization. Libavtp source code
+can be found in https://github.com/AVnu/libavtp as well as instructions to
+build and install it. If libavtp isn't detected by configure, the plugin isn't
+built.
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-10 10:29   ` Takashi Iwai
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support Andre Guedes
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

This patch implements the infrastructure to load the plugin
configuration from ALSA configuration file. The configuration
is loaded in open() callback.

All configuration parameters are described in details in doc/aaf.txt
file.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 doc/aaf.txt   |  52 +++++++++++++++++++++++
 2 files changed, 183 insertions(+)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index 7890e10..32db15e 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -20,11 +20,138 @@
 
 #include <alsa/asoundlib.h>
 #include <alsa/pcm_external.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <string.h>
+#include <stdint.h>
+
+#define NSEC_PER_USEC 1000
 
 typedef struct {
 	snd_pcm_ioplug_t io;
+
+	char ifname[IFNAMSIZ];
+	unsigned char addr[ETH_ALEN];
+	int prio;
+	uint64_t streamid;
+	int mtt;
+	int t_uncertainty;
+	snd_pcm_uframes_t frames_per_pdu;
 } snd_pcm_aaf_t;
 
+static int aaf_load_config(snd_pcm_aaf_t *aaf, snd_config_t *conf)
+{
+	snd_config_iterator_t cur, next;
+
+	snd_config_for_each(cur, next, conf) {
+		snd_config_t *entry = snd_config_iterator_entry(cur);
+		const char *id;
+
+		if (snd_config_get_id(entry, &id) < 0)
+			goto err;
+
+		if (strcmp(id, "comment") == 0 ||
+		    strcmp(id, "type") == 0 ||
+		    strcmp(id, "hint") == 0)
+			continue;
+
+		if (strcmp(id, "ifname") == 0) {
+			const char *ifname;
+
+			if (snd_config_get_string(entry, &ifname) < 0)
+				goto err;
+
+			snprintf(aaf->ifname, sizeof(aaf->ifname), "%s",
+				 ifname);
+		} else if (strcmp(id, "addr") == 0) {
+			const char *addr;
+			int n;
+
+			if (snd_config_get_string(entry, &addr) < 0)
+				goto err;
+
+			n = sscanf(addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+				   &aaf->addr[0], &aaf->addr[1],
+				   &aaf->addr[2], &aaf->addr[3],
+				   &aaf->addr[4], &aaf->addr[5]);
+			if (n != 6)
+				goto err;
+		} else if (strcmp(id, "prio") == 0) {
+			long prio;
+
+			if (snd_config_get_integer(entry, &prio) < 0)
+				goto err;
+
+			if (prio < 0)
+				goto err;
+
+			aaf->prio = prio;
+		} else if (strcmp(id, "streamid") == 0) {
+			const char *streamid;
+			unsigned char addr[6];
+			unsigned short unique_id;
+			int n;
+
+			if (snd_config_get_string(entry, &streamid) < 0)
+				goto err;
+
+			n = sscanf(streamid,
+				   "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hx",
+				   &addr[0], &addr[1], &addr[2], &addr[3],
+				   &addr[4], &addr[5], &unique_id);
+			if (n != 7)
+				goto err;
+
+			aaf->streamid = (uint64_t) addr[0] << 56 |
+					(uint64_t) addr[1] << 48 |
+					(uint64_t) addr[2] << 40 |
+					(uint64_t) addr[3] << 32 |
+					(uint64_t) addr[4] << 24 |
+					(uint64_t) addr[5] << 16 |
+					unique_id;
+		} else if (strcmp(id, "mtt") == 0) {
+			long mtt;
+
+			if (snd_config_get_integer(entry, &mtt) < 0)
+				goto err;
+
+			if (mtt < 0)
+				goto err;
+
+			aaf->mtt = mtt * NSEC_PER_USEC;
+		} else if (strcmp(id, "time_uncertainty") == 0) {
+			long t_uncertainty;
+
+			if (snd_config_get_integer(entry, &t_uncertainty) < 0)
+				goto err;
+
+			if (t_uncertainty < 0)
+				goto err;
+
+			aaf->t_uncertainty = t_uncertainty * NSEC_PER_USEC;
+		} else if (strcmp(id, "frames_per_pdu") == 0) {
+			long frames_per_pdu;
+
+			if (snd_config_get_integer(entry, &frames_per_pdu) < 0)
+				goto err;
+
+			if (frames_per_pdu < 0)
+				goto err;
+
+			aaf->frames_per_pdu = frames_per_pdu;
+		} else {
+			SNDERR("Invalid configuration: %s", id);
+			goto err;
+		}
+	}
+
+	return 0;
+
+err:
+	SNDERR("Error loading device configuration");
+	return -EINVAL;
+}
+
 static int aaf_close(snd_pcm_ioplug_t *io)
 {
 	snd_pcm_aaf_t *aaf = io->private_data;
@@ -70,6 +197,10 @@ SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
 		return -ENOMEM;
 	}
 
+	res = aaf_load_config(aaf, conf);
+	if (res < 0)
+		goto err;
+
 	aaf->io.version = SND_PCM_IOPLUG_VERSION;
 	aaf->io.name = "AVTP Audio Format (AAF) Plugin";
 	aaf->io.callback = &aaf_callback;
diff --git a/doc/aaf.txt b/doc/aaf.txt
index b260a26..d817249 100644
--- a/doc/aaf.txt
+++ b/doc/aaf.txt
@@ -16,3 +16,55 @@ The AAF plugin uses libavtp to handle AVTP packetization. Libavtp source code
 can be found in https://github.com/AVnu/libavtp as well as instructions to
 build and install it. If libavtp isn't detected by configure, the plugin isn't
 built.
+
+Plugin Configuration and Usage
+------------------------------
+
+The plugin parameters are passed via ALSA configuration file. They are defined
+as follows:
+
+	* ifname: Network interface used to transmit/receive AVTP packets.
+
+	* addr: Stream destination MAC address.
+
+	* prio: Priority used by the plugin to transmit AVTP traffic. This
+	  option is relevant only when operating in playback mode.
+
+	* streamid: Stream ID associated with the AAF stream transmitted or
+	  received by the plugin.
+
+	* mtt: Maximum Transit Time (in microseconds) as defined in AVTP spec
+	  section 4.3.3. This option is relevant only when operating in
+	  playback mode.
+
+	* time_uncertainty: Maximum Time Uncertainty (in microseconds) as
+	  defined by AVTP spec section 4.3.3. This option is relevant only when
+	  operating in playback mode.
+
+	* frames_per_pdu: Number of audio frames transmitted in one AVTPDU.
+
+The plugin provides the PCM type "aaf". Configure an AAF PCM virtual device
+according to the AAF stream you want to transmit or receive. A hypothetical
+configuration file is shown below:
+
+	pcm.aaf {
+		type aaf
+		ifname eth0
+		addr AA:AA:AA:AA:AA:AA
+		prio 3
+		streamid BB:BB:BB:BB:BB:BB:0001
+		mtt 2000
+		time_uncertainty 125
+		frames_per_pdu 6
+	}
+
+Put the above to ~/.asoundrc (or /etc/asound.conf), and use the AAF PCM virtual
+device with your ALSA apps. For example, to stream the content from a wav file
+through the network, run:
+
+	$ aplay -Daaf foo.wav
+
+To receive the AAF stream generated by the previous command, run the following
+command in another host:
+
+	$ arecord -Daaf
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-10 10:34   ` Takashi Iwai
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 4/7] aaf: Prepare for Capture " Andre Guedes
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

This patch implements the playback mode support from the AAF plugin.
Simply put, this mode works as follows: PCM samples provided by alsa-lib
layer are encapsulated into AVTPDUs and transmitted through the network.
In summary, the playback mode implements a typical AVTP Talker.

When the AAF device is put in running state, its media clock is started.
At every tick from the media clock, audio frames are consumed from the
audio buffer, encapsulated into an AVTPDU, and transmitted to the
network. The presentation time from each AVTPDU is calculated taking in
consideration the maximum transit time and time uncertainty values
configured by the user.

Below follows some discussion about implementation details:

AVTP protocol doesn't support all formats and rates available in ALSA so
the plugin sets some constraints to ensure only supported configurations
are used (see aaf_set_hw_constraint function).

The plugin implements a media clock which is the source from AVTP
timestamps. The AVTP timestamp is based on PTP time which uses
International Atomic Time (TAI) coordinate system. The media clock is
implemented through a periodic timer using timerfd infrastructure so the
plugin requires that system clock and PTP clock are synchronized
(instructions on how to sync these clocks are provided in doc/aaf.txt).
CLOCK_TAI clockid isn't currently supported by timerfd so the timer fd
is created using CLOCK_REALTIME and the start time is converted from TAI
to UTC.

Even though only one file descriptor is used to implement the playback
mode, this patch doesn't leverage ioplug->poll_fd but defines poll
callbacks instead. The reason is these callbacks will be required to
support capture mode (to be implemented by upcoming patch).

The TSN data plane interface is the AF_PACKET socket family so the
plugin uses an AF_PACKET socket to send/receive AVTPDUs. Linux requires
CAP_NET_RAW capability in order to open an AF_PACKET socket so the
application that instantiates the plugin must have it. For further info
about AF_PACKET socket family see packet(7).

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 646 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 doc/aaf.txt   |  72 +++++++
 2 files changed, 718 insertions(+)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index 32db15e..8f8bcfd 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -20,12 +20,32 @@
 
 #include <alsa/asoundlib.h>
 #include <alsa/pcm_external.h>
+#include <arpa/inet.h>
+#include <avtp.h>
+#include <avtp_aaf.h>
+#include <limits.h>
 #include <linux/if.h>
 #include <linux/if_ether.h>
+#include <linux/if_packet.h>
 #include <string.h>
 #include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/timerfd.h>
+
+#ifdef AAF_DEBUG
+#define pr_debug(...) SNDERR(__VA_ARGS__)
+#else
+#define pr_debug(...) (void)0
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
 
 #define NSEC_PER_USEC 1000
+#define NSEC_PER_SEC  1000000000
+#define TAI_OFFSET    (37ULL * NSEC_PER_SEC)
+#define TAI_TO_UTC(t) (t - TAI_OFFSET)
+
+#define FD_COUNT_PLAYBACK 1
 
 typedef struct {
 	snd_pcm_ioplug_t io;
@@ -37,8 +57,71 @@ typedef struct {
 	int mtt;
 	int t_uncertainty;
 	snd_pcm_uframes_t frames_per_pdu;
+
+	int sk_fd;
+	int timer_fd;
+
+	struct sockaddr_ll sk_addr;
+
+	struct avtp_stream_pdu *pdu;
+	int pdu_size;
+	uint8_t pdu_seq;
+
+	uint64_t mclk_start_time;
+	uint64_t mclk_period;
+	uint64_t mclk_ticks;
+
+	snd_pcm_channel_area_t *audiobuf_areas;
+	snd_pcm_channel_area_t *payload_areas;
+
+	snd_pcm_uframes_t hw_ptr;
+	snd_pcm_uframes_t boundary;
 } snd_pcm_aaf_t;
 
+static unsigned int alsa_to_avtp_format(snd_pcm_format_t format)
+{
+	switch (format) {
+	case SND_PCM_FORMAT_S16_BE:
+		return AVTP_AAF_FORMAT_INT_16BIT;
+	case SND_PCM_FORMAT_S24_3BE:
+		return AVTP_AAF_FORMAT_INT_24BIT;
+	case SND_PCM_FORMAT_S32_BE:
+		return AVTP_AAF_FORMAT_INT_32BIT;
+	case SND_PCM_FORMAT_FLOAT_BE:
+		return AVTP_AAF_FORMAT_FLOAT_32BIT;
+	default:
+		return AVTP_AAF_FORMAT_USER;
+	}
+}
+
+static unsigned int alsa_to_avtp_rate(unsigned int rate)
+{
+	switch (rate) {
+	case 8000:
+		return AVTP_AAF_PCM_NSR_8KHZ;
+	case 16000:
+		return AVTP_AAF_PCM_NSR_16KHZ;
+	case 24000:
+		return AVTP_AAF_PCM_NSR_24KHZ;
+	case 32000:
+		return AVTP_AAF_PCM_NSR_32KHZ;
+	case 44100:
+		return AVTP_AAF_PCM_NSR_44_1KHZ;
+	case 48000:
+		return AVTP_AAF_PCM_NSR_48KHZ;
+	case 88200:
+		return AVTP_AAF_PCM_NSR_88_2KHZ;
+	case 96000:
+		return AVTP_AAF_PCM_NSR_96KHZ;
+	case 176400:
+		return AVTP_AAF_PCM_NSR_176_4KHZ;
+	case 192000:
+		return AVTP_AAF_PCM_NSR_192KHZ;
+	default:
+		return AVTP_AAF_PCM_NSR_USER;
+	}
+}
+
 static int aaf_load_config(snd_pcm_aaf_t *aaf, snd_config_t *conf)
 {
 	snd_config_iterator_t cur, next;
@@ -152,6 +235,393 @@ err:
 	return -EINVAL;
 }
 
+static int aaf_init_socket(snd_pcm_aaf_t *aaf)
+{
+	int fd, res;
+	struct ifreq req;
+
+	fd = socket(AF_PACKET, SOCK_DGRAM|SOCK_NONBLOCK, htons(ETH_P_TSN));
+	if (fd < 0) {
+		SNDERR("Failed to open AF_PACKET socket");
+		return -errno;
+	}
+
+	snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", aaf->ifname);
+	res = ioctl(fd, SIOCGIFINDEX, &req);
+	if (res < 0) {
+		SNDERR("Failed to get network interface index");
+		res = -errno;
+		goto err;
+	}
+
+	aaf->sk_addr.sll_family = AF_PACKET;
+	aaf->sk_addr.sll_protocol = htons(ETH_P_TSN);
+	aaf->sk_addr.sll_halen = ETH_ALEN;
+	aaf->sk_addr.sll_ifindex = req.ifr_ifindex;
+	memcpy(&aaf->sk_addr.sll_addr, aaf->addr, ETH_ALEN);
+
+	res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &aaf->prio,
+			 sizeof(aaf->prio));
+	if (res < 0) {
+		SNDERR("Failed to set socket priority");
+		res = -errno;
+		goto err;
+	}
+
+	aaf->sk_fd = fd;
+	return 0;
+
+err:
+	close(fd);
+	return res;
+}
+
+static int aaf_init_timer(snd_pcm_aaf_t *aaf)
+{
+	int fd;
+
+	fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
+	if (fd < 0)
+		return -errno;
+
+	aaf->timer_fd = fd;
+	return 0;
+}
+
+static int aaf_init_pdu(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	struct avtp_stream_pdu *pdu;
+	ssize_t frame_size, payload_size, pdu_size;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	frame_size = snd_pcm_format_size(io->format, io->channels);
+	if (frame_size < 0)
+		return frame_size;
+
+	payload_size = frame_size * aaf->frames_per_pdu;
+	pdu_size = sizeof(*pdu) + payload_size;
+	pdu = calloc(1, pdu_size);
+	if (!pdu)
+		return -ENOMEM;
+
+	res = avtp_aaf_pdu_init(pdu);
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TV, 1);
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_ID, aaf->streamid);
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_FORMAT,
+			       alsa_to_avtp_format(io->format));
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_NSR,
+			       alsa_to_avtp_rate(io->rate));
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME,
+			       io->channels);
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_BIT_DEPTH,
+			       snd_pcm_format_width(io->format));
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN,
+			       payload_size);
+	if (res < 0)
+		goto err;
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SP, AVTP_AAF_PCM_SP_NORMAL);
+	if (res < 0)
+		goto err;
+
+	aaf->pdu = pdu;
+	aaf->pdu_size = pdu_size;
+	return 0;
+
+err:
+	free(pdu);
+	return res;
+}
+
+static int aaf_init_areas(snd_pcm_aaf_t *aaf, snd_pcm_channel_area_t *areas,
+			  void *buf)
+{
+	ssize_t sample_size, frame_size;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	sample_size = snd_pcm_format_size(io->format, 1);
+	if (sample_size < 0)
+		return sample_size;
+
+	frame_size = sample_size * io->channels;
+
+	for (unsigned int i = 0; i < io->channels; i++) {
+		areas[i].addr = buf;
+		areas[i].first = i * sample_size * 8;
+		areas[i].step = frame_size * 8;
+	}
+
+	return 0;
+}
+
+static int aaf_init_payload_areas(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	snd_pcm_channel_area_t *areas;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	areas = calloc(io->channels, sizeof(snd_pcm_channel_area_t));
+	if (!areas)
+		return -ENOMEM;
+
+	res = aaf_init_areas(aaf, areas, aaf->pdu->avtp_payload);
+	if (res < 0)
+		goto err;
+
+	aaf->payload_areas = areas;
+	return 0;
+
+err:
+	free(areas);
+	return res;
+}
+
+static int aaf_init_audiobuf_areas(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	char *buf;
+	ssize_t frame_size;
+	snd_pcm_channel_area_t *areas;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	frame_size = snd_pcm_format_size(io->format, io->channels);
+	if (frame_size < 0)
+		return frame_size;
+
+	buf = calloc(io->buffer_size, frame_size);
+	if (!buf)
+		return -ENOMEM;
+
+	areas = calloc(io->channels, sizeof(snd_pcm_channel_area_t));
+	if (!areas) {
+		res = -ENOMEM;
+		goto err_free_buf;
+	}
+
+	res = aaf_init_areas(aaf, areas, buf);
+	if (res < 0)
+		goto err_free_areas;
+
+	aaf->audiobuf_areas = areas;
+	return 0;
+
+err_free_areas:
+	free(areas);
+err_free_buf:
+	free(buf);
+	return res;
+}
+
+static void aaf_inc_hw_ptr(snd_pcm_aaf_t *aaf, snd_pcm_uframes_t val)
+{
+	aaf->hw_ptr += val;
+
+	if (aaf->hw_ptr >= aaf->boundary)
+		aaf->hw_ptr -= aaf->boundary;
+}
+
+static int aaf_mclk_start_playback(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	struct timespec now;
+	struct itimerspec itspec;
+	uint64_t time_utc;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	res = clock_gettime(CLOCK_TAI, &now);
+	if (res < 0) {
+		SNDERR("Failed to get time from clock");
+		return -errno;
+	}
+
+	aaf->mclk_period = (uint64_t)NSEC_PER_SEC * aaf->frames_per_pdu /
+			   io->rate;
+	aaf->mclk_ticks = 0;
+	aaf->mclk_start_time = now.tv_sec * NSEC_PER_SEC + now.tv_nsec +
+			       aaf->mclk_period;
+
+	time_utc = TAI_TO_UTC(aaf->mclk_start_time);
+	itspec.it_value.tv_sec = time_utc / NSEC_PER_SEC;
+	itspec.it_value.tv_nsec = time_utc % NSEC_PER_SEC;
+	itspec.it_interval.tv_sec = 0;
+	itspec.it_interval.tv_nsec = aaf->mclk_period;
+	res = timerfd_settime(aaf->timer_fd, TFD_TIMER_ABSTIME, &itspec, NULL);
+	if (res < 0)
+		return -errno;
+
+	return 0;
+}
+
+static int aaf_mclk_reset(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	struct itimerspec itspec = { 0 };
+
+	res = timerfd_settime(aaf->timer_fd, 0, &itspec, NULL);
+	if (res < 0) {
+		SNDERR("Failed to stop media clock");
+		return res;
+	}
+
+	aaf->mclk_start_time = 0;
+	aaf->mclk_period = 0;
+	aaf->mclk_ticks = 0;
+	return 0;
+}
+
+static uint64_t aaf_mclk_gettime(snd_pcm_aaf_t *aaf)
+{
+	return aaf->mclk_start_time + aaf->mclk_period * aaf->mclk_ticks;
+}
+
+static int aaf_tx_pdu(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	uint64_t ptime;
+	ssize_t n;
+	snd_pcm_uframes_t hw_avail;
+	snd_pcm_ioplug_t *io = &aaf->io;
+	struct avtp_stream_pdu *pdu = aaf->pdu;
+
+	hw_avail = snd_pcm_ioplug_hw_avail(io, aaf->hw_ptr, io->appl_ptr);
+	if (hw_avail == 0) {
+		/* If there is no frames available for transmission, we reached
+		 * an underrun state.
+		 */
+		return -EPIPE;
+	}
+	if (hw_avail < aaf->frames_per_pdu) {
+		/* If there isn't enough frames to fill the AVTPDU, we drop
+		 * them. This behavior is suggested by IEEE 1722-2016 spec,
+		 * section 7.3.5.
+		 */
+		aaf_inc_hw_ptr(aaf, hw_avail);
+		return 0;
+	}
+
+	res = snd_pcm_areas_copy_wrap(aaf->payload_areas, 0,
+				      aaf->frames_per_pdu,
+				      aaf->audiobuf_areas,
+				      (aaf->hw_ptr % io->buffer_size),
+				      io->buffer_size, io->channels,
+				      aaf->frames_per_pdu, io->format);
+	if (res < 0) {
+		SNDERR("Failed to copy data to AVTP payload");
+		return res;
+	}
+
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SEQ_NUM, aaf->pdu_seq++);
+	if (res < 0)
+		return res;
+
+	ptime = aaf_mclk_gettime(aaf) + aaf->mtt + aaf->t_uncertainty;
+	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TIMESTAMP, ptime);
+	if (res < 0)
+		return res;
+
+	n = sendto(aaf->sk_fd, aaf->pdu, aaf->pdu_size, 0,
+		   (struct sockaddr *) &aaf->sk_addr,
+		   sizeof(aaf->sk_addr));
+	if (n < 0 || n != aaf->pdu_size) {
+		SNDERR("Failed to send AAF PDU");
+		return -EIO;
+	}
+
+	aaf_inc_hw_ptr(aaf, aaf->frames_per_pdu);
+	return 0;
+}
+
+static int aaf_mclk_timeout_playback(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	ssize_t n;
+	uint64_t expirations;
+
+	n = read(aaf->timer_fd, &expirations, sizeof(uint64_t));
+	if (n < 0) {
+		SNDERR("Failed to read() timer");
+		return -errno;
+	}
+
+	if (expirations != 1)
+		pr_debug("Missed %llu tx interval(s) ", expirations - 1);
+
+	while (expirations--) {
+		res = aaf_tx_pdu(aaf);
+		if (res < 0)
+			return res;
+		aaf->mclk_ticks++;
+	}
+
+	return 0;
+}
+
+static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	snd_pcm_ioplug_t *io = &aaf->io;
+	const unsigned int accesses[] = {
+		SND_PCM_ACCESS_RW_INTERLEAVED,
+	};
+	const unsigned int formats[] = {
+		SND_PCM_FORMAT_S16_BE,
+		SND_PCM_FORMAT_S24_3BE,
+		SND_PCM_FORMAT_S32_BE,
+		SND_PCM_FORMAT_FLOAT_BE,
+	};
+	const unsigned int rates[] = {
+		8000,
+		16000,
+		24000,
+		32000,
+		44100,
+		48000,
+		88200,
+		96000,
+		176400,
+		192000,
+	};
+
+	res = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
+					    ARRAY_SIZE(accesses), accesses);
+	if (res < 0)
+		return res;
+
+	res = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
+					    ARRAY_SIZE(formats), formats);
+	if (res < 0)
+		return res;
+
+	res = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE,
+					    ARRAY_SIZE(rates), rates);
+	if (res < 0)
+		return res;
+
+	return 0;
+}
+
 static int aaf_close(snd_pcm_ioplug_t *io)
 {
 	snd_pcm_aaf_t *aaf = io->private_data;
@@ -164,26 +634,185 @@ static int aaf_close(snd_pcm_ioplug_t *io)
 	return 0;
 }
 
+static int aaf_hw_params(snd_pcm_ioplug_t *io,
+			 snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
+{
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	res = aaf_init_socket(aaf);
+	if (res < 0)
+		return res;
+
+	res = aaf_init_timer(aaf);
+	if (res < 0)
+		goto err_close_sk;
+
+	res = aaf_init_pdu(aaf);
+	if (res < 0)
+		goto err_close_timer;
+
+	res = aaf_init_payload_areas(aaf);
+	if (res < 0)
+		goto err_free_pdu;
+
+	res = aaf_init_audiobuf_areas(aaf);
+	if (res < 0)
+		goto err_free_payload_areas;
+
+	return 0;
+
+err_free_payload_areas:
+	free(aaf->payload_areas);
+err_free_pdu:
+	free(aaf->pdu);
+err_close_timer:
+	close(aaf->timer_fd);
+err_close_sk:
+	close(aaf->sk_fd);
+	return res;
+}
+
+static int aaf_hw_free(snd_pcm_ioplug_t *io)
+{
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	close(aaf->sk_fd);
+	close(aaf->timer_fd);
+	free(aaf->pdu);
+	free(aaf->payload_areas);
+	free(aaf->audiobuf_areas->addr);
+	free(aaf->audiobuf_areas);
+	return 0;
+}
+
+static int aaf_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
+{
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	res = snd_pcm_sw_params_get_boundary(params, &aaf->boundary);
+	if (res < 0)
+		return res;
+
+	return 0;
+}
+
 static snd_pcm_sframes_t aaf_pointer(snd_pcm_ioplug_t *io)
 {
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	return aaf->hw_ptr;
+}
+
+static int aaf_poll_descriptors_count(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED)
+{
+	return FD_COUNT_PLAYBACK;
+}
+
+static int aaf_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
+				unsigned int space)
+{
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	if (space != FD_COUNT_PLAYBACK)
+		return -EINVAL;
+
+	pfd[0].fd = aaf->timer_fd;
+	pfd[0].events = POLLIN;
+	return space;
+}
+
+static int aaf_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
+			    unsigned int nfds, unsigned short *revents)
+{
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	if (nfds != FD_COUNT_PLAYBACK)
+		return -EINVAL;
+
+	if (pfd[0].revents & POLLIN) {
+		res = aaf_mclk_timeout_playback(aaf);
+		if (res < 0)
+			return res;
+
+		*revents = POLLIN;
+	}
+
+	return 0;
+}
+
+static int aaf_prepare(snd_pcm_ioplug_t *io)
+{
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	aaf->pdu_seq = 0;
+	aaf->hw_ptr = 0;
+
+	res = aaf_mclk_reset(aaf);
+	if (res < 0)
+		return res;
+
 	return 0;
 }
 
 static int aaf_start(snd_pcm_ioplug_t *io)
 {
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	res = aaf_mclk_start_playback(aaf);
+	if (res < 0)
+		return res;
+
 	return 0;
 }
 
 static int aaf_stop(snd_pcm_ioplug_t *io)
 {
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	res = aaf_mclk_reset(aaf);
+	if (res < 0)
+		return res;
+
 	return 0;
 }
 
+static snd_pcm_sframes_t aaf_transfer(snd_pcm_ioplug_t *io,
+				      const snd_pcm_channel_area_t *areas,
+				      snd_pcm_uframes_t offset,
+				      snd_pcm_uframes_t size)
+{
+	int res;
+	snd_pcm_aaf_t *aaf = io->private_data;
+
+	res = snd_pcm_areas_copy_wrap(aaf->audiobuf_areas,
+				      (io->appl_ptr % io->buffer_size),
+				      io->buffer_size, areas, offset, size,
+				      io->channels, size, io->format);
+	if (res < 0)
+		return res;
+
+	return size;
+}
+
 static const snd_pcm_ioplug_callback_t aaf_callback = {
 	.close = aaf_close,
+	.hw_params = aaf_hw_params,
+	.hw_free = aaf_hw_free,
+	.sw_params = aaf_sw_params,
 	.pointer = aaf_pointer,
+	.poll_descriptors_count = aaf_poll_descriptors_count,
+	.poll_descriptors = aaf_poll_descriptors,
+	.poll_revents = aaf_poll_revents,
+	.prepare = aaf_prepare,
 	.start = aaf_start,
 	.stop = aaf_stop,
+	.transfer = aaf_transfer,
 };
 
 SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
@@ -191,12 +820,21 @@ SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
 	snd_pcm_aaf_t *aaf;
 	int res;
 
+	/* For now the plugin only supports Playback mode i.e. AAF Talker
+	 * functionality.
+	 */
+	if (stream != SND_PCM_STREAM_PLAYBACK)
+		return -EINVAL;
+
 	aaf = calloc(1, sizeof(*aaf));
 	if (!aaf) {
 		SNDERR("Failed to allocate memory");
 		return -ENOMEM;
 	}
 
+	aaf->sk_fd = -1;
+	aaf->timer_fd = -1;
+
 	res = aaf_load_config(aaf, conf);
 	if (res < 0)
 		goto err;
@@ -205,12 +843,20 @@ SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
 	aaf->io.name = "AVTP Audio Format (AAF) Plugin";
 	aaf->io.callback = &aaf_callback;
 	aaf->io.private_data = aaf;
+	aaf->io.flags = SND_PCM_IOPLUG_FLAG_BOUNDARY_WA;
 	res = snd_pcm_ioplug_create(&aaf->io, name, stream, mode);
 	if (res < 0) {
 		SNDERR("Failed to create ioplug instance");
 		goto err;
 	}
 
+	res = aaf_set_hw_constraint(aaf);
+	if (res < 0) {
+		SNDERR("Failed to set hw constraints");
+		snd_pcm_ioplug_delete(&aaf->io);
+		goto err;
+	}
+
 	*pcmp = aaf->io.pcm;
 	return 0;
 
diff --git a/doc/aaf.txt b/doc/aaf.txt
index d817249..ac9dd9d 100644
--- a/doc/aaf.txt
+++ b/doc/aaf.txt
@@ -9,6 +9,78 @@ to transmit/receive audio samples through a Time-Sensitive Network (TSN)
 capable network. The plugin enables media applications to easily implement AVTP
 Talker and Listener functionalities.
 
+AVTP is designed to take advantage of generalized Precision Time Protocol
+(gPTP) and Forwarding and Queuing Enhancements for Time-Sensitive Streams
+(FQTSS). gPTP ensures AVTP talkers and listeners share the same time reference
+so the presentation time from AVTP can be used to inform when PCM samples
+should be presented to the application layer. FQTSS provides bandwidth
+reservation and traffic prioritization for the AVTP stream.
+
+gPTP functionality is provided by the Linuxptp project while FQTSS
+functionality is provided by Linux Traffic Control system since kernel version
+4.15.
+
+gPTP Setup
+----------
+
+The Linuxptp project provides the ptp4l daemon, which synchronizes the PTP
+clock from NIC, and the pmc tool which communicates with ptp4l to get/set
+some runtime settings. The project also provides the phc2sys daemon which
+synchronizes the PTP clock and system clock.
+
+The AAF Plugin requires system clock is synchronized with PTP clock and TAI
+offset is properly set in the kernel. ptp4l and phc2sys can be set up in many
+different ways, below we provide an example that fullfils the plugin
+requirements. For further information check ptp4l(8) and phc2sys(8).
+
+In the following instructions, replace $IFNAME by your PTP capable NIC
+interface. The gPTP.cfg file mentioned below can be found in /usr/share/
+doc/linuxptp/ (depending on your distro).
+
+Synchronize PTP clock with PTP time:
+
+	$ ptp4l -f gPTP.cfg -i $IFNAME
+
+Enable TAI offset to be automatically set by phc2sys:
+
+	$ pmc -u -t 1 -b 0 'SET GRANDMASTER_SETTINGS_NP \
+		clockClass 248 clockAccuracy 0xfe \
+		offsetScaledLogVariance 0xffff \
+		currentUtcOffset 37 leap61 0 leap59 0 \
+		currentUtcOffsetValid 1 p pTimescale 1 \
+		timeTraceable 1 frequencyTraceable 0 timeSource 0xa0'
+
+Synchronize system clock with PTP clock:
+
+	$ phc2sys -f gPTP.cfg -s $IFNAME -c CLOCK_REALTIME -w
+
+The commands above should be run on both AVTP Talker and Listener hosts.
+
+FQTSS Setup
+-----------
+
+The Linux Traffic Control system provides the mqprio and cbs qdiscs which
+enable FQTSS on Linux. Below we provide an example to configure those qdiscs in
+order to transmit an AAF stream with 48 kHz sampling rate, 16-bit sample size,
+stereo. For further information on how to configure these qdiscs check
+tc-mqprio(8) and tc-cbs(8) man pages.
+
+On the host that will run as AVTP Talker (i.e. plugin in playback mode), run
+the following commands:
+
+Configure mpqrio qdisc (replace $HANDLE_ID by an unused handle ID):
+
+	$ tc qdisc add dev $IFNAME parent root handle $HANDLE_ID mqprio \
+			num_tc 3 map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \
+			queues 1@0 1@1 2@2 hw 0
+
+Configure cbs qdisc:
+
+	$ tc qdisc replace dev $IFNAME parent $HANDLE_ID:1 cbs idleslope 5760 \
+			sendslope -994240 hicredit 9 locredit -89 offload 1
+
+No FQTSS configuration is required at the host running as AVTP Listener.
+
 Plugin Dependencies
 -------------------
 
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 4/7] aaf: Prepare for Capture mode support
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
                   ` (2 preceding siblings ...)
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 5/7] aaf: Implement " Andre Guedes
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

The plugin code assumes only Playback mode is supported. This patch
prepares the code to support both Playback and Capture mode. Capture
mode support is implemented by a follow-up patch.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 159 ++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 94 insertions(+), 65 deletions(-)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index 8f8bcfd..74cda97 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -239,6 +239,7 @@ static int aaf_init_socket(snd_pcm_aaf_t *aaf)
 {
 	int fd, res;
 	struct ifreq req;
+	snd_pcm_ioplug_t *io = &aaf->io;
 
 	fd = socket(AF_PACKET, SOCK_DGRAM|SOCK_NONBLOCK, htons(ETH_P_TSN));
 	if (fd < 0) {
@@ -260,12 +261,17 @@ static int aaf_init_socket(snd_pcm_aaf_t *aaf)
 	aaf->sk_addr.sll_ifindex = req.ifr_ifindex;
 	memcpy(&aaf->sk_addr.sll_addr, aaf->addr, ETH_ALEN);
 
-	res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &aaf->prio,
-			 sizeof(aaf->prio));
-	if (res < 0) {
-		SNDERR("Failed to set socket priority");
-		res = -errno;
-		goto err;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		res = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &aaf->prio,
+				 sizeof(aaf->prio));
+		if (res < 0) {
+			SNDERR("Failed to set socket priority");
+			res = -errno;
+			goto err;
+		}
+	} else {
+		/* TODO: Implement Capture mode support. */
+		return -ENOTSUP;
 	}
 
 	aaf->sk_fd = fd;
@@ -305,46 +311,50 @@ static int aaf_init_pdu(snd_pcm_aaf_t *aaf)
 	if (!pdu)
 		return -ENOMEM;
 
-	res = avtp_aaf_pdu_init(pdu);
-	if (res < 0)
-		goto err;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		res = avtp_aaf_pdu_init(pdu);
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TV, 1);
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TV, 1);
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_ID, aaf->streamid);
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_ID,
+				       aaf->streamid);
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_FORMAT,
-			       alsa_to_avtp_format(io->format));
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_FORMAT,
+				       alsa_to_avtp_format(io->format));
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_NSR,
-			       alsa_to_avtp_rate(io->rate));
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_NSR,
+				       alsa_to_avtp_rate(io->rate));
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME,
-			       io->channels);
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME,
+				       io->channels);
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_BIT_DEPTH,
-			       snd_pcm_format_width(io->format));
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_BIT_DEPTH,
+				       snd_pcm_format_width(io->format));
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN,
-			       payload_size);
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN,
+				       payload_size);
+		if (res < 0)
+			goto err;
 
-	res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SP, AVTP_AAF_PCM_SP_NORMAL);
-	if (res < 0)
-		goto err;
+		res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_SP,
+				       AVTP_AAF_PCM_SP_NORMAL);
+		if (res < 0)
+			goto err;
+	}
 
 	aaf->pdu = pdu;
 	aaf->pdu_size = pdu_size;
@@ -707,7 +717,10 @@ static snd_pcm_sframes_t aaf_pointer(snd_pcm_ioplug_t *io)
 
 static int aaf_poll_descriptors_count(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED)
 {
-	return FD_COUNT_PLAYBACK;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK)
+		return FD_COUNT_PLAYBACK;
+	else
+		return -ENOTSUP;
 }
 
 static int aaf_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
@@ -715,11 +728,17 @@ static int aaf_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
 {
 	snd_pcm_aaf_t *aaf = io->private_data;
 
-	if (space != FD_COUNT_PLAYBACK)
-		return -EINVAL;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		if (space != FD_COUNT_PLAYBACK)
+			return -EINVAL;
+
+		pfd[0].fd = aaf->timer_fd;
+		pfd[0].events = POLLIN;
+	} else {
+		/* TODO: Implement Capture mode support. */
+		return -ENOTSUP;
+	}
 
-	pfd[0].fd = aaf->timer_fd;
-	pfd[0].events = POLLIN;
 	return space;
 }
 
@@ -729,15 +748,20 @@ static int aaf_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
 	int res;
 	snd_pcm_aaf_t *aaf = io->private_data;
 
-	if (nfds != FD_COUNT_PLAYBACK)
-		return -EINVAL;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		if (nfds != FD_COUNT_PLAYBACK)
+			return -EINVAL;
 
-	if (pfd[0].revents & POLLIN) {
-		res = aaf_mclk_timeout_playback(aaf);
-		if (res < 0)
-			return res;
+		if (pfd[0].revents & POLLIN) {
+			res = aaf_mclk_timeout_playback(aaf);
+			if (res < 0)
+				return res;
 
-		*revents = POLLIN;
+			*revents = POLLIN;
+		}
+	} else {
+		/* TODO: Implement Capture mode support. */
+		return -ENOTSUP;
 	}
 
 	return 0;
@@ -763,9 +787,14 @@ static int aaf_start(snd_pcm_ioplug_t *io)
 	int res;
 	snd_pcm_aaf_t *aaf = io->private_data;
 
-	res = aaf_mclk_start_playback(aaf);
-	if (res < 0)
-		return res;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		res = aaf_mclk_start_playback(aaf);
+		if (res < 0)
+			return res;
+	} else {
+		/* TODO: Implement Capture mode support. */
+		return -ENOTSUP;
+	}
 
 	return 0;
 }
@@ -790,12 +819,18 @@ static snd_pcm_sframes_t aaf_transfer(snd_pcm_ioplug_t *io,
 	int res;
 	snd_pcm_aaf_t *aaf = io->private_data;
 
-	res = snd_pcm_areas_copy_wrap(aaf->audiobuf_areas,
-				      (io->appl_ptr % io->buffer_size),
-				      io->buffer_size, areas, offset, size,
-				      io->channels, size, io->format);
-	if (res < 0)
-		return res;
+	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
+		res = snd_pcm_areas_copy_wrap(aaf->audiobuf_areas,
+					      (io->appl_ptr % io->buffer_size),
+					      io->buffer_size, areas, offset,
+					      size, io->channels, size,
+					      io->format);
+		if (res < 0)
+			return res;
+	} else {
+		/* TODO: Implement Capture mode support. */
+		return -ENOTSUP;
+	}
 
 	return size;
 }
@@ -820,12 +855,6 @@ SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
 	snd_pcm_aaf_t *aaf;
 	int res;
 
-	/* For now the plugin only supports Playback mode i.e. AAF Talker
-	 * functionality.
-	 */
-	if (stream != SND_PCM_STREAM_PLAYBACK)
-		return -EINVAL;
-
 	aaf = calloc(1, sizeof(*aaf));
 	if (!aaf) {
 		SNDERR("Failed to allocate memory");
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 5/7] aaf: Implement Capture mode support
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
                   ` (3 preceding siblings ...)
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 4/7] aaf: Prepare for Capture " Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 6/7] aaf: Implement dump() ioplug callback Andre Guedes
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

This patch implements the capture mode support from the AAF plugin.
Simply put, this mode works as follows: AVTPDUs are received from the
network, the PCM samples are retrieved and presented to the alsa-lib
layer at the presentation time. In summary, the capture mode implements
a typical AVTP Listener.

Once the AAF device is put in running state, packet reception is
started. Every time an AVTPDU is received, the plugin checks if it is
valid (according to the stream configuration provided by the user) and
copies the PCM samples to the audio buffer. Note that at this moment,
the samples are not presented to the alsa-lib layer yet (i.e. hw_ptr is
not incremented).

The media clock starts at the presentation time from the first AVTPDU.
At every tick from the media clock, PCM samples are presented to the
alsa-lib layer.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 367 insertions(+), 21 deletions(-)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index 74cda97..e5c222f 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -28,6 +28,7 @@
 #include <linux/if_ether.h>
 #include <linux/if_packet.h>
 #include <string.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <sys/ioctl.h>
 #include <sys/timerfd.h>
@@ -46,6 +47,7 @@
 #define TAI_TO_UTC(t) (t - TAI_OFFSET)
 
 #define FD_COUNT_PLAYBACK 1
+#define FD_COUNT_CAPTURE  2
 
 typedef struct {
 	snd_pcm_ioplug_t io;
@@ -66,6 +68,7 @@ typedef struct {
 	struct avtp_stream_pdu *pdu;
 	int pdu_size;
 	uint8_t pdu_seq;
+	uint64_t pdu_count;
 
 	uint64_t mclk_start_time;
 	uint64_t mclk_period;
@@ -75,6 +78,7 @@ typedef struct {
 	snd_pcm_channel_area_t *payload_areas;
 
 	snd_pcm_uframes_t hw_ptr;
+	snd_pcm_uframes_t hw_virt_ptr;
 	snd_pcm_uframes_t boundary;
 } snd_pcm_aaf_t;
 
@@ -122,6 +126,106 @@ static unsigned int alsa_to_avtp_rate(unsigned int rate)
 	}
 }
 
+static bool is_pdu_valid(struct avtp_stream_pdu *pdu, uint64_t streamid,
+			 unsigned int data_len, unsigned int format,
+			 unsigned int nsr, unsigned int channels,
+			 unsigned int depth)
+{
+	int res;
+	uint64_t val64;
+	uint32_t val32;
+	struct avtp_common_pdu *common = (struct avtp_common_pdu *) pdu;
+
+	res = avtp_pdu_get(common, AVTP_FIELD_SUBTYPE, &val32);
+	if (res < 0)
+		return false;
+	if (val32 != AVTP_SUBTYPE_AAF) {
+		pr_debug("Subtype mismatch: expected %u, got %u",
+			 AVTP_SUBTYPE_AAF, val32);
+		return false;
+	}
+
+	res = avtp_pdu_get(common, AVTP_FIELD_VERSION, &val32);
+	if (res < 0)
+		return false;
+	if (val32 != 0) {
+		pr_debug("Version mismatch: expected %u, got %u", 0, val32);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_STREAM_ID, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != streamid) {
+		pr_debug("Streamid mismatch: expected %lu, got %lu", streamid,
+			 val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_TV, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != 1) {
+		pr_debug("TV mismatch: expected %u, got %lu", 1, val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_SP, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != AVTP_AAF_PCM_SP_NORMAL) {
+		pr_debug("SP mismatch: expected %u, got %lu",
+			 AVTP_AAF_PCM_SP_NORMAL, val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_FORMAT, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != format) {
+		pr_debug("Format mismatch: expected %u, got %lu", format,
+			 val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_NSR, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != nsr) {
+		pr_debug("NSR mismatch: expected %u, got %lu", nsr, val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != channels) {
+		pr_debug("Channels mismatch: expected %u, got %lu", channels,
+			 val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_BIT_DEPTH, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != depth) {
+		pr_debug("Bit depth mismatch: expected %u, got %lu", depth,
+			 val64);
+		return false;
+	}
+
+	res = avtp_aaf_pdu_get(pdu, AVTP_AAF_FIELD_STREAM_DATA_LEN, &val64);
+	if (res < 0)
+		return false;
+	if (val64 != data_len) {
+		pr_debug("Data len mismatch: expected %u, got %lu",
+			 data_len, val64);
+		return false;
+	}
+
+	return true;
+}
+
 static int aaf_load_config(snd_pcm_aaf_t *aaf, snd_config_t *conf)
 {
 	snd_config_iterator_t cur, next;
@@ -270,8 +374,27 @@ static int aaf_init_socket(snd_pcm_aaf_t *aaf)
 			goto err;
 		}
 	} else {
-		/* TODO: Implement Capture mode support. */
-		return -ENOTSUP;
+		struct packet_mreq mreq = { 0 };
+
+		res = bind(fd, (struct sockaddr *) &aaf->sk_addr,
+			   sizeof(aaf->sk_addr));
+		if (res < 0) {
+			SNDERR("Failed to bind socket");
+			res = -errno;
+			goto err;
+		}
+
+		mreq.mr_ifindex = req.ifr_ifindex;
+		mreq.mr_type = PACKET_MR_MULTICAST;
+		mreq.mr_alen = ETH_ALEN;
+		memcpy(&mreq.mr_address, aaf->addr, ETH_ALEN);
+		res = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP,
+				 &mreq, sizeof(struct packet_mreq));
+		if (res < 0) {
+			SNDERR("Failed to add multicast address");
+			res = -errno;
+			goto err;
+		}
 	}
 
 	aaf->sk_fd = fd;
@@ -444,12 +567,13 @@ err_free_buf:
 	return res;
 }
 
-static void aaf_inc_hw_ptr(snd_pcm_aaf_t *aaf, snd_pcm_uframes_t val)
+static void aaf_inc_ptr(snd_pcm_uframes_t *ptr, snd_pcm_uframes_t val,
+			snd_pcm_uframes_t boundary)
 {
-	aaf->hw_ptr += val;
+	*ptr += val;
 
-	if (aaf->hw_ptr >= aaf->boundary)
-		aaf->hw_ptr -= aaf->boundary;
+	if (*ptr > boundary)
+		*ptr -= boundary;
 }
 
 static int aaf_mclk_start_playback(snd_pcm_aaf_t *aaf)
@@ -484,6 +608,54 @@ static int aaf_mclk_start_playback(snd_pcm_aaf_t *aaf)
 	return 0;
 }
 
+static int aaf_mclk_start_capture(snd_pcm_aaf_t *aaf, uint32_t avtp_time)
+{
+	int res;
+	struct timespec tspec;
+	struct itimerspec itspec;
+	uint64_t ptime, now, time_utc;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	res = clock_gettime(CLOCK_TAI, &tspec);
+	if (res < 0) {
+		SNDERR("Failed to get time from clock");
+		return -errno;
+	}
+
+	now = (uint64_t)tspec.tv_sec * NSEC_PER_SEC + tspec.tv_nsec;
+
+	/* The avtp_timestamp within AAF packet is the lower part (32
+	 * less-significant bits) from presentation time calculated by the
+	 * talker.
+	 */
+	ptime = (now & 0xFFFFFFFF00000000ULL) | avtp_time;
+
+	/* If 'ptime' is less than the 'now', it means the higher part
+	 * from 'ptime' needs to be incremented by 1 in order to recover the
+	 * presentation time set by the talker.
+	 */
+	if (ptime < now)
+		ptime += (1ULL << 32);
+
+	aaf->mclk_period = (uint64_t)NSEC_PER_SEC * aaf->frames_per_pdu /
+			   io->rate;
+	aaf->mclk_ticks = 0;
+	aaf->mclk_start_time = ptime;
+
+	time_utc = TAI_TO_UTC(ptime);
+	itspec.it_value.tv_sec = time_utc / NSEC_PER_SEC;
+	itspec.it_value.tv_nsec = time_utc % NSEC_PER_SEC;
+	itspec.it_interval.tv_sec = 0;
+	itspec.it_interval.tv_nsec = aaf->mclk_period;
+	res = timerfd_settime(aaf->timer_fd, TFD_TIMER_ABSTIME, &itspec, NULL);
+	if (res < 0) {
+		SNDERR("Failed to set timer");
+		return -errno;
+	}
+
+	return 0;
+}
+
 static int aaf_mclk_reset(snd_pcm_aaf_t *aaf)
 {
 	int res;
@@ -527,7 +699,7 @@ static int aaf_tx_pdu(snd_pcm_aaf_t *aaf)
 		 * them. This behavior is suggested by IEEE 1722-2016 spec,
 		 * section 7.3.5.
 		 */
-		aaf_inc_hw_ptr(aaf, hw_avail);
+		aaf_inc_ptr(&aaf->hw_ptr, hw_avail, aaf->boundary);
 		return 0;
 	}
 
@@ -559,7 +731,117 @@ static int aaf_tx_pdu(snd_pcm_aaf_t *aaf)
 		return -EIO;
 	}
 
-	aaf_inc_hw_ptr(aaf, aaf->frames_per_pdu);
+	aaf_inc_ptr(&aaf->hw_ptr, aaf->frames_per_pdu, aaf->boundary);
+	return 0;
+}
+
+static int aaf_rx_pdu(snd_pcm_aaf_t *aaf)
+{
+	int res;
+	ssize_t n;
+	uint64_t seq, avtp_time;
+	snd_pcm_uframes_t hw_avail;
+	snd_pcm_ioplug_t *io = &aaf->io;
+	snd_pcm_t *pcm = io->pcm;
+
+	n = recv(aaf->sk_fd, aaf->pdu, aaf->pdu_size, 0);
+	if (n < 0 || n != aaf->pdu_size) {
+		SNDERR("Failed to receive data");
+		return -EIO;
+	}
+
+	if (io->state == SND_PCM_STATE_DRAINING) {
+		/* If device is in DRAIN state, we shouldn't copy any more data
+		 * to audio buffer. So we are done here.
+		 */
+		return 0;
+	}
+
+	if (!is_pdu_valid(aaf->pdu, aaf->streamid,
+			  snd_pcm_frames_to_bytes(pcm, aaf->frames_per_pdu),
+			  alsa_to_avtp_format(io->format),
+			  alsa_to_avtp_rate(io->rate),
+			  io->channels, snd_pcm_format_width(io->format)))
+		return 0;
+
+	res = avtp_aaf_pdu_get(aaf->pdu, AVTP_AAF_FIELD_SEQ_NUM, &seq);
+	if (res < 0)
+		return res;
+	if (seq != aaf->pdu_seq) {
+		pr_debug("Sequence mismatch: expected %u, got %lu",
+			 aaf->pdu_seq, seq);
+		aaf->pdu_seq = seq;
+	}
+	aaf->pdu_seq++;
+
+	res = avtp_aaf_pdu_get(aaf->pdu, AVTP_AAF_FIELD_TIMESTAMP, &avtp_time);
+	if (res < 0)
+		return res;
+
+	if (aaf->mclk_start_time == 0) {
+		res = aaf_mclk_start_capture(aaf, avtp_time);
+		if (res < 0)
+			return res;
+	} else {
+		uint64_t ptime = aaf->mclk_start_time + aaf->mclk_period *
+				 aaf->pdu_count;
+
+		if (avtp_time != ptime % (1ULL << 32)) {
+			pr_debug("Packet dropped: PT not expected");
+			return 0;
+		}
+		if (ptime < aaf_mclk_gettime(aaf)) {
+			pr_debug("Packet dropped: PT in the past");
+			return 0;
+		}
+	}
+
+	hw_avail = snd_pcm_ioplug_hw_avail(io, aaf->hw_virt_ptr, io->appl_ptr);
+	if (hw_avail < aaf->frames_per_pdu) {
+		/* If there isn't enough space available on buffer to copy the
+		 * samples from AVTPDU, it means we've reached an overrun
+		 * state.
+		 */
+		return -EPIPE;
+	}
+
+	res = snd_pcm_areas_copy_wrap(aaf->audiobuf_areas,
+				      (aaf->hw_virt_ptr % io->buffer_size),
+				      io->buffer_size, aaf->payload_areas,
+				      0, aaf->frames_per_pdu, io->channels,
+				      aaf->frames_per_pdu, io->format);
+	if (res < 0) {
+		SNDERR("Failed to copy data from AVTP payload");
+		return res;
+	}
+
+	aaf->pdu_count++;
+	aaf_inc_ptr(&aaf->hw_virt_ptr, aaf->frames_per_pdu, aaf->boundary);
+	return 0;
+}
+
+static int aaf_flush_rx_buf(snd_pcm_aaf_t *aaf)
+{
+	char *tmp;
+	ssize_t n;
+
+	tmp = malloc(aaf->pdu_size);
+	if (!tmp)
+		return -ENOMEM;
+
+	do {
+		n = recv(aaf->sk_fd, tmp, aaf->pdu_size, 0);
+	} while (n != -1);
+
+	if (errno != EAGAIN && errno != EWOULDBLOCK) {
+		/* Something unexpected has happened while flushing the socket
+		 * rx buffer so we return error.
+		 */
+		free(tmp);
+		return -errno;
+	}
+
+	free(tmp);
 	return 0;
 }
 
@@ -588,6 +870,41 @@ static int aaf_mclk_timeout_playback(snd_pcm_aaf_t *aaf)
 	return 0;
 }
 
+static int aaf_mclk_timeout_capture(snd_pcm_aaf_t *aaf)
+{
+	ssize_t n;
+	uint64_t expirations;
+	snd_pcm_ioplug_t *io = &aaf->io;
+
+	n = read(aaf->timer_fd, &expirations, sizeof(uint64_t));
+	if (n < 0) {
+		SNDERR("Failed to read() timer");
+		return -errno;
+	}
+
+	if (expirations != 1)
+		pr_debug("Missed %llu presentation time(s) ", expirations - 1);
+
+	while (expirations--) {
+		snd_pcm_sframes_t len = aaf->hw_virt_ptr - aaf->hw_ptr;
+		if (len < 0)
+			len += aaf->boundary;
+
+		if ((snd_pcm_uframes_t) len > io->buffer_size) {
+			/* If the distance between hw virtual pointer and hw
+			 * pointer is greater than the buffer size, it means we
+			 * had an overrun error so -EPIPE is returned.
+			 */
+			return -EPIPE;
+		}
+
+		aaf_inc_ptr(&aaf->hw_ptr, aaf->frames_per_pdu, aaf->boundary);
+		aaf->mclk_ticks++;
+	}
+
+	return 0;
+}
+
 static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
 {
 	int res;
@@ -720,7 +1037,7 @@ static int aaf_poll_descriptors_count(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED)
 	if (io->stream == SND_PCM_STREAM_PLAYBACK)
 		return FD_COUNT_PLAYBACK;
 	else
-		return -ENOTSUP;
+		return FD_COUNT_CAPTURE;
 }
 
 static int aaf_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
@@ -735,8 +1052,13 @@ static int aaf_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd,
 		pfd[0].fd = aaf->timer_fd;
 		pfd[0].events = POLLIN;
 	} else {
-		/* TODO: Implement Capture mode support. */
-		return -ENOTSUP;
+		if (space != FD_COUNT_CAPTURE)
+			return -EINVAL;
+
+		pfd[0].fd = aaf->timer_fd;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = aaf->sk_fd;
+		pfd[1].events = POLLIN;
 	}
 
 	return space;
@@ -760,8 +1082,22 @@ static int aaf_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
 			*revents = POLLIN;
 		}
 	} else {
-		/* TODO: Implement Capture mode support. */
-		return -ENOTSUP;
+		if (nfds != FD_COUNT_CAPTURE)
+			return -EINVAL;
+
+		if (pfd[0].revents & POLLIN) {
+			res = aaf_mclk_timeout_capture(aaf);
+			if (res < 0)
+				return res;
+
+			*revents = POLLIN;
+		}
+
+		if (pfd[1].revents & POLLIN) {
+			res = aaf_rx_pdu(aaf);
+			if (res < 0)
+				return res;
+		}
 	}
 
 	return 0;
@@ -773,7 +1109,9 @@ static int aaf_prepare(snd_pcm_ioplug_t *io)
 	snd_pcm_aaf_t *aaf = io->private_data;
 
 	aaf->pdu_seq = 0;
+	aaf->pdu_count = 0;
 	aaf->hw_ptr = 0;
+	aaf->hw_virt_ptr = 0;
 
 	res = aaf_mclk_reset(aaf);
 	if (res < 0)
@@ -789,13 +1127,17 @@ static int aaf_start(snd_pcm_ioplug_t *io)
 
 	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
 		res = aaf_mclk_start_playback(aaf);
-		if (res < 0)
-			return res;
 	} else {
-		/* TODO: Implement Capture mode support. */
-		return -ENOTSUP;
+		/* Discard any packet on socket buffer to ensure the plugin
+		 * process only packets that arrived after the device has
+		 * started.
+		 */
+		res = aaf_flush_rx_buf(aaf);
 	}
 
+	if (res < 0)
+		return res;
+
 	return 0;
 }
 
@@ -825,13 +1167,17 @@ static snd_pcm_sframes_t aaf_transfer(snd_pcm_ioplug_t *io,
 					      io->buffer_size, areas, offset,
 					      size, io->channels, size,
 					      io->format);
-		if (res < 0)
-			return res;
 	} else {
-		/* TODO: Implement Capture mode support. */
-		return -ENOTSUP;
+		res = snd_pcm_areas_copy_wrap(areas, offset, (offset + size),
+					      aaf->audiobuf_areas,
+					      (io->appl_ptr % io->buffer_size),
+					      io->buffer_size, io->channels,
+					      size, io->format);
 	}
 
+	if (res < 0)
+		return res;
+
 	return size;
 }
 
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 6/7] aaf: Implement dump() ioplug callback
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
                   ` (4 preceding siblings ...)
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 5/7] aaf: Implement " Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 7/7] aaf: Add support for direct read/write transfers Andre Guedes
  2018-10-10 10:25 ` [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Takashi Iwai
  7 siblings, 0 replies; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

This patch introduces the aaf_dump() function which prints information
about both PCM and AVTP setup.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index e5c222f..f3159a1 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -23,6 +23,7 @@
 #include <arpa/inet.h>
 #include <avtp.h>
 #include <avtp_aaf.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <linux/if.h>
 #include <linux/if_ether.h>
@@ -961,6 +962,28 @@ static int aaf_close(snd_pcm_ioplug_t *io)
 	return 0;
 }
 
+static void aaf_dump(snd_pcm_ioplug_t *io, snd_output_t *out)
+{
+	snd_pcm_aaf_t *aaf = io->private_data;
+	snd_pcm_t *pcm = io->pcm;
+
+	snd_output_printf(out, "%s\n", io->name);
+	snd_output_printf(out, "PCM setup is:\n");
+	snd_pcm_dump_setup(pcm, out);
+	snd_output_printf(out, "AVTP setup is:\n");
+	snd_output_printf(out, "  ifname: %s\n", aaf->ifname);
+	snd_output_printf(out, "  macaddr: %.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",
+			  aaf->addr[0], aaf->addr[1], aaf->addr[2],
+			  aaf->addr[3], aaf->addr[4], aaf->addr[5]);
+	snd_output_printf(out, "  priority: %d\n", aaf->prio);
+	snd_output_printf(out, "  streamid: %"PRIX64"\n", aaf->streamid);
+	snd_output_printf(out, "  mtt: %d\n", aaf->mtt / NSEC_PER_USEC);
+	snd_output_printf(out, "  time uncertainty: %d\n",
+			  aaf->t_uncertainty / NSEC_PER_USEC);
+	snd_output_printf(out, "  frame per AVTPDU: %lu\n",
+			  aaf->frames_per_pdu);
+}
+
 static int aaf_hw_params(snd_pcm_ioplug_t *io,
 			 snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
 {
@@ -1183,6 +1206,7 @@ static snd_pcm_sframes_t aaf_transfer(snd_pcm_ioplug_t *io,
 
 static const snd_pcm_ioplug_callback_t aaf_callback = {
 	.close = aaf_close,
+	.dump = aaf_dump,
 	.hw_params = aaf_hw_params,
 	.hw_free = aaf_hw_free,
 	.sw_params = aaf_sw_params,
-- 
2.14.4

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

* [PATCH - AAF PCM plugin 7/7] aaf: Add support for direct read/write transfers
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
                   ` (5 preceding siblings ...)
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 6/7] aaf: Implement dump() ioplug callback Andre Guedes
@ 2018-10-03 23:45 ` Andre Guedes
  2018-10-10 10:25 ` [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Takashi Iwai
  7 siblings, 0 replies; 21+ messages in thread
From: Andre Guedes @ 2018-10-03 23:45 UTC (permalink / raw)
  To: alsa-devel; +Cc: tiwai, liam.r.girdwood

This patch adds support for direct read/write transfers (i.e. mmap
access mode) to the AAF plugin.

In order to enable direct read/write transfers, the AAF plugin is
required to implement ioplug's pseudo mmap mode. In this mode, the audio
buffer management (e.g. areas allocation, clean up, and data copy) is
handled at upper layers, making the AAF plugin simpler. So this patch
removes all code related to audio buffer management as well as the
transfer() callback from the AAF plugin.

Signed-off-by: Andre Guedes <andre.guedes@intel.com>
---
 aaf/pcm_aaf.c | 78 +++--------------------------------------------------------
 1 file changed, 4 insertions(+), 74 deletions(-)

diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
index f3159a1..463d457 100644
--- a/aaf/pcm_aaf.c
+++ b/aaf/pcm_aaf.c
@@ -75,7 +75,7 @@ typedef struct {
 	uint64_t mclk_period;
 	uint64_t mclk_ticks;
 
-	snd_pcm_channel_area_t *audiobuf_areas;
+	const snd_pcm_channel_area_t *audiobuf_areas;
 	snd_pcm_channel_area_t *payload_areas;
 
 	snd_pcm_uframes_t hw_ptr;
@@ -532,42 +532,6 @@ err:
 	return res;
 }
 
-static int aaf_init_audiobuf_areas(snd_pcm_aaf_t *aaf)
-{
-	int res;
-	char *buf;
-	ssize_t frame_size;
-	snd_pcm_channel_area_t *areas;
-	snd_pcm_ioplug_t *io = &aaf->io;
-
-	frame_size = snd_pcm_format_size(io->format, io->channels);
-	if (frame_size < 0)
-		return frame_size;
-
-	buf = calloc(io->buffer_size, frame_size);
-	if (!buf)
-		return -ENOMEM;
-
-	areas = calloc(io->channels, sizeof(snd_pcm_channel_area_t));
-	if (!areas) {
-		res = -ENOMEM;
-		goto err_free_buf;
-	}
-
-	res = aaf_init_areas(aaf, areas, buf);
-	if (res < 0)
-		goto err_free_areas;
-
-	aaf->audiobuf_areas = areas;
-	return 0;
-
-err_free_areas:
-	free(areas);
-err_free_buf:
-	free(buf);
-	return res;
-}
-
 static void aaf_inc_ptr(snd_pcm_uframes_t *ptr, snd_pcm_uframes_t val,
 			snd_pcm_uframes_t boundary)
 {
@@ -912,6 +876,7 @@ static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
 	snd_pcm_ioplug_t *io = &aaf->io;
 	const unsigned int accesses[] = {
 		SND_PCM_ACCESS_RW_INTERLEAVED,
+		SND_PCM_ACCESS_MMAP_INTERLEAVED,
 	};
 	const unsigned int formats[] = {
 		SND_PCM_FORMAT_S16_BE,
@@ -1006,14 +971,8 @@ static int aaf_hw_params(snd_pcm_ioplug_t *io,
 	if (res < 0)
 		goto err_free_pdu;
 
-	res = aaf_init_audiobuf_areas(aaf);
-	if (res < 0)
-		goto err_free_payload_areas;
-
 	return 0;
 
-err_free_payload_areas:
-	free(aaf->payload_areas);
 err_free_pdu:
 	free(aaf->pdu);
 err_close_timer:
@@ -1031,8 +990,6 @@ static int aaf_hw_free(snd_pcm_ioplug_t *io)
 	close(aaf->timer_fd);
 	free(aaf->pdu);
 	free(aaf->payload_areas);
-	free(aaf->audiobuf_areas->addr);
-	free(aaf->audiobuf_areas);
 	return 0;
 }
 
@@ -1131,6 +1088,7 @@ static int aaf_prepare(snd_pcm_ioplug_t *io)
 	int res;
 	snd_pcm_aaf_t *aaf = io->private_data;
 
+	aaf->audiobuf_areas = snd_pcm_ioplug_mmap_areas(io);
 	aaf->pdu_seq = 0;
 	aaf->pdu_count = 0;
 	aaf->hw_ptr = 0;
@@ -1176,34 +1134,6 @@ static int aaf_stop(snd_pcm_ioplug_t *io)
 	return 0;
 }
 
-static snd_pcm_sframes_t aaf_transfer(snd_pcm_ioplug_t *io,
-				      const snd_pcm_channel_area_t *areas,
-				      snd_pcm_uframes_t offset,
-				      snd_pcm_uframes_t size)
-{
-	int res;
-	snd_pcm_aaf_t *aaf = io->private_data;
-
-	if (io->stream == SND_PCM_STREAM_PLAYBACK) {
-		res = snd_pcm_areas_copy_wrap(aaf->audiobuf_areas,
-					      (io->appl_ptr % io->buffer_size),
-					      io->buffer_size, areas, offset,
-					      size, io->channels, size,
-					      io->format);
-	} else {
-		res = snd_pcm_areas_copy_wrap(areas, offset, (offset + size),
-					      aaf->audiobuf_areas,
-					      (io->appl_ptr % io->buffer_size),
-					      io->buffer_size, io->channels,
-					      size, io->format);
-	}
-
-	if (res < 0)
-		return res;
-
-	return size;
-}
-
 static const snd_pcm_ioplug_callback_t aaf_callback = {
 	.close = aaf_close,
 	.dump = aaf_dump,
@@ -1217,7 +1147,6 @@ static const snd_pcm_ioplug_callback_t aaf_callback = {
 	.prepare = aaf_prepare,
 	.start = aaf_start,
 	.stop = aaf_stop,
-	.transfer = aaf_transfer,
 };
 
 SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
@@ -1243,6 +1172,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(aaf)
 	aaf->io.callback = &aaf_callback;
 	aaf->io.private_data = aaf;
 	aaf->io.flags = SND_PCM_IOPLUG_FLAG_BOUNDARY_WA;
+	aaf->io.mmap_rw = 1;
 	res = snd_pcm_ioplug_create(&aaf->io, name, stream, mode);
 	if (res < 0) {
 		SNDERR("Failed to create ioplug instance");
-- 
2.14.4

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

* Re: [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin
  2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
                   ` (6 preceding siblings ...)
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 7/7] aaf: Add support for direct read/write transfers Andre Guedes
@ 2018-10-10 10:25 ` Takashi Iwai
  2018-10-10 21:19   ` Guedes, Andre
  7 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2018-10-10 10:25 UTC (permalink / raw)
  To: Andre Guedes; +Cc: alsa-devel, liam.r.girdwood

On Thu, 04 Oct 2018 01:45:40 +0200,
Andre Guedes wrote:
> 
> Hi all,
> 
> The AAF plugin RFC series v2 has been around on alsa-devel ML for more than two
> weeks so I'm moving forward and sending it as PATCH series now.
> 
> The first 5 patches from this series are pretty much the same from the previous
> version. The news are the top 2 patches which add more functionality to the
> plugin. Patch 6/7 implements the dump() callback which has been useful for
> debugging purposes and Patch 7/7 adds support for direct read/write transfers.
> 
> For general context information about the AAF plugin, I'm quoting below the
> cover letter from the RFC series v1:
> 
> 	"This patchset introduces the AAF plugin to the alsa-plugins project
> 	which enables TSN audio applications on Linux.
> 
> 	For those not familiarized with TSN, it stands for Time-Sensitive
> 	Networking, and it's a set of IEEE technologies (enhancements to IEEE
> 	802.1Q and Ethernet protocols) that provide precise time
> 	synchronization, bounded latency and application interoperability to
> 	bridged networks. Those technologies enable time-sensitive applications
> 	such as audio, video and control loops to run on top of bridged
> 	networks, co-existing with regular applications. TSN technologies are a
> 	super set of Audio Video Bridging (AVB) technologies also developed by
> 	IEEE. AVTP is the protocol defined to transport data in a TSN system,
> 	and AAF is one of the formats defined by AVTP to encapsulate audio
> 	data. AAF is specified in Chapter 7 from the AVTP spec [1].
> 
> 	This work is part of the effort to enable TSN technologies on upstream
> 	Linux ecosystem. All building-blocks required to enable TSN audio
> 	applications have been already developed and pushed to upstream
> 	projects. Time synchronization features are provided by linuxptp
> 	project [2], bounded latency features are provided by Linux Traffic
> 	Control subsystem since kernel version 4.15 [3], and AVTP packetization
> 	is provided by libavtp [4]. What is currently missing in the ecosystem
> 	to enable TSN audio applications is a piece of software that plumbs it
> 	all together and interfaces with Linux Audio system properly. That's
> 	the point of the AAF plugin introduced here.
> 
> 	The AAF plugin is a PCM plugin that uses AVTP protocol to transmit /
> 	receive audio data through a TSN capable network.  When operating in
> 	playback mode, the plugin reads PCM samples from the audio buffer,
> 	encapsulates into AVTP packets and transmits to the network, mimicking
> 	a typical AVTP Talker. When operating in capture mode, the plugin
> 	receives AVTP packets from the network, retrieves the PCM samples, and
> 	present them to alsa-lib layer at the presentation time, mimicking a
> 	typical AVTP Listener."
> 
> For further information about what has been previously discussed, please refer
> to RFC series v1 and v2 archives in [5] and [6]. Finally, all versions of this
> series can be also found in my alsa-plugins tree in github [7].

The codes look mostly good.  I'll post just a new nitpicking later.

But I'd like to postpone the merge until Pierre back from his
vacation and getting some feedback from him.

BTW, what's the good way to test this stuff locally?


Thanks!

Takashi

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

* Re: [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton Andre Guedes
@ 2018-10-10 10:27   ` Takashi Iwai
  2018-10-10 21:22     ` Guedes, Andre
  0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2018-10-10 10:27 UTC (permalink / raw)
  To: Andre Guedes; +Cc: alsa-devel, liam.r.girdwood

On Thu, 04 Oct 2018 01:45:41 +0200,
Andre Guedes wrote:
> 
> +static int aaf_close(snd_pcm_ioplug_t *io)
> +{
> +	snd_pcm_aaf_t *aaf = io->private_data;
> +
> +	if (!aaf)
> +		return -EBADFD;
> +
> +	free(aaf);
> +	aaf = NULL;

This makes no sense.
The function can be simplified to

static int aaf_close(snd_pcm_ioplug_t *io)
{
	free(io->private_data);
	return 0;
}


thanks,

Takashi

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

* Re: [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters Andre Guedes
@ 2018-10-10 10:29   ` Takashi Iwai
  0 siblings, 0 replies; 21+ messages in thread
From: Takashi Iwai @ 2018-10-10 10:29 UTC (permalink / raw)
  To: Andre Guedes; +Cc: alsa-devel, liam.r.girdwood

On Thu, 04 Oct 2018 01:45:42 +0200,
Andre Guedes wrote:
> 
> This patch implements the infrastructure to load the plugin
> configuration from ALSA configuration file. The configuration
> is loaded in open() callback.
> 
> All configuration parameters are described in details in doc/aaf.txt
> file.
> 
> Signed-off-by: Andre Guedes <andre.guedes@intel.com>
> ---
>  aaf/pcm_aaf.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  doc/aaf.txt   |  52 +++++++++++++++++++++++
>  2 files changed, 183 insertions(+)
> 
> diff --git a/aaf/pcm_aaf.c b/aaf/pcm_aaf.c
> index 7890e10..32db15e 100644
> --- a/aaf/pcm_aaf.c
> +++ b/aaf/pcm_aaf.c
> @@ -20,11 +20,138 @@
>  
>  #include <alsa/asoundlib.h>
>  #include <alsa/pcm_external.h>
> +#include <linux/if.h>
> +#include <linux/if_ether.h>

Ideally these standard headers should be checked in configure script.
(Not that important, though.)


thanks,

Takashi

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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-03 23:45 ` [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support Andre Guedes
@ 2018-10-10 10:34   ` Takashi Iwai
  2018-10-10 21:27     ` Guedes, Andre
  0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2018-10-10 10:34 UTC (permalink / raw)
  To: Andre Guedes; +Cc: alsa-devel, liam.r.girdwood

On Thu, 04 Oct 2018 01:45:43 +0200,
Andre Guedes wrote:
> 
> +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
> +{
> +	int res;
> +	snd_pcm_ioplug_t *io = &aaf->io;
> +	const unsigned int accesses[] = {
> +		SND_PCM_ACCESS_RW_INTERLEAVED,
> +	};
> +	const unsigned int formats[] = {
> +		SND_PCM_FORMAT_S16_BE,
> +		SND_PCM_FORMAT_S24_3BE,
> +		SND_PCM_FORMAT_S32_BE,
> +		SND_PCM_FORMAT_FLOAT_BE,
> +	};
> +	const unsigned int rates[] = {
> +		8000,
> +		16000,
> +		24000,
> +		32000,
> +		44100,
> +		48000,
> +		88200,
> +		96000,
> +		176400,
> +		192000,
> +	};

Use static arrays for the above.


thanks,

Takashi

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

* Re: [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin
  2018-10-10 10:25 ` [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Takashi Iwai
@ 2018-10-10 21:19   ` Guedes, Andre
  2018-10-22 16:51     ` Pierre-Louis Bossart
  0 siblings, 1 reply; 21+ messages in thread
From: Guedes, Andre @ 2018-10-10 21:19 UTC (permalink / raw)
  To: tiwai; +Cc: alsa-devel, Girdwood, Liam R


[-- Attachment #1.1: Type: text/plain, Size: 4258 bytes --]

Hi Takashi,

On Wed, 2018-10-10 at 12:25 +0200, Takashi Iwai wrote:
> On Thu, 04 Oct 2018 01:45:40 +0200,
> Andre Guedes wrote:
> > 
> > Hi all,
> > 
> > The AAF plugin RFC series v2 has been around on alsa-devel ML for
> > more than two
> > weeks so I'm moving forward and sending it as PATCH series now.
> > 
> > The first 5 patches from this series are pretty much the same from
> > the previous
> > version. The news are the top 2 patches which add more
> > functionality to the
> > plugin. Patch 6/7 implements the dump() callback which has been
> > useful for
> > debugging purposes and Patch 7/7 adds support for direct read/write
> > transfers.
> > 
> > For general context information about the AAF plugin, I'm quoting
> > below the
> > cover letter from the RFC series v1:
> > 
> > 	"This patchset introduces the AAF plugin to the alsa-plugins
> > project
> > 	which enables TSN audio applications on Linux.
> > 
> > 	For those not familiarized with TSN, it stands for Time-
> > Sensitive
> > 	Networking, and it's a set of IEEE technologies (enhancements
> > to IEEE
> > 	802.1Q and Ethernet protocols) that provide precise time
> > 	synchronization, bounded latency and application
> > interoperability to
> > 	bridged networks. Those technologies enable time-sensitive
> > applications
> > 	such as audio, video and control loops to run on top of bridged
> > 	networks, co-existing with regular applications. TSN
> > technologies are a
> > 	super set of Audio Video Bridging (AVB) technologies also
> > developed by
> > 	IEEE. AVTP is the protocol defined to transport data in a TSN
> > system,
> > 	and AAF is one of the formats defined by AVTP to encapsulate
> > audio
> > 	data. AAF is specified in Chapter 7 from the AVTP spec [1].
> > 
> > 	This work is part of the effort to enable TSN technologies on
> > upstream
> > 	Linux ecosystem. All building-blocks required to enable TSN
> > audio
> > 	applications have been already developed and pushed to upstream
> > 	projects. Time synchronization features are provided by
> > linuxptp
> > 	project [2], bounded latency features are provided by Linux
> > Traffic
> > 	Control subsystem since kernel version 4.15 [3], and AVTP
> > packetization
> > 	is provided by libavtp [4]. What is currently missing in the
> > ecosystem
> > 	to enable TSN audio applications is a piece of software that
> > plumbs it
> > 	all together and interfaces with Linux Audio system properly.
> > That's
> > 	the point of the AAF plugin introduced here.
> > 
> > 	The AAF plugin is a PCM plugin that uses AVTP protocol to
> > transmit /
> > 	receive audio data through a TSN capable network.  When
> > operating in
> > 	playback mode, the plugin reads PCM samples from the audio
> > buffer,
> > 	encapsulates into AVTP packets and transmits to the network,
> > mimicking
> > 	a typical AVTP Talker. When operating in capture mode, the
> > plugin
> > 	receives AVTP packets from the network, retrieves the PCM
> > samples, and
> > 	present them to alsa-lib layer at the presentation time,
> > mimicking a
> > 	typical AVTP Listener."
> > 
> > For further information about what has been previously discussed,
> > please refer
> > to RFC series v1 and v2 archives in [5] and [6]. Finally, all
> > versions of this
> > series can be also found in my alsa-plugins tree in github [7].
> 
> The codes look mostly good.  I'll post just a new nitpicking later.
> 
> But I'd like to postpone the merge until Pierre back from his
> vacation and getting some feedback from him.

Ok, thanks for letting me know. I'll address your comments locally and
wait for his feedback before pushing the v2.

> BTW, what's the good way to test this stuff locally?

The minimal setup to test the plugin requires 2 machines connected
back-to-back. The machines should be equipped with a TSN-capable NIC
that supports PTP and FQTSS features. I'm using Intel i210 NIC.

doc/aaf.txt provides detailed instructions on how to setup the PTP and
FQTSS features from the NIC as well as how to run the plugin. If you
have any trouble with the instructions, please let me know and I'll fix
it for the next series version.

Thanks,

Andre

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton
  2018-10-10 10:27   ` Takashi Iwai
@ 2018-10-10 21:22     ` Guedes, Andre
  0 siblings, 0 replies; 21+ messages in thread
From: Guedes, Andre @ 2018-10-10 21:22 UTC (permalink / raw)
  To: tiwai; +Cc: alsa-devel, Girdwood, Liam R


[-- Attachment #1.1: Type: text/plain, Size: 254 bytes --]

On Wed, 2018-10-10 at 12:27 +0200, Takashi Iwai wrote:
> The function can be simplified to
> 
> static int aaf_close(snd_pcm_ioplug_t *io)
> {
>         free(io->private_data);
>         return 0;
> }

Ok, I'll implement it like that.

- Andre

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-10 10:34   ` Takashi Iwai
@ 2018-10-10 21:27     ` Guedes, Andre
  2018-10-11  7:31       ` Takashi Iwai
  0 siblings, 1 reply; 21+ messages in thread
From: Guedes, Andre @ 2018-10-10 21:27 UTC (permalink / raw)
  To: tiwai; +Cc: alsa-devel, Girdwood, Liam R


[-- Attachment #1.1: Type: text/plain, Size: 1085 bytes --]

On Wed, 2018-10-10 at 12:34 +0200, Takashi Iwai wrote:
> > +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
> > +{
> > +     int res;
> > +     snd_pcm_ioplug_t *io = &aaf->io;
> > +     const unsigned int accesses[] = {
> > +             SND_PCM_ACCESS_RW_INTERLEAVED,
> > +     };
> > +     const unsigned int formats[] = {
> > +             SND_PCM_FORMAT_S16_BE,
> > +             SND_PCM_FORMAT_S24_3BE,
> > +             SND_PCM_FORMAT_S32_BE,
> > +             SND_PCM_FORMAT_FLOAT_BE,
> > +     };
> > +     const unsigned int rates[] = {
> > +             8000,
> > +             16000,
> > +             24000,
> > +             32000,
> > +             44100,
> > +             48000,
> > +             88200,
> > +             96000,
> > +             176400,
> > +             192000,
> > +     };
> 
> Use static arrays for the above.

It isn't clear to me what is the point in allocating these variables in
the global scope. May I ask what is the benefit in adding the 'static'
modifier to these local variables?

Thanks,

Andre

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-10 21:27     ` Guedes, Andre
@ 2018-10-11  7:31       ` Takashi Iwai
  2018-10-11  7:45         ` Takashi Sakamoto
  0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2018-10-11  7:31 UTC (permalink / raw)
  To: Guedes, Andre; +Cc: alsa-devel, Girdwood, Liam R

On Wed, 10 Oct 2018 23:27:29 +0200,
Guedes, Andre wrote:
> 
> On Wed, 2018-10-10 at 12:34 +0200, Takashi Iwai wrote:
> > > +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
> > > +{
> > > +     int res;
> > > +     snd_pcm_ioplug_t *io = &aaf->io;
> > > +     const unsigned int accesses[] = {
> > > +             SND_PCM_ACCESS_RW_INTERLEAVED,
> > > +     };
> > > +     const unsigned int formats[] = {
> > > +             SND_PCM_FORMAT_S16_BE,
> > > +             SND_PCM_FORMAT_S24_3BE,
> > > +             SND_PCM_FORMAT_S32_BE,
> > > +             SND_PCM_FORMAT_FLOAT_BE,
> > > +     };
> > > +     const unsigned int rates[] = {
> > > +             8000,
> > > +             16000,
> > > +             24000,
> > > +             32000,
> > > +             44100,
> > > +             48000,
> > > +             88200,
> > > +             96000,
> > > +             176400,
> > > +             192000,
> > > +     };
> > 
> > Use static arrays for the above.
> 
> It isn't clear to me what is the point in allocating these variables in
> the global scope. May I ask what is the benefit in adding the 'static'
> modifier to these local variables?

Yes.


Takashi

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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-11  7:31       ` Takashi Iwai
@ 2018-10-11  7:45         ` Takashi Sakamoto
  2018-10-11  8:00           ` Takashi Iwai
  0 siblings, 1 reply; 21+ messages in thread
From: Takashi Sakamoto @ 2018-10-11  7:45 UTC (permalink / raw)
  To: Takashi Iwai, Guedes, Andre; +Cc: alsa-devel, Girdwood, Liam R

On Oct 11 2018 16:31, Takashi Iwai wrote:
> On Wed, 10 Oct 2018 23:27:29 +0200,
> Guedes, Andre wrote:
>>
>> On Wed, 2018-10-10 at 12:34 +0200, Takashi Iwai wrote:
>>>> +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
>>>> +{
>>>> +     int res;
>>>> +     snd_pcm_ioplug_t *io = &aaf->io;
>>>> +     const unsigned int accesses[] = {
>>>> +             SND_PCM_ACCESS_RW_INTERLEAVED,
>>>> +     };
>>>> +     const unsigned int formats[] = {
>>>> +             SND_PCM_FORMAT_S16_BE,
>>>> +             SND_PCM_FORMAT_S24_3BE,
>>>> +             SND_PCM_FORMAT_S32_BE,
>>>> +             SND_PCM_FORMAT_FLOAT_BE,
>>>> +     };
>>>> +     const unsigned int rates[] = {
>>>> +             8000,
>>>> +             16000,
>>>> +             24000,
>>>> +             32000,
>>>> +             44100,
>>>> +             48000,
>>>> +             88200,
>>>> +             96000,
>>>> +             176400,
>>>> +             192000,
>>>> +     };
>>>
>>> Use static arrays for the above.
>>
>> It isn't clear to me what is the point in allocating these variables in
>> the global scope. May I ask what is the benefit in adding the 'static'
>> modifier to these local variables?
> 
> Yes.

In this case, it's not relevant to access scope.

Just with 'const' qualifier, the symbol allocates in .text section of
ELF binary. With additional 'static' qualifier, the symbol allocates in 
.rodata section.

In loading, symbol in .rodata section locates in segments of VMA with
'read-only' flag. This brings safety from several bugs such as buffer
over-run.


Regards

Takashi Sakamoto

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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-11  7:45         ` Takashi Sakamoto
@ 2018-10-11  8:00           ` Takashi Iwai
  2018-10-11 19:53             ` Guedes, Andre
  0 siblings, 1 reply; 21+ messages in thread
From: Takashi Iwai @ 2018-10-11  8:00 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: Guedes, Andre, alsa-devel, Girdwood, Liam R

On Thu, 11 Oct 2018 09:45:07 +0200,
Takashi Sakamoto wrote:
> 
> On Oct 11 2018 16:31, Takashi Iwai wrote:
> > On Wed, 10 Oct 2018 23:27:29 +0200,
> > Guedes, Andre wrote:
> >>
> >> On Wed, 2018-10-10 at 12:34 +0200, Takashi Iwai wrote:
> >>>> +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
> >>>> +{
> >>>> +     int res;
> >>>> +     snd_pcm_ioplug_t *io = &aaf->io;
> >>>> +     const unsigned int accesses[] = {
> >>>> +             SND_PCM_ACCESS_RW_INTERLEAVED,
> >>>> +     };
> >>>> +     const unsigned int formats[] = {
> >>>> +             SND_PCM_FORMAT_S16_BE,
> >>>> +             SND_PCM_FORMAT_S24_3BE,
> >>>> +             SND_PCM_FORMAT_S32_BE,
> >>>> +             SND_PCM_FORMAT_FLOAT_BE,
> >>>> +     };
> >>>> +     const unsigned int rates[] = {
> >>>> +             8000,
> >>>> +             16000,
> >>>> +             24000,
> >>>> +             32000,
> >>>> +             44100,
> >>>> +             48000,
> >>>> +             88200,
> >>>> +             96000,
> >>>> +             176400,
> >>>> +             192000,
> >>>> +     };
> >>>
> >>> Use static arrays for the above.
> >>
> >> It isn't clear to me what is the point in allocating these variables in
> >> the global scope. May I ask what is the benefit in adding the 'static'
> >> modifier to these local variables?
> >
> > Yes.
> 
> In this case, it's not relevant to access scope.
> 
> Just with 'const' qualifier, the symbol allocates in .text section of
> ELF binary. With additional 'static' qualifier, the symbol allocates
> in .rodata section.
> 
> In loading, symbol in .rodata section locates in segments of VMA with
> 'read-only' flag. This brings safety from several bugs such as buffer
> over-run.

OK, then scratch my comments.


thanks,

Takashi

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

* Re: [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support
  2018-10-11  8:00           ` Takashi Iwai
@ 2018-10-11 19:53             ` Guedes, Andre
  0 siblings, 0 replies; 21+ messages in thread
From: Guedes, Andre @ 2018-10-11 19:53 UTC (permalink / raw)
  To: o-takashi, tiwai; +Cc: alsa-devel, Girdwood, Liam R


[-- Attachment #1.1: Type: text/plain, Size: 2425 bytes --]

On Thu, 2018-10-11 at 10:00 +0200, Takashi Iwai wrote:
> On Thu, 11 Oct 2018 09:45:07 +0200,
> Takashi Sakamoto wrote:
> > 
> > On Oct 11 2018 16:31, Takashi Iwai wrote:
> > > On Wed, 10 Oct 2018 23:27:29 +0200,
> > > Guedes, Andre wrote:
> > > > 
> > > > On Wed, 2018-10-10 at 12:34 +0200, Takashi Iwai wrote:
> > > > > > +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf)
> > > > > > +{
> > > > > > +     int res;
> > > > > > +     snd_pcm_ioplug_t *io = &aaf->io;
> > > > > > +     const unsigned int accesses[] = {
> > > > > > +             SND_PCM_ACCESS_RW_INTERLEAVED,
> > > > > > +     };
> > > > > > +     const unsigned int formats[] = {
> > > > > > +             SND_PCM_FORMAT_S16_BE,
> > > > > > +             SND_PCM_FORMAT_S24_3BE,
> > > > > > +             SND_PCM_FORMAT_S32_BE,
> > > > > > +             SND_PCM_FORMAT_FLOAT_BE,
> > > > > > +     };
> > > > > > +     const unsigned int rates[] = {
> > > > > > +             8000,
> > > > > > +             16000,
> > > > > > +             24000,
> > > > > > +             32000,
> > > > > > +             44100,
> > > > > > +             48000,
> > > > > > +             88200,
> > > > > > +             96000,
> > > > > > +             176400,
> > > > > > +             192000,
> > > > > > +     };
> > > > > 
> > > > > Use static arrays for the above.
> > > > 
> > > > It isn't clear to me what is the point in allocating these
> > > > variables in
> > > > the global scope. May I ask what is the benefit in adding the
> > > > 'static'
> > > > modifier to these local variables?
> > > 
> > > Yes.
> > 
> > In this case, it's not relevant to access scope.
> > 
> > Just with 'const' qualifier, the symbol allocates in .text section
> > of ELF binary. With additional 'static' qualifier, the symbol
> > allocates in .rodata section.
> > 
> > In loading, symbol in .rodata section locates in segments of VMA
> > with 'read-only' flag. This brings safety from several bugs such as
> > buffer over-run.

My understanding is that the arrays are allocated in the stack frame,
not in .text section, and the 'const' qualifier here has no influence
on that. But I get your point in moving the arrays to .rodata.

> OK, then scratch my comments.

I checked other plugins and it seems having 'static' to these variables
is the "norm" so I'm adding it for the sake of consistency.

Thanks,

Andre

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

* Re: [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin
  2018-10-10 21:19   ` Guedes, Andre
@ 2018-10-22 16:51     ` Pierre-Louis Bossart
  2018-10-23 20:01       ` Guedes, Andre
  0 siblings, 1 reply; 21+ messages in thread
From: Pierre-Louis Bossart @ 2018-10-22 16:51 UTC (permalink / raw)
  To: Guedes, Andre, tiwai; +Cc: alsa-devel, Girdwood, Liam R


On 10/10/18 4:19 PM, Guedes, Andre wrote:
> Hi Takashi,
>
> On Wed, 2018-10-10 at 12:25 +0200, Takashi Iwai wrote:
>> On Thu, 04 Oct 2018 01:45:40 +0200,
>> Andre Guedes wrote:
>>> Hi all,
>>>
>>> The AAF plugin RFC series v2 has been around on alsa-devel ML for
>>> more than two
>>> weeks so I'm moving forward and sending it as PATCH series now.
>>>
>>> The first 5 patches from this series are pretty much the same from
>>> the previous
>>> version. The news are the top 2 patches which add more
>>> functionality to the
>>> plugin. Patch 6/7 implements the dump() callback which has been
>>> useful for
>>> debugging purposes and Patch 7/7 adds support for direct read/write
>>> transfers.
>>>
>>> For general context information about the AAF plugin, I'm quoting
>>> below the
>>> cover letter from the RFC series v1:
>>>
>>> 	"This patchset introduces the AAF plugin to the alsa-plugins
>>> project
>>> 	which enables TSN audio applications on Linux.
>>>
>>> 	For those not familiarized with TSN, it stands for Time-
>>> Sensitive
>>> 	Networking, and it's a set of IEEE technologies (enhancements
>>> to IEEE
>>> 	802.1Q and Ethernet protocols) that provide precise time
>>> 	synchronization, bounded latency and application
>>> interoperability to
>>> 	bridged networks. Those technologies enable time-sensitive
>>> applications
>>> 	such as audio, video and control loops to run on top of bridged
>>> 	networks, co-existing with regular applications. TSN
>>> technologies are a
>>> 	super set of Audio Video Bridging (AVB) technologies also
>>> developed by
>>> 	IEEE. AVTP is the protocol defined to transport data in a TSN
>>> system,
>>> 	and AAF is one of the formats defined by AVTP to encapsulate
>>> audio
>>> 	data. AAF is specified in Chapter 7 from the AVTP spec [1].
>>>
>>> 	This work is part of the effort to enable TSN technologies on
>>> upstream
>>> 	Linux ecosystem. All building-blocks required to enable TSN
>>> audio
>>> 	applications have been already developed and pushed to upstream
>>> 	projects. Time synchronization features are provided by
>>> linuxptp
>>> 	project [2], bounded latency features are provided by Linux
>>> Traffic
>>> 	Control subsystem since kernel version 4.15 [3], and AVTP
>>> packetization
>>> 	is provided by libavtp [4]. What is currently missing in the
>>> ecosystem
>>> 	to enable TSN audio applications is a piece of software that
>>> plumbs it
>>> 	all together and interfaces with Linux Audio system properly.
>>> That's
>>> 	the point of the AAF plugin introduced here.
>>>
>>> 	The AAF plugin is a PCM plugin that uses AVTP protocol to
>>> transmit /
>>> 	receive audio data through a TSN capable network.  When
>>> operating in
>>> 	playback mode, the plugin reads PCM samples from the audio
>>> buffer,
>>> 	encapsulates into AVTP packets and transmits to the network,
>>> mimicking
>>> 	a typical AVTP Talker. When operating in capture mode, the
>>> plugin
>>> 	receives AVTP packets from the network, retrieves the PCM
>>> samples, and
>>> 	present them to alsa-lib layer at the presentation time,
>>> mimicking a
>>> 	typical AVTP Listener."
>>>
>>> For further information about what has been previously discussed,
>>> please refer
>>> to RFC series v1 and v2 archives in [5] and [6]. Finally, all
>>> versions of this
>>> series can be also found in my alsa-plugins tree in github [7].
>> The codes look mostly good.  I'll post just a new nitpicking later.
>>
>> But I'd like to postpone the merge until Pierre back from his
>> vacation and getting some feedback from him.

I'm back and will look at those patches this week. I'll probably have to 
talk to Andre to figure out how the clocks are managed, I didn't fully 
understand the answers from August that

"The plugin requires both CLOCK_REALTIME and PTP to be synchronized, and 
this can add some usage scenarios limitation, indeed. However, the 
scenario you described looks still feasible. For instance, at the host 
running as PTP master, we could have NTP disciplining CLOCK_REALTIME 
(ntp daemon) and CLOCK_REALTIME disciplining PTP (phc2sys daemon). At 
the hosts running as PTP slave, we have PTP disciplining CLOCK_REALTIME 
(phc2sys daemon). This way, CLOCK_REALTIME time from all systems is NTP 
time while CLOCK_REALTIME and PTP clock are in sync"

For audio usages we typically have all clocks derived from ART, and we'd 
need the CLOCK_MONOTONIC_RAW to align with the PTP clock to allow for 
loopbacks without ASRC (or ASRC between the audio clock and ART). Not 
sure who uses CLOCK_REALTIME for audio?

> Ok, thanks for letting me know. I'll address your comments locally and
> wait for his feedback before pushing the v2.
>
>> BTW, what's the good way to test this stuff locally?
> The minimal setup to test the plugin requires 2 machines connected
> back-to-back. The machines should be equipped with a TSN-capable NIC
> that supports PTP and FQTSS features. I'm using Intel i210 NIC.
>
> doc/aaf.txt provides detailed instructions on how to setup the PTP and
> FQTSS features from the NIC as well as how to run the plugin. If you
> have any trouble with the instructions, please let me know and I'll fix
> it for the next series version.

I was planning to experiment with two stacked MinnowBoard Turbot quad 
core dual ethernet [1], they have i210 support and would be the 
cheapest/compact way to test all this. You can connect to each board 
from your workstation with one of the two ports for 
updates/command/control and dedicate the second port to AVB/TSN 
transfers between the two boards.

[1] 
https://store.netgate.com/MinnowBoard-Turbot-Dual-Ethernet-Quad-Core-Board-P2780.aspx

>
> Thanks,
>
> Andre
>
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

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

* Re: [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin
  2018-10-22 16:51     ` Pierre-Louis Bossart
@ 2018-10-23 20:01       ` Guedes, Andre
  0 siblings, 0 replies; 21+ messages in thread
From: Guedes, Andre @ 2018-10-23 20:01 UTC (permalink / raw)
  To: pierre-louis.bossart, tiwai; +Cc: alsa-devel, Girdwood, Liam R


[-- Attachment #1.1: Type: text/plain, Size: 2763 bytes --]

Hi Pierre,

On Mon, 2018-10-22 at 11:51 -0500, Pierre-Louis Bossart wrote:
> > > The codes look mostly good.  I'll post just a new nitpicking
> > > later.
> > > 
> > > But I'd like to postpone the merge until Pierre back from his
> > > vacation and getting some feedback from him.
> 
> I'm back and will look at those patches this week.

Thanks for letting me know. I just found a bug when multiple streams
are running at the same time. I have it fixed already, and since
Takashi's comments have been addressed as well, I'm going to push the
v2 so you can review the latest code.

I'm finishing testing the v2 series and should post it by tomorrow.

> I'll probably have to talk to Andre to figure out how the clocks are
> managed, I didn't fully understand the answers from August that 
> "The plugin requires both CLOCK_REALTIME and PTP to be synchronized,
> and this can add some usage scenarios limitation, indeed. However,
> the scenario you described looks still feasible. For instance, at the
> host running as PTP master, we could have NTP disciplining
> CLOCK_REALTIME (ntp daemon) and CLOCK_REALTIME disciplining PTP
> (phc2sys daemon). At the hosts running as PTP slave, we have PTP
> disciplining CLOCK_REALTIME (phc2sys daemon). This way,
> CLOCK_REALTIME time from all systems is NTP time while CLOCK_REALTIME
> and PTP clock are in sync"
> For audio usages we typically have all clocks derived from ART, and
> we'd need the CLOCK_MONOTONIC_RAW to align with the PTP clock to
> allow for loopbacks without ASRC (or ASRC between the audio clock and
> ART). Not sure who uses CLOCK_REALTIME for audio?

Sure, let's follow up on this when you're available.

> > > BTW, what's the good way to test this stuff locally?
> > 
> > The minimal setup to test the plugin requires 2 machines connected
> > back-to-back. The machines should be equipped with a TSN-capable
> > NIC that supports PTP and FQTSS features. I'm using Intel i210 NIC.
> > 
> > doc/aaf.txt provides detailed instructions on how to setup the PTP
> > and FQTSS features from the NIC as well as how to run the plugin.
> > If you have any trouble with the instructions, please let me know
> > and I'll fix it for the next series version.
> 
> I was planning to experiment with two stacked MinnowBoard Turbot quad
> core dual ethernet [1], they have i210 support and would be the
> cheapest/compact way to test all this. You can connect to each board
> from your workstation with one of the two ports for
> updates/command/control and dedicate the second port to AVB/TSN
> transfers between the two boards.

I'm not using MinnowBoard Turbot but the setup you described here is
pretty much what my testing setup is.

Regards,

Andre

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3262 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



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

end of thread, other threads:[~2018-10-23 20:01 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-03 23:45 [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Andre Guedes
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 1/7] aaf: Introduce plugin skeleton Andre Guedes
2018-10-10 10:27   ` Takashi Iwai
2018-10-10 21:22     ` Guedes, Andre
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 2/7] aaf: Load configuration parameters Andre Guedes
2018-10-10 10:29   ` Takashi Iwai
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 3/7] aaf: Implement Playback mode support Andre Guedes
2018-10-10 10:34   ` Takashi Iwai
2018-10-10 21:27     ` Guedes, Andre
2018-10-11  7:31       ` Takashi Iwai
2018-10-11  7:45         ` Takashi Sakamoto
2018-10-11  8:00           ` Takashi Iwai
2018-10-11 19:53             ` Guedes, Andre
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 4/7] aaf: Prepare for Capture " Andre Guedes
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 5/7] aaf: Implement " Andre Guedes
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 6/7] aaf: Implement dump() ioplug callback Andre Guedes
2018-10-03 23:45 ` [PATCH - AAF PCM plugin 7/7] aaf: Add support for direct read/write transfers Andre Guedes
2018-10-10 10:25 ` [PATCH - AAF PCM plugin 0/7] Introduce AVTP Audio Format (AAF) plugin Takashi Iwai
2018-10-10 21:19   ` Guedes, Andre
2018-10-22 16:51     ` Pierre-Louis Bossart
2018-10-23 20:01       ` Guedes, Andre

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.