Hello community, here is the log from the commit of package alsa-plugins for openSUSE:Factory checked in at 2019-01-15 09:12:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/alsa-plugins (Old) and /work/SRC/openSUSE:Factory/.alsa-plugins.new.28833 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "alsa-plugins" Tue Jan 15 09:12:36 2019 rev:104 rq:664283 version:1.1.8 Changes: -------- --- /work/SRC/openSUSE:Factory/alsa-plugins/alsa-plugins.changes 2018-12-10 12:23:14.878831798 +0100 +++ /work/SRC/openSUSE:Factory/.alsa-plugins.new.28833/alsa-plugins.changes 2019-01-15 09:13:05.434418423 +0100 @@ -1,0 +2,58 @@ +Thu Jan 10 00:50:08 UTC 2019 - Jan Engelhardt <[email protected]> + +- Drop idempotent %if..%endif guards. + +------------------------------------------------------------------- +Tue Jan 8 12:24:54 CET 2019 - [email protected] + +- Update to alsa-plugins 1.1.8: + * A52 Output plugin: + a52_close: set slave to NULL to avoid double pcm free in open fcn + * AVTP Audio Format (AAF) PCM plugin + aaf: AVTPDU transmission periodicity + aaf: Implement Playback mode support + aaf: Load configuration parameters + aaf: Introduce plugin skeleton + aaf: Tx multiple AVTPDUs per media clock tick + aaf: Refactor timeout routines + aaf: Refactor AVTPDU reception routines + aaf: Refactor AVTPDU transmission routines + aaf: Add presentation time tolerance + aaf: do not free twice aaf - snd_pcm_close() is called from snd_pcm_ioplug_delete() + aaf: Add support for direct read/write transfers + aaf: Implement dump() ioplug callback + aaf: Implement Capture mode support + aaf: Prepare for Capture mode support + aaf: Implement Playback mode support + aaf: Load configuration parameters + aaf: Introduce plugin skeleton + * Documentation: + aaf: AVTPDU transmission periodicity + aaf: Tx multiple AVTPDUs per media clock tick + aaf: Add presentation time tolerance + doc: Fix typo in AAF doc + aaf: Implement Capture mode support + aaf: Implement Playback mode support + aaf: Load configuration parameters + aaf: Introduce plugin skeleton + * Jack PCM plugin: + Revert "jack: Fix leaks when jack_set_hw_constraint() fails" + * OSS Mixer -> ALSA Control plugin: + Revert "oss: Fix leaks when oss_hw_constraint() fails" + * USB stream plugin: + Revert "usb_stream: Fix leaks when us_set_hw_constraint() fails" + pcm_usb_stream: fix signess issues + pcm_usb_stream: remove unused parameter in snd_pcm_us_read() + pcm_usb_stream: fix another leak in snd_pcm_us_open() +- Drop obsoleted patches: + 0001-pcm_usb_stream-fix-another-leak-in-snd_pcm_us_open.patch + 0002-pcm_usb_stream-remove-unused-parameter-in-snd_pcm_us.patch + 0003-pcm_usb_stream-fix-signess-issues.patch + 0004-a52_close-set-slave-to-NULL-to-avoid-double-pcm-free.patch + 0005-Revert-jack-Fix-leaks-when-jack_set_hw_constraint-fa.patch + 0006-Revert-usb_stream-Fix-leaks-when-us_set_hw_constrain.patch + 0007-Revert-oss-Fix-leaks-when-oss_hw_constraint-fails.patch +- Add a new subpackage alsa-plugins-aaf; + currently built only for TW + +------------------------------------------------------------------- Old: ---- 0001-pcm_usb_stream-fix-another-leak-in-snd_pcm_us_open.patch 0002-pcm_usb_stream-remove-unused-parameter-in-snd_pcm_us.patch 0003-pcm_usb_stream-fix-signess-issues.patch 0004-a52_close-set-slave-to-NULL-to-avoid-double-pcm-free.patch 0005-Revert-jack-Fix-leaks-when-jack_set_hw_constraint-fa.patch 0006-Revert-usb_stream-Fix-leaks-when-us_set_hw_constrain.patch 0007-Revert-oss-Fix-leaks-when-oss_hw_constraint-fails.patch alsa-plugins-1.1.7.tar.bz2 New: ---- alsa-plugins-1.1.8.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ alsa-plugins.spec ++++++ --- /var/tmp/diff_new_pack.2FU8SK/_old 2019-01-15 09:13:05.798418086 +0100 +++ /var/tmp/diff_new_pack.2FU8SK/_new 2019-01-15 09:13:05.798418086 +0100 @@ -1,7 +1,7 @@ # # spec file for package alsa-plugins # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -21,24 +21,21 @@ %else %define build_avcodec 0 %endif -%define package_version 1.1.7 +%if 0%{?suse_version} >= 1550 +%define build_aaf 1 +%else +%define build_aaf 0 +%endif Name: alsa-plugins -Version: 1.1.7 +Version: 1.1.8 Release: 0 Summary: Extra Plug-Ins for the ALSA Library License: LGPL-2.1-or-later Group: System/Libraries Url: http://www.alsa-project.org/ -Source: ftp://ftp.alsa-project.org/pub/plugins/alsa-plugins-%{package_version}.tar.bz2 +Source: ftp://ftp.alsa-project.org/pub/plugins/alsa-plugins-%{version}.tar.bz2 Source1: baselibs.conf -Patch1: 0001-pcm_usb_stream-fix-another-leak-in-snd_pcm_us_open.patch -Patch2: 0002-pcm_usb_stream-remove-unused-parameter-in-snd_pcm_us.patch -Patch3: 0003-pcm_usb_stream-fix-signess-issues.patch -Patch4: 0004-a52_close-set-slave-to-NULL-to-avoid-double-pcm-free.patch -Patch5: 0005-Revert-jack-Fix-leaks-when-jack_set_hw_constraint-fa.patch -Patch6: 0006-Revert-usb_stream-Fix-leaks-when-us_set_hw_constrain.patch -Patch7: 0007-Revert-oss-Fix-leaks-when-oss_hw_constraint-fails.patch BuildRequires: alsa-devel BuildRequires: dbus-1-devel %if %build_avcodec @@ -46,6 +43,9 @@ BuildRequires: pkgconfig(libavresample) = 4.0.0 BuildRequires: pkgconfig(libavutil) = 56.22.100 %endif +%if %build_aaf +BuildRequires: libavtp-devel +%endif BuildRequires: libjack-devel BuildRequires: libjack0 BuildRequires: libsamplerate-devel @@ -126,7 +126,6 @@ This package contains the Speex preprocessor plugin for the ALSA library using libspeexdsp. -%if %build_avcodec %package a52 Summary: A52 Output Plug-In for the ALSA Library License: LGPL-2.1-or-later @@ -145,17 +144,18 @@ %description lavrate This package contains the sample rate converter plugin for the ALSA library using libavcodec. -%endif + +%package aaf +Summary: AVTP Audio Format PCM Plug-In for the ALSA Library +License: LGPL-2.1-or-later +Group: System/Libraries + +%description aaf +This package contains the AVTP AUdio Format (AAF) I/O plug-in +for the ALSA library. %prep -%setup -q -n %{name}-%{package_version} -%patch1 -p1 -%patch2 -p1 -%patch3 -p1 -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 +%setup -q %build export AUTOMAKE_JOBS="%{?_smp_mflags}" @@ -165,7 +165,7 @@ make %{?_smp_mflags} %install -make DESTDIR=%{buildroot} install %{?_smp_mflags} +%make_install %{?_smp_mflags} mkdir -p %{buildroot}%{_sysconfdir}/alsa/conf.d touch %{buildroot}%{_sysconfdir}/alsa/conf.d/99-pulseaudio-default.conf # modules don't need *.la files @@ -303,4 +303,12 @@ %{_sysconfdir}/alsa/conf.d/10-rate-lav.conf %endif +%if %build_aaf +%files aaf +%defattr(-, root, root) +%license COPYING +# %doc doc/aaf.txt +%{_libdir}/alsa-lib/libasound_module_pcm_aaf.so +%endif + %changelog ++++++ alsa-plugins-1.1.7.tar.bz2 -> alsa-plugins-1.1.8.tar.bz2 ++++++ ++++ 2666 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/Makefile.am new/alsa-plugins-1.1.8/Makefile.am --- old/alsa-plugins-1.1.7/Makefile.am 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/Makefile.am 2019-01-07 13:55:43.000000000 +0100 @@ -35,6 +35,9 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/a52/pcm_a52.c new/alsa-plugins-1.1.8/a52/pcm_a52.c --- old/alsa-plugins-1.1.7/a52/pcm_a52.c 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/a52/pcm_a52.c 2019-01-07 13:55:43.000000000 +0100 @@ -654,10 +654,13 @@ static int a52_close(snd_pcm_ioplug_t *io) { struct a52_ctx *rec = io->private_data; + snd_pcm_t *slave = rec->slave; a52_free(rec); - if (rec->slave) - return snd_pcm_close(rec->slave); + if (slave) { + rec->slave = NULL; + return snd_pcm_close(slave); + } return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/aaf/Makefile.am new/alsa-plugins-1.1.8/aaf/Makefile.am --- old/alsa-plugins-1.1.7/aaf/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ new/alsa-plugins-1.1.8/aaf/Makefile.am 2019-01-07 13:55:43.000000000 +0100 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/aaf/pcm_aaf.c new/alsa-plugins-1.1.8/aaf/pcm_aaf.c --- old/alsa-plugins-1.1.7/aaf/pcm_aaf.c 1970-01-01 01:00:00.000000000 +0100 +++ new/alsa-plugins-1.1.8/aaf/pcm_aaf.c 2019-01-07 13:55:43.000000000 +0100 @@ -0,0 +1,1400 @@ +/* + * 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> +#include <arpa/inet.h> +#include <avtp.h> +#include <avtp_aaf.h> +#include <inttypes.h> +#include <limits.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/net_tstamp.h> +#include <net/if.h> +#include <string.h> +#include <stdbool.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 +#define FD_COUNT_CAPTURE 2 + +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; + int ptime_tolerance; + + int sk_fd; + int timer_fd; + + struct sockaddr_ll sk_addr; + + struct avtp_stream_pdu *pdu; + int pdu_size; + uint8_t pdu_seq; + + struct msghdr *msg; + struct cmsghdr *cmsg; + + uint64_t timer_starttime; + uint64_t timer_period; + uint64_t timer_expirations; + + const snd_pcm_channel_area_t *audiobuf_areas; + 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; + + uint64_t prev_ptime; + + int pdu_period; +} 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; + + 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 if (strcmp(id, "ptime_tolerance") == 0) { + long ptime_tolerance; + + if (snd_config_get_integer(entry, + &ptime_tolerance) < 0) + goto err; + + if (ptime_tolerance < 0) + goto err; + + aaf->ptime_tolerance = ptime_tolerance * NSEC_PER_USEC; + } else { + SNDERR("Invalid configuration: %s", id); + goto err; + } + } + + return 0; + +err: + SNDERR("Error loading device configuration"); + return -EINVAL; +} + +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) { + 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); + + if (io->stream == SND_PCM_STREAM_PLAYBACK) { + struct sock_txtime txtime_cfg; + + 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; + } + + txtime_cfg.clockid = CLOCK_TAI; + txtime_cfg.flags = 0; + res = setsockopt(fd, SOL_SOCKET, SO_TXTIME, &txtime_cfg, + sizeof(txtime_cfg)); + if (res < 0) { + SNDERR("Failed to configure txtime"); + res = -errno; + goto err; + } + } else { + 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; + 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; + + 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_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_msghdr(snd_pcm_aaf_t *aaf) +{ + int res; + struct iovec *iov; + char *control; + size_t controllen; + struct msghdr *msg; + struct cmsghdr *cmsg; + + iov = malloc(sizeof(struct iovec)); + if (!iov) { + SNDERR("Failed to allocate iovec"); + return -ENOMEM; + } + + iov->iov_base = aaf->pdu; + iov->iov_len = aaf->pdu_size; + + controllen = CMSG_SPACE(sizeof(__u64)); + control = malloc(controllen); + if (!control) { + SNDERR("Failed to allocate control buffer"); + res = -ENOMEM; + goto err_free_iov; + } + + msg = malloc(sizeof(struct msghdr)); + if (!msg) { + SNDERR("Failed to allocate msghdr"); + res = -ENOMEM; + goto err_free_control; + } + + msg->msg_name = &aaf->sk_addr; + msg->msg_namelen = sizeof(aaf->sk_addr); + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = control; + msg->msg_controllen = controllen; + + cmsg = CMSG_FIRSTHDR(msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_TXTIME; + cmsg->cmsg_len = CMSG_LEN(sizeof(__u64)); + + aaf->msg = msg; + aaf->cmsg = cmsg; + return 0; + +err_free_control: + free(control); +err_free_iov: + free(iov); + return res; +} + +static void aaf_inc_ptr(snd_pcm_uframes_t *ptr, snd_pcm_uframes_t val, + snd_pcm_uframes_t boundary) +{ + *ptr += val; + + if (*ptr > boundary) + *ptr -= boundary; +} + +static int aaf_mclk_start(snd_pcm_aaf_t *aaf, uint64_t time, uint64_t period) +{ + int res; + struct itimerspec itspec; + uint64_t time_utc; + + aaf->timer_expirations = 0; + aaf->timer_period = period; + aaf->timer_starttime = time; + + time_utc = TAI_TO_UTC(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->timer_period; + res = timerfd_settime(aaf->timer_fd, TFD_TIMER_ABSTIME, &itspec, NULL); + if (res < 0) + return -errno; + + return 0; +} + +static int aaf_mclk_start_playback(snd_pcm_aaf_t *aaf) +{ + int res; + struct timespec now; + uint64_t time, period; + 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; + } + + period = (uint64_t)NSEC_PER_SEC * io->period_size / io->rate; + time = now.tv_sec * NSEC_PER_SEC + now.tv_nsec + period; + res = aaf_mclk_start(aaf, time, period); + if (res < 0) + return res; + + return 0; +} + +static int aaf_mclk_start_capture(snd_pcm_aaf_t *aaf, uint32_t avtp_time) +{ + int res; + struct timespec tspec; + uint64_t now, ptime, time, period; + 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); + + period = (uint64_t)NSEC_PER_SEC * io->period_size / io->rate; + time = ptime + period; + res = aaf_mclk_start(aaf, time, period); + if (res < 0) + return res; + + aaf->prev_ptime = ptime; + 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->timer_starttime = 0; + aaf->timer_period = 0; + aaf->timer_expirations = 0; + return 0; +} + +static uint64_t aaf_mclk_gettime(snd_pcm_aaf_t *aaf) +{ + if (aaf->timer_expirations == 0) + return 0; + + return aaf->timer_starttime + aaf->timer_period * + (aaf->timer_expirations - 1); +} + +static int aaf_tx_pdu(snd_pcm_aaf_t *aaf, snd_pcm_uframes_t ptr, + uint64_t ptime, __u64 txtime) +{ + int res; + ssize_t n; + snd_pcm_ioplug_t *io = &aaf->io; + struct avtp_stream_pdu *pdu = aaf->pdu; + + *(__u64 *)CMSG_DATA(aaf->cmsg) = txtime; + + res = snd_pcm_areas_copy_wrap(aaf->payload_areas, 0, + aaf->frames_per_pdu, + aaf->audiobuf_areas, + (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; + + res = avtp_aaf_pdu_set(pdu, AVTP_AAF_FIELD_TIMESTAMP, ptime); + if (res < 0) + return res; + + n = sendmsg(aaf->sk_fd, aaf->msg, 0); + if (n < 0 || n != aaf->pdu_size) { + SNDERR("Failed to send AAF PDU"); + return -EIO; + } + + return 0; +} + +static int aaf_tx_pdus(snd_pcm_aaf_t *aaf, int pdu_count) +{ + int res; + uint64_t ptime, txtime; + snd_pcm_uframes_t ptr; + + txtime = aaf_mclk_gettime(aaf) + aaf->t_uncertainty; + ptime = txtime + aaf->mtt; + ptr = aaf->hw_ptr; + + while (pdu_count--) { + res = aaf_tx_pdu(aaf, ptr, ptime, txtime); + if (res < 0) + return res; + + txtime += aaf->pdu_period; + ptime += aaf->pdu_period; + ptr += aaf->frames_per_pdu; + } + + return 0; +} + +static bool is_ptime_valid(snd_pcm_aaf_t *aaf, uint32_t avtp_time) +{ + const uint64_t exp_ptime = aaf->prev_ptime + aaf->pdu_period; + const uint64_t lower_bound = exp_ptime - aaf->ptime_tolerance; + const uint64_t upper_bound = exp_ptime + aaf->ptime_tolerance; + const uint64_t ptime = (exp_ptime & 0xFFFFFFFF00000000ULL) | avtp_time; + + if (ptime < lower_bound || ptime > upper_bound) { + pr_debug("Presentation time not expected"); + return false; + } + + if (ptime < aaf_mclk_gettime(aaf)) { + pr_debug("Presentation time in the past"); + return false; + } + + aaf->prev_ptime = ptime; + return true; +} + +static bool is_pdu_valid(snd_pcm_aaf_t *aaf) +{ + int res; + uint64_t val64; + uint32_t val32; + snd_pcm_ioplug_t *io = &aaf->io; + snd_pcm_t *pcm = io->pcm; + const uint64_t data_len = snd_pcm_frames_to_bytes(pcm, aaf->frames_per_pdu); + const uint64_t format = alsa_to_avtp_format(io->format); + const uint64_t nsr = alsa_to_avtp_rate(io->rate); + const uint64_t depth = snd_pcm_format_width(io->format); + struct avtp_common_pdu *common = (struct avtp_common_pdu *) aaf->pdu; + + 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(aaf->pdu, AVTP_AAF_FIELD_STREAM_ID, &val64); + if (res < 0) + return false; + if (val64 != aaf->streamid) { + pr_debug("Streamid mismatch: expected %lu, got %lu", + aaf->streamid, val64); + return false; + } + + res = avtp_aaf_pdu_get(aaf->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(aaf->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(aaf->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(aaf->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(aaf->pdu, AVTP_AAF_FIELD_CHAN_PER_FRAME, &val64); + if (res < 0) + return false; + if (val64 != io->channels) { + pr_debug("Channels mismatch: expected %u, got %lu", + io->channels, val64); + return false; + } + + res = avtp_aaf_pdu_get(aaf->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(aaf->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; + } + + res = avtp_aaf_pdu_get(aaf->pdu, AVTP_AAF_FIELD_SEQ_NUM, &val64); + if (res < 0) + return false; + if (val64 != aaf->pdu_seq) { + pr_debug("Sequence mismatch: expected %u, got %lu", + aaf->pdu_seq, val64); + aaf->pdu_seq = val64; + } + aaf->pdu_seq++; + + if (aaf->timer_starttime) { + /* If media clock has started, it means we have already + * received an AVTPDU, so we are able to check if the + * Presentation Time from this AVTPDU is valid. + */ + uint64_t avtp_time; + + res = avtp_aaf_pdu_get(aaf->pdu, AVTP_AAF_FIELD_TIMESTAMP, + &avtp_time); + if (res < 0) + return false; + + if (!is_ptime_valid(aaf, avtp_time)) { + pr_debug("Packet dropped: PT not valid"); + return false; + } + } + + return true; +} + +static int aaf_copy_pdu_payload(snd_pcm_aaf_t *aaf) +{ + int res; + snd_pcm_uframes_t hw_avail; + snd_pcm_ioplug_t *io = &aaf->io; + + 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_inc_ptr(&aaf->hw_virt_ptr, aaf->frames_per_pdu, aaf->boundary); + return 0; +} + +static int aaf_dispatch_pdu_aaf(snd_pcm_aaf_t *aaf) +{ + int res; + + if (!is_pdu_valid(aaf)) { + pr_debug("AAF PDU dropped: Bad field(s)"); + return 0; + } + + res = aaf_copy_pdu_payload(aaf); + if (res < 0) + return res; + + if (aaf->timer_starttime == 0) { + /* If the media clock has not been started yet (which means + * this is the first AAF PDU received by the plugin), we start + * it. + */ + uint64_t avtp_time; + + res = avtp_aaf_pdu_get(aaf->pdu, AVTP_AAF_FIELD_TIMESTAMP, + &avtp_time); + if (res < 0) + return res; + + res = aaf_mclk_start_capture(aaf, avtp_time); + if (res < 0) + return res; + } + + return 0; +} + +static int aaf_dispatch_pdu(snd_pcm_aaf_t *aaf) +{ + int res; + uint32_t subtype; + struct avtp_common_pdu *common = (struct avtp_common_pdu *) aaf->pdu; + + res = avtp_pdu_get(common, AVTP_FIELD_SUBTYPE, &subtype); + if (res < 0) + return res; + + switch (subtype) { + case AVTP_SUBTYPE_AAF: + return aaf_dispatch_pdu_aaf(aaf); + default: + pr_debug("AVTPDU dropped: subtype not supported"); + return 0; + } +} + +static int aaf_socket_new_data(snd_pcm_aaf_t *aaf) +{ + ssize_t n; + snd_pcm_ioplug_t *io = &aaf->io; + + n = recv(aaf->sk_fd, aaf->pdu, aaf->pdu_size, 0); + if (n < 0) { + SNDERR("Failed to receive data"); + return -errno; + } + if (n != aaf->pdu_size) { + pr_debug("AVTPDU dropped: Invalid size"); + return 0; + } + + if (io->state == SND_PCM_STATE_DRAINING) { + /* If device is in DRAIN state, there is no point in + * dispatching the AVTPDU just received so we are done + * here. + */ + return 0; + } + + return aaf_dispatch_pdu(aaf); +} + +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; +} + +static int aaf_tx_frames(snd_pcm_aaf_t *aaf) +{ + int res; + snd_pcm_uframes_t hw_avail; + int pdu_count; + snd_pcm_ioplug_t *io = &aaf->io; + + hw_avail = snd_pcm_ioplug_hw_avail(io, aaf->hw_ptr, io->appl_ptr); + if (hw_avail < io->period_size) { + /* If the number of available frames is less than the period + * size, we reached an underrun state. + */ + return -EPIPE; + } + + pdu_count = io->period_size / aaf->frames_per_pdu; + res = aaf_tx_pdus(aaf, pdu_count); + if (res < 0) + return res; + + aaf_inc_ptr(&aaf->hw_ptr, io->period_size, aaf->boundary); + return 0; +} + +static int aaf_present_frames(snd_pcm_aaf_t *aaf) +{ + snd_pcm_sframes_t len; + snd_pcm_ioplug_t *io = &aaf->io; + + 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, io->period_size, aaf->boundary); + return 0; +} + +static int aaf_process_frames(snd_pcm_aaf_t *aaf) +{ + snd_pcm_ioplug_t *io = &aaf->io; + + return (io->stream == SND_PCM_STREAM_PLAYBACK) ? + aaf_tx_frames(aaf) : + aaf_present_frames(aaf); +} + +static int aaf_timer_timeout(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 expirations ", expirations - 1); + + while (expirations--) { + aaf->timer_expirations++; + + res = aaf_process_frames(aaf); + if (res < 0) + return res; + } + + return 0; +} + +static int aaf_set_hw_constraint(snd_pcm_aaf_t *aaf) +{ + int res; + snd_pcm_ioplug_t *io = &aaf->io; + static const unsigned int accesses[] = { + SND_PCM_ACCESS_RW_INTERLEAVED, + SND_PCM_ACCESS_MMAP_INTERLEAVED, + }; + static const unsigned int formats[] = { + SND_PCM_FORMAT_S16_BE, + SND_PCM_FORMAT_S24_3BE, + SND_PCM_FORMAT_S32_BE, + SND_PCM_FORMAT_FLOAT_BE, + }; + static 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) +{ + free(io->private_data); + 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, " frames per AVTPDU: %lu\n", + aaf->frames_per_pdu); + snd_output_printf(out, " ptime tolerance: %d\n", + aaf->ptime_tolerance / NSEC_PER_USEC); +} + +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_msghdr(aaf); + if (res < 0) + goto err_free_areas; + + if (io->period_size % aaf->frames_per_pdu) { + /* The plugin requires that the period size is multiple of the + * configuration frames_per_pdu. Return error if this + * requirement isn't satisfied. + */ + SNDERR("Period size must be multiple of frames_per_pdu"); + res = -EINVAL; + goto err_free_msghdr; + } + + aaf->pdu_period = (uint64_t)NSEC_PER_SEC * aaf->frames_per_pdu / + io->rate; + return 0; + +err_free_msghdr: + free(aaf->msg->msg_iov); + free(aaf->msg->msg_control); + free(aaf->msg); +err_free_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->msg->msg_iov); + free(aaf->msg->msg_control); + free(aaf->msg); + 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) +{ + if (io->stream == SND_PCM_STREAM_PLAYBACK) + return FD_COUNT_PLAYBACK; + else + return FD_COUNT_CAPTURE; +} + +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 (io->stream == SND_PCM_STREAM_PLAYBACK) { + if (space != FD_COUNT_PLAYBACK) + return -EINVAL; + + pfd[0].fd = aaf->timer_fd; + pfd[0].events = POLLIN; + } else { + 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; +} + +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 (io->stream == SND_PCM_STREAM_PLAYBACK) { + if (nfds != FD_COUNT_PLAYBACK) + return -EINVAL; + + if (pfd[0].revents & POLLIN) { + res = aaf_timer_timeout(aaf); + if (res < 0) + return res; + + *revents = POLLIN; + } + } else { + if (nfds != FD_COUNT_CAPTURE) + return -EINVAL; + + if (pfd[0].revents & POLLIN) { + res = aaf_timer_timeout(aaf); + if (res < 0) + return res; + + *revents = POLLIN; + } + + if (pfd[1].revents & POLLIN) { + res = aaf_socket_new_data(aaf); + if (res < 0) + return res; + } + } + + return 0; +} + +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->hw_ptr = 0; + aaf->hw_virt_ptr = 0; + aaf->prev_ptime = 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; + + if (io->stream == SND_PCM_STREAM_PLAYBACK) { + res = aaf_mclk_start_playback(aaf); + } else { + /* 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; +} + +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 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, + .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, +}; + +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->sk_fd = -1; + aaf->timer_fd = -1; + + 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; + 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"); + goto err; + } + + res = aaf_set_hw_constraint(aaf); + if (res < 0) { + SNDERR("Failed to set hw constraints"); + snd_pcm_ioplug_delete(&aaf->io); + return res; + } + + *pcmp = aaf->io.pcm; + return 0; + +err: + free(aaf); + return res; +} + +SND_PCM_PLUGIN_SYMBOL(aaf); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/config.h.in new/alsa-plugins-1.1.8/config.h.in --- old/alsa-plugins-1.1.7/config.h.in 2018-10-16 14:01:58.000000000 +0200 +++ new/alsa-plugins-1.1.8/config.h.in 2019-01-07 14:22:45.000000000 +0100 @@ -21,6 +21,15 @@ /* Define to 1 if you have the `asound' library (-lasound). */ #undef HAVE_LIBASOUND +/* Define to 1 if you have the <linux/if_ether.h> header file. */ +#undef HAVE_LINUX_IF_ETHER_H + +/* Define to 1 if you have the <linux/if_packet.h> header file. */ +#undef HAVE_LINUX_IF_PACKET_H + +/* Define to 1 if you have the <linux/net_tstamp.h> header file. */ +#undef HAVE_LINUX_NET_TSTAMP_H + /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/configure.ac new/alsa-plugins-1.1.8/configure.ac --- old/alsa-plugins-1.1.7/configure.ac 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/configure.ac 2019-01-07 13:55:43.000000000 +0100 @@ -1,5 +1,5 @@ AC_PREREQ(2.59) -AC_INIT(alsa-plugins, 1.1.7) +AC_INIT(alsa-plugins, 1.1.8) AM_INIT_AUTOMAKE AC_PREFIX_DEFAULT(/usr) @@ -176,6 +176,15 @@ 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]) + AC_CHECK_HEADERS([linux/if_ether.h linux/if_packet.h linux/net_tstamp.h], [], [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 +260,7 @@ usb_stream/Makefile speex/Makefile arcam-av/Makefile + aaf/Makefile ]) dnl Show the build conditions @@ -289,3 +299,4 @@ echo " speexdsp_CFLAGS: $speexdsp_CFLAGS" echo " speexdsp_LIBS: $speexdsp_LIBS" fi +echo "AAF plugin: $HAVE_AAF" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/jack/pcm_jack.c new/alsa-plugins-1.1.8/jack/pcm_jack.c --- old/alsa-plugins-1.1.7/jack/pcm_jack.c 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/jack/pcm_jack.c 2019-01-07 13:55:43.000000000 +0100 @@ -538,7 +538,6 @@ err = jack_set_hw_constraint(jack); if (err < 0) { snd_pcm_ioplug_delete(&jack->io); - snd_pcm_jack_free(jack); return err; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/oss/pcm_oss.c new/alsa-plugins-1.1.8/oss/pcm_oss.c --- old/alsa-plugins-1.1.7/oss/pcm_oss.c 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/oss/pcm_oss.c 2019-01-07 13:55:43.000000000 +0100 @@ -413,7 +413,7 @@ if ((err = oss_hw_constraint(oss)) < 0) { snd_pcm_ioplug_delete(&oss->io); - goto error; + return err; } *pcmp = oss->io.pcm; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/usb_stream/pcm_usb_stream.c new/alsa-plugins-1.1.8/usb_stream/pcm_usb_stream.c --- old/alsa-plugins-1.1.7/usb_stream/pcm_usb_stream.c 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/usb_stream/pcm_usb_stream.c 2019-01-07 13:55:43.000000000 +0100 @@ -297,12 +297,12 @@ return size; } -static int usb_stream_read(struct user_usb_stream *uus, void *to, unsigned bytes) +static int usb_stream_read(struct user_usb_stream *uus, void *to) { struct usb_stream *s = uus->s; - int p = s->inpacket_split, l = 0; + unsigned p = s->inpacket_split, l = 0; void *i = (void *)s + s->inpacket[p].offset + s->inpacket_split_at; - int il = s->inpacket[p].length - s->inpacket_split_at; + unsigned il = s->inpacket[p].length - s->inpacket_split_at; do { if (l + il > s->period_size) @@ -337,8 +337,7 @@ return -EINVAL; } if (us->uus->s->periods_done - us->periods_done == 1) { - red = usb_stream_read(us->uus, to, size * frame_size) / - frame_size; + red = usb_stream_read(us->uus, to) / frame_size; us->periods_done++; return red; } @@ -424,8 +423,10 @@ } VDBG("%i %s", stream, us_name); us->uus = get_uus(card); - if (!us->uus) + if (!us->uus) { + free(us); return -ENOMEM; + } err = snd_hwdep_open(&us->hwdep, us_name, O_RDWR); if (err < 0) { us_free(us); @@ -455,7 +456,6 @@ err = us_set_hw_constraint(us); if (err < 0) { snd_pcm_ioplug_delete(&us->io); - us_free(us); return err; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/usb_stream/usb_stream.h new/alsa-plugins-1.1.8/usb_stream/usb_stream.h --- old/alsa-plugins-1.1.7/usb_stream/usb_stream.h 2018-10-16 14:00:22.000000000 +0200 +++ new/alsa-plugins-1.1.8/usb_stream/usb_stream.h 2019-01-07 13:55:43.000000000 +0100 @@ -39,7 +39,7 @@ unsigned read_size; unsigned write_size; - int period_size; + unsigned period_size; unsigned state; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/alsa-plugins-1.1.7/version new/alsa-plugins-1.1.8/version --- old/alsa-plugins-1.1.7/version 2018-10-16 14:02:01.000000000 +0200 +++ new/alsa-plugins-1.1.8/version 2019-01-07 14:22:50.000000000 +0100 @@ -1 +1 @@ -1.1.7 +1.1.8
