diff -Nru alsa-lib-1.2.2/Makefile.am alsa-lib-1.2.6.1/Makefile.am --- alsa-lib-1.2.2/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -16,7 +16,7 @@ endif endif SUBDIRS += test utils -EXTRA_DIST=ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ +EXTRA_DIST=README.md ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ depcomp version MEMORY-LEAK m4/attributes.m4 AUTOMAKE_OPTIONS=foreign diff -Nru alsa-lib-1.2.2/Makefile.in alsa-lib-1.2.6.1/Makefile.in --- alsa-lib-1.2.2/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -332,6 +332,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -343,7 +344,7 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = doc include src $(am__append_1) $(am__append_2) \ $(am__append_3) $(am__append_4) test utils -EXTRA_DIST = ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ +EXTRA_DIST = README.md ChangeLog INSTALL TODO NOTES configure gitcompile libtool \ depcomp version MEMORY-LEAK m4/attributes.m4 AUTOMAKE_OPTIONS = foreign @@ -586,6 +587,10 @@ tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) +dist-zstd: distdir + tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst + $(am__post_remove_distdir) + dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @@ -628,6 +633,8 @@ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ + *.tar.zst*) \ + zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) @@ -805,7 +812,7 @@ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar \ - dist-tarZ dist-xz dist-zip distcheck distclean \ + dist-tarZ dist-xz dist-zip dist-zstd distcheck distclean \ distclean-generic distclean-libtool distclean-tags \ distcleancheck distdir distuninstallcheck dvi dvi-am html \ html-am info info-am install install-am install-data \ diff -Nru alsa-lib-1.2.2/README.md alsa-lib-1.2.6.1/README.md --- alsa-lib-1.2.2/README.md 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/README.md 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,25 @@ +# alsa-lib +## Advanced Linux Sound Architecture (ALSA) project + +![Build alsa-lib](https://github.com/alsa-project/alsa-lib/workflows/Build%20alsa-lib/badge.svg?branch=master) + +The alsa-lib is a library to interface with ALSA in the Linux kernel and +virtual devices using a plugin system. + +The up-to-date reference generated from sources can be accessed here: + +http://www.alsa-project.org/alsa-doc/alsa-lib/ + +You may give a look for more information about the ALSA project to URL +http://www.alsa-project.org. + +### Submitting patches + +The preferred way to submit patches is by sending them by email to the +alsa-devel mailing list. Sending mail to the list requires subscription, +subscribe here: https://mailman.alsa-project.org/mailman/listinfo/alsa-devel + +Add Takashi Iwai `` and/or Jaroslav Kysela `` to +Cc so that your patch won't be missed. + +Patches are also accepted as GitHub pull requests. diff -Nru alsa-lib-1.2.2/aclocal.m4 alsa-lib-1.2.6.1/aclocal.m4 --- alsa-lib-1.2.2/aclocal.m4 2020-02-19 10:25:23.000000000 +0000 +++ alsa-lib-1.2.6.1/aclocal.m4 2021-12-09 14:53:49.000000000 +0000 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.2 -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,7 @@ If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2018 Free Software Foundation, Inc. +# Copyright (C) 2002-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -35,7 +35,7 @@ [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.16.1], [], +m4_if([$1], [1.16.2], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,14 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.16.1])dnl +[AM_AUTOMAKE_VERSION([1.16.2])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -110,7 +110,7 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2018 Free Software Foundation, Inc. +# Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -141,7 +141,7 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -332,7 +332,7 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -371,7 +371,9 @@ done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. Try re-running configure with the + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE="gmake" (or whatever is + necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi @@ -398,7 +400,7 @@ # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -595,7 +597,7 @@ done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -616,7 +618,7 @@ fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2018 Free Software Foundation, Inc. +# Copyright (C) 2003-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -638,7 +640,7 @@ # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -673,7 +675,7 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -716,7 +718,7 @@ # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2018 Free Software Foundation, Inc. +# Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -755,7 +757,7 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -784,7 +786,7 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -831,7 +833,7 @@ # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -850,7 +852,7 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -931,7 +933,7 @@ rm -f conftest.file ]) -# Copyright (C) 2009-2018 Free Software Foundation, Inc. +# Copyright (C) 2009-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -991,7 +993,7 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1019,7 +1021,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2018 Free Software Foundation, Inc. +# Copyright (C) 2006-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1038,7 +1040,7 @@ # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2018 Free Software Foundation, Inc. +# Copyright (C) 2004-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff -Nru alsa-lib-1.2.2/alsalisp/Makefile.in alsa-lib-1.2.6.1/alsalisp/Makefile.in --- alsa-lib-1.2.2/alsalisp/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/alsalisp/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -296,6 +296,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/aserver/Makefile.in alsa-lib-1.2.6.1/aserver/Makefile.in --- alsa-lib-1.2.2/aserver/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/aserver/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -297,6 +297,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/compile alsa-lib-1.2.6.1/compile --- alsa-lib-1.2.2/compile 2020-02-19 10:25:24.000000000 +0000 +++ alsa-lib-1.2.6.1/compile 2021-12-09 14:53:50.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -53,7 +53,7 @@ MINGW*) file_conv=mingw ;; - CYGWIN*) + CYGWIN* | MSYS*) file_conv=cygwin ;; *) @@ -67,7 +67,7 @@ mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; - cygwin/*) + cygwin/* | msys/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) diff -Nru alsa-lib-1.2.2/config.guess alsa-lib-1.2.6.1/config.guess --- alsa-lib-1.2.2/config.guess 2020-02-19 10:25:24.000000000 +0000 +++ alsa-lib-1.2.6.1/config.guess 2021-12-09 14:53:51.000000000 +0000 @@ -2,7 +2,7 @@ # Attempt to guess a canonical system name. # Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2018-03-08' +timestamp='2018-08-29' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -84,8 +84,6 @@ exit 1 fi -trap 'exit 1' 1 2 15 - # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a @@ -96,34 +94,39 @@ # Portable tmp directory creation inspired by the Autoconf team. -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > "$dummy.c" ; - for c in cc gcc c89 c99 ; do - if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 1 2 13 15 +trap 'exitcode=$?; test -z "$tmp" || rm -fr "$tmp"; exit $exitcode' 0 + +set_cc_for_build() { + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD="$driver" + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then +if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi @@ -138,7 +141,7 @@ # We could probably try harder. LIBC=gnu - eval "$set_cc_for_build" + set_cc_for_build cat <<-EOF > "$dummy.c" #include #if defined(__UCLIBC__) @@ -199,7 +202,7 @@ os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval "$set_cc_for_build" + set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then @@ -237,7 +240,7 @@ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi}" + echo "$machine-${os}${release}${abi-}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` @@ -389,20 +392,15 @@ echo i386-pc-auroraux"$UNAME_RELEASE" exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - eval "$set_cc_for_build" - SUN_ARCH=i386 - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if [ "$CC_FOR_BUILD" != no_compiler_found ]; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - SUN_ARCH=x86_64 - fi - fi - echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`" + UNAME_REL="`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`" + case `isainfo -b` in + 32) + echo i386-pc-solaris2"$UNAME_REL" + ;; + 64) + echo x86_64-pc-solaris2"$UNAME_REL" + ;; + esac exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize @@ -482,7 +480,7 @@ echo clipper-intergraph-clix"$UNAME_RELEASE" exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ @@ -579,7 +577,7 @@ exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include @@ -660,7 +658,7 @@ esac fi if [ "$HP_ARCH" = "" ]; then - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE @@ -700,7 +698,7 @@ esac if [ "$HP_ARCH" = hppa2.0w ] then - eval "$set_cc_for_build" + set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler @@ -726,7 +724,7 @@ echo ia64-hp-hpux"$HPUX_REV" exit ;; 3050*:HI-UX:*:*) - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int @@ -840,6 +838,17 @@ *:BSD/OS:*:*) echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" exit ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi + else + echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf + fi + exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case "$UNAME_PROCESSOR" in @@ -894,8 +903,8 @@ # other systems with GNU libc and userland echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC" exit ;; - i*86:Minix:*:*) - echo "$UNAME_MACHINE"-pc-minix + *:Minix:*:*) + echo "$UNAME_MACHINE"-unknown-minix exit ;; aarch64:Linux:*:*) echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" @@ -922,7 +931,7 @@ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; arm*:Linux:*:*) - eval "$set_cc_for_build" + set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then @@ -971,7 +980,7 @@ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" exit ;; mips:Linux:*:* | mips64:Linux:*:*) - eval "$set_cc_for_build" + set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef ${UNAME_MACHINE} @@ -1285,7 +1294,7 @@ exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - eval "$set_cc_for_build" + set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi @@ -1358,6 +1367,7 @@ # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. + # shellcheck disable=SC2154 if test "$cputype" = 386; then UNAME_MACHINE=i386 else diff -Nru alsa-lib-1.2.2/config.sub alsa-lib-1.2.6.1/config.sub --- alsa-lib-1.2.2/config.sub 2020-02-19 10:25:24.000000000 +0000 +++ alsa-lib-1.2.6.1/config.sub 2021-12-09 14:53:51.000000000 +0000 @@ -2,7 +2,7 @@ # Configuration validation subroutine script. # Copyright 1992-2018 Free Software Foundation, Inc. -timestamp='2018-05-05' +timestamp='2018-08-29' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -110,16 +110,20 @@ exit 1;; esac -# Spilt fields of configuration type +# Split fields of configuration type IFS="-" read -r field1 field2 field3 field4 <&2 + exit 1 + ;; *-*-*-*) basic_machine=$field1-$field2 - os=-$field3-$field4 + os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two @@ -132,1227 +136,1133 @@ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova*) basic_machine=$field1 - os=-$maybe_os + os=$maybe_os ;; android-linux) basic_machine=$field1-unknown - os=-linux-android + os=linux-android ;; *) basic_machine=$field1-$field2 - os=-$field3 + os=$field3 ;; esac ;; *-*) - basic_machine=$field1 - os=-$field2 + # A lone config we happen to match not fitting any patern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + os= + ;; + *) + basic_machine=$field1 + os=$field2 + ;; + esac + ;; + esac ;; *) - basic_machine=$1 - os= - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray | -microblaze*) - os= - basic_machine=$1 - ;; - -bluegene*) - os=-cnk - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*178) - os=-lynxos178 - ;; - -lynx*5) - os=-lynxos5 - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + os=bsd + ;; + a29khif) + basic_machine=a29k-amd + os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=scout + ;; + alliant) + basic_machine=fx80-alliant + os= + ;; + altos | altos3068) + basic_machine=m68k-altos + os= + ;; + am29k) + basic_machine=a29k-none + os=bsd + ;; + amdahl) + basic_machine=580-amdahl + os=sysv + ;; + amiga) + basic_machine=m68k-unknown + os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=bsd + ;; + aros) + basic_machine=i386-pc + os=aros + ;; + aux) + basic_machine=m68k-apple + os=aux + ;; + balance) + basic_machine=ns32k-sequent + os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=linux + ;; + cegcc) + basic_machine=arm-unknown + os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=bsd + ;; + convex-c2) + basic_machine=c2-convex + os=bsd + ;; + convex-c32) + basic_machine=c32-convex + os=bsd + ;; + convex-c34) + basic_machine=c34-convex + os=bsd + ;; + convex-c38) + basic_machine=c38-convex + os=bsd + ;; + cray) + basic_machine=j90-cray + os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + os= + ;; + da30) + basic_machine=m68k-da30 + os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + os= + ;; + delta88) + basic_machine=m88k-motorola + os=sysv3 + ;; + dicos) + basic_machine=i686-pc + os=dicos + ;; + djgpp) + basic_machine=i586-pc + os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=ose + ;; + gmicro) + basic_machine=tron-gmicro + os=sysv + ;; + go32) + basic_machine=i386-pc + os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=hms + ;; + harris) + basic_machine=m88k-harris + os=sysv3 + ;; + hp300) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=hpux + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=proelf + ;; + i386mach) + basic_machine=i386-mach + os=mach + ;; + vsta) + basic_machine=i386-pc + os=vsta + ;; + isi68 | isi) + basic_machine=m68k-isi + os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=sysv + ;; + merlin) + basic_machine=ns32k-utek + os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + os=coff + ;; + morphos) + basic_machine=powerpc-unknown + os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=moxiebox + ;; + msdos) + basic_machine=i386-pc + os=msdos + ;; + msys) + basic_machine=i686-pc + os=msys + ;; + mvs) + basic_machine=i370-ibm + os=mvs + ;; + nacl) + basic_machine=le32-unknown + os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=newsos + ;; + news1000) + basic_machine=m68030-sony + os=newsos + ;; + necv70) + basic_machine=v70-nec + os=sysv + ;; + nh3000) + basic_machine=m68k-harris + os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=cxux + ;; + nindy960) + basic_machine=i960-intel + os=nindy + ;; + mon960) + basic_machine=i960-intel + os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=ose + ;; + os68k) + basic_machine=m68k-none + os=os68k + ;; + paragon) + basic_machine=i860-intel + os=osf + ;; + parisc) + basic_machine=hppa-unknown + os=linux + ;; + pw32) + basic_machine=i586-unknown + os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=rdos + ;; + rdos32) + basic_machine=i386-pc + os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=coff + ;; + sa29200) + basic_machine=a29k-amd + os=udi + ;; + sei) + basic_machine=mips-sei + os=seiux + ;; + sequent) + basic_machine=i386-sequent + os= + ;; + sps7) + basic_machine=m68k-bull + os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + os= + ;; + stratus) + basic_machine=i860-stratus + os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + os= + ;; + sun2os3) + basic_machine=m68000-sun + os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + os= + ;; + sun3os3) + basic_machine=m68k-sun + os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + os= + ;; + sun4os3) + basic_machine=sparc-sun + os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + os= + ;; + sv1) + basic_machine=sv1-cray + os=unicos + ;; + symmetry) + basic_machine=i386-sequent + os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=unicos + ;; + t90) + basic_machine=t90-cray + os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + os=tpf + ;; + udi29k) + basic_machine=a29k-amd + os=udi + ;; + ultra3) + basic_machine=a29k-nyu + os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=none + ;; + vaxv) + basic_machine=vax-dec + os=sysv + ;; + vms) + basic_machine=vax-dec + os=vms + ;; + vxworks960) + basic_machine=i960-wrs + os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=vxworks + ;; + xbox) + basic_machine=i686-pc + os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + os=unicos + ;; + *) + basic_machine=$1 + os= + ;; + esac ;; esac -# Decode aliases for certain CPU-COMPANY combinations. +# Decode 1-component or ad-hoc basic machines case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | aarch64 | aarch64_be \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arceb \ - | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv6m | armv[78][arm] \ - | avr | avr32 \ - | ba \ - | be32 | be64 \ - | bfin \ - | c4x | c8051 | clipper | csky \ - | d10v | d30v | dlx | dsp16xx \ - | e2k | epiphany \ - | fido | fr30 | frv | ft32 \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | hexagon \ - | i370 | i860 | i960 | ia16 | ia64 \ - | ip2k | iq2000 \ - | k1om \ - | le32 | le64 \ - | lm32 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | moxie \ - | mt \ - | msp430 \ - | nds32 | nds32le | nds32be \ - | nfp \ - | nios | nios2 | nios2eb | nios2el \ - | ns16k | ns32k \ - | open8 | or1k | or1knd | or32 \ - | pdp10 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle \ - | pru \ - | pyramid \ - | riscv32 | riscv64 \ - | rl78 | rx \ - | score \ - | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu \ - | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ - | ubicom32 \ - | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ - | visium \ - | wasm32 \ - | x86 | xc16x | xstormy16 | xtensa \ - | z8k | z80) - basic_machine=$basic_machine-unknown - ;; - c54x) - basic_machine=tic54x-unknown - ;; - c55x) - basic_machine=tic55x-unknown - ;; - c6x) - basic_machine=tic6x-unknown - ;; - leon|leon[3-9]) - basic_machine=sparc-$basic_machine - ;; - m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65) + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond ;; - m9s12z | m68hcs12z | hcs12z | s12z) - basic_machine=s12z-unknown - os=-none + op50n) + cpu=hppa1.1 + vendor=oki ;; - ms1) - basic_machine=mt-unknown + op60c) + cpu=hppa1.1 + vendor=oki ;; - - strongarm | thumb | xscale) - basic_machine=arm-unknown + ibm*) + cpu=i370 + vendor=ibm ;; - xgate) - basic_machine=$basic_machine-unknown - os=-none + orion105) + cpu=clipper + vendor=highlevel ;; - xscaleeb) - basic_machine=armeb-unknown + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple ;; - - xscaleel) - basic_machine=armel-unknown + pmac | pmac-mpw) + cpu=powerpc + vendor=apple ;; - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | aarch64-* | aarch64_be-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | ba-* \ - | be32-* | be64-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* \ - | c8051-* | clipper-* | craynv-* | csky-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | e2k-* | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | hexagon-* \ - | i*86-* | i860-* | i960-* | ia16-* | ia64-* \ - | ip2k-* | iq2000-* \ - | k1om-* \ - | le32-* | le64-* \ - | lm32-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ - | microblaze-* | microblazeel-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64octeon-* | mips64octeonel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64r5900-* | mips64r5900el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa32r6-* | mipsisa32r6el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64r6-* | mipsisa64r6el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipsr5900-* | mipsr5900el-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nds32-* | nds32le-* | nds32be-* \ - | nfp-* \ - | nios-* | nios2-* | nios2eb-* | nios2el-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | open8-* \ - | or1k*-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ - | pru-* \ - | pyramid-* \ - | riscv32-* | riscv64-* \ - | rl78-* | romp-* | rs6000-* | rx-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ - | tahoe-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tile*-* \ - | tron-* \ - | ubicom32-* \ - | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ - | vax-* \ - | visium-* \ - | wasm32-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-* | z80-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-pc - os=-bsd - ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att + cpu=m68000 + vendor=att ;; 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aros) - basic_machine=i386-pc - os=-aros - ;; - asmjs) - basic_machine=asmjs-unknown - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux + cpu=we32k + vendor=att ;; bluegene*) - basic_machine=powerpc-ibm - os=-cnk - ;; - c54x-*) - basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c55x-*) - basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c6x-*) - basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'` - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - cegcc) - basic_machine=arm-unknown - os=-cegcc - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16 | cr16-*) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec + cpu=powerpc + vendor=ibm + os=cnk ;; decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 + cpu=pdp10 + vendor=dec + os=tops10 ;; decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 + cpu=pdp10 + vendor=dec + os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - dicos) - basic_machine=i686-pc - os=-dicos - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx + cpu=m68k + vendor=motorola ;; dpx2*) - basic_machine=m68k-bull - os=-sysv3 - ;; - e500v[12]) - basic_machine=powerpc-unknown - os=$os"spe" - ;; - e500v[12]-*) - basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=$os"spe" - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd + cpu=m68k + vendor=bull + os=sysv3 ;; encore | umax | mmax) - basic_machine=ns32k-encore + cpu=ns32k + vendor=encore ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose + elxsi) + cpu=elxsi + vendor=elxsi + os=${os:-bsd} ;; fx2800) - basic_machine=i860-alliant + cpu=i860 + vendor=alliant ;; genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 + cpu=ns32k + vendor=ns ;; h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp + cpu=m68000 + vendor=hp ;; hp9k3[2-9][0-9]) - basic_machine=m68k-hp + cpu=m68k + vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp + cpu=hppa1.0 + vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp + cpu=hppa1.1 + vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm + cpu=hppa1.0 + vendor=hp ;; i*86v32) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv32 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv32 ;; i*86v4*) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv4 + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv4 ;; i*86v) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-sysv + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv ;; i*86sol2) - basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - vsta) - basic_machine=i386-unknown - os=-vsta + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + os=${os:-unicos} ;; iris | iris4d) - basic_machine=mips-sgi + cpu=mips + vendor=sgi case $os in - -irix*) + irix*) ;; *) - os=-irix4 + os=irix4 ;; esac ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - leon-*|leon[3-9]-*) - basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'` - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - microblaze*) - basic_machine=microblaze-xilinx - ;; - mingw64) - basic_machine=x86_64-pc - os=-mingw64 - ;; - mingw32) - basic_machine=i686-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; miniframe) - basic_machine=m68000-convergent + cpu=m68000 + vendor=convergent ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - moxiebox) - basic_machine=moxie-unknown - os=-moxiebox - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'` - ;; - msys) - basic_machine=i686-pc - os=-msys - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - nacl) - basic_machine=le32-unknown - os=-nacl - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + os=mint ;; news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv + cpu=mips + vendor=sony + os=newsos ;; next | m*-next) - basic_machine=m68k-next + cpu=m68k + vendor=next case $os in - -nextstep* ) + nextstep* ) ;; - -ns2*) - os=-nextstep2 + ns2*) + os=nextstep2 ;; *) - os=-nextstep3 + os=nextstep3 ;; esac ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; np1) - basic_machine=np1-gould - ;; - neo-tandem) - basic_machine=neo-tandem - ;; - nse-tandem) - basic_machine=nse-tandem - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - nsv-tandem) - basic_machine=nsv-tandem - ;; - nsx-tandem) - basic_machine=nsx-tandem + cpu=np1 + vendor=gould ;; op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k + cpu=hppa1.1 + vendor=oki + os=proelf ;; pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'` - os=-linux + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 ;; pbd) - basic_machine=sparc-tti + cpu=sparc + vendor=tti ;; pbb) - basic_machine=m68k-tti + cpu=m68k + vendor=tti ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 + pc532) + cpu=ns32k + vendor=pc532 ;; - pc98) - basic_machine=i386-pc + pn) + cpu=pn + vendor=gould ;; - pc98-*) - basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'` + power) + cpu=power + vendor=ibm ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc + ps2) + cpu=i386 + vendor=ibm ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc + rm[46]00) + cpu=mips + vendor=siemens ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc + rtpc | rtpc-*) + cpu=romp + vendor=ibm ;; - pentium4) - basic_machine=i786-pc + sde) + cpu=mipsisa32 + vendor=sde + os=${os:-elf} ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'` + simso-wrs) + cpu=sparclite + vendor=wrs + os=vxworks ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` + tower | tower-32) + cpu=m68k + vendor=ncr ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'` + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu ;; - pentium4-*) - basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'` + w65) + cpu=w65 + vendor=wdc ;; - pn) - basic_machine=pn-gould + w89k-*) + cpu=hppa1.1 + vendor=winbond + os=proelf ;; - power) basic_machine=power-ibm + none) + cpu=none + vendor=none ;; - ppc | ppcbe) basic_machine=powerpc-unknown + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine ;; - ppc-* | ppcbe-*) - basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'` + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; - ppcle | powerpclittle) - basic_machine=powerpcle-unknown + + *-*) + IFS="-" read -r cpu vendor <&2 - exit 1 + # Recognize the cannonical CPU types that are allowed with any + # company name. + case $cpu in + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | abacus \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \ + | alphapca5[67] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[lb]e | arme[lb] | armv* \ + | avr | avr32 \ + | asmjs \ + | ba \ + | be32 | be64 \ + | bfin | bs2000 \ + | c[123]* | c30 | [cjt]90 | c4x \ + | c8051 | clipper | craynv | csky | cydra \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | elxsi | epiphany \ + | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \ + | h8300 | h8500 \ + | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i*86 | i860 | i960 | ia16 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle \ + | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k | v70 | w65 \ + | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip \ + | m88110 | m88k | maxq | mb | mcore | mep | metag \ + | microblaze | microblazeel \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mmix \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nfp \ + | nios | nios2 | nios2eb | nios2el \ + | none | np1 | ns16k | ns32k \ + | open8 \ + | or1k* \ + | or32 \ + | orion \ + | pdp10 | pdp11 | pj | pjl | pn | power \ + | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \ + | pru \ + | pyramid \ + | riscv | riscv32 | riscv64 \ + | rl78 | romp | rs6000 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \ + | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \ + | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \ + | spu \ + | tahoe \ + | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \ + | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \ + | vax \ + | visium \ + | wasm32 \ + | we32k \ + | x86 | x86_64 | xc16x | xgate | xps100 \ + | xstormy16 | xtensa* \ + | ymp \ + | z8k | z80) + ;; + + *) + echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2 + exit 1 + ;; + esac ;; esac # Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'` +case $vendor in + digital*) + vendor=dec ;; - *-commodore*) - basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'` + commodore*) + vendor=cbm ;; *) ;; @@ -1365,200 +1275,240 @@ case $os in # First match some system type aliases that might get confused # with valid system types. - # -solaris* is a basic system type, with this one exception. - -auroraux) - os=-auroraux + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux ;; - -solaris1 | -solaris1.*) + bluegene*) + os=cnk + ;; + solaris1 | solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; - -solaris) - os=-solaris2 + solaris) + os=solaris2 ;; - -unixware*) - os=-sysv4.2uw + unixware*) + os=sysv4.2uw ;; - -gnu/linux*) + gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # es1800 is here to avoid being matched by es* (a different OS) - -es1800*) - os=-ose + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos ;; # Now accept the basic system types. # The portable systems comes first. # Each alternative MUST end in a * to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ - | -sym* | -kopensolaris* | -plan9* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* | -aros* | -cloudabi* | -sortix* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \ - | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* | -hcos* \ - | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ - | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ - | -linux-newlib* | -linux-musl* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ - | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \ - | -midnightbsd*) + # sysv* is not here because it comes later, after sysvr4. + gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | kopensolaris* | plan9* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | knetbsd* | mirbsd* | netbsd* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* \ + | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ + | linux-newlib* | linux-musl* | linux-uclibc* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* \ + | morphos* | superux* | rtmk* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd*) # Remember, each alternative MUST END IN *, to match a version number. ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) + qnx*) + case $cpu in + x86 | i*86) ;; *) - os=-nto$os + os=nto-$os ;; esac ;; - -nto-qnx*) + hiux*) + os=hiuxwe2 ;; - -nto*) + nto-qnx*) + ;; + nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; - -sim | -xray | -os68k* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + sim | xray | os68k* | v88r* \ + | windows* | osx | abug | netware* | os9* \ + | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) + ;; + linux-dietlibc) + os=linux-dietlibc + ;; + linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; - -mac*) + lynx*178) + os=lynxos178 + ;; + lynx*5) + os=lynxos5 + ;; + lynx*) + os=lynxos + ;; + mac*) os=`echo "$os" | sed -e 's|mac|macos|'` ;; - -linux-dietlibc) - os=-linux-dietlibc + opened*) + os=openedition ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` + os400*) + os=os400 ;; - -sunos5*) + sunos5*) os=`echo "$os" | sed -e 's|sunos5|solaris2|'` ;; - -sunos6*) + sunos6*) os=`echo "$os" | sed -e 's|sunos6|solaris3|'` ;; - -opened*) - os=-openedition + wince*) + os=wince ;; - -os400*) - os=-os400 + utek*) + os=bsd ;; - -wince*) - os=-wince + dynix*) + os=bsd ;; - -utek*) - os=-bsd + acis*) + os=aos ;; - -dynix*) - os=-bsd + atheos*) + os=atheos ;; - -acis*) - os=-aos + syllable*) + os=syllable ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd + 386bsd) + os=bsd ;; - -ctix* | -uts*) - os=-sysv + ctix* | uts*) + os=sysv ;; - -nova*) - os=-rtmk-nova + nova*) + os=rtmk-nova ;; - -ns2) - os=-nextstep2 + ns2) + os=nextstep2 ;; - -nsk*) - os=-nsk + nsk*) + os=nsk ;; # Preserve the version number of sinix5. - -sinix5.*) + sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; - -sinix*) - os=-sysv4 + sinix*) + os=sysv4 ;; - -tpf*) - os=-tpf + tpf*) + os=tpf ;; - -triton*) - os=-sysv3 + triton*) + os=sysv3 ;; - -oss*) - os=-sysv3 + oss*) + os=sysv3 ;; - -svr4*) - os=-sysv4 + svr4*) + os=sysv4 ;; - -svr3) - os=-sysv3 + svr3) + os=sysv3 ;; - -sysvr4) - os=-sysv4 + sysvr4) + os=sysv4 ;; - # This must come after -sysvr4. - -sysv*) + # This must come after sysvr4. + sysv*) ;; - -ose*) - os=-ose + ose*) + os=ose ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint ;; - -zvmoe) - os=-zvmoe + zvmoe) + os=zvmoe ;; - -dicos*) - os=-dicos + dicos*) + os=dicos ;; - -pikeos*) + pikeos*) # Until real need of OS specific support for # particular features comes up, bare metal # configurations are quite functional. - case $basic_machine in + case $cpu in arm*) - os=-eabi + os=eabi ;; *) - os=-elf + os=elf ;; esac ;; - -nacl*) + nacl*) ;; - -ios) + ios) ;; - -none) + none) ;; - -*-eabi) - case $basic_machine in - arm*) - ;; - esac + *-eabi) ;; *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 exit 1 ;; @@ -1575,254 +1525,261 @@ # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. -case $basic_machine in +case $cpu-$vendor in score-*) - os=-elf + os=elf ;; spu-*) - os=-elf + os=elf ;; *-acorn) - os=-riscix1.2 + os=riscix1.2 ;; arm*-rebel) - os=-linux + os=linux ;; arm*-semi) - os=-aout + os=aout ;; c4x-* | tic4x-*) - os=-coff + os=coff ;; c8051-*) - os=-elf + os=elf + ;; + clipper-intergraph) + os=clix ;; hexagon-*) - os=-elf + os=elf ;; tic54x-*) - os=-coff + os=coff ;; tic55x-*) - os=-coff + os=coff ;; tic6x-*) - os=-coff + os=coff ;; # This must come before the *-dec entry. pdp10-*) - os=-tops20 + os=tops20 ;; pdp11-*) - os=-none + os=none ;; *-dec | vax-*) - os=-ultrix4.2 + os=ultrix4.2 ;; m68*-apollo) - os=-domain + os=domain ;; i386-sun) - os=-sunos4.0.2 + os=sunos4.0.2 ;; m68000-sun) - os=-sunos3 + os=sunos3 ;; m68*-cisco) - os=-aout + os=aout ;; mep-*) - os=-elf + os=elf ;; mips*-cisco) - os=-elf + os=elf ;; mips*-*) - os=-elf + os=elf ;; or32-*) - os=-coff + os=coff ;; *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 + os=sysv3 ;; sparc-* | *-sun) - os=-sunos4.1.1 + os=sunos4.1.1 ;; pru-*) - os=-elf + os=elf ;; *-be) - os=-beos + os=beos ;; *-ibm) - os=-aix + os=aix ;; *-knuth) - os=-mmixware + os=mmixware ;; *-wec) - os=-proelf + os=proelf ;; *-winbond) - os=-proelf + os=proelf ;; *-oki) - os=-proelf + os=proelf ;; *-hp) - os=-hpux + os=hpux ;; *-hitachi) - os=-hiux + os=hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv + os=sysv ;; *-cbm) - os=-amigaos + os=amigaos ;; *-dg) - os=-dgux + os=dgux ;; *-dolphin) - os=-sysv3 + os=sysv3 ;; m68k-ccur) - os=-rtu + os=rtu ;; m88k-omron*) - os=-luna + os=luna ;; *-next) - os=-nextstep + os=nextstep ;; *-sequent) - os=-ptx + os=ptx ;; *-crds) - os=-unos + os=unos ;; *-ns) - os=-genix + os=genix ;; i370-*) - os=-mvs + os=mvs ;; *-gould) - os=-sysv + os=sysv ;; *-highlevel) - os=-bsd + os=bsd ;; *-encore) - os=-bsd + os=bsd ;; *-sgi) - os=-irix + os=irix ;; *-siemens) - os=-sysv4 + os=sysv4 ;; *-masscomp) - os=-rtu + os=rtu ;; f30[01]-fujitsu | f700-fujitsu) - os=-uxpv + os=uxpv ;; *-rom68k) - os=-coff + os=coff ;; *-*bug) - os=-coff + os=coff ;; *-apple) - os=-macos + os=macos ;; *-atari*) - os=-mint + os=mint + ;; + *-wrs) + os=vxworks ;; *) - os=-none + os=none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) +case $vendor in + unknown) case $os in - -riscix*) + riscix*) vendor=acorn ;; - -sunos*) + sunos*) vendor=sun ;; - -cnk*|-aix*) + cnk*|-aix*) vendor=ibm ;; - -beos*) + beos*) vendor=be ;; - -hpux*) + hpux*) vendor=hp ;; - -mpeix*) + mpeix*) vendor=hp ;; - -hiux*) + hiux*) vendor=hitachi ;; - -unos*) + unos*) vendor=crds ;; - -dgux*) + dgux*) vendor=dg ;; - -luna*) + luna*) vendor=omron ;; - -genix*) + genix*) vendor=ns ;; - -mvs* | -opened*) + clix*) + vendor=intergraph + ;; + mvs* | opened*) vendor=ibm ;; - -os400*) + os400*) vendor=ibm ;; - -ptx*) + ptx*) vendor=sequent ;; - -tpf*) + tpf*) vendor=ibm ;; - -vxsim* | -vxworks* | -windiss*) + vxsim* | vxworks* | windiss*) vendor=wrs ;; - -aux*) + aux*) vendor=apple ;; - -hms*) + hms*) vendor=hitachi ;; - -mpw* | -macos*) + mpw* | macos*) vendor=apple ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) vendor=atari ;; - -vos*) + vos*) vendor=stratus ;; esac - basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"` ;; esac -echo "$basic_machine$os" +echo "$cpu-$vendor-$os" exit # Local variables: diff -Nru alsa-lib-1.2.2/configure alsa-lib-1.2.6.1/configure --- alsa-lib-1.2.2/configure 2020-02-19 10:25:27.000000000 +0000 +++ alsa-lib-1.2.6.1/configure 2021-12-09 14:53:53.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for alsa-lib 1.2.2. +# Generated by GNU Autoconf 2.69 for alsa-lib 1.2.6.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ # Identity of this package. PACKAGE_NAME='alsa-lib' PACKAGE_TARNAME='alsa-lib' -PACKAGE_VERSION='1.2.2' -PACKAGE_STRING='alsa-lib 1.2.2' +PACKAGE_VERSION='1.2.6.1' +PACKAGE_STRING='alsa-lib 1.2.6.1' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -638,6 +638,8 @@ BUILD_CTL_PLUGIN_EXT_TRUE BUILD_CTL_PLUGIN_SHM_FALSE BUILD_CTL_PLUGIN_SHM_TRUE +BUILD_CTL_PLUGIN_REMAP_FALSE +BUILD_CTL_PLUGIN_REMAP_TRUE BUILD_CTL_PLUGIN_FALSE BUILD_CTL_PLUGIN_TRUE BUILD_PCM_PLUGIN_MMAP_EMUL_FALSE @@ -836,6 +838,7 @@ docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -903,6 +906,7 @@ with_pythonlibs with_pythonincludes with_pcm_plugins +enable_lockless_dmix with_ctl_plugins with_max_cards enable_thread_safety @@ -955,6 +959,7 @@ sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1207,6 +1212,15 @@ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1344,7 +1358,7 @@ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1457,7 +1471,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures alsa-lib 1.2.2 to adapt to many kinds of systems. +\`configure' configures alsa-lib 1.2.6.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1497,6 +1511,7 @@ --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1527,7 +1542,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of alsa-lib 1.2.2:";; + short | recursive ) echo "Configuration of alsa-lib 1.2.6.1:";; esac cat <<\_ACEOF @@ -1569,6 +1584,7 @@ --enable-mixer-pymods enable the mixer python modules (experimental) --disable-python disable the python components --enable-python2 prefer python2 + --enable-lockless-dmix use lockless dmix as default on x86 --disable-thread-safety disable thread-safe API functions Optional Packages: @@ -1688,7 +1704,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -alsa-lib configure 1.2.2 +alsa-lib configure 1.2.6.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2099,7 +2115,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by alsa-lib $as_me 1.2.2, which was +It was created by alsa-lib $as_me 1.2.6.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3037,7 +3053,7 @@ # Define the identity of the package. PACKAGE='alsa-lib' - VERSION='1.2.2' + VERSION='1.2.6.1' cat >>confdefs.h <<_ACEOF @@ -12690,6 +12706,17 @@ fi done +for ac_func in eaccess +do : + ac_fn_c_check_func "$LINENO" "eaccess" "ac_cv_func_eaccess" +if test "x$ac_cv_func_eaccess" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_EACCESS 1 +_ACEOF + +fi +done + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library version" >&5 @@ -13505,7 +13532,10 @@ pythonlibs0= pythoninc0= if test "$build_python2" != "yes"; then - pythonlibs0=$(python3-config --libs) + pythonlibs0=$(python3-config --libs --embed 2> /dev/null) + if test -z "$pythonlibs0"; then + pythonlibs0=$(python3-config --libs) + fi pythoninc0=$(python3-config --includes) fi if test -z "$pythonlibs0"; then @@ -13705,6 +13735,26 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_have_atomics" >&5 $as_echo "$gcc_have_atomics" >&6; } + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +__asm__ volatile ("" : : : "mm0"); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + +$as_echo "#define HAVE_MMX \"1\"" >>confdefs.h + +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug mmap_emul" build_pcm_plugin="no" @@ -14019,6 +14069,27 @@ fi +if test "$build_pcm_dmix" = "yes"; then +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for default lockless dmix" >&5 +$as_echo_n "checking for default lockless dmix... " >&6; } +# Check whether --enable-lockless-dmix was given. +if test "${enable_lockless_dmix+set}" = set; then : + enableval=$enable_lockless_dmix; lockless_dmix="$enableval" +else + lockless_dmix="no" +fi + +if test "$lockless_dmix" = "yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +$as_echo "#define LOCKLESS_DMIX_DEFAULT \"1\"" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +fi rm -f "$srcdir"/src/pcm/pcm_symbols_list.c touch "$srcdir"/src/pcm/pcm_symbols_list.c @@ -14038,7 +14109,7 @@ fi -CTL_PLUGIN_LIST="shm ext" +CTL_PLUGIN_LIST="remap shm ext" build_ctl_plugin="no" for t in $CTL_PLUGIN_LIST; do @@ -14067,6 +14138,14 @@ BUILD_CTL_PLUGIN_FALSE= fi + if test x$build_ctl_remap = xyes; then + BUILD_CTL_PLUGIN_REMAP_TRUE= + BUILD_CTL_PLUGIN_REMAP_FALSE='#' +else + BUILD_CTL_PLUGIN_REMAP_TRUE='#' + BUILD_CTL_PLUGIN_REMAP_FALSE= +fi + if test x$build_ctl_shm = xyes; then BUILD_CTL_PLUGIN_SHM_TRUE= BUILD_CTL_PLUGIN_SHM_FALSE='#' @@ -14144,7 +14223,7 @@ ln -sf . "$srcdir"/include/alsa fi -ac_config_files="$ac_config_files Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg include/Makefile include/sound/Makefile include/sound/uapi/Makefile src/Versions src/Makefile src/control/Makefile src/mixer/Makefile src/pcm/Makefile src/pcm/scopes/Makefile src/rawmidi/Makefile src/timer/Makefile src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile src/alisp/Makefile src/topology/Makefile src/conf/Makefile src/conf/cards/Makefile src/conf/pcm/Makefile modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile alsalisp/Makefile aserver/Makefile test/Makefile test/lsb/Makefile utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc" +ac_config_files="$ac_config_files Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg include/Makefile include/sound/Makefile include/sound/uapi/Makefile src/Versions src/Makefile src/control/Makefile src/mixer/Makefile src/pcm/Makefile src/pcm/scopes/Makefile src/rawmidi/Makefile src/timer/Makefile src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile src/alisp/Makefile src/topology/Makefile src/conf/Makefile src/conf/cards/Makefile src/conf/ctl/Makefile src/conf/pcm/Makefile modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile alsalisp/Makefile aserver/Makefile test/Makefile test/lsb/Makefile utils/Makefile utils/alsa-lib.spec utils/alsa.pc utils/alsa-topology.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -14459,6 +14538,10 @@ as_fn_error $? "conditional \"BUILD_CTL_PLUGIN\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${BUILD_CTL_PLUGIN_REMAP_TRUE}" && test -z "${BUILD_CTL_PLUGIN_REMAP_FALSE}"; then + as_fn_error $? "conditional \"BUILD_CTL_PLUGIN_REMAP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${BUILD_CTL_PLUGIN_SHM_TRUE}" && test -z "${BUILD_CTL_PLUGIN_SHM_FALSE}"; then as_fn_error $? "conditional \"BUILD_CTL_PLUGIN_SHM\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -14864,7 +14947,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by alsa-lib $as_me 1.2.2, which was +This file was extended by alsa-lib $as_me 1.2.6.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -14930,7 +15013,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -alsa-lib config.status 1.2.2 +alsa-lib config.status 1.2.6.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -15367,6 +15450,7 @@ "src/topology/Makefile") CONFIG_FILES="$CONFIG_FILES src/topology/Makefile" ;; "src/conf/Makefile") CONFIG_FILES="$CONFIG_FILES src/conf/Makefile" ;; "src/conf/cards/Makefile") CONFIG_FILES="$CONFIG_FILES src/conf/cards/Makefile" ;; + "src/conf/ctl/Makefile") CONFIG_FILES="$CONFIG_FILES src/conf/ctl/Makefile" ;; "src/conf/pcm/Makefile") CONFIG_FILES="$CONFIG_FILES src/conf/pcm/Makefile" ;; "modules/Makefile") CONFIG_FILES="$CONFIG_FILES modules/Makefile" ;; "modules/mixer/Makefile") CONFIG_FILES="$CONFIG_FILES modules/mixer/Makefile" ;; @@ -16056,7 +16140,9 @@ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. Try re-running configure with the + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE=\"gmake\" (or whatever is + necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } diff -Nru alsa-lib-1.2.2/configure.ac alsa-lib-1.2.6.1/configure.ac --- alsa-lib-1.2.2/configure.ac 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/configure.ac 2021-12-09 13:17:59.000000000 +0000 @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -AC_INIT(alsa-lib, 1.2.2) +AC_INIT(alsa-lib, 1.2.6.1) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -50,6 +50,7 @@ dnl Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_CHECK_FUNCS([uselocale]) +AC_CHECK_FUNCS([eaccess]) SAVE_LIBRARY_VERSION AC_SUBST(LIBTOOL_VERSION_INFO) @@ -423,7 +424,10 @@ pythonlibs0= pythoninc0= if test "$build_python2" != "yes"; then - pythonlibs0=$(python3-config --libs) + pythonlibs0=$(python3-config --libs --embed 2> /dev/null) + if test -z "$pythonlibs0"; then + pythonlibs0=$(python3-config --libs) + fi pythoninc0=$(python3-config --includes) fi if test -z "$pythonlibs0"; then @@ -513,6 +517,13 @@ fi AC_MSG_RESULT($gcc_have_atomics) +dnl check mmx register for pcm_dmix_i386 + +AC_TRY_LINK([], + [__asm__ volatile ("" : : : "mm0");], + [AC_DEFINE([HAVE_MMX], "1", [MMX technology is enabled])], + []) + PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug mmap_emul" build_pcm_plugin="no" @@ -619,6 +630,19 @@ AC_DEFINE([BUILD_PCM_PLUGIN_MMAP_EMUL], "1", [Build PCM mmap-emul plugin]) fi +if test "$build_pcm_dmix" = "yes"; then +AC_MSG_CHECKING(for default lockless dmix) +AC_ARG_ENABLE(lockless-dmix, + AS_HELP_STRING([--enable-lockless-dmix], + [use lockless dmix as default on x86]), + lockless_dmix="$enableval", lockless_dmix="no") +if test "$lockless_dmix" = "yes"; then + AC_MSG_RESULT(yes) + AC_DEFINE([LOCKLESS_DMIX_DEFAULT], "1", [Lockless dmix as default]) +else + AC_MSG_RESULT(no) +fi +fi dnl Create PCM plugin symbol list for static library rm -f "$srcdir"/src/pcm/pcm_symbols_list.c @@ -636,7 +660,7 @@ [build control plugins (default = all)]), [ctl_plugins="$withval"], [ctl_plugins="all"]) -CTL_PLUGIN_LIST="shm ext" +CTL_PLUGIN_LIST="remap shm ext" build_ctl_plugin="no" for t in $CTL_PLUGIN_LIST; do @@ -658,6 +682,7 @@ fi AM_CONDITIONAL([BUILD_CTL_PLUGIN], [test x$build_ctl_plugin = xyes]) +AM_CONDITIONAL([BUILD_CTL_PLUGIN_REMAP], [test x$build_ctl_remap = xyes]) AM_CONDITIONAL([BUILD_CTL_PLUGIN_SHM], [test x$build_ctl_shm = xyes]) AM_CONDITIONAL([BUILD_CTL_PLUGIN_EXT], [test x$build_ctl_ext = xyes]) @@ -716,6 +741,7 @@ src/alisp/Makefile src/topology/Makefile \ src/conf/Makefile \ src/conf/cards/Makefile \ + src/conf/ctl/Makefile \ src/conf/pcm/Makefile \ modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ alsalisp/Makefile aserver/Makefile \ diff -Nru alsa-lib-1.2.2/debian/changelog alsa-lib-1.2.6.1/debian/changelog --- alsa-lib-1.2.2/debian/changelog 2020-03-07 18:21:22.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/changelog 2024-04-16 21:53:33.000000000 +0000 @@ -1,8 +1,139 @@ +alsa-lib (1.2.6.1-1ubuntu1~20.04.sav0) focal; urgency=medium + + * Backport to Focal + * debian/control: Set debhelper-compat (= 12) BD + + -- Rob Savoury Tue, 16 Apr 2024 14:53:33 -0700 + +alsa-lib (1.2.6.1-1ubuntu1) jammy; urgency=medium + + * Merge from Debian unstable, remaining changes: + + Build with -flto-partition=none when building with lto + * Fix build failure with Python 3.10 + + -- Graham Inggs Wed, 19 Jan 2022 14:00:50 +0000 + +alsa-lib (1.2.6.1-1) unstable; urgency=medium + + * New upstream release (closes: #1001697). + * Import upstream signing key and configure watch file to check for sigs. + * Update symbols file with newly added symbols. + * Install missing /usr/share/alsa/ctl dir in libasound2-udeb + (closes: #992536, with apologies for the long wait for this fix). + * Update Standards-Version to 4.6.0, with no changes needed. + * Update copyright years. + + -- Jordi Mallach Wed, 12 Jan 2022 00:14:07 +0100 + +alsa-lib (1.2.5.1-1ubuntu2) jammy; urgency=medium + + * No-change rebuild with Python 3.10 as default version + + -- Graham Inggs Fri, 14 Jan 2022 11:24:35 +0000 + +alsa-lib (1.2.5.1-1ubuntu1) jammy; urgency=low + + * Merge from Debian unstable. Remaining changes: + - Build with -flto-partition=none when building with lto. + + -- Timo Aaltonen Fri, 26 Nov 2021 15:09:02 +0200 + +alsa-lib (1.2.5.1-1) unstable; urgency=medium + + [ Jordi Mallach ] + * New upstream release. + * Acknowledge 1.2.4-1.1 NMU by Thorsten Glaser. + * Drop 976895.diff, cherry-picked from current version. + * Update symbols file with newly added symbols. + * Add several entries to upstream metadata. + + [ Debian Janitor ] + * Set upstream metadata fields: Repository. + * Fix day-of-week for changelog entry 1.0.11-5. + + -- Jordi Mallach Wed, 18 Aug 2021 00:31:12 +0200 + +alsa-lib (1.2.4-1.1ubuntu4) jammy; urgency=medium + + * d/p/0001-ucm-fix-regexec-REG_NOMATCH-state-handling-for-defin.patch + - ucm: Fix a Regex parser bug, when there is no match, need to + set err to 0, otherwise, the caller will get a wrong match instead of + no match. (LP: #1949329) + + -- Hui Wang Mon, 01 Nov 2021 15:05:47 +0800 + +alsa-lib (1.2.4-1.1ubuntu3) impish; urgency=medium + + * No-change rebuild to build packages with zstd compression. + + -- Matthias Klose Thu, 07 Oct 2021 12:09:16 +0200 + +alsa-lib (1.2.4-1.1ubuntu2) hirsute; urgency=medium + + * d/p/0001-conf-USB-add-Cmedia-Audio-to-USB-Audio.pcm.iec958_de.patch: + conf: USB - add "Cmedia Audio" to USB-Audio.pcm.iec958_device + https://github.com/alsa-project/alsa-lib/pull/122 (LP: #1921452) + + -- Shengyao Xue Fri, 26 Mar 2021 12:03:07 +0800 + +alsa-lib (1.2.4-1.1ubuntu1) hirsute; urgency=medium + + * Build with -flto-partition=none when building with lto. + + -- Matthias Klose Mon, 29 Mar 2021 16:31:48 +0200 + +alsa-lib (1.2.4-1.1build1) hirsute; urgency=medium + + * No-change rebuild to drop the udeb package. + + -- Matthias Klose Mon, 22 Feb 2021 10:29:58 +0100 + +alsa-lib (1.2.4-1.1) unstable; urgency=high + + * Non-maintainer upload. + * Add upstream patch fixing a severe regression (Closes: #976895) + + -- Thorsten Glaser Wed, 30 Dec 2020 14:14:11 +0100 + +alsa-lib (1.2.4-1) unstable; urgency=medium + + * New upstream release. + * Update Standards-Version to 4.5.1, with no changes needed. + + -- Jordi Mallach Mon, 07 Dec 2020 22:02:09 +0100 + +alsa-lib (1.2.3.2-1) unstable; urgency=medium + + * New upstream release. + * Acknowledge all previous NMUs. + * Drop python 3.8 patch, now included upstream. + * Update symbols file with two newly added symbols. + * Move to debhelper compat v13. + + -- Jordi Mallach Thu, 20 Aug 2020 20:18:24 +0200 + +alsa-lib (1.2.2-2.3) unstable; urgency=medium + + * Non-maintainer upload. + * debian/rules: + - Added override_dh_auto_clean to allow the package build twice. + (Closes: #962849) + + -- Francisco Vilmar Cardoso Ruviaro Mon, 15 Jun 2020 04:16:18 +0000 + +alsa-lib (1.2.2-2.2) unstable; urgency=medium + + * Non-maintainer upload. + * debian/patches/python3.8.diff: + - Updated as upstream commit '1654f38', fixed FTBFS. (Closes: #953568) + + -- Francisco Vilmar Cardoso Ruviaro Wed, 10 Jun 2020 06:26:40 +0000 + alsa-lib (1.2.2-2.1) unstable; urgency=medium [ Matthias Klose ] * Non-maintainer upload. - + [ Sebastien Bacher ] * debian/patches/python3.8.diff: - fix the build with python 3.8 @@ -1033,7 +1164,7 @@ debian/patches/10_add-maintainer-mode.dpatch, 40_relibtoolise.dpatch, 41_configure_cross_compile.dpatch] - -- Jordi Mallach Wed, 26 May 2006 10:57:03 +0200 + -- Jordi Mallach Fri, 26 May 2006 10:57:03 +0200 alsa-lib (1.0.11-4) unstable; urgency=low @@ -2022,3 +2153,4 @@ * Initial release -- Wichert Akkerman Sun, 7 Jun 1998 16:53:01 +0200 + diff -Nru alsa-lib-1.2.2/debian/control alsa-lib-1.2.6.1/debian/control --- alsa-lib-1.2.2/debian/control 2020-02-27 15:24:22.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/control 2024-04-16 21:53:33.000000000 +0000 @@ -1,7 +1,8 @@ Source: alsa-lib Section: libs Priority: optional -Maintainer: Debian ALSA Maintainers +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian ALSA Maintainers Uploaders: Jordi Mallach , Elimar Riesebieter , Luke Yelavich @@ -9,7 +10,7 @@ python3-dev:native, libpython3-dev Build-Depends-Indep: doxygen, graphviz -Standards-Version: 4.5.0 +Standards-Version: 4.6.0 Homepage: https://www.alsa-project.org/ Vcs-Git: https://salsa.debian.org/alsa-team/alsa-lib.git Vcs-Browser: https://salsa.debian.org/alsa-team/alsa-lib diff -Nru alsa-lib-1.2.2/debian/copyright alsa-lib-1.2.6.1/debian/copyright --- alsa-lib-1.2.2/debian/copyright 2019-10-11 11:02:05.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/copyright 2022-01-11 23:14:00.000000000 +0000 @@ -8,13 +8,13 @@ the pkg-alsa project at alioth.debian.org. Files: * -Copyright: 1998-2017 Jarsolav Kysela and others +Copyright: 1998-2022 Jarsolav Kysela and others License: LPGL-2.1+ Files: debian/* Copyright: 1998-1999 Wichert Akkerman 1999-2002 Masato Taruishi - 2002-2017 Debian ALSA Maintainers + 2002-2022 Debian ALSA Maintainers License: LPGL-2.1+ License: LPGL-2.1+ diff -Nru alsa-lib-1.2.2/debian/libasound2-udeb.install alsa-lib-1.2.6.1/debian/libasound2-udeb.install --- alsa-lib-1.2.2/debian/libasound2-udeb.install 2020-01-16 09:50:31.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/libasound2-udeb.install 2022-01-11 23:10:46.000000000 +0000 @@ -2,3 +2,4 @@ usr/share/alsa/alsa.conf usr/share/alsa/cards usr/share/alsa/pcm +usr/share/alsa/ctl diff -Nru alsa-lib-1.2.2/debian/libasound2.symbols alsa-lib-1.2.6.1/debian/libasound2.symbols --- alsa-lib-1.2.2/debian/libasound2.symbols 2020-01-16 10:19:48.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/libasound2.symbols 2022-01-11 23:10:46.000000000 +0000 @@ -8,7 +8,9 @@ ALSA_0.9.7@ALSA_0.9.7 1.0.16 ALSA_0.9@ALSA_0.9 1.0.16 ALSA_1.1.6@ALSA_1.1.6 1.1.6 + __snd_ctl_empty_open_dlsym_control_001@ALSA_0.9 1.2.5 __snd_ctl_hw_open_dlsym_control_001@ALSA_0.9 1.0.16 + __snd_ctl_remap_open_dlsym_control_001@ALSA_0.9 1.2.5 __snd_ctl_shm_open_dlsym_control_001@ALSA_0.9 1.0.16 __snd_hwdep_hw_open_dlsym_hwdep_001@ALSA_0.9 1.0.16 __snd_pcm_adpcm_open_dlsym_pcm_001@ALSA_0.9 1.0.16 @@ -110,8 +112,10 @@ __snd_timer_query_hw_open_dlsym_timer_query_001@ALSA_0.9 1.0.16 _snd_config_hook_load_dlsym_config_hook_001@ALSA_0.9 1.0.16 _snd_config_hook_load_for_all_cards_dlsym_config_hook_001@ALSA_0.9 1.0.16 + _snd_ctl_empty_open@ALSA_0.9 1.2.5 _snd_ctl_hw_open@ALSA_0.9 1.0.16 _snd_ctl_poll_descriptor@ALSA_0.9 1.0.16 + _snd_ctl_remap_open@ALSA_0.9 1.2.5 _snd_ctl_shm_open@ALSA_0.9 1.0.16 _snd_func_card_driver_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 _snd_func_card_id_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 @@ -126,6 +130,7 @@ _snd_func_pcm_args_by_class_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 _snd_func_pcm_id_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 _snd_func_private_card_driver_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 + _snd_func_private_integer_dlsym_config_evaluate_001@ALSA_0.9 1.2.5 _snd_func_private_pcm_subdevice_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 _snd_func_private_string_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 _snd_func_refer_dlsym_config_evaluate_001@ALSA_0.9 1.0.16 @@ -189,10 +194,13 @@ snd_config_delete@ALSA_0.9 1.0.16 snd_config_delete_compound_members@ALSA_0.9 1.0.16 snd_config_evaluate@ALSA_0.9 1.0.16 + snd_config_evaluate_string@ALSA_0.9 1.2.6.1 snd_config_expand@ALSA_0.9 1.0.16 + snd_config_expand_custom@ALSA_0.9 1.2.6.1 snd_config_get_ascii@ALSA_0.9 1.0.16 snd_config_get_bool@ALSA_0.9 1.0.16 snd_config_get_bool_ascii@ALSA_0.9 1.0.16 + snd_config_get_card@ALSA_0.9 1.2.5 snd_config_get_ctl_iface@ALSA_0.9 1.0.16 snd_config_get_ctl_iface_ascii@ALSA_0.9 1.0.16 snd_config_get_id@ALSA_0.9 1.0.16 @@ -212,19 +220,24 @@ snd_config_imake_real@ALSA_0.9 1.0.16 snd_config_imake_safe_string@ALSA_0.9 1.1.0 snd_config_imake_string@ALSA_0.9 1.0.16 + snd_config_is_array@ALSA_0.9 1.2.3 + snd_config_is_empty@ALSA_0.9 1.2.5 snd_config_iterator_end@ALSA_0.9 1.0.16 snd_config_iterator_entry@ALSA_0.9 1.0.16 snd_config_iterator_first@ALSA_0.9 1.0.16 snd_config_iterator_next@ALSA_0.9 1.0.16 snd_config_load@ALSA_0.9 1.0.16 snd_config_load_override@ALSA_0.9 1.0.16 + snd_config_load_string@ALSA_0.9 1.2.6.1 snd_config_make@ALSA_0.9 1.0.16 snd_config_make_compound@ALSA_0.9 1.0.16 snd_config_make_integer64@ALSA_0.9 1.0.16 snd_config_make_integer@ALSA_0.9 1.0.16 + snd_config_make_path@ALSA_0.9 1.2.5 snd_config_make_pointer@ALSA_0.9 1.0.16 snd_config_make_real@ALSA_0.9 1.0.16 snd_config_make_string@ALSA_0.9 1.0.16 + snd_config_merge@ALSA_0.9 1.2.5 snd_config_ref@ALSA_0.9 1.1.2 snd_config_remove@ALSA_0.9 1.0.16 snd_config_save@ALSA_0.9 1.0.16 @@ -285,6 +298,8 @@ snd_ctl_elem_add_integer64@ALSA_0.9 1.0.16 snd_ctl_elem_add_integer@ALSA_0.9 1.0.16 snd_ctl_elem_id_clear@ALSA_0.9 1.0.16 + snd_ctl_elem_id_compare_numid@ALSA_0.9 1.2.5 + snd_ctl_elem_id_compare_set@ALSA_0.9 1.2.5 snd_ctl_elem_id_copy@ALSA_0.9 1.0.16 snd_ctl_elem_id_free@ALSA_0.9 1.0.16 snd_ctl_elem_id_get_device@ALSA_0.9 1.0.16 @@ -341,12 +356,15 @@ snd_ctl_elem_info_set_device@ALSA_0.9 1.0.16 snd_ctl_elem_info_set_dimension@ALSA_0.9 1.1.2 snd_ctl_elem_info_set_id@ALSA_0.9 1.0.16 + snd_ctl_elem_info_set_inactive@ALSA_0.9 1.2.5 snd_ctl_elem_info_set_index@ALSA_0.9 1.0.16 snd_ctl_elem_info_set_interface@ALSA_0.9 1.0.16 snd_ctl_elem_info_set_item@ALSA_0.9 1.0.16 snd_ctl_elem_info_set_name@ALSA_0.9 1.0.16 snd_ctl_elem_info_set_numid@ALSA_0.9 1.0.16 + snd_ctl_elem_info_set_read_write@ALSA_0.9 1.2.5 snd_ctl_elem_info_set_subdevice@ALSA_0.9 1.0.16 + snd_ctl_elem_info_set_tlv_read_write@ALSA_0.9 1.2.5 snd_ctl_elem_info_sizeof@ALSA_0.9 1.0.16 snd_ctl_elem_list@ALSA_0.9 1.0.16 snd_ctl_elem_list_alloc_space@ALSA_0.9 1.0.16 @@ -446,6 +464,7 @@ snd_ctl_rawmidi_next_device@ALSA_0.9 1.0.16 snd_ctl_rawmidi_prefer_subdevice@ALSA_0.9 1.0.16 snd_ctl_read@ALSA_0.9 1.0.16 + snd_ctl_remap_open@ALSA_0.9 1.2.5 snd_ctl_set_power_state@ALSA_0.9 1.0.16 snd_ctl_shm_open@ALSA_0.9 1.0.16 snd_ctl_subscribe_events@ALSA_0.9 1.0.16 @@ -458,6 +477,7 @@ snd_dlclose@ALSA_0.9 1.0.16 snd_dlopen@ALSA_0.9 1.0.16 snd_dlopen@ALSA_1.1.6 1.1.6 + snd_dlpath@ALSA_0.9 1.2.3 snd_dlsym@ALSA_0.9 1.0.16 snd_err_msg@ALSA_0.9 1.0.16 snd_func_card_driver@ALSA_0.9 1.0.16 @@ -473,6 +493,7 @@ snd_func_pcm_args_by_class@ALSA_0.9 1.0.16 snd_func_pcm_id@ALSA_0.9 1.0.16 snd_func_private_card_driver@ALSA_0.9 1.0.16 + snd_func_private_integer@ALSA_0.9 1.2.5 snd_func_private_pcm_subdevice@ALSA_0.9 1.0.16 snd_func_private_string@ALSA_0.9 1.0.16 snd_func_refer@ALSA_0.9 1.0.16 @@ -749,6 +770,7 @@ snd_names_list@ALSA_0.9 1.0.16 snd_names_list_free@ALSA_0.9 1.0.16 snd_output_buffer_open@ALSA_0.9 1.0.16 + snd_output_buffer_steal@ALSA_0.9 1.2.5 snd_output_buffer_string@ALSA_0.9 1.0.16 snd_output_close@ALSA_0.9 1.0.16 snd_output_flush@ALSA_0.9 1.0.16 @@ -1125,7 +1147,7 @@ snd_pcm_open@ALSA_0.9 1.0.16 snd_pcm_open_fallback@ALSA_0.9 1.0.25 snd_pcm_open_lconf@ALSA_0.9 1.0.16 - snd_pcm_parse_control_id@ALSA_0.9 1.0.16 + snd_pcm_parse_control_id@ALSA_0.9 1.2.6.1 snd_pcm_pause@ALSA_0.9 1.0.16 snd_pcm_plug_open@ALSA_0.9 1.0.16 snd_pcm_poll_descriptors@ALSA_0.9 1.0.16 @@ -1278,11 +1300,15 @@ snd_rawmidi_params_free@ALSA_0.9 1.0.16 snd_rawmidi_params_get_avail_min@ALSA_0.9 1.0.16 snd_rawmidi_params_get_buffer_size@ALSA_0.9 1.0.16 + snd_rawmidi_params_get_clock_type@ALSA_0.9 1.2.6.1 snd_rawmidi_params_get_no_active_sensing@ALSA_0.9 1.0.16 + snd_rawmidi_params_get_read_mode@ALSA_0.9 1.2.6.1 snd_rawmidi_params_malloc@ALSA_0.9 1.0.16 snd_rawmidi_params_set_avail_min@ALSA_0.9 1.0.16 snd_rawmidi_params_set_buffer_size@ALSA_0.9 1.0.16 + snd_rawmidi_params_set_clock_type@ALSA_0.9 1.2.6.1 snd_rawmidi_params_set_no_active_sensing@ALSA_0.9 1.0.16 + snd_rawmidi_params_set_read_mode@ALSA_0.9 1.2.6.1 snd_rawmidi_params_sizeof@ALSA_0.9 1.0.16 snd_rawmidi_poll_descriptors@ALSA_0.9 1.0.16 snd_rawmidi_poll_descriptors_count@ALSA_0.9 1.0.16 @@ -1297,6 +1323,7 @@ snd_rawmidi_status_malloc@ALSA_0.9 1.0.16 snd_rawmidi_status_sizeof@ALSA_0.9 1.0.16 snd_rawmidi_stream@ALSA_0.9 1.0.16 + snd_rawmidi_tread@ALSA_0.9 1.2.6.1 snd_rawmidi_type@ALSA_0.9 1.0.16 snd_rawmidi_virtual_open@ALSA_0.9 1.0.16 snd_rawmidi_write@ALSA_0.9 1.0.16 diff -Nru alsa-lib-1.2.2/debian/patches/python3.8.diff alsa-lib-1.2.6.1/debian/patches/python3.8.diff --- alsa-lib-1.2.2/debian/patches/python3.8.diff 2020-03-04 08:23:20.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/patches/python3.8.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -# Description: fixes the build with python 3.8 -# Upstream: https://github.com/alsa-project/alsa-lib/issues/33 -# Author: Matthias Klose -Index: b/configure.ac -=================================================================== ---- a/configure.ac -+++ b/configure.ac -@@ -423,7 +423,7 @@ if test "$build_python" = "yes" -a "$bui - pythonlibs0= - pythoninc0= - if test "$build_python2" != "yes"; then -- pythonlibs0=$(python3-config --libs) -+ pythonlibs0=$(python3-config --libs --embed) - pythoninc0=$(python3-config --includes) - fi - if test -z "$pythonlibs0"; then diff -Nru alsa-lib-1.2.2/debian/patches/series alsa-lib-1.2.6.1/debian/patches/series --- alsa-lib-1.2.2/debian/patches/series 2020-03-04 08:23:20.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/patches/series 2024-04-16 21:53:33.000000000 +0000 @@ -1,2 +1 @@ 0001-Enabled-extended-namehints-in-alsa.conf.patch -python3.8.diff diff -Nru alsa-lib-1.2.2/debian/rules alsa-lib-1.2.6.1/debian/rules --- alsa-lib-1.2.2/debian/rules 2020-02-29 15:51:16.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/rules 2022-01-14 16:15:05.000000000 +0000 @@ -3,6 +3,11 @@ DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) export DEB_BUILD_MAINT_OPTIONS = hardening=+all +ifneq (,$(findstring -flto, $(shell dpkg-buildflags --get CFLAGS))) + export DEB_CFLAGS_MAINT_APPEND = -flto-partition=none + export DEB_CXXFLAGS_MAINT_APPEND = -flto-partition=none + export DEB_LDFLAGS_MAINT_APPEND = -flto-partition=none +endif %: dh $@ @@ -38,3 +43,17 @@ override_dh_makeshlibs: dh_makeshlibs -- -c4 + +override_dh_auto_clean: + rm -rf doc/doxygen + rm -f src/control/ctl_symbols_list.c \ + src/control/libcontrol.la \ + src/hwdep/libhwdep.la \ + src/mixer/libmixer.la \ + src/pcm/libpcm.la \ + src/pcm/pcm_symbols_list.c \ + src/rawmidi/librawmidi.la \ + src/seq/libseq.la \ + src/timer/libtimer.la \ + src/ucm/libucm.la + dh_auto_clean diff -Nru alsa-lib-1.2.2/debian/upstream/metadata alsa-lib-1.2.6.1/debian/upstream/metadata --- alsa-lib-1.2.2/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/upstream/metadata 2021-08-17 22:29:44.000000000 +0000 @@ -0,0 +1,5 @@ +--- +Bug-Database: https://github.com/alsa-project/alsa-lib/issues +Bug-Submit: https://github.com/alsa-project/alsa-lib/issues/new +Repository: git://git.alsa-project.org/alsa-lib.git +Repository-Browse: https://git.alsa-project.org/?p=alsa-lib.git diff -Nru alsa-lib-1.2.2/debian/upstream/signing-key.asc alsa-lib-1.2.6.1/debian/upstream/signing-key.asc --- alsa-lib-1.2.2/debian/upstream/signing-key.asc 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/upstream/signing-key.asc 2021-09-09 10:02:16.000000000 +0000 @@ -0,0 +1,29 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBF+PBUIBEACQ0eLhY6zJkZCztI3Zj7GiEkGyDYXFr10wMn2BmAxXEOhvGs5C +XtZYokNnYnojU2ehb14pdQiIYVm7YwjNjPhFmc6DnSJ74tlDtzI0ZfZYgU8B3/AU +hHCIe7NeH+uqaS4hQ4gQF3HB19GNQ21keQPtm/cZzNvkAwWQ6wxiIas/odQGt6sg +TULub4gXQw42iCW0jNeliNgeOc/4/2qtznN+Ss5G+Hs+EBh0kFCG4+5RcpgtKmYO +LEy+8qI/UmlgOeT+47eTXm4LjxHfRuKcbvSExfICUB5XLD2ZmeZbFyP48jkDvB7w +A4jNfImtlRfTvoviNlOl08Pk3aJoPR8IZIGw+EQCf3ChXTziadqOg37SAy4yGJ14 +BjirKgBNO4zB+lkLOAknsVKJOKRt3w54MevCuj0GGbfSyErZAeHvliXrfBa1ACLu +c2ynRDR/5j0FA3vPzRsHyOGFC4F34W5BhkXCuJBG2o2VDv/zANzjg/hw+0IbmH3G +kYn73EDIaz5giKJolE32WaWkz39DwuuoUaLLyfxyiN+c0p2/XQFCrOSqHNUMfI5z +Ll62nL1XruF6u9ApLKRv/uDJN7tLgds83rteYJP7/5/JSYCPB8PcUkmhB8OoVdpr +tYI8C/6Y6KREm3Jk4AdkySIFnPXZQCkiOR2qOmAuaQPeesjsrSkUtJYZcwARAQAB +tEVBTFNBIFJlbGVhc2UgVGVhbSAoUGFja2FnZSBTaWduaW5nIEtleSB2MSkgPHJl +bGVhc2VAYWxzYS1wcm9qZWN0Lm9yZz6JAk4EEwEIADgWIQTwTfUHN6waiExLPXGD +gFltpuWckQUCX48FQgIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCDgFlt +puWckRhVD/45DNoD79lHBJEu0Wv93OSlPZDUKR+BnkkdEDgDEhTvk+Bin/EWdb3g +Unc+rEczgAqjqblLH7ndBtFZPNGiQ2nu8eAdN0TrOFQx7bFg4Do7KXNUUqdPUETC +AgkozF3ucsgOcBbv6LwwDfdQDFjkg7CuNGy8CRcLSO5dLK7domq3RxF5mKl1dkOv +4brfw6ssn+pja/jkgM9BKt80ycIDiJtVRuXMBJD19ZB8EzQO9OQBdAPERSG8BwLX +LemTLAZqNd0mixVbn50qCQil64UOkOd5DHVBBokxgtlJEcDujqWaCUYSygSiP2ke +44ArDr7hRwG5KhhbFOigXo3nBYnKyiuWs0jpnrTQD9+uTxcetyBpJ6BW/i/Fy2a9 +NBFNhbDI07rCMXM25cLP8qbI5i6nTSDlkpLP40At9yUVYt8gIO6ZpGpO04/lAhT1 +NxtbZkKMo1nphMHU/MDSusoHyLxUXpppdbXTdMgsuFrXae3gcFDowQbLZqb6ZAJ5 +opX4sDBIdoHDBsI6wCGTV7AAWB8RXWW0RTYMkyIxqixiiB2N2jaau4yHfqv9QWOY +oPcx6ySKqGN0HV50PeRis5eeq7kHIdRrVIOBd0tPIqCKIvyJZk1QXReKvvE84Km2 +dYPusBlhhF7/4WVovlE6HFwyLSOyb32m4qf5dV35op07ew8i4DHpkQ== +=n4lM +-----END PGP PUBLIC KEY BLOCK----- diff -Nru alsa-lib-1.2.2/debian/watch alsa-lib-1.2.6.1/debian/watch --- alsa-lib-1.2.2/debian/watch 2019-11-22 20:20:12.000000000 +0000 +++ alsa-lib-1.2.6.1/debian/watch 2021-09-09 10:05:53.000000000 +0000 @@ -1,2 +1,3 @@ version=4 +opts="pgpsigurlmangle=s/$/.sig/" \ ftp://ftp.alsa-project.org/pub/lib/alsa-lib-(.+)\.tar\.bz2 diff -Nru alsa-lib-1.2.2/depcomp alsa-lib-1.2.6.1/depcomp --- alsa-lib-1.2.2/depcomp 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/depcomp 2021-12-09 14:53:51.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff -Nru alsa-lib-1.2.2/doc/Makefile.in alsa-lib-1.2.6.1/doc/Makefile.in --- alsa-lib-1.2.2/doc/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/doc/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -305,6 +305,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/doc/doxygen.cfg alsa-lib-1.2.6.1/doc/doxygen.cfg --- alsa-lib-1.2.2/doc/doxygen.cfg 2020-02-19 10:25:37.000000000 +0000 +++ alsa-lib-1.2.6.1/doc/doxygen.cfg 2021-12-09 14:54:02.000000000 +0000 @@ -40,7 +40,16 @@ ../src/names.c \ ../src/shmarea.c \ ../src/userfile.c \ - ../src/control \ + ../src/control/cards.c \ + ../src/control/control.c \ + ../src/control/control_plugin.c \ + ../src/control/control_hw.c \ + ../src/control/control_remap.c \ + ../src/control/control_shm.c \ + ../src/control/ctlparse.c \ + ../src/control/hcontrol.c \ + ../src/control/setup.c \ + ../src/control/tlv.c \ ../src/mixer \ ../src/pcm/pcm.c \ ../src/pcm/pcm_mmap.c \ diff -Nru alsa-lib-1.2.2/doc/doxygen.cfg.in alsa-lib-1.2.6.1/doc/doxygen.cfg.in --- alsa-lib-1.2.2/doc/doxygen.cfg.in 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/doc/doxygen.cfg.in 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,16 @@ @top_srcdir@/src/names.c \ @top_srcdir@/src/shmarea.c \ @top_srcdir@/src/userfile.c \ - @top_srcdir@/src/control \ + @top_srcdir@/src/control/cards.c \ + @top_srcdir@/src/control/control.c \ + @top_srcdir@/src/control/control_plugin.c \ + @top_srcdir@/src/control/control_hw.c \ + @top_srcdir@/src/control/control_remap.c \ + @top_srcdir@/src/control/control_shm.c \ + @top_srcdir@/src/control/ctlparse.c \ + @top_srcdir@/src/control/hcontrol.c \ + @top_srcdir@/src/control/setup.c \ + @top_srcdir@/src/control/tlv.c \ @top_srcdir@/src/mixer \ @top_srcdir@/src/pcm/pcm.c \ @top_srcdir@/src/pcm/pcm_mmap.c \ diff -Nru alsa-lib-1.2.2/doc/index.doxygen alsa-lib-1.2.6.1/doc/index.doxygen --- alsa-lib-1.2.2/doc/index.doxygen 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/doc/index.doxygen 2021-12-09 13:17:59.000000000 +0000 @@ -1,56 +1,60 @@ -/*! \mainpage Index Preamble and License +/*! \mainpage Index, Preamble and License \author Jaroslav Kysela \author Abramo Bagnara \author Takashi Iwai \author Frank van de Pol -

Preface

-

The Advanced Linux Sound Architecture (\e ALSA) comes with a kernel +Preface +------- + +The Advanced Linux Sound Architecture (\e ALSA) comes with a kernel API and a library API. This document describes the library API and how -it interfaces with the kernel API.

+it interfaces with the kernel API. -

Documentation License

+### Documentation License -

This documentation is free; you can redistribute it without -any restrictions. Modifications or derived work must retain -the copyright and list all authors.

+ This documentation is free; you can redistribute it without + any restrictions. Modifications or derived work must retain + the copyright and list all authors. -

This documentation 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.

+ This documentation 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. + +API usage +--------- -

API usage

-

Application programmers should use the library API rather than the +Application programmers should use the library API rather than the kernel API. The library offers 100% of the functionality of the kernel API, but adds major improvements in usability, making the application code simpler and better looking. In addition, future fixes or compatibility code -may be placed in the library code instead of the kernel driver.

+may be placed in the library code instead of the kernel driver. -

API links

+API links +--------- -
    -
  • Page \ref control explains the primitive controls API. -
  • Page \ref hcontrol explains the high-level primitive controls API. -
  • Page \ref mixer explains the mixer controls API. -
  • Page \ref pcm explains the design of the PCM (digital audio) API. -
  • Page \ref pcm_plugins explains the design of PCM (digital audio) plugins. -
  • Page \ref pcm_external_plugins explains the external PCM plugin SDK. -
  • Page \ref ctl_external_plugins explains the external control plugin SDK. -
  • Page \ref rawmidi explains the design of the RawMidi API. -
  • Page \ref timer explains the design of the Timer API. -
  • Page \ref seq explains the design of the Sequencer API. -
  • Page \ref ucm explains the use case API. -
  • Page \ref topology explains the DSP topology API. -
- -

Configuration

- -
    -
  • Page \ref conf explains the syntax of library configuration files. -
  • Page \ref confarg explains the run-time argument syntax. -
  • Page \ref conffunc explains run-time function definitions and their usage. -
  • Page \ref confhooks explains run-time hook definitions and their usage. -
+- Page \subpage control explains the primitive controls API. +- Page \subpage control_plugins explains the design of primitive control plugins. +- Page \subpage hcontrol explains the high-level primitive controls API. +- Page \subpage mixer explains the mixer controls API. +- Page \subpage pcm explains the design of the PCM (digital audio) API. +- Page \subpage pcm_plugins explains the design of PCM (digital audio) plugins. +- Page \subpage pcm_external_plugins explains the external PCM plugin SDK. +- Page \subpage ctl_external_plugins explains the external control plugin SDK. +- Page \subpage rawmidi explains the design of the RawMidi API. +- Page \subpage timer explains the design of the Timer API. +- Page \subpage seq explains the design of the Sequencer API. +- Page \subpage ucm explains the use case API. +- Page \subpage topology explains the DSP topology API. + +Configuration +------------- + +- Page \subpage conf explains the syntax of library configuration. +- Page \subpage confarg explains the run-time argument syntax. +- Page \subpage conffunc explains run-time function definitions and their usage. +- Page \subpage confhooks explains run-time hook definitions and their usage. +- Page \subpage ucm_conf explains the UCM configuration and their usage. */ diff -Nru alsa-lib-1.2.2/doc/pictures/Makefile.in alsa-lib-1.2.6.1/doc/pictures/Makefile.in --- alsa-lib-1.2.2/doc/pictures/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/doc/pictures/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -245,6 +245,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/include/Makefile.am alsa-lib-1.2.6.1/include/Makefile.am --- alsa-lib-1.2.2/include/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -7,6 +7,9 @@ version.h global.h input.h output.h error.h \ conf.h control.h +if BUILD_CTL_PLUGIN +alsainclude_HEADERS += control_plugin.h +endif if BUILD_CTL_PLUGIN_EXT alsainclude_HEADERS += control_external.h endif @@ -63,11 +66,18 @@ DISTCLEANFILES = stamp-vh version.h alsa asoundlib.h -alsa: - ln -s $(top_srcdir)/include alsa - -version.h: stamp-vh alsa - @: +.DUMMY: alsa_link +alsa_link: + if ! test -r alsa/local.h; then \ + ln -s $(top_srcdir)/include alsa; \ + fi + +version.h: stamp-vh alsa_link + for f in asoundlib.h version.h; do \ + if ! test -r $(top_srcdir)/include/$$f; then \ + ln -s $(abs_top_builddir)/include/$$f $(top_srcdir)/include/$$f; \ + fi; \ + done stamp-vh: $(top_builddir)/configure.ac @echo "/*" > ver.tmp @@ -79,9 +89,8 @@ @echo "#define SND_LIB_SUBMINOR $(SND_LIB_SUBMINOR) /**< subminor number of library version */" >> ver.tmp @echo "#define SND_LIB_EXTRAVER $(SND_LIB_EXTRAVER) /**< extra version number, used mainly for betas */" >> ver.tmp @echo "/** library version */" >> ver.tmp - @echo "#define SND_LIB_VERSION ((SND_LIB_MAJOR<<16)|\\" >> ver.tmp - @echo " (SND_LIB_MINOR<<8)|\\" >> ver.tmp - @echo " SND_LIB_SUBMINOR)" >> ver.tmp + @echo "#define SND_LIB_VER(maj, min, sub) (((maj)<<16)|((min)<<8)|(sub))" >> ver.tmp + @echo "#define SND_LIB_VERSION SND_LIB_VER(SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR)" >> ver.tmp @echo "/** library version (string) */" >> ver.tmp @echo "#define SND_LIB_VERSION_STR \"$(SND_LIB_VERSION)\"" >> ver.tmp @echo >> ver.tmp diff -Nru alsa-lib-1.2.2/include/Makefile.in alsa-lib-1.2.6.1/include/Makefile.in --- alsa-lib-1.2.2/include/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/include/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -88,20 +88,21 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -@BUILD_CTL_PLUGIN_EXT_TRUE@am__append_1 = control_external.h -@BUILD_PCM_TRUE@am__append_2 = pcm.h pcm_old.h timer.h -@BUILD_PCM_PLUGIN_TRUE@@BUILD_PCM_TRUE@am__append_3 = pcm_plugin.h -@BUILD_PCM_PLUGIN_RATE_TRUE@@BUILD_PCM_TRUE@am__append_4 = pcm_rate.h -@BUILD_PCM_PLUGIN_EXTPLUG_TRUE@@BUILD_PCM_TRUE@am__append_5 = pcm_external.h pcm_extplug.h -@BUILD_PCM_PLUGIN_EXTPLUG_FALSE@@BUILD_PCM_PLUGIN_IOPLUG_TRUE@@BUILD_PCM_TRUE@am__append_6 = pcm_external.h -@BUILD_PCM_PLUGIN_IOPLUG_TRUE@@BUILD_PCM_TRUE@am__append_7 = pcm_ioplug.h -@BUILD_RAWMIDI_TRUE@am__append_8 = rawmidi.h -@BUILD_HWDEP_TRUE@am__append_9 = hwdep.h -@BUILD_MIXER_TRUE@am__append_10 = mixer.h mixer_abst.h -@BUILD_SEQ_TRUE@am__append_11 = seq_event.h seq.h seqmid.h seq_midi_event.h -@BUILD_UCM_TRUE@am__append_12 = use-case.h -@BUILD_TOPOLOGY_TRUE@am__append_13 = topology.h -@BUILD_ALISP_TRUE@am__append_14 = alisp.h +@BUILD_CTL_PLUGIN_TRUE@am__append_1 = control_plugin.h +@BUILD_CTL_PLUGIN_EXT_TRUE@am__append_2 = control_external.h +@BUILD_PCM_TRUE@am__append_3 = pcm.h pcm_old.h timer.h +@BUILD_PCM_PLUGIN_TRUE@@BUILD_PCM_TRUE@am__append_4 = pcm_plugin.h +@BUILD_PCM_PLUGIN_RATE_TRUE@@BUILD_PCM_TRUE@am__append_5 = pcm_rate.h +@BUILD_PCM_PLUGIN_EXTPLUG_TRUE@@BUILD_PCM_TRUE@am__append_6 = pcm_external.h pcm_extplug.h +@BUILD_PCM_PLUGIN_EXTPLUG_FALSE@@BUILD_PCM_PLUGIN_IOPLUG_TRUE@@BUILD_PCM_TRUE@am__append_7 = pcm_external.h +@BUILD_PCM_PLUGIN_IOPLUG_TRUE@@BUILD_PCM_TRUE@am__append_8 = pcm_ioplug.h +@BUILD_RAWMIDI_TRUE@am__append_9 = rawmidi.h +@BUILD_HWDEP_TRUE@am__append_10 = hwdep.h +@BUILD_MIXER_TRUE@am__append_11 = mixer.h mixer_abst.h +@BUILD_SEQ_TRUE@am__append_12 = seq_event.h seq.h seqmid.h seq_midi_event.h +@BUILD_UCM_TRUE@am__append_13 = use-case.h +@BUILD_TOPOLOGY_TRUE@am__append_14 = topology.h +@BUILD_ALISP_TRUE@am__append_15 = alisp.h subdir = include ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ @@ -146,10 +147,11 @@ esac am__alsainclude_HEADERS_DIST = asoundlib.h asoundef.h version.h \ global.h input.h output.h error.h conf.h control.h \ - control_external.h pcm.h pcm_old.h timer.h pcm_plugin.h \ - pcm_rate.h pcm_external.h pcm_extplug.h pcm_ioplug.h rawmidi.h \ - hwdep.h mixer.h mixer_abst.h seq_event.h seq.h seqmid.h \ - seq_midi_event.h use-case.h topology.h alisp.h + control_plugin.h control_external.h pcm.h pcm_old.h timer.h \ + pcm_plugin.h pcm_rate.h pcm_external.h pcm_extplug.h \ + pcm_ioplug.h rawmidi.h hwdep.h mixer.h mixer_abst.h \ + seq_event.h seq.h seqmid.h seq_midi_event.h use-case.h \ + topology.h alisp.h am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -187,8 +189,8 @@ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ - $(LISP)config.h.in +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. @@ -357,6 +359,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -374,7 +377,7 @@ $(am__append_5) $(am__append_6) $(am__append_7) \ $(am__append_8) $(am__append_9) $(am__append_10) \ $(am__append_11) $(am__append_12) $(am__append_13) \ - $(am__append_14) + $(am__append_14) $(am__append_15) noinst_HEADERS = alsa sys.h search.h list.h aserver.h local.h alsa-symbols.h \ asoundlib-head.h asoundlib-tail.h bswap.h type_compat.h @@ -740,11 +743,18 @@ .PRECIOUS: Makefile -alsa: - ln -s $(top_srcdir)/include alsa +.DUMMY: alsa_link +alsa_link: + if ! test -r alsa/local.h; then \ + ln -s $(top_srcdir)/include alsa; \ + fi -version.h: stamp-vh alsa - @: +version.h: stamp-vh alsa_link + for f in asoundlib.h version.h; do \ + if ! test -r $(top_srcdir)/include/$$f; then \ + ln -s $(abs_top_builddir)/include/$$f $(top_srcdir)/include/$$f; \ + fi; \ + done stamp-vh: $(top_builddir)/configure.ac @echo "/*" > ver.tmp @@ -756,9 +766,8 @@ @echo "#define SND_LIB_SUBMINOR $(SND_LIB_SUBMINOR) /**< subminor number of library version */" >> ver.tmp @echo "#define SND_LIB_EXTRAVER $(SND_LIB_EXTRAVER) /**< extra version number, used mainly for betas */" >> ver.tmp @echo "/** library version */" >> ver.tmp - @echo "#define SND_LIB_VERSION ((SND_LIB_MAJOR<<16)|\\" >> ver.tmp - @echo " (SND_LIB_MINOR<<8)|\\" >> ver.tmp - @echo " SND_LIB_SUBMINOR)" >> ver.tmp + @echo "#define SND_LIB_VER(maj, min, sub) (((maj)<<16)|((min)<<8)|(sub))" >> ver.tmp + @echo "#define SND_LIB_VERSION SND_LIB_VER(SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR)" >> ver.tmp @echo "/** library version (string) */" >> ver.tmp @echo "#define SND_LIB_VERSION_STR \"$(SND_LIB_VERSION)\"" >> ver.tmp @echo >> ver.tmp diff -Nru alsa-lib-1.2.2/include/alsa-symbols.h alsa-lib-1.2.6.1/include/alsa-symbols.h --- alsa-lib-1.2.2/include/alsa-symbols.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/alsa-symbols.h 2021-12-09 13:17:59.000000000 +0000 @@ -34,7 +34,11 @@ #define default_symbol_version(real, name, version) \ __asm__ (".symver " ASM_NAME(#real) "," ASM_NAME(#name) "@@" #version) +#ifdef __clang__ +#define EXPORT_SYMBOL __attribute__((visibility("default"))) +#else #define EXPORT_SYMBOL __attribute__((visibility("default"),externally_visible)) +#endif #ifdef USE_VERSIONED_SYMBOLS #define use_symbol_version(real, name, version) \ diff -Nru alsa-lib-1.2.2/include/conf.h alsa-lib-1.2.6.1/include/conf.h --- alsa-lib-1.2.2/include/conf.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/conf.h 2021-12-09 13:17:59.000000000 +0000 @@ -89,6 +89,7 @@ int snd_config_top(snd_config_t **config); int snd_config_load(snd_config_t *config, snd_input_t *in); +int snd_config_load_string(snd_config_t **config, const char *s, size_t size); int snd_config_load_override(snd_config_t *config, snd_input_t *in); int snd_config_save(snd_config_t *config, snd_output_t *out); int snd_config_update(void); @@ -108,11 +109,18 @@ const char *base, const char *key, snd_config_t **result); +typedef int (*snd_config_expand_fcn_t)(snd_config_t **dst, const char *s, void *private_data); + +int snd_config_expand_custom(snd_config_t *config, snd_config_t *root, + snd_config_expand_fcn_t fcn, void *private_data, + snd_config_t **result); int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args, snd_config_t *private_data, snd_config_t **result); int snd_config_evaluate(snd_config_t *config, snd_config_t *root, snd_config_t *private_data, snd_config_t **result); +int snd_config_evaluate_string(snd_config_t **dst, const char *s, + snd_config_expand_fcn_t fcn, void *private_data); int snd_config_add(snd_config_t *config, snd_config_t *child); int snd_config_add_before(snd_config_t *before, snd_config_t *child); @@ -121,6 +129,7 @@ int snd_config_delete(snd_config_t *config); int snd_config_delete_compound_members(const snd_config_t *config); int snd_config_copy(snd_config_t **dst, snd_config_t *src); +int snd_config_merge(snd_config_t *dst, snd_config_t *src, int override); int snd_config_make(snd_config_t **config, const char *key, snd_config_type_t type); @@ -130,6 +139,8 @@ int snd_config_make_string(snd_config_t **config, const char *key); int snd_config_make_pointer(snd_config_t **config, const char *key); int snd_config_make_compound(snd_config_t **config, const char *key, int join); +int snd_config_make_path(snd_config_t **config, snd_config_t *root, const char *key, + int join, int override); int snd_config_imake_integer(snd_config_t **config, const char *key, const long value); int snd_config_imake_integer64(snd_config_t **config, const char *key, const long long value); @@ -139,6 +150,8 @@ int snd_config_imake_pointer(snd_config_t **config, const char *key, const void *ptr); snd_config_type_t snd_config_get_type(const snd_config_t *config); +int snd_config_is_array(const snd_config_t *config); +int snd_config_is_empty(const snd_config_t *config); int snd_config_set_id(snd_config_t *config, const char *id); int snd_config_set_integer(snd_config_t *config, long value); @@ -186,6 +199,7 @@ int snd_config_get_bool_ascii(const char *ascii); int snd_config_get_bool(const snd_config_t *conf); +int snd_config_get_card(const snd_config_t *conf); int snd_config_get_ctl_iface_ascii(const char *ascii); int snd_config_get_ctl_iface(const snd_config_t *conf); diff -Nru alsa-lib-1.2.2/include/config.h.in alsa-lib-1.2.6.1/include/config.h.in --- alsa-lib-1.2.2/include/config.h.in 2020-02-19 10:25:23.000000000 +0000 +++ alsa-lib-1.2.6.1/include/config.h.in 2021-12-09 14:53:50.000000000 +0000 @@ -66,6 +66,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Define to 1 if you have the `eaccess' function. */ +#undef HAVE_EACCESS + /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H @@ -87,6 +90,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* MMX technology is enabled */ +#undef HAVE_MMX + /* Define if your pthreads implementation have PTHREAD_MUTEX_RECURSIVE */ #undef HAVE_PTHREAD_MUTEX_RECURSIVE @@ -129,6 +135,9 @@ /* Define to 1 if compiler supports __thread */ #undef HAVE___THREAD +/* Lockless dmix as default */ +#undef LOCKLESS_DMIX_DEFAULT + /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR diff -Nru alsa-lib-1.2.2/include/control.h alsa-lib-1.2.6.1/include/control.h --- alsa-lib-1.2.2/include/control.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/control.h 2021-12-09 13:17:59.000000000 +0000 @@ -50,19 +50,163 @@ unsigned char dig_subframe[4]; /**< AES/IEC958 subframe bits */ } snd_aes_iec958_t; -/** CTL card info container */ +/** \brief CTL card info container. + * + * This type contains meta information about a sound card, such as the index, + * name, longname, etc. + * + * \par Memory management + * + * Before using a snd_ctl_card_info_t object, it must be allocated using + * snd_ctl_card_info_alloca() or snd_ctl_card_info_malloc(). When using the + * latter, it must be freed again using snd_ctl_card_info_free(). + * + * A card info object can be zeroed out using snd_ctl_card_info_clear(). + * + * A card info object can be copied to another one using + * snd_ctl_card_info_copy(). + * + * \par Obtaining the Information + * + * To obtain the card information, it must first be opened using + * snd_ctl_open(), and a snd_ctl_card_info_t container must be + * allocated. Then, the information can be read using + * snd_ctl_card_info_get_card(). + * + * Thereafter, the card properties can be read using the + * snd_ctl_card_info_get_*() functions. + */ typedef struct _snd_ctl_card_info snd_ctl_card_info_t; /** CTL element identifier container */ typedef struct _snd_ctl_elem_id snd_ctl_elem_id_t; -/** CTL element identifier list container */ +/** CTL element list container + * + * This is a list of CTL elements. The list contains management + * information (e.g. how many elements the sound card has) as well as + * the element identifiers. All functions which operate on the list + * are named snd_ctl_elem_list_*(). + * + * \par Memory management + * + * There are two memory areas to deal with: The list container itself + * and the memory for the element identifiers. + * + * To manage the area for the list container, the following functions + * are used: + * + * - snd_ctl_elem_list_malloc() / snd_ctl_elem_list_free() to allocate + * and free memory on the heap, or + * - snd_ctl_elem_list_alloca() to allocate the memory on the + * stack. This memory is auto-released when the stack is unwound. + * + * To manage the space for the element identifiers, the + * snd_ctl_elem_list_alloc_space() and snd_ctl_elem_list_free_space() + * are used. Allocating the right amount of space can be achieved by + * first obtaining the number of elements and then calling + * snd_ctl_elem_list_alloc_space(): + * + * \code + * snd_ctl_elem_list_t* list; + * int count; + * + * // Initialise list + * snd_ctl_elem_list_malloc(&list); + * + * // Get number of elements + * snd_ctl_elem_list(ctl, list); + * count = snd_ctl_elem_list_get_count(list); + * + * // Allocate space for identifiers + * snd_ctl_elem_list_alloc_space(list, count); + * + * // Get identifiers + * snd_ctl_elem_list(ctl, list); // yes, this is same as above :) + * + * // Do something useful with the list... + * + * // Cleanup + * snd_ctl_elem_list_free_space(list); + * snd_ctl_elem_list_free(list); + * \endcode + * + * + * \par The Elements + * + * The elements in the list are accessed using an index. This index is + * the location in the list; Don't confuse it with the 'index' of the + * element identifier. For example: + * + * \code + * snd_ctl_elem_list_t list; + * unsigned int element_index; + * + * // Allocate space, fill list ... + * + * element_index = snd_ctl_elem_list_get_index(&list, 2); + * \endcode + * + * This will access the 3rd element in the list (index=2) and get the + * elements index from the driver (which might be 13, for example). + */ typedef struct _snd_ctl_elem_list snd_ctl_elem_list_t; /** CTL element info container */ typedef struct _snd_ctl_elem_info snd_ctl_elem_info_t; -/** CTL element value container */ +/** CTL element value container. + * + * Contains the value(s) (i.e. members) of a single element. All + * values of a given element are of the same type. + * + * \par Memory management + * + * To access a value, a snd_ctl_elem_value_t must be allocated using + * snd_ctl_elem_value_alloca() or snd_ctl_elem_value_malloc(). When + * using the latter, it must be freed again using + * snd_ctl_elem_value_free(). + * + * A value object can be zeroed out using snd_ctl_elem_value_clear(). + * + * A value object can be copied to another one using + * snd_ctl_elem_value_copy(). + * + * \par Identifier + * + * Then, the ID must be filled. It is sufficient to fill only the + * numid, if known. Otherwise, interface type, device, subdevice, + * name, index must all be given. The following functions can be used + * to fill the ID: + * + * - snd_ctl_elem_value_set_id(): Set the ID. Requires an + * snd_ctl_elem_id_t object. + * - snd_ctl_elem_value_set_numid(): Set the numid. + * - Or use all of the following: + * + * - snd_ctl_elem_value_set_interface() + * - snd_ctl_elem_value_set_device() + * - snd_ctl_elem_value_set_subdevice() + * - snd_ctl_elem_value_set_name() + * - snd_ctl_elem_value_set_index() + * + * When communicating with the driver (snd_ctl_elem_read(), + * snd_ctl_elem_write()), and the numid was given, the interface, + * device, ... are filled (even if you set the before). When the numid + * is unset (i.e. it is 0), it is filled. + * + * \par Communicating with the driver + * + * After the value container was created and filled with the ID of the + * desired element, the value(s) can be fetched from the driver (and + * thus from the hardware) or written to the driver. + * + * To fetch a value, use snd_ctl_elem_read(). Thereafter, use the + * snd_ctl_elem_value_get_*() functions to obtain the actual value. + * + * To write a new value, first use a snd_ctl_elem_value_set_*() to set + * it, then call snd_ctl_elem_write() to write it to the driver. + */ typedef struct _snd_ctl_elem_value snd_ctl_elem_value_t; /** CTL event container */ @@ -198,7 +342,9 @@ /** INET client CTL (not yet implemented) */ SND_CTL_TYPE_INET, /** External control plugin */ - SND_CTL_TYPE_EXT + SND_CTL_TYPE_EXT, + /** Control functionality remapping */ + SND_CTL_TYPE_REMAP, } snd_ctl_type_t; /** Non blocking mode (flag for open mode) \hideinitializer */ @@ -310,6 +456,8 @@ void snd_ctl_elem_id_free(snd_ctl_elem_id_t *obj); void snd_ctl_elem_id_clear(snd_ctl_elem_id_t *obj); void snd_ctl_elem_id_copy(snd_ctl_elem_id_t *dst, const snd_ctl_elem_id_t *src); +int snd_ctl_elem_id_compare_numid(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2); +int snd_ctl_elem_id_compare_set(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2); unsigned int snd_ctl_elem_id_get_numid(const snd_ctl_elem_id_t *obj); snd_ctl_elem_iface_t snd_ctl_elem_id_get_interface(const snd_ctl_elem_id_t *obj); unsigned int snd_ctl_elem_id_get_device(const snd_ctl_elem_id_t *obj); @@ -324,11 +472,20 @@ void snd_ctl_elem_id_set_index(snd_ctl_elem_id_t *obj, unsigned int val); size_t snd_ctl_card_info_sizeof(void); + /** \hideinitializer - * \brief allocate an invalid #snd_ctl_card_info_t using standard alloca - * \param ptr returned pointer + * \brief Allocate an invalid #snd_ctl_card_info_t on the stack. + * + * Allocate space for a card info object on the stack. The allocated + * memory need not be freed, because it is on the stack. + * + * See snd_ctl_card_info_t for details. + * + * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address + * of the allocated space will returned here. */ #define snd_ctl_card_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_info) + int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr); void snd_ctl_card_info_free(snd_ctl_card_info_t *obj); void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj); @@ -354,11 +511,18 @@ snd_ctl_event_type_t snd_ctl_event_get_type(const snd_ctl_event_t *obj); size_t snd_ctl_elem_list_sizeof(void); + /** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_list_t using standard alloca - * \param ptr returned pointer + * + * \brief Allocate a #snd_ctl_elem_list_t using standard alloca. + * + * The memory is allocated on the stack and will automatically be + * released when the stack unwinds (i.e. no free() is needed). + * + * \param ptr Pointer to allocated memory. */ #define snd_ctl_elem_list_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_list) + int snd_ctl_elem_list_malloc(snd_ctl_elem_list_t **ptr); void snd_ctl_elem_list_free(snd_ctl_elem_list_t *obj); void snd_ctl_elem_list_clear(snd_ctl_elem_list_t *obj); @@ -424,6 +588,9 @@ void snd_ctl_elem_info_set_subdevice(snd_ctl_elem_info_t *obj, unsigned int val); void snd_ctl_elem_info_set_name(snd_ctl_elem_info_t *obj, const char *val); void snd_ctl_elem_info_set_index(snd_ctl_elem_info_t *obj, unsigned int val); +void snd_ctl_elem_info_set_read_write(snd_ctl_elem_info_t *obj, int rval, int wval); +void snd_ctl_elem_info_set_tlv_read_write(snd_ctl_elem_info_t *obj, int rval, int wval); +void snd_ctl_elem_info_set_inactive(snd_ctl_elem_info_t *obj, int val); int snd_ctl_add_integer_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, unsigned int element_count, @@ -454,11 +621,20 @@ int snd_ctl_elem_remove(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); size_t snd_ctl_elem_value_sizeof(void); + /** \hideinitializer - * \brief allocate an invalid #snd_ctl_elem_value_t using standard alloca - * \param ptr returned pointer + * \brief Allocate an invalid #snd_ctl_elem_value_t on the stack. + * + * Allocate space for a value object on the stack. The allocated + * memory need not be freed, because it is on the stack. + * + * See snd_ctl_elem_value_t for details. + * + * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address + * of the allocated space will returned here. */ #define snd_ctl_elem_value_alloca(ptr) __snd_alloca(ptr, snd_ctl_elem_value) + int snd_ctl_elem_value_malloc(snd_ctl_elem_value_t **ptr); void snd_ctl_elem_value_free(snd_ctl_elem_value_t *obj); void snd_ctl_elem_value_clear(snd_ctl_elem_value_t *obj); diff -Nru alsa-lib-1.2.2/include/control_plugin.h alsa-lib-1.2.6.1/include/control_plugin.h --- alsa-lib-1.2.2/include/control_plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/include/control_plugin.h 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,56 @@ +/** + * \file include/control_plugin.h + * \brief Common control plugin code + * \author Jaroslav Kysela + * \date 2021 + * + * Application interface library for the ALSA driver. + * See the \ref control_plugins page for more details. + * + * \warning Using of contents of this header file might be dangerous + * in the sense of compatibility reasons. The contents might be + * freely changed in future. + */ +/* + * 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 + * + */ + +#ifndef __ALSA_CONTROL_PLUGIN_H +#define __ALSA_CONTROL_PLUGIN_H + +/** + * \defgroup Control_Plugins Primitive Control Plugins + * \ingroup Control + * See the \ref control_plugins page for more details. + * \{ + */ + +/* + * Control HW + */ +int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode); +int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_config_t *conf, int mode); + +/* + * Control Remap & Map + */ +int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap, + snd_config_t *map, snd_ctl_t *child, int mode); +int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_config_t *conf, int mode); + +/** \} */ + +#endif /* __ALSA_CONTROL_PLUGIN_H */ diff -Nru alsa-lib-1.2.2/include/global.h alsa-lib-1.2.6.1/include/global.h --- alsa-lib-1.2.2/include/global.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/global.h 2021-12-09 13:17:59.000000000 +0000 @@ -97,6 +97,7 @@ /** \brief Returns the version of a dynamic symbol as a string. */ #define SND_DLSYM_VERSION(version) __STRING(version) +int snd_dlpath(char *path, size_t path_len, const char *name); void *snd_dlopen(const char *file, int mode, char *errbuf, size_t errbuflen); void *snd_dlsym(void *handle, const char *name, const char *version); int snd_dlclose(void *handle); diff -Nru alsa-lib-1.2.2/include/local.h alsa-lib-1.2.6.1/include/local.h --- alsa-lib-1.2.2/include/local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/local.h 2021-12-09 13:17:59.000000000 +0000 @@ -232,7 +232,10 @@ size_t page_size(void); size_t page_ptr(size_t object_offset, size_t object_size, size_t *offset, size_t *mmap_offset); -int safe_strtol(const char *str, long *val); +int safe_strtoll_base(const char *str, long long *val, int base); +static inline int safe_strtoll(const char *str, long long *val) { return safe_strtoll_base(str, val, 0); } +int safe_strtol_base(const char *str, long *val, int base); +static inline int safe_strtol(const char *str, long *val) { return safe_strtol_base(str, val, 0); } int snd_send_fd(int sock, void *data, size_t len, int fd); int snd_receive_fd(int sock, void *data, size_t len, int *fd); @@ -374,4 +377,21 @@ void *INTERNAL(snd_dlopen)(const char *name, int mode, char *errbuf, size_t errbuflen); #endif +#ifdef BUILD_UCM + +const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name); + +static inline int _snd_is_ucm_device(const char *name) +{ + return name && name[0] == '_' && name[1] == 'u' && name[2] == 'c' && name[3] == 'm'; +} + +#else + +static inline const char *uc_mgr_alibcfg_by_device(snd_config_t **config, const char *name) { return NULL; } +static inline int _snd_is_ucm_device(const char *name) { return 0; } + + +#endif + #endif diff -Nru alsa-lib-1.2.2/include/output.h alsa-lib-1.2.6.1/include/output.h --- alsa-lib-1.2.2/include/output.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/output.h 2021-12-09 13:17:59.000000000 +0000 @@ -65,6 +65,7 @@ int snd_output_stdio_attach(snd_output_t **outputp, FILE *fp, int _close); int snd_output_buffer_open(snd_output_t **outputp); size_t snd_output_buffer_string(snd_output_t *output, char **buf); +size_t snd_output_buffer_steal(snd_output_t *output, char **buf); int snd_output_close(snd_output_t *output); int snd_output_printf(snd_output_t *output, const char *format, ...) #ifndef DOC_HIDDEN diff -Nru alsa-lib-1.2.2/include/pcm.h alsa-lib-1.2.6.1/include/pcm.h --- alsa-lib-1.2.2/include/pcm.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/pcm.h 2021-12-09 13:17:59.000000000 +0000 @@ -350,6 +350,20 @@ SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, } snd_pcm_tstamp_type_t; +typedef enum _snd_pcm_audio_tstamp_type { + /** + * first definition for backwards compatibility only, + * maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else + */ + SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0, + SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /**< DMA time, reported as per hw_ptr */ + SND_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /**< link time reported by sample or wallclock counter, reset on startup */ + SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /**< link time reported by sample or wallclock counter, not reset on startup */ + SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /**< link time estimated indirectly */ + SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /**< link time synchronized with system time */ + SND_PCM_AUDIO_TSTAMP_TYPE_LAST = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED +} snd_pcm_audio_tstamp_type_t; + typedef struct _snd_pcm_audio_tstamp_config { /* 5 of max 16 bits used */ unsigned int type_requested:4; @@ -1159,6 +1173,29 @@ snd_pcm_uframes_t frames, const snd_pcm_format_t format); +/** + * \brief get the address of the given PCM channel area + * \param area PCM channel area + * \param offset Offset in frames + * + * Returns the pointer corresponding to the given offset on the channel area. + */ +static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) +{ + return (char *)area->addr + (area->first + area->step * offset) / 8; +} + +/** + * \brief get the step size of the given PCM channel area in bytes + * \param area PCM channel area + * + * Returns the step size in bytes from the given channel area. + */ +static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area) +{ + return area->step / 8; +} + /** \} */ /** diff -Nru alsa-lib-1.2.2/include/pcm_external.h alsa-lib-1.2.6.1/include/pcm_external.h --- alsa-lib-1.2.2/include/pcm_external.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/pcm_external.h 2021-12-09 13:17:59.000000000 +0000 @@ -59,7 +59,7 @@ #include "pcm_extplug.h" int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp, - int *cchannelsp, int *hwctlp); + int *cchannelsp, int *hwctlp) __attribute__((deprecated)); /** \} */ diff -Nru alsa-lib-1.2.2/include/pcm_rate.h alsa-lib-1.2.6.1/include/pcm_rate.h --- alsa-lib-1.2.2/include/pcm_rate.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/pcm_rate.h 2021-12-09 13:17:59.000000000 +0000 @@ -38,7 +38,7 @@ /** * Protocol version */ -#define SND_PCM_RATE_PLUGIN_VERSION 0x010002 +#define SND_PCM_RATE_PLUGIN_VERSION 0x010003 /** hw_params information for a single side */ typedef struct snd_pcm_rate_side_info { @@ -55,6 +55,11 @@ unsigned int channels; } snd_pcm_rate_info_t; +enum { + SND_PCM_RATE_FLAG_INTERLEAVED = (1U << 0), /** only interleaved format */ + SND_PCM_RATE_FLAG_SYNC_FORMATS = (1U << 1), /** both input and output formats have to be identical */ +}; + /** Callback table of rate-converter */ typedef struct snd_pcm_rate_ops { /** @@ -114,6 +119,13 @@ * new ops since version 0x010002 */ void (*dump)(void *obj, snd_output_t *out); + /** + * get the supported input and output formats (optional); + * new ops since version 0x010003 + */ + int (*get_supported_formats)(void *obj, uint64_t *in_formats, + uint64_t *out_formats, + unsigned int *flags); } snd_pcm_rate_ops_t; /** open function type */ @@ -147,6 +159,28 @@ snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); } snd_pcm_rate_old_ops_t; + +/* old rate_ops for protocol version 0x010002 */ +typedef struct snd_pcm_rate_v2_ops { + void (*close)(void *obj); + int (*init)(void *obj, snd_pcm_rate_info_t *info); + void (*free)(void *obj); + void (*reset)(void *obj); + int (*adjust_pitch)(void *obj, snd_pcm_rate_info_t *info); + void (*convert)(void *obj, + const snd_pcm_channel_area_t *dst_areas, + snd_pcm_uframes_t dst_offset, unsigned int dst_frames, + const snd_pcm_channel_area_t *src_areas, + snd_pcm_uframes_t src_offset, unsigned int src_frames); + void (*convert_s16)(void *obj, int16_t *dst, unsigned int dst_frames, + const int16_t *src, unsigned int src_frames); + snd_pcm_uframes_t (*input_frames)(void *obj, snd_pcm_uframes_t frames); + snd_pcm_uframes_t (*output_frames)(void *obj, snd_pcm_uframes_t frames); + unsigned int version; + int (*get_supported_rates)(void *obj, unsigned int *rate_min, + unsigned int *rate_max); + void (*dump)(void *obj, snd_output_t *out); +} snd_pcm_rate_v2_ops_t; #endif #ifdef __cplusplus diff -Nru alsa-lib-1.2.2/include/rawmidi.h alsa-lib-1.2.6.1/include/rawmidi.h --- alsa-lib-1.2.2/include/rawmidi.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/rawmidi.h 2021-12-09 13:17:59.000000000 +0000 @@ -79,6 +79,20 @@ SND_RAWMIDI_TYPE_VIRTUAL } snd_rawmidi_type_t; +/** Type of clock used with rawmidi timestamp */ +typedef enum _snd_rawmidi_clock { + SND_RAWMIDI_CLOCK_NONE = 0, + SND_RAWMIDI_CLOCK_REALTIME = 1, + SND_RAWMIDI_CLOCK_MONOTONIC = 2, + SND_RAWMIDI_CLOCK_MONOTONIC_RAW = 3, +} snd_rawmidi_clock_t; + +/** Select the read mode (standard or with timestamps) */ +typedef enum _snd_rawmidi_read_mode { + SND_RAWMIDI_READ_STANDARD = 0, + SND_RAWMIDI_READ_TSTAMP = 1, +} snd_rawmidi_read_mode_t; + int snd_rawmidi_open(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode); int snd_rawmidi_open_lconf(snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, @@ -126,6 +140,11 @@ size_t snd_rawmidi_params_get_avail_min(const snd_rawmidi_params_t *params); int snd_rawmidi_params_set_no_active_sensing(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params, int val); int snd_rawmidi_params_get_no_active_sensing(const snd_rawmidi_params_t *params); +int snd_rawmidi_params_set_read_mode(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_read_mode_t val); +snd_rawmidi_read_mode_t snd_rawmidi_params_get_read_mode(const snd_rawmidi_params_t *params); +int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_clock_t val); +snd_rawmidi_clock_t snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params); + int snd_rawmidi_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params); int snd_rawmidi_params_current(snd_rawmidi_t *rmidi, snd_rawmidi_params_t *params); size_t snd_rawmidi_status_sizeof(void); @@ -145,6 +164,7 @@ int snd_rawmidi_drop(snd_rawmidi_t *rmidi); ssize_t snd_rawmidi_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size); ssize_t snd_rawmidi_read(snd_rawmidi_t *rmidi, void *buffer, size_t size); +ssize_t snd_rawmidi_tread(snd_rawmidi_t *rmidi, struct timespec *tstamp, void *buffer, size_t size); const char *snd_rawmidi_name(snd_rawmidi_t *rmidi); snd_rawmidi_type_t snd_rawmidi_type(snd_rawmidi_t *rmidi); snd_rawmidi_stream_t snd_rawmidi_stream(snd_rawmidi_t *rawmidi); diff -Nru alsa-lib-1.2.2/include/sound/Makefile.in alsa-lib-1.2.6.1/include/sound/Makefile.in --- alsa-lib-1.2.2/include/sound/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -336,6 +336,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/include/sound/asound.h alsa-lib-1.2.6.1/include/sound/asound.h --- alsa-lib-1.2.2/include/sound/asound.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/asound.h 2021-12-09 13:17:59.000000000 +0000 @@ -1 +1,4 @@ +/* workaround for building with old glibc / kernel headers */ +#include + #include diff -Nru alsa-lib-1.2.2/include/sound/type_compat.h alsa-lib-1.2.6.1/include/sound/type_compat.h --- alsa-lib-1.2.2/include/sound/type_compat.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/type_compat.h 2021-12-09 13:17:59.000000000 +0000 @@ -44,6 +44,10 @@ #define __be8 __u8 #endif +#ifndef __kernel_long_t +#define __kernel_long_t long +#endif + #ifndef __user #define __user #endif diff -Nru alsa-lib-1.2.2/include/sound/uapi/Makefile.in alsa-lib-1.2.6.1/include/sound/uapi/Makefile.in --- alsa-lib-1.2.2/include/sound/uapi/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/uapi/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -294,6 +294,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/include/sound/uapi/asoc.h alsa-lib-1.2.6.1/include/sound/uapi/asoc.h --- alsa-lib-1.2.2/include/sound/uapi/asoc.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/uapi/asoc.h 2021-12-09 13:17:59.000000000 +0000 @@ -169,16 +169,22 @@ #define SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP (1 << 3) /* DAI topology BCLK parameter - * For the backwards capability, by default codec is bclk master + * For the backwards capability, by default codec is bclk provider */ -#define SND_SOC_TPLG_BCLK_CM 0 /* codec is bclk master */ -#define SND_SOC_TPLG_BCLK_CS 1 /* codec is bclk slave */ +#define SND_SOC_TPLG_BCLK_CP 0 /* codec is bclk provider */ +#define SND_SOC_TPLG_BCLK_CC 1 /* codec is bclk consumer */ +/* keep previous definitions for compatibility */ +#define SND_SOC_TPLG_BCLK_CM SND_SOC_TPLG_BCLK_CP +#define SND_SOC_TPLG_BCLK_CS SND_SOC_TPLG_BCLK_CC /* DAI topology FSYNC parameter - * For the backwards capability, by default codec is fsync master + * For the backwards capability, by default codec is fsync provider */ -#define SND_SOC_TPLG_FSYNC_CM 0 /* codec is fsync master */ -#define SND_SOC_TPLG_FSYNC_CS 1 /* codec is fsync slave */ +#define SND_SOC_TPLG_FSYNC_CP 0 /* codec is fsync provider */ +#define SND_SOC_TPLG_FSYNC_CC 1 /* codec is fsync consumer */ +/* keep previous definitions for compatibility */ +#define SND_SOC_TPLG_FSYNC_CM SND_SOC_TPLG_FSYNC_CP +#define SND_SOC_TPLG_FSYNC_CS SND_SOC_TPLG_FSYNC_CC /* * Block Header. @@ -335,8 +341,8 @@ __u8 clock_gated; /* SND_SOC_TPLG_DAI_CLK_GATE_ value */ __u8 invert_bclk; /* 1 for inverted BCLK, 0 for normal */ __u8 invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - __u8 bclk_master; /* SND_SOC_TPLG_BCLK_ value */ - __u8 fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ + __u8 bclk_provider; /* SND_SOC_TPLG_BCLK_ value */ + __u8 fsync_provider; /* SND_SOC_TPLG_FSYNC_ value */ __u8 mclk_direction; /* SND_SOC_TPLG_MCLK_ value */ __le16 reserved; /* for 32bit alignment */ __le32 mclk_rate; /* MCLK or SYSCLK freqency in Hz */ diff -Nru alsa-lib-1.2.2/include/sound/uapi/asound.h alsa-lib-1.2.6.1/include/sound/uapi/asound.h --- alsa-lib-1.2.2/include/sound/uapi/asound.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/sound/uapi/asound.h 2021-12-09 13:17:59.000000000 +0000 @@ -702,7 +702,7 @@ * Raw MIDI section - /dev/snd/midi?? */ -#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 1) +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2) enum { SNDRV_RAWMIDI_STREAM_OUTPUT = 0, @@ -728,12 +728,38 @@ unsigned char reserved[64]; /* reserved for future use */ }; +#define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0) +#define SNDRV_RAWMIDI_MODE_FRAMING_SHIFT 0 +#define SNDRV_RAWMIDI_MODE_FRAMING_NONE (0<<0) +#define SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP (1<<0) +#define SNDRV_RAWMIDI_MODE_CLOCK_MASK (7<<3) +#define SNDRV_RAWMIDI_MODE_CLOCK_SHIFT 3 +#define SNDRV_RAWMIDI_MODE_CLOCK_NONE (0<<3) +#define SNDRV_RAWMIDI_MODE_CLOCK_REALTIME (1<<3) +#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC (2<<3) +#define SNDRV_RAWMIDI_MODE_CLOCK_MONOTONIC_RAW (3<<3) + +#define SNDRV_RAWMIDI_FRAMING_DATA_LENGTH 16 + +struct snd_rawmidi_framing_tstamp { + /* For now, frame_type is always 0. Midi 2.0 is expected to add new + * types here. Applications are expected to skip unknown frame types. + */ + __u8 frame_type; + __u8 length; /* number of valid bytes in data field */ + __u8 reserved[2]; + __u32 tv_nsec; /* nanoseconds */ + __u64 tv_sec; /* seconds */ + __u8 data[SNDRV_RAWMIDI_FRAMING_DATA_LENGTH]; +} __packed; + struct snd_rawmidi_params { int stream; size_t buffer_size; /* queue size in bytes */ size_t avail_min; /* minimum avail bytes for wakeup */ unsigned int no_active_sensing: 1; /* do not send active sensing byte in close() */ - unsigned char reserved[16]; /* reserved for future use */ + unsigned int mode; /* For input data only, frame incoming data */ + unsigned char reserved[12]; /* reserved for future use */ }; struct snd_rawmidi_status { @@ -747,6 +773,7 @@ #define SNDRV_RAWMIDI_IOCTL_PVERSION _IOR('W', 0x00, int) #define SNDRV_RAWMIDI_IOCTL_INFO _IOR('W', 0x01, struct snd_rawmidi_info) +#define SNDRV_RAWMIDI_IOCTL_USER_PVERSION _IOW('W', 0x02, int) #define SNDRV_RAWMIDI_IOCTL_PARAMS _IOWR('W', 0x10, struct snd_rawmidi_params) #define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) #define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) diff -Nru alsa-lib-1.2.2/include/topology.h alsa-lib-1.2.6.1/include/topology.h --- alsa-lib-1.2.2/include/topology.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/topology.h 2021-12-09 13:17:59.000000000 +0000 @@ -658,8 +658,8 @@ * * id "1" # used for binding to the config * format "I2S" # physical audio format. - * bclk "master" # Platform is master of bit clock - * fsync "slave" # Platform is slave of fsync + * bclk "codec_provider" # Codec provides the bit clock + * fsync "codec_consumer" # Codec follows the fsync * } * * @@ -1028,8 +1028,8 @@ unsigned char clock_gated; /* SND_SOC_TPLG_DAI_CLK_GATE_ value */ unsigned char invert_bclk; /* 1 for inverted BCLK, 0 for normal */ unsigned char invert_fsync; /* 1 for inverted frame clock, 0 for normal */ - unsigned char bclk_master; /* SND_SOC_TPLG_BCLK_ value */ - unsigned char fsync_master; /* SND_SOC_TPLG_FSYNC_ value */ + unsigned char bclk_provider; /* SND_SOC_TPLG_BCLK_ value */ + unsigned char fsync_provider; /* SND_SOC_TPLG_FSYNC_ value */ unsigned char mclk_direction; /* SND_SOC_TPLG_MCLK_ value */ unsigned short reserved; /* for 32bit alignment */ unsigned int mclk_rate; /* MCLK or SYSCLK freqency in Hz */ diff -Nru alsa-lib-1.2.2/include/use-case.h alsa-lib-1.2.6.1/include/use-case.h --- alsa-lib-1.2.2/include/use-case.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/include/use-case.h 2021-12-09 13:17:59.000000000 +0000 @@ -117,7 +117,10 @@ * * If multiple devices with the same name exists, the number suffixes should * be added to these names like HDMI1,HDMI2,HDMI3 etc. No number gaps are - * allowed. The names with numbers must be continuous. + * allowed. The names with numbers must be continuous. It is allowed to put + * a whitespace between name and index (like 'Line 1') for the better + * readability. The device names 'Line 1' and 'Line1' are equal for + * this purpose. * * If EnableSequence/DisableSequence controls independent paths in the hardware * it is also recommended to split playback and capture UCM devices and use @@ -137,6 +140,7 @@ #define SND_USE_CASE_DEV_EARPIECE "Earpiece" /**< Earpiece Device */ #define SND_USE_CASE_DEV_SPDIF "SPDIF" /**< SPDIF Device */ #define SND_USE_CASE_DEV_HDMI "HDMI" /**< HDMI Device */ +#define SND_USE_CASE_DEV_USB "USB" /**< USB Device (multifunctional) */ /* add new devices to end of list */ @@ -146,6 +150,13 @@ * The use case modifier allows runtime configuration changes to deal with * asynchronous events. * + * If multiple modifiers with the same name exists, the number suffixes should + * be added to these names like 'Echo Reference 1','Echo Reference 2' etc. + * No number gaps are allowed. The names with numbers must be continuous. + * It is allowed to put a whitespace between name and index for the better + * readability. The modifier names 'Something 1' and 'Something1' are equal + * for this purpose. + * * e.g. to record a voice call :- * 1. Set verb to SND_USE_CASE_VERB_VOICECALL (for voice call) * 2. Set modifier SND_USE_CASE_MOD_CAPTURE_VOICE when capture required. @@ -246,6 +257,8 @@ * - NULL - return current card * - _verb - return current verb * - _file - return configuration file loaded for current card + * - _alibcfg - return private alsa-lib's configuration for current card + * - _alibpref - return private alsa-lib's configuration device prefix for current card * * - [=]{NAME}[/[{modifier}|{/device}][/{verb}]] * - value identifier {NAME} @@ -272,6 +285,11 @@ * "=Variable/Modifier/Verb" * * Recommended names for values: + * - Linked + * - value "True" or "1" (case insensitive) + * - this is a linked UCM card + * - don't use this UCM card, because the other UCM card refers devices + * - valid only in the ValueDefaults section (query '=Linked') * - TQ * - Tone Quality * - Priority @@ -402,6 +420,11 @@ * \return Zero if success, otherwise a negative error code * * Known identifiers: + * - _fboot - execute the fixed boot sequence (value = NULL) + * - _boot - execute the boot sequence (value = NULL) + * - only when driver controls identifiers are changed + * (otherwise the old control values are restored) + * - _defaults - execute the 'defaults' sequence (value = NULL) * - _verb - set current verb = value * - _enadev - enable given device = value * - _disdev - disable given device = value diff -Nru alsa-lib-1.2.2/include/version.h alsa-lib-1.2.6.1/include/version.h --- alsa-lib-1.2.2/include/version.h 2020-02-19 10:25:40.000000000 +0000 +++ alsa-lib-1.2.6.1/include/version.h 2021-12-09 14:54:04.000000000 +0000 @@ -4,12 +4,11 @@ #define SND_LIB_MAJOR 1 /**< major number of library version */ #define SND_LIB_MINOR 2 /**< minor number of library version */ -#define SND_LIB_SUBMINOR 2 /**< subminor number of library version */ +#define SND_LIB_SUBMINOR 6 /**< subminor number of library version */ #define SND_LIB_EXTRAVER 1000000 /**< extra version number, used mainly for betas */ /** library version */ -#define SND_LIB_VERSION ((SND_LIB_MAJOR<<16)|\ - (SND_LIB_MINOR<<8)|\ - SND_LIB_SUBMINOR) +#define SND_LIB_VER(maj, min, sub) (((maj)<<16)|((min)<<8)|(sub)) +#define SND_LIB_VERSION SND_LIB_VER(SND_LIB_MAJOR, SND_LIB_MINOR, SND_LIB_SUBMINOR) /** library version (string) */ -#define SND_LIB_VERSION_STR "1.2.2" +#define SND_LIB_VERSION_STR "1.2.6.1" diff -Nru alsa-lib-1.2.2/install-sh alsa-lib-1.2.6.1/install-sh --- alsa-lib-1.2.2/install-sh 2020-02-19 10:25:24.000000000 +0000 +++ alsa-lib-1.2.6.1/install-sh 2021-12-09 14:53:51.000000000 +0000 @@ -451,7 +451,18 @@ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # diff -Nru alsa-lib-1.2.2/libtool alsa-lib-1.2.6.1/libtool --- alsa-lib-1.2.2/libtool 2020-02-19 10:25:39.000000000 +0000 +++ alsa-lib-1.2.6.1/libtool 2021-12-09 14:54:04.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh -# Generated automatically by config.status (alsa-lib) 1.2.2 -# Libtool was configured on host e010f88cea4a: +# Generated automatically by config.status (alsa-lib) 1.2.6.1 +# Libtool was configured on host f420bc43a183: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. @@ -282,7 +282,7 @@ hardcode_into_libs=yes # Compile-time system search path for libraries. -sys_lib_search_path_spec="/usr/lib/gcc/x86_64-redhat-linux/8 /usr/lib64 /lib64 /usr/lib /lib " +sys_lib_search_path_spec="/usr/lib/gcc/x86_64-redhat-linux/11 /usr/lib64 /lib64 /usr/lib /lib " # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib /usr/lib64/qt-3.3/lib " diff -Nru alsa-lib-1.2.2/missing alsa-lib-1.2.6.1/missing --- alsa-lib-1.2.2/missing 2020-02-19 10:25:24.000000000 +0000 +++ alsa-lib-1.2.6.1/missing 2021-12-09 14:53:51.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify diff -Nru alsa-lib-1.2.2/modules/Makefile.in alsa-lib-1.2.6.1/modules/Makefile.in --- alsa-lib-1.2.2/modules/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/modules/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -305,6 +305,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/modules/mixer/Makefile.in alsa-lib-1.2.6.1/modules/mixer/Makefile.in --- alsa-lib-1.2.2/modules/mixer/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/modules/mixer/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -305,6 +305,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/modules/mixer/simple/Makefile.in alsa-lib-1.2.6.1/modules/mixer/simple/Makefile.in --- alsa-lib-1.2.2/modules/mixer/simple/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/modules/mixer/simple/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -368,6 +368,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/modules/mixer/simple/python.c alsa-lib-1.2.6.1/modules/mixer/simple/python.c --- alsa-lib-1.2.2/modules/mixer/simple/python.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/modules/mixer/simple/python.c 2021-12-09 13:17:59.000000000 +0000 @@ -21,6 +21,7 @@ #include "Python.h" #include +#include #include "config.h" #include "asoundlib.h" #include "mixer_abst.h" @@ -36,7 +37,7 @@ PyObject *py_mixer; }; -#define SCRIPT ALSA_PLUGIN_DIR "/smixer/python/main.py" +#define SCRIPT "smixer/python/main.py" struct pymelem { PyObject_HEAD @@ -1110,6 +1111,7 @@ FILE *fp; const char *file; PyObject *obj, *py_mod; + char path[PATH_MAX]; priv = calloc(1, sizeof(*priv)); if (priv == NULL) @@ -1119,8 +1121,10 @@ snd_mixer_sbasic_set_private_free(class, alsa_mixer_simple_free); file = getenv("ALSA_MIXER_SIMPLE_MPYTHON"); - if (file == NULL) - file = SCRIPT; + if (file == NULL) { + snd_dlpath(path, sizeof(path), SCRIPT); + file = path; + } fp = fopen(file, "r"); if (fp == NULL) { diff -Nru alsa-lib-1.2.2/modules/mixer/simple/sbasedl.c alsa-lib-1.2.6.1/modules/mixer/simple/sbasedl.c --- alsa-lib-1.2.2/modules/mixer/simple/sbasedl.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/modules/mixer/simple/sbasedl.c 2021-12-09 13:17:59.000000000 +0000 @@ -33,7 +33,7 @@ #include "mixer_abst.h" #include "sbase.h" -#define SO_PATH ALSA_PLUGIN_DIR "/smixer" +#define SO_PATH "smixer" int mixer_simple_basic_dlopen(snd_mixer_class_t *class, bclass_base_ops_t **ops) diff -Nru alsa-lib-1.2.2/src/Makefile.am alsa-lib-1.2.6.1/src/Makefile.am --- alsa-lib-1.2.2/src/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -14,7 +14,7 @@ endif lib_LTLIBRARIES = libasound.la -libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c +libasound_la_SOURCES = conf.c confeval.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c SUBDIRS=control libasound_la_LIBADD = control/libcontrol.la diff -Nru alsa-lib-1.2.2/src/Makefile.in alsa-lib-1.2.6.1/src/Makefile.in --- alsa-lib-1.2.2/src/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -148,9 +148,9 @@ libasound_la_DEPENDENCIES = control/libcontrol.la $(am__append_2) \ $(am__append_4) $(am__append_6) $(am__append_8) \ $(am__append_10) $(am__append_12) $(am__append_14) -am_libasound_la_OBJECTS = conf.lo confmisc.lo input.lo output.lo \ - async.lo error.lo dlmisc.lo socket.lo shmarea.lo userfile.lo \ - names.lo +am_libasound_la_OBJECTS = conf.lo confeval.lo confmisc.lo input.lo \ + output.lo async.lo error.lo dlmisc.lo socket.lo shmarea.lo \ + userfile.lo names.lo libasound_la_OBJECTS = $(am_libasound_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -175,11 +175,11 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/async.Plo ./$(DEPDIR)/conf.Plo \ - ./$(DEPDIR)/confmisc.Plo ./$(DEPDIR)/dlmisc.Plo \ - ./$(DEPDIR)/error.Plo ./$(DEPDIR)/input.Plo \ - ./$(DEPDIR)/names.Plo ./$(DEPDIR)/output.Plo \ - ./$(DEPDIR)/shmarea.Plo ./$(DEPDIR)/socket.Plo \ - ./$(DEPDIR)/userfile.Plo + ./$(DEPDIR)/confeval.Plo ./$(DEPDIR)/confmisc.Plo \ + ./$(DEPDIR)/dlmisc.Plo ./$(DEPDIR)/error.Plo \ + ./$(DEPDIR)/input.Plo ./$(DEPDIR)/names.Plo \ + ./$(DEPDIR)/output.Plo ./$(DEPDIR)/shmarea.Plo \ + ./$(DEPDIR)/socket.Plo ./$(DEPDIR)/userfile.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -393,6 +393,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -408,7 +409,7 @@ @SYMBOLIC_FUNCTIONS_FALSE@SYMFUNCS = @SYMBOLIC_FUNCTIONS_TRUE@SYMFUNCS = -Wl,-Bsymbolic-functions lib_LTLIBRARIES = libasound.la -libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c +libasound_la_SOURCES = conf.c confeval.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c SUBDIRS = control $(am__append_1) $(am__append_3) $(am__append_5) \ $(am__append_7) $(am__append_9) $(am__append_11) \ $(am__append_13) conf @@ -500,6 +501,7 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/async.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confeval.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confmisc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dlmisc.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@ # am--include-marker @@ -746,6 +748,7 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/async.Plo -rm -f ./$(DEPDIR)/conf.Plo + -rm -f ./$(DEPDIR)/confeval.Plo -rm -f ./$(DEPDIR)/confmisc.Plo -rm -f ./$(DEPDIR)/dlmisc.Plo -rm -f ./$(DEPDIR)/error.Plo @@ -802,6 +805,7 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/async.Plo -rm -f ./$(DEPDIR)/conf.Plo + -rm -f ./$(DEPDIR)/confeval.Plo -rm -f ./$(DEPDIR)/confmisc.Plo -rm -f ./$(DEPDIR)/dlmisc.Plo -rm -f ./$(DEPDIR)/error.Plo diff -Nru alsa-lib-1.2.2/src/alisp/Makefile.in alsa-lib-1.2.6.1/src/alisp/Makefile.in --- alsa-lib-1.2.2/src/alisp/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/alisp/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -296,6 +296,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/conf/Makefile.am alsa-lib-1.2.6.1/src/conf/Makefile.am --- alsa-lib-1.2.2/src/conf/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -1,4 +1,4 @@ -SUBDIRS=cards pcm +SUBDIRS=cards ctl pcm cfg_files = alsa.conf if BUILD_ALISP diff -Nru alsa-lib-1.2.2/src/conf/Makefile.in alsa-lib-1.2.6.1/src/conf/Makefile.in --- alsa-lib-1.2.2/src/conf/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -337,6 +337,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -345,7 +346,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -SUBDIRS = cards pcm +SUBDIRS = cards ctl pcm cfg_files = alsa.conf $(am__append_1) $(am__append_2) EXTRA_DIST = $(cfg_files) alsaconfigdir = @ALSA_CONFIG_DIR@ diff -Nru alsa-lib-1.2.2/src/conf/alsa.conf alsa-lib-1.2.6.1/src/conf/alsa.conf --- alsa-lib-1.2.2/src/conf/alsa.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/alsa.conf 2021-12-09 13:17:59.000000000 +0000 @@ -8,9 +8,24 @@ { func load files [ + "/var/lib/alsa/conf.d" + "/usr/etc/alsa/conf.d" "/etc/alsa/conf.d" - "/etc/asound.conf" + "/etc/asound.conf|||/usr/etc/asound.conf" "~/.asoundrc" + { + @func concat + strings [ + { + @func getenv + vars [ + XDG_CONFIG_HOME + ] + default "~/.config" + } + "/alsa/asoundrc" + ] + } ] errors false } @@ -43,7 +58,35 @@ ".conf" ] } + { + root { + @func private_integer + } + file { + @func concat + strings [ + "/var/lib/alsa/card" + { @func private_integer } + ".conf.d" + ] + } + } ] + table { + id { + @func concat + strings [ + { @func private_integer } + ] + } + value { + @func concat + strings [ + "cards." + { @func private_string } + ] + } + } errors false } ] @@ -69,10 +112,11 @@ defaults.pcm.ipc_key 5678293 defaults.pcm.ipc_gid audio defaults.pcm.ipc_perm 0660 +defaults.pcm.tstamp_type default defaults.pcm.dmix.max_periods 0 defaults.pcm.dmix.channels 2 defaults.pcm.dmix.rate 48000 -defaults.pcm.dmix.format "unchanged" +defaults.pcm.dmix.format unchanged defaults.pcm.dmix.card defaults.pcm.card defaults.pcm.dmix.device defaults.pcm.device defaults.pcm.dsnoop.card defaults.pcm.card @@ -101,9 +145,8 @@ defaults.pcm.iec958.device defaults.pcm.device defaults.pcm.modem.card defaults.pcm.card defaults.pcm.modem.device defaults.pcm.device -# truncate files via file or tee PCM -defaults.pcm.file_format "raw" -defaults.pcm.file_truncate true +defaults.pcm.file_format raw +defaults.pcm.file_truncate true # truncate files via file or tee PCM defaults.rawmidi.card 0 defaults.rawmidi.device 0 defaults.rawmidi.subdevice -1 @@ -119,29 +162,6 @@ # PCM interface # -# redirect to load-on-demand extended pcm definitions -pcm.cards cards.pcm - -pcm.default cards.pcm.default -pcm.sysdefault cards.pcm.default -pcm.front cards.pcm.front -pcm.rear cards.pcm.rear -pcm.center_lfe cards.pcm.center_lfe -pcm.side cards.pcm.side -pcm.surround21 cards.pcm.surround21 -pcm.surround40 cards.pcm.surround40 -pcm.surround41 cards.pcm.surround41 -pcm.surround50 cards.pcm.surround50 -pcm.surround51 cards.pcm.surround51 -pcm.surround71 cards.pcm.surround71 -pcm.iec958 cards.pcm.iec958 -pcm.spdif iec958 -pcm.hdmi cards.pcm.hdmi -pcm.dmix cards.pcm.dmix -pcm.dsnoop cards.pcm.dsnoop -pcm.modem cards.pcm.modem -pcm.phoneline cards.pcm.phoneline - pcm.hw { @args [ CARD DEV SUBDEV ] @args.CARD { @@ -323,26 +343,35 @@ } } +# redirect to load-on-demand extended pcm definitions +pcm.cards cards.pcm + +pcm.default cards.pcm.default +pcm.sysdefault cards.pcm.default +pcm.front cards.pcm.front +pcm.rear cards.pcm.rear +pcm.center_lfe cards.pcm.center_lfe +pcm.side cards.pcm.side +pcm.surround21 cards.pcm.surround21 +pcm.surround40 cards.pcm.surround40 +pcm.surround41 cards.pcm.surround41 +pcm.surround50 cards.pcm.surround50 +pcm.surround51 cards.pcm.surround51 +pcm.surround71 cards.pcm.surround71 +pcm.iec958 cards.pcm.iec958 +pcm.spdif iec958 +pcm.hdmi cards.pcm.hdmi +pcm.dmix cards.pcm.dmix +pcm.dsnoop cards.pcm.dsnoop +pcm.modem cards.pcm.modem +pcm.phoneline cards.pcm.phoneline + # # Control interface # -ctl.sysdefault { - type hw - card { - @func getenv - vars [ - ALSA_CTL_CARD - ALSA_CARD - ] - default { - @func refer - name defaults.ctl.card - } - } - hint.description "Default control device" -} -ctl.default ctl.sysdefault +ctl.default cards.ctl.default +ctl.sysdefault cards.ctl.default ctl.hw { @args [ CARD ] diff -Nru alsa-lib-1.2.2/src/conf/cards/HDA-Intel.conf alsa-lib-1.2.6.1/src/conf/cards/HDA-Intel.conf --- alsa-lib-1.2.2/src/conf/cards/HDA-Intel.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/cards/HDA-Intel.conf 2021-12-09 13:17:59.000000000 +0000 @@ -398,6 +398,75 @@ } } +HDA-Intel.pcm.hdmi.8 { + @args [ CARD AES0 AES1 AES2 AES3 ] + @args.CARD { type string } + @args.AES0 { type integer } + @args.AES1 { type integer } + @args.AES2 { type integer } + @args.AES3 { type integer } + @func refer + name { + @func concat + strings [ + "cards.HDA-Intel.pcm.hdmi.common:" + "CARD=" $CARD "," + "DEVICE=14," + "CTLINDEX=8," + "AES0=" $AES0 "," + "AES1=" $AES1 "," + "AES2=" $AES2 "," + "AES3=" $AES3 + ] + } +} + +HDA-Intel.pcm.hdmi.9 { + @args [ CARD AES0 AES1 AES2 AES3 ] + @args.CARD { type string } + @args.AES0 { type integer } + @args.AES1 { type integer } + @args.AES2 { type integer } + @args.AES3 { type integer } + @func refer + name { + @func concat + strings [ + "cards.HDA-Intel.pcm.hdmi.common:" + "CARD=" $CARD "," + "DEVICE=15," + "CTLINDEX=9," + "AES0=" $AES0 "," + "AES1=" $AES1 "," + "AES2=" $AES2 "," + "AES3=" $AES3 + ] + } +} + +HDA-Intel.pcm.hdmi.10 { + @args [ CARD AES0 AES1 AES2 AES3 ] + @args.CARD { type string } + @args.AES0 { type integer } + @args.AES1 { type integer } + @args.AES2 { type integer } + @args.AES3 { type integer } + @func refer + name { + @func concat + strings [ + "cards.HDA-Intel.pcm.hdmi.common:" + "CARD=" $CARD "," + "DEVICE=16," + "CTLINDEX=10," + "AES0=" $AES0 "," + "AES1=" $AES1 "," + "AES2=" $AES2 "," + "AES3=" $AES3 + ] + } +} + HDA-Intel.pcm.modem.0 { diff -Nru alsa-lib-1.2.2/src/conf/cards/Makefile.in alsa-lib-1.2.6.1/src/conf/cards/Makefile.in --- alsa-lib-1.2.2/src/conf/cards/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/cards/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -276,6 +276,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/conf/cards/USB-Audio.conf alsa-lib-1.2.6.1/src/conf/cards/USB-Audio.conf --- alsa-lib-1.2.2/src/conf/cards/USB-Audio.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/cards/USB-Audio.conf 2021-12-09 13:17:59.000000000 +0000 @@ -38,11 +38,21 @@ USB-Audio.pcm.iec958_device { # "NoiseBlaster 3000" 42 "USB Sound Blaster HD" 1 - "Xonar U7" 1 + "SB Omni Surround 5.1" 1 + "Xonar U7" 1 + "Xonar U7 MKII" 1 + "ASUS XONAR U5" 1 + "XONAR U5" 1 + "XONAR SOUND CARD" 1 + "Xonar SoundCard" 2 + # The below don't have digital in/out, so prevent them from being opened. "Andrea PureAudio USB-SA Headset" 999 "Blue Snowball" 999 + "C-Media USB Headphone Set" 999 + "Cmedia Audio" 999 + "DELL PROFESSIONAL SOUND BAR AE5" 999 "HP Digital Stereo Headset" 999 "GN 9330" 999 "Logitech Speaker Lapdesk N700" 999 @@ -50,6 +60,7 @@ "Logitech USB Headset" 999 "Logitech USB Headset H540" 999 "Logitech Wireless Headset" 999 + "Plantronics Blackwire 3220 Seri" 999 "Plantronics GameCom 780" 999 "Plantronics USB Headset" 999 "Plantronics Wireless Audio" 999 @@ -58,6 +69,10 @@ "Scarlett 2i4 USB" 999 "Sennheiser USB headset" 999 "SWTOR Gaming Headset by Razer" 999 + "ThinkStation P620 Main" 999 + "ThinkStation P620 Rear" 999 + "Thunderbolt Dock Audio Headset" 999 + "Thunderbolt Dock Audio Module" 999 "USB Device 0x46d_0x821" 999 "USB Device 0x46d_0x992" 999 "WD15 Dock" 999 diff -Nru alsa-lib-1.2.2/src/conf/cards/aliases.conf alsa-lib-1.2.6.1/src/conf/cards/aliases.conf --- alsa-lib-1.2.2/src/conf/cards/aliases.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/cards/aliases.conf 2021-12-09 13:17:59.000000000 +0000 @@ -58,6 +58,7 @@ pistachio cards.pistachio-card VC4-HDMI cards.vc4-hdmi + diff -Nru alsa-lib-1.2.2/src/conf/ctl/Makefile.am alsa-lib-1.2.6.1/src/conf/ctl/Makefile.am --- alsa-lib-1.2.2/src/conf/ctl/Makefile.am 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/ctl/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,7 @@ +cfg_files = default.conf + +EXTRA_DIST = $(cfg_files) + +alsaconfigdir = @ALSA_CONFIG_DIR@ +alsadir = $(alsaconfigdir)/ctl +alsa_DATA = $(cfg_files) diff -Nru alsa-lib-1.2.2/src/conf/ctl/Makefile.in alsa-lib-1.2.6.1/src/conf/ctl/Makefile.in --- alsa-lib-1.2.2/src/conf/ctl/Makefile.in 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/ctl/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -0,0 +1,514 @@ +# Makefile.in generated by automake 1.16.2 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/conf/ctl +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(alsadir)" +DATA = $(alsa_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALSA_CONFIG_DIR = @ALSA_CONFIG_DIR@ +ALSA_DEPLIBS = @ALSA_DEPLIBS@ +ALSA_PKGCONF_DIR = @ALSA_PKGCONF_DIR@ +ALSA_PLUGIN_DIR = @ALSA_PLUGIN_DIR@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_NOUNDEFINED = @LDFLAGS_NOUNDEFINED@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_VERSION_INFO = @LIBTOOL_VERSION_INFO@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PYTHON_INCLUDES = @PYTHON_INCLUDES@ +PYTHON_LIBS = @PYTHON_LIBS@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SND_LIB_EXTRAVER = @SND_LIB_EXTRAVER@ +SND_LIB_MAJOR = @SND_LIB_MAJOR@ +SND_LIB_MINOR = @SND_LIB_MINOR@ +SND_LIB_SUBMINOR = @SND_LIB_SUBMINOR@ +SND_LIB_VERSION = @SND_LIB_VERSION@ +STRIP = @STRIP@ +SYMBOL_PREFIX = @SYMBOL_PREFIX@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +cfg_files = default.conf +EXTRA_DIST = $(cfg_files) +alsaconfigdir = @ALSA_CONFIG_DIR@ +alsadir = $(alsaconfigdir)/ctl +alsa_DATA = $(cfg_files) +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/conf/ctl/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/conf/ctl/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-alsaDATA: $(alsa_DATA) + @$(NORMAL_INSTALL) + @list='$(alsa_DATA)'; test -n "$(alsadir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(alsadir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(alsadir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(alsadir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(alsadir)" || exit $$?; \ + done + +uninstall-alsaDATA: + @$(NORMAL_UNINSTALL) + @list='$(alsa_DATA)'; test -n "$(alsadir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(alsadir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) +installdirs: + for dir in "$(DESTDIR)$(alsadir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-alsaDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-alsaDATA + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-alsaDATA install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-alsaDATA uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff -Nru alsa-lib-1.2.2/src/conf/ctl/default.conf alsa-lib-1.2.6.1/src/conf/ctl/default.conf --- alsa-lib-1.2.2/src/conf/ctl/default.conf 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/ctl/default.conf 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,42 @@ +# +# Default control device +# + +ctl.!default { + @args [ CARD ] + @args.CARD { + type string + default { + @func getenv + vars [ + ALSA_PCM_CARD + ALSA_CARD + ] + default { + @func refer + name defaults.ctl.card + } + } + } + type empty + child { + # use card-specific definition if exists + @func refer + name { + @func concat + strings [ + "cards." + { + @func card_inum + card $CARD + } + ".ctl.default:CARD=" $CARD + ] + } + default { + type hw + card $CARD + } + } + hint.description "Default Control Device" +} diff -Nru alsa-lib-1.2.2/src/conf/pcm/Makefile.in alsa-lib-1.2.6.1/src/conf/pcm/Makefile.in --- alsa-lib-1.2.2/src/conf/pcm/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -275,6 +275,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/conf/pcm/center_lfe.conf alsa-lib-1.2.6.1/src/conf/pcm/center_lfe.conf --- alsa-lib-1.2.2/src/conf/pcm/center_lfe.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/center_lfe.conf 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.center_lfe." $DEV ":CARD=" $CARD diff -Nru alsa-lib-1.2.2/src/conf/pcm/default.conf alsa-lib-1.2.6.1/src/conf/pcm/default.conf --- alsa-lib-1.2.2/src/conf/pcm/default.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/default.conf 2021-12-09 13:17:59.000000000 +0000 @@ -27,7 +27,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.default:CARD=" $CARD diff -Nru alsa-lib-1.2.2/src/conf/pcm/dmix.conf alsa-lib-1.2.6.1/src/conf/pcm/dmix.conf --- alsa-lib-1.2.2/src/conf/pcm/dmix.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/dmix.conf 2021-12-09 13:17:59.000000000 +0000 @@ -56,6 +56,10 @@ @func refer name defaults.pcm.ipc_perm } + tstamp_type { + @func refer + name defaults.pcm.tstamp_type + } slave { pcm { type hw @@ -73,7 +77,7 @@ strings [ "defaults.dmix." { - @func card_driver + @func card_id card $CARD } ".period_size" @@ -88,7 +92,7 @@ strings [ "defaults.dmix." { - @func card_driver + @func card_id card $CARD } ".period_time" @@ -103,7 +107,7 @@ strings [ "defaults.dmix." { - @func card_driver + @func card_id card $CARD } ".periods" diff -Nru alsa-lib-1.2.2/src/conf/pcm/dsnoop.conf alsa-lib-1.2.6.1/src/conf/pcm/dsnoop.conf --- alsa-lib-1.2.2/src/conf/pcm/dsnoop.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/dsnoop.conf 2021-12-09 13:17:59.000000000 +0000 @@ -49,6 +49,10 @@ @func refer name defaults.pcm.ipc_perm } + tstamp_type { + @func refer + name defaults.pcm.tstamp_type + } slave { pcm { type hw @@ -65,7 +69,7 @@ strings [ "cards." { - @func card_driver + @func card_id card $CARD } ".pcm.dsnoop.period_size" @@ -80,7 +84,7 @@ strings [ "cards." { - @func card_driver + @func card_id card $CARD } ".pcm.dsnoop.period_time" @@ -95,7 +99,7 @@ strings [ "cards." { - @func card_driver + @func card_id card $CARD } ".pcm.dsnoop.periods" diff -Nru alsa-lib-1.2.2/src/conf/pcm/front.conf alsa-lib-1.2.6.1/src/conf/pcm/front.conf --- alsa-lib-1.2.2/src/conf/pcm/front.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/front.conf 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.front." $DEV ":CARD=" $CARD @@ -52,7 +52,9 @@ @func refer name defaults.namehint.basic } - description "Front speakers" - device $DEV + description "Front output / input" + device_output $DEV + device_input $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/hdmi.conf alsa-lib-1.2.6.1/src/conf/pcm/hdmi.conf --- alsa-lib-1.2.2/src/conf/pcm/hdmi.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/hdmi.conf 2021-12-09 13:17:59.000000000 +0000 @@ -60,7 +60,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.hdmi." $DEV ":" diff -Nru alsa-lib-1.2.2/src/conf/pcm/iec958.conf alsa-lib-1.2.6.1/src/conf/pcm/iec958.conf --- alsa-lib-1.2.2/src/conf/pcm/iec958.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/iec958.conf 2021-12-09 13:17:59.000000000 +0000 @@ -60,7 +60,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.iec958." $DEV ":" @@ -78,6 +78,6 @@ name defaults.namehint.basic } description "IEC958 (S/PDIF) Digital Audio Output" - device $DEV + device_output $DEV } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/modem.conf alsa-lib-1.2.6.1/src/conf/pcm/modem.conf --- alsa-lib-1.2.2/src/conf/pcm/modem.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/modem.conf 2021-12-09 13:17:59.000000000 +0000 @@ -38,7 +38,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.modem." $DEV ":CARD=" $CARD diff -Nru alsa-lib-1.2.2/src/conf/pcm/rear.conf alsa-lib-1.2.6.1/src/conf/pcm/rear.conf --- alsa-lib-1.2.2/src/conf/pcm/rear.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/rear.conf 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.rear." $DEV ":CARD=" $CARD diff -Nru alsa-lib-1.2.2/src/conf/pcm/side.conf alsa-lib-1.2.6.1/src/conf/pcm/side.conf --- alsa-lib-1.2.2/src/conf/pcm/side.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/side.conf 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.side." $DEV ":CARD=" $CARD diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround21.conf alsa-lib-1.2.6.1/src/conf/pcm/surround21.conf --- alsa-lib-1.2.2/src/conf/pcm/surround21.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround21.conf 2021-12-09 13:17:59.000000000 +0000 @@ -44,7 +44,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround51." $DEV ":CARD=" $CARD @@ -57,5 +57,6 @@ hint { description "2.1 Surround output to Front and Subwoofer speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround40.conf alsa-lib-1.2.6.1/src/conf/pcm/surround40.conf --- alsa-lib-1.2.2/src/conf/pcm/surround40.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround40.conf 2021-12-09 13:17:59.000000000 +0000 @@ -45,7 +45,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround40." $DEV ":CARD=" $CARD @@ -55,5 +55,6 @@ hint { description "4.0 Surround output to Front and Rear speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround41.conf alsa-lib-1.2.6.1/src/conf/pcm/surround41.conf --- alsa-lib-1.2.2/src/conf/pcm/surround41.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround41.conf 2021-12-09 13:17:59.000000000 +0000 @@ -46,7 +46,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround51." $DEV ":CARD=" $CARD @@ -61,5 +61,6 @@ hint { description "4.1 Surround output to Front, Rear and Subwoofer speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround50.conf alsa-lib-1.2.6.1/src/conf/pcm/surround50.conf --- alsa-lib-1.2.2/src/conf/pcm/surround50.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround50.conf 2021-12-09 13:17:59.000000000 +0000 @@ -46,7 +46,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround51." $DEV ":CARD=" $CARD @@ -61,5 +61,6 @@ hint { description "5.0 Surround output to Front, Center and Rear speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround51.conf alsa-lib-1.2.6.1/src/conf/pcm/surround51.conf --- alsa-lib-1.2.2/src/conf/pcm/surround51.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround51.conf 2021-12-09 13:17:59.000000000 +0000 @@ -47,7 +47,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround51." $DEV ":CARD=" $CARD @@ -57,5 +57,6 @@ hint { description "5.1 Surround output to Front, Center, Rear and Subwoofer speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf/pcm/surround71.conf alsa-lib-1.2.6.1/src/conf/pcm/surround71.conf --- alsa-lib-1.2.2/src/conf/pcm/surround71.conf 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf/pcm/surround71.conf 2021-12-09 13:17:59.000000000 +0000 @@ -49,7 +49,7 @@ strings [ "cards." { - @func card_driver + @func card_inum card $CARD } ".pcm.surround71." $DEV ":CARD=" $CARD @@ -59,5 +59,6 @@ hint { description "7.1 Surround output to Front, Center, Side, Rear and Woofer speakers" device_output $DEV + omit_noargs true } } diff -Nru alsa-lib-1.2.2/src/conf.c alsa-lib-1.2.6.1/src/conf.c --- alsa-lib-1.2.2/src/conf.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/conf.c 2021-12-09 13:17:59.000000000 +0000 @@ -318,6 +318,15 @@ card $CARD \endcode +\section confarg_math simple math expressions + +The simple math expressions are identified using a unix shell like expression syntax +with a dollar-sign ($) and bracket ([): + +\code + card "$[$CARD + 1]" +\endcode + \section confarg_usage Usage To use a block with arguments, write the argument values after the key, @@ -355,6 +364,7 @@ } \endcode + */ /*! \page conffunc Runtime functions in configuration files @@ -416,6 +426,7 @@ #include "local.h" #include +#include #include #include #include @@ -443,7 +454,7 @@ const void *ptr; struct { struct list_head fields; - int join; + bool join; } compound; } u; struct list_head list; @@ -652,29 +663,30 @@ return err; } -static int safe_strtoll(const char *str, long long *val) +int safe_strtoll_base(const char *str, long long *val, int base) { - long long v; - int endidx; + char *end; + long v; if (!*str) return -EINVAL; errno = 0; - if (sscanf(str, "%lli%n", &v, &endidx) < 1) - return -EINVAL; - if (str[endidx]) + v = strtoll(str, &end, base); + if (errno) + return -errno; + if (*end) return -EINVAL; *val = v; return 0; } -int safe_strtol(const char *str, long *val) +int safe_strtol_base(const char *str, long *val, int base) { char *end; long v; if (!*str) return -EINVAL; errno = 0; - v = strtol(str, &end, 0); + v = strtol(str, &end, base); if (errno) return -errno; if (*end) @@ -886,7 +898,7 @@ if (c >= '0' && c <= '9') num |= (c - '0') << 0; else if (c >= 'a' && c <= 'f') num |= (c - 'a') << 0; else if (c >= 'A' && c <= 'F') num |= (c - 'A') << 0; - return c; + return num; } static int get_quotedchar(input_t *input) @@ -1393,7 +1405,7 @@ SNDERR("%s is not a compound", id); return -EINVAL; } - n->u.compound.join = 1; + n->u.compound.join = true; parent = n; free(id); continue; @@ -1408,7 +1420,7 @@ err = _snd_config_make_add(&n, &id, SND_CONFIG_TYPE_COMPOUND, parent); if (err < 0) goto __end; - n->u.compound.join = 1; + n->u.compound.join = true; parent = n; } if (c == '=') { @@ -1507,6 +1519,7 @@ static void string_print(char *str, int id, snd_output_t *out) { + int q; unsigned char *p = (unsigned char *)str; if (!p || !*p) { snd_output_puts(out, "''"); @@ -1535,6 +1548,8 @@ case ']': case '\'': case '"': + case '*': + case '#': goto quoted; default: if (*p <= 31 || *p >= 127) @@ -1546,7 +1561,8 @@ snd_output_puts(out, str); return; quoted: - snd_output_putc(out, '\''); + q = strchr(str, '\'') ? '"' : '\''; + snd_output_putc(out, q); p = (unsigned char *)str; while (*p) { int c; @@ -1576,30 +1592,39 @@ snd_output_putc(out, '\\'); snd_output_putc(out, 'f'); break; - case '\'': - snd_output_putc(out, '\\'); - snd_output_putc(out, c); - break; default: - if (c >= 32 && c <= 126 && c != '\'') + if (c == q) { + snd_output_putc(out, '\\'); snd_output_putc(out, c); - else - snd_output_printf(out, "\\%04o", c); + } else { + if (c >= 32 && c <= 126) + snd_output_putc(out, c); + else + snd_output_printf(out, "\\%04o", c); + } break; } p++; } - snd_output_putc(out, '\''); + snd_output_putc(out, q); +} + +static void level_print(snd_output_t *out, unsigned int level) +{ + char a[level + 1]; + memset(a, '\t', level); + a[level] = '\0'; + snd_output_puts(out, a); } static int _snd_config_save_children(snd_config_t *config, snd_output_t *out, - unsigned int level, unsigned int joins); + unsigned int level, unsigned int joins, + int array); -static int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out, - unsigned int level) +int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out, + unsigned int level) { - int err; - unsigned int k; + int err, array; switch (n->type) { case SND_CONFIG_TYPE_INTEGER: snd_output_printf(out, "%ld", n->u.integer); @@ -1617,15 +1642,14 @@ SNDERR("cannot save runtime pointer type"); return -EINVAL; case SND_CONFIG_TYPE_COMPOUND: - snd_output_putc(out, '{'); + array = snd_config_is_array(n); + snd_output_putc(out, array ? '[' : '{'); snd_output_putc(out, '\n'); - err = _snd_config_save_children(n, out, level + 1, 0); + err = _snd_config_save_children(n, out, level + 1, 0, array); if (err < 0) return err; - for (k = 0; k < level; ++k) { - snd_output_putc(out, '\t'); - } - snd_output_putc(out, '}'); + level_print(out, level); + snd_output_putc(out, array ? ']' : '}'); break; } return 0; @@ -1642,9 +1666,9 @@ } static int _snd_config_save_children(snd_config_t *config, snd_output_t *out, - unsigned int level, unsigned int joins) + unsigned int level, unsigned int joins, + int array) { - unsigned int k; int err; snd_config_iterator_t i, next; assert(config && out); @@ -1652,20 +1676,19 @@ snd_config_t *n = snd_config_iterator_entry(i); if (n->type == SND_CONFIG_TYPE_COMPOUND && n->u.compound.join) { - err = _snd_config_save_children(n, out, level, joins + 1); + err = _snd_config_save_children(n, out, level, joins + 1, 0); if (err < 0) return err; continue; } - for (k = 0; k < level; ++k) { - snd_output_putc(out, '\t'); - } - id_print(n, out, joins); + level_print(out, level); + if (!array) { + id_print(n, out, joins); + snd_output_putc(out, ' '); #if 0 - snd_output_putc(out, ' '); - snd_output_putc(out, '='); + snd_output_putc(out, '='); #endif - snd_output_putc(out, ' '); + } err = _snd_config_save_node_value(n, out, level); if (err < 0) return err; @@ -1685,8 +1708,9 @@ * \param src Handle to the source node. Must not be the same as \a dst. * \return Zero if successful, otherwise a negative error code. * - * If both nodes are compounds, the source compound node members are - * appended to the destination compound node. + * If both nodes are compounds, the source compound node members will + * be moved to the destination compound node. The original destination + * compound node members will be deleted (overwritten). * * If the destination node is a compound and the source node is * an ordinary type, the compound members are deleted (including @@ -1701,8 +1725,13 @@ int snd_config_substitute(snd_config_t *dst, snd_config_t *src) { assert(dst && src); + if (dst->type == SND_CONFIG_TYPE_COMPOUND) { + int err = snd_config_delete_compound_members(dst); + if (err < 0) + return err; + } if (dst->type == SND_CONFIG_TYPE_COMPOUND && - src->type == SND_CONFIG_TYPE_COMPOUND) { /* append */ + src->type == SND_CONFIG_TYPE_COMPOUND) { /* overwrite */ snd_config_iterator_t i, next; snd_config_for_each(i, next, src) { snd_config_t *n = snd_config_iterator_entry(i); @@ -1710,11 +1739,6 @@ } src->u.compound.fields.next->prev = &dst->u.compound.fields; src->u.compound.fields.prev->next = &dst->u.compound.fields; - } else if (dst->type == SND_CONFIG_TYPE_COMPOUND) { - int err; - err = snd_config_delete_compound_members(dst); - if (err < 0) - return err; } free(dst->id); dst->id = src->id; @@ -1783,6 +1807,59 @@ return config->type; } +static int check_array_item(const char *id, int index) +{ + const char *p; + long val; + + for (p = id; *p; p++) { + if (*p < '0' || *p > '9') + return 0; + } + + if (safe_strtol(id, &val)) + return 0; + return val == index; +} + +/** + * \brief Returns if the compound is an array (and count of items). + * \param config Handle to the configuration node. + * \return A count of items in array, zero when the compound is not an array, + * otherwise a negative error code. + */ +int snd_config_is_array(const snd_config_t *config) +{ + int idx; + snd_config_iterator_t i, next; + snd_config_t *node; + + assert(config); + if (config->type != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + idx = 0; + snd_config_for_each(i, next, config) { + node = snd_config_iterator_entry(i); + if (!check_array_item(node->id, idx)) + return 0; + idx++; + } + return idx; +} + +/** + * \brief Returns if the compound has no fields (is empty). + * \param config Handle to the configuration node. + * \return A positive value when true, zero when false, otherwise a negative error code. + */ +int snd_config_is_empty(const snd_config_t *config) +{ + assert(config); + if (config->type != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + return list_empty(&config->u.compound.fields); +} + /** * \brief Returns the id of a configuration node. * \param[in] config Handle to the configuration node. @@ -1928,11 +2005,14 @@ SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "_toplevel_", fd->line, fd->column, str); goto _end; } - if (get_char(&input) != LOCAL_UNEXPECTED_EOF) { + err = get_char(&input); + fd = input.current; + if (err != LOCAL_UNEXPECTED_EOF) { SNDERR("%s:%d:%d:Unexpected }", fd->name ? fd->name : "", fd->line, fd->column); err = -EINVAL; goto _end; } + err = 0; _end: while (fd->next) { fd_next = fd->next; @@ -1971,6 +2051,49 @@ } /** + * \brief Loads a configuration tree from a string. + * \param[out] The function puts the handle to the configuration + * node loaded from the file(s) at the address specified + * by \a config. + * \param[in] s String with the ASCII configuration + * \param[in] size String size, if zero, a C string is expected (with termination) + * \return Zero if successful, otherwise a negative error code. + * + * The definitions loaded from the string are put to \a config, which + * is created as a new top node. + * + * \par Errors: + * Any errors encountered when parsing the input or returned by hooks or + * functions. + */ +int snd_config_load_string(snd_config_t **config, const char *s, size_t size) +{ + snd_input_t *input; + snd_config_t *dst; + int err; + + assert(config && s); + if (size == 0) + size = strlen(s); + err = snd_input_buffer_open(&input, s, size); + if (err < 0) + return err; + err = snd_config_top(&dst); + if (err < 0) { + snd_input_close(input); + return err; + } + err = snd_config_load(dst, input); + snd_input_close(input); + if (err < 0) { + snd_config_delete(dst); + return err; + } + *config = dst; + return 0; +} + +/** * \brief Loads a configuration tree and overrides existing configuration nodes. * \param config Handle to a top level configuration node. * \param in Input handle to read the configuration from. @@ -2107,6 +2230,103 @@ return 0; } +/* + * append all src items to the end of dst arrray + */ +static int _snd_config_array_merge(snd_config_t *dst, snd_config_t *src, int index) +{ + snd_config_iterator_t si, snext; + int err; + + snd_config_for_each(si, snext, src) { + snd_config_t *sn = snd_config_iterator_entry(si); + char id[16]; + snd_config_remove(sn); + snprintf(id, sizeof(id), "%d", index++); + err = snd_config_set_id(sn, id); + if (err < 0) { + snd_config_delete(sn); + return err; + } + sn->parent = dst; + list_add_tail(&sn->list, &dst->u.compound.fields); + } + snd_config_delete(src); + return 0; +} + +/** + * \brief In-place merge of two config handles + * \param dst[out] Config handle for the merged contents + * \param src[in] Config handle to merge into dst (may be NULL) + * \param override[in] Override flag + * \return Zero if successful, otherwise a negative error code. + * + * This function merges all fields from the source compound to the destination compound. + * When the \a override flag is set, the related subtree in \a dst is replaced from \a src. + * + * When \a override is not set, the child compounds are traversed and merged. + * + * The configuration elements other than compounds are always substituted (overwritten) + * from the \a src config handle. + * + * The src handle is deleted. + * + * Note: On error, config handles may be modified. + * + * \par Errors: + *
+ *
-EEXIST
identifier already exists (!overwrite) + *
-ENOMEM
not enough memory + *
+ */ +int snd_config_merge(snd_config_t *dst, snd_config_t *src, int override) +{ + snd_config_iterator_t di, si, dnext, snext; + bool found; + int err, array; + + assert(dst); + if (src == NULL) + return 0; + if (dst->type != SND_CONFIG_TYPE_COMPOUND || src->type != SND_CONFIG_TYPE_COMPOUND) + return snd_config_substitute(dst, src); + array = snd_config_is_array(dst); + if (array && snd_config_is_array(src)) + return _snd_config_array_merge(dst, src, array); + snd_config_for_each(si, snext, src) { + snd_config_t *sn = snd_config_iterator_entry(si); + found = false; + snd_config_for_each(di, dnext, dst) { + snd_config_t *dn = snd_config_iterator_entry(di); + if (strcmp(sn->id, dn->id) == 0) { + if (override || + sn->type != SND_CONFIG_TYPE_COMPOUND || + dn->type != SND_CONFIG_TYPE_COMPOUND) { + snd_config_remove(sn); + err = snd_config_substitute(dn, sn); + if (err < 0) + return err; + } else { + err = snd_config_merge(dn, sn, 0); + if (err < 0) + return err; + } + found = true; + break; + } + } + if (!found) { + /* move config from src to dst */ + snd_config_remove(sn); + sn->parent = dst; + list_add_tail(&sn->list, &dst->u.compound.fields); + } + } + snd_config_delete(src); + return 0; +} + /** * \brief Removes a configuration node from its tree. * \param config Handle to the configuration node to be removed. @@ -2427,6 +2647,103 @@ } /** + * \brief Creates an empty compound configuration node in the path. + * \param[out] config The function puts the handle to the new or + * existing compound node at the address specified + * by \a config. + * \param[in] root The id of the new node. + * \param[in] key The id of the new node. + * \param[in] join Join flag. + * \param[in] override Override flag. + * \return Zero if successful, otherwise a negative error code. + * + * This function creates a new empty node of type + * #SND_CONFIG_TYPE_COMPOUND if the path does not exist. Otherwise, + * the node from the current configuration tree is returned without + * any modification. The \a join argument is ignored in this case. + * + * \a join determines how the compound node's id is printed when the + * configuration is saved to a text file. For example, if the join flag + * of compound node \c a is zero, the output will look as follows: + * \code + * a { + * b "hello" + * c 42 + * } + * \endcode + * If, however, the join flag of \c a is nonzero, its id will be joined + * with its children's ids, like this: + * \code + * a.b "hello" + * a.c 42 + * \endcode + * An \e empty compound node with its join flag set would result in no + * output, i.e., after saving and reloading the configuration file, that + * compound node would be lost. + * + * \par Errors: + *
+ *
-ENOMEM
Out of memory. + *
-EACCESS
Path exists, but it's not a compound (!override) + *
+ */ +int snd_config_make_path(snd_config_t **config, snd_config_t *root, + const char *key, int join, int override) +{ + snd_config_t *n; + const char *p; + int err; + + while (1) { + p = strchr(key, '.'); + if (p) { + err = _snd_config_search(root, key, p - key, &n); + if (err < 0) { + size_t l = p - key; + char *s = malloc(l + 1); + if (s == NULL) + return -ENOMEM; + strncpy(s, key, l); + s[l] = '\0'; + err = snd_config_make_compound(&n, s, join); + free(s); + if (err < 0) + return err; + err = snd_config_add(root, n); + if (err < 0) + return err; + } + root = n; + key = p + 1; + } else { + err = _snd_config_search(root, key, -1, config); + if (err == 0) { + if ((*config)->type != SND_CONFIG_TYPE_COMPOUND) { + if (override) { + err = snd_config_delete(*config); + if (err < 0) + return err; + goto __make; + } else { + return -EACCES; + } + } + return 0; + } +__make: + err = snd_config_make_compound(&n, key, join); + if (err < 0) + return err; + err = snd_config_add(root, n); + if (err < 0) + return err; + *config = n; + return 0; + } + } +} + +/** * \brief Creates an integer configuration node with the given initial value. * \param[out] config The function puts the handle to the new node at * the address specified by \a config. @@ -3082,10 +3399,12 @@ int snd_config_save(snd_config_t *config, snd_output_t *out) { assert(config && out); - if (config->type == SND_CONFIG_TYPE_COMPOUND) - return _snd_config_save_children(config, out, 0, 0); - else + if (config->type == SND_CONFIG_TYPE_COMPOUND) { + int array = snd_config_is_array(config); + return _snd_config_save_children(config, out, 0, 0, array); + } else { return _snd_config_save_node_value(config, out, 0); + } } /* @@ -3779,6 +4098,127 @@ return err; } +static int config_file_load(snd_config_t *root, const char *fn, int errors) +{ + struct stat st; + struct dirent **namelist; + int err, n; + + if (!errors && access(fn, R_OK) < 0) + return 1; + if (stat(fn, &st) < 0) { + SNDERR("cannot stat file/directory %s", fn); + return 1; + } + if (!S_ISDIR(st.st_mode)) + return config_file_open(root, fn); +#ifndef DOC_HIDDEN +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID) +#define SORTFUNC versionsort +#else +#define SORTFUNC alphasort +#endif +#endif + n = scandir(fn, &namelist, config_filename_filter, SORTFUNC); + if (n > 0) { + int j; + err = 0; + for (j = 0; j < n; ++j) { + if (err >= 0) { + int sl = strlen(fn) + strlen(namelist[j]->d_name) + 2; + char *filename = malloc(sl); + snprintf(filename, sl, "%s/%s", fn, namelist[j]->d_name); + filename[sl-1] = '\0'; + + err = config_file_open(root, filename); + free(filename); + } + free(namelist[j]); + } + free(namelist); + if (err < 0) + return err; + } + return 0; +} + +static int config_file_load_user(snd_config_t *root, const char *fn, int errors) +{ + char *fn2; + int err; + + err = snd_user_file(fn, &fn2); + if (err < 0) + return config_file_load(root, fn, errors); + err = config_file_load(root, fn2, errors); + free(fn2); + return err; +} + +static int config_file_load_user_all(snd_config_t *_root, snd_config_t *_file, int errors) +{ + snd_config_t *file = _file, *root = _root, *n; + char *name, *name2, *remain, *rname = NULL; + int err; + + if (snd_config_get_type(_file) == SND_CONFIG_TYPE_COMPOUND) { + if ((err = snd_config_search(_file, "file", &file)) < 0) { + SNDERR("Field file not found"); + return err; + } + if ((err = snd_config_search(_file, "root", &root)) >= 0) { + err = snd_config_get_ascii(root, &rname); + if (err < 0) { + SNDERR("Field root is bad"); + return err; + } + err = snd_config_make_compound(&root, rname, 0); + if (err < 0) + return err; + } + } + if ((err = snd_config_get_ascii(file, &name)) < 0) + goto _err; + name2 = name; + remain = strstr(name, "|||"); + while (1) { + if (remain) { + *remain = '\0'; + remain += 3; + } + err = config_file_load_user(root, name2, errors); + if (err < 0) + goto _err; + if (err == 0) /* first hit wins */ + break; + if (!remain) + break; + name2 = remain; + remain = strstr(remain, "|||"); + } +_err: + if (root != _root) { + if (err == 0) { + if (snd_config_get_type(root) == SND_CONFIG_TYPE_COMPOUND) { + if (snd_config_is_empty(root)) + goto _del; + } + err = snd_config_make_path(&n, _root, rname, 0, 1); + if (err < 0) + goto _del; + err = snd_config_substitute(n, root); + if (err == 0) + goto _fin; + } +_del: + snd_config_delete(root); + } +_fin: + free(name); + free(rname); + return err; +} + /** * \brief Loads and parses the given configurations files. * \param[in] root Handle to the root configuration node. @@ -3795,17 +4235,11 @@ { snd_config_t *n; snd_config_iterator_t i, next; - struct finfo *fi = NULL; - int err, idx = 0, fi_count = 0, errors = 1, hit; + int err, idx = 0, errors = 1, hit; assert(root && dst); if ((err = snd_config_search(config, "errors", &n)) >= 0) { - char *tmp; - err = snd_config_get_ascii(n, &tmp); - if (err < 0) - return err; - errors = snd_config_get_bool_ascii(tmp); - free(tmp); + errors = snd_config_get_bool(n); if (errors < 0) { SNDERR("Invalid bool value in field errors"); return errors; @@ -3823,20 +4257,6 @@ SNDERR("Invalid type for field filenames"); goto _err; } - snd_config_for_each(i, next, n) { - snd_config_t *c = snd_config_iterator_entry(i); - const char *str; - if ((err = snd_config_get_string(c, &str)) < 0) { - SNDERR("Field %s is not a string", c->id); - goto _err; - } - fi_count++; - } - fi = calloc(fi_count, sizeof(*fi)); - if (fi == NULL) { - err = -ENOMEM; - goto _err; - } do { hit = 0; snd_config_for_each(i, next, n) { @@ -3850,67 +4270,17 @@ goto _err; } if (i == idx) { - char *name; - if ((err = snd_config_get_ascii(n, &name)) < 0) + err = config_file_load_user_all(root, n, errors); + if (err < 0) goto _err; - if ((err = snd_user_file(name, &fi[idx].name)) < 0) - fi[idx].name = name; - else - free(name); idx++; hit = 1; } } } while (hit); - for (idx = 0; idx < fi_count; idx++) { - struct stat st; - if (!errors && access(fi[idx].name, R_OK) < 0) - continue; - if (stat(fi[idx].name, &st) < 0) { - SNDERR("cannot stat file/directory %s", fi[idx].name); - continue; - } - if (S_ISDIR(st.st_mode)) { - struct dirent **namelist; - int n; - -#ifndef DOC_HIDDEN -#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID) -#define SORTFUNC versionsort -#else -#define SORTFUNC alphasort -#endif -#endif - n = scandir(fi[idx].name, &namelist, config_filename_filter, SORTFUNC); - if (n > 0) { - int j; - err = 0; - for (j = 0; j < n; ++j) { - if (err >= 0) { - int sl = strlen(fi[idx].name) + strlen(namelist[j]->d_name) + 2; - char *filename = malloc(sl); - snprintf(filename, sl, "%s/%s", fi[idx].name, namelist[j]->d_name); - filename[sl-1] = '\0'; - - err = config_file_open(root, filename); - free(filename); - } - free(namelist[j]); - } - free(namelist); - if (err < 0) - goto _err; - } - } else if ((err = config_file_open(root, fi[idx].name)) < 0) - goto _err; - } *dst = NULL; err = 0; _err: - if (fi) - for (idx = 0; idx < fi_count; idx++) - free(fi[idx].name); - free(fi); snd_config_delete(n); return err; } @@ -3922,6 +4292,75 @@ int snd_determine_driver(int card, char **driver); #endif +snd_config_t *_snd_config_hook_private_data(int card, const char *driver) +{ + snd_config_t *private_data, *v; + int err; + + err = snd_config_make_compound(&private_data, NULL, 0); + if (err < 0) + goto __err; + err = snd_config_imake_integer(&v, "integer", card); + if (err < 0) + goto __err; + err = snd_config_add(private_data, v); + if (err < 0) { + snd_config_delete(v); + goto __err; + } + err = snd_config_imake_string(&v, "string", driver); + if (err < 0) + goto __err; + err = snd_config_add(private_data, v); + if (err < 0) { + snd_config_delete(v); + goto __err; + } + return private_data; + +__err: + snd_config_delete(private_data); + return NULL; +} + +static int _snd_config_hook_table(snd_config_t *root, snd_config_t *config, snd_config_t *private_data) +{ + snd_config_t *n, *tn; + const char *id; + int err; + + if (snd_config_search(config, "table", &n) < 0) + return 0; + if ((err = snd_config_expand(n, root, NULL, private_data, &n)) < 0) { + SNDERR("Unable to expand table compound"); + return err; + } + if (snd_config_search(n, "id", &tn) < 0 || + snd_config_get_string(tn, &id) < 0) { + SNDERR("Unable to find field table.id"); + snd_config_delete(n); + return -EINVAL; + } + if (snd_config_search(n, "value", &tn) < 0 || + snd_config_get_type(tn) != SND_CONFIG_TYPE_STRING) { + SNDERR("Unable to find field table.value"); + snd_config_delete(n); + return -EINVAL; + } + snd_config_remove(tn); + if ((err = snd_config_set_id(tn, id)) < 0) { + snd_config_delete(tn); + snd_config_delete(n); + return err; + } + snd_config_delete(n); + if ((err = snd_config_add(root, tn)) < 0) { + snd_config_delete(tn); + return err; + } + return 0; +} + /** * \brief Loads and parses the given configurations files for each * installed sound card. @@ -3940,22 +4379,31 @@ int snd_config_hook_load_for_all_cards(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data ATTRIBUTE_UNUSED) { int card = -1, err; + snd_config_t *loaded; // trace loaded cards + err = snd_config_top(&loaded); + if (err < 0) + return err; do { err = snd_card_next(&card); if (err < 0) - return err; + goto __fin_err; if (card >= 0) { - snd_config_t *n, *private_data = NULL; + snd_config_t *n, *m, *private_data = NULL; const char *driver; char *fdriver = NULL; + bool load; err = snd_determine_driver(card, &fdriver); if (err < 0) - return err; + goto __fin_err; if (snd_config_search(root, fdriver, &n) >= 0) { - if (snd_config_get_string(n, &driver) < 0) + if (snd_config_get_string(n, &driver) < 0) { + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND) { + snd_config_get_id(n, &driver); + goto __std; + } goto __err; - assert(driver); + } while (1) { char *s = strchr(driver, '.'); if (s == NULL) @@ -3967,20 +4415,44 @@ } else { driver = fdriver; } - err = snd_config_imake_string(&private_data, "string", driver); + __std: + load = true; + err = snd_config_imake_integer(&m, driver, 1); if (err < 0) goto __err; - err = snd_config_hook_load(root, config, &n, private_data); + err = snd_config_add(loaded, m); + if (err < 0) { + if (err == -EEXIST) { + snd_config_delete(m); + load = false; + } else { + goto __err; + } + } + private_data = _snd_config_hook_private_data(card, driver); + if (!private_data) { + err = -ENOMEM; + goto __err; + } + err = _snd_config_hook_table(root, config, private_data); + if (err < 0) + goto __err; + if (load) + err = snd_config_hook_load(root, config, &n, private_data); __err: if (private_data) snd_config_delete(private_data); free(fdriver); if (err < 0) - return err; + goto __fin_err; } } while (card >= 0); + snd_config_delete(loaded); *dst = NULL; return 0; +__fin_err: + snd_config_delete(loaded); + return err; } #ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(snd_config_hook_load_for_all_cards, SND_CONFIG_DLSYM_VERSION_HOOK); @@ -4383,21 +4855,23 @@ snd_config_t *root, snd_config_t **dst, snd_config_walk_pass_t pass, - snd_config_t *private_data); + snd_config_expand_fcn_t fcn, + void *private_data); #endif static int snd_config_walk(snd_config_t *src, snd_config_t *root, snd_config_t **dst, snd_config_walk_callback_t callback, - snd_config_t *private_data) + snd_config_expand_fcn_t fcn, + void *private_data) { int err; snd_config_iterator_t i, next; switch (snd_config_get_type(src)) { case SND_CONFIG_TYPE_COMPOUND: - err = callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, private_data); + err = callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, fcn, private_data); if (err <= 0) return err; snd_config_for_each(i, next, src) { @@ -4405,7 +4879,7 @@ snd_config_t *d = NULL; err = snd_config_walk(s, root, (dst && *dst) ? &d : NULL, - callback, private_data); + callback, fcn, private_data); if (err < 0) goto _error; if (err && d) { @@ -4414,7 +4888,7 @@ goto _error; } } - err = callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, private_data); + err = callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, fcn, private_data); if (err <= 0) { _error: if (dst && *dst) @@ -4422,7 +4896,7 @@ } break; default: - err = callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, private_data); + err = callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, fcn, private_data); break; } return err; @@ -4432,7 +4906,8 @@ snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t **dst, snd_config_walk_pass_t pass, - snd_config_t *private_data ATTRIBUTE_UNUSED) + snd_config_expand_fcn_t fcn ATTRIBUTE_UNUSED, + void *private_data ATTRIBUTE_UNUSED) { int err; const char *id = src->id; @@ -4513,14 +4988,25 @@ int snd_config_copy(snd_config_t **dst, snd_config_t *src) { - return snd_config_walk(src, NULL, dst, _snd_config_copy, NULL); + return snd_config_walk(src, NULL, dst, _snd_config_copy, NULL, NULL); +} + +static int _snd_config_expand_vars(snd_config_t **dst, const char *s, void *private_data) +{ + snd_config_t *val, *vars = private_data; + if (snd_config_search(vars, s, &val) < 0) { + *dst = NULL; + return 0; + } + return snd_config_copy(dst, val); } static int _snd_config_expand(snd_config_t *src, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t **dst, snd_config_walk_pass_t pass, - snd_config_t *private_data) + snd_config_expand_fcn_t fcn, + void *private_data) { int err; const char *id = src->id; @@ -4570,16 +5056,14 @@ case SND_CONFIG_TYPE_STRING: { const char *s; - snd_config_t *val; snd_config_t *vars = private_data; snd_config_get_string(src, &s); if (s && *s == '$') { - s++; - if (snd_config_search(vars, s, &val) < 0) - return 0; - err = snd_config_copy(dst, val); + err = snd_config_evaluate_string(dst, s, fcn, vars); if (err < 0) return err; + if (*dst == NULL) + return 0; err = snd_config_set_id(*dst, id); if (err < 0) { snd_config_delete(*dst); @@ -4606,7 +5090,8 @@ snd_config_t *root, snd_config_t **dst ATTRIBUTE_UNUSED, snd_config_walk_pass_t pass, - snd_config_t *private_data) + snd_config_expand_fcn_t fcn ATTRIBUTE_UNUSED, + void *private_data) { int err; if (pass == SND_CONFIG_WALK_PASS_PRE) { @@ -4692,13 +5177,8 @@ if (err < 0) SNDERR("function %s returned error: %s", func_name, snd_strerror(err)); snd_dlclose(h); - if (err >= 0 && eval) { - /* substitute merges compound members */ - /* we don't want merging at all */ - err = snd_config_delete_compound_members(src); - if (err >= 0) - err = snd_config_substitute(src, eval); - } + if (err >= 0 && eval) + err = snd_config_substitute(src, eval); } _errbuf: free(buf); @@ -4725,7 +5205,7 @@ { /* FIXME: Only in place evaluation is currently implemented */ assert(result == NULL); - return snd_config_walk(config, root, result, _snd_config_evaluate, private_data); + return snd_config_walk(config, root, result, _snd_config_evaluate, NULL, private_data); } static int load_defaults(snd_config_t *subs, snd_config_t *defs) @@ -5017,6 +5497,8 @@ const char *new = str; const char *tmp; char *val = NULL; + + sub = NULL; err = parse_arg(&new, &varlen, &val); if (err < 0) goto _err; @@ -5041,6 +5523,7 @@ err = snd_config_search(subs, var, &sub); if (err >= 0) snd_config_delete(sub); + sub = NULL; err = snd_config_search(def, "type", &typ); if (err < 0) { _invalid_type: @@ -5106,6 +5589,8 @@ err = snd_config_add(subs, sub); if (err < 0) { _err: + if (sub) + snd_config_delete(sub); free(val); return err; } @@ -5124,6 +5609,41 @@ * \brief Expands a configuration node, applying arguments and functions. * \param[in] config Handle to the configuration node. * \param[in] root Handle to the root configuration node. + * \param[in] fcn Custom function to obtain the referred variable name + * \param[in] private_data Private data node for the custom function + * \param[out] result The function puts the handle to the result + * configuration node at the address specified by + * \a result. + * \return A non-negative value if successful, otherwise a negative error code. + * + * If \a config has arguments (defined by a child with id \c \@args), + * this function replaces any string node beginning with $ with the + * respective argument value, or the default argument value, or nothing. + * Furthermore, any functions are evaluated (see #snd_config_evaluate). + * The resulting copy of \a config is returned in \a result. + * + * The new tree is not evaluated (\ref snd_config_evaluate). + */ +int snd_config_expand_custom(snd_config_t *config, snd_config_t *root, + snd_config_expand_fcn_t fcn, void *private_data, + snd_config_t **result) +{ + snd_config_t *res; + int err; + + err = snd_config_walk(config, root, &res, _snd_config_expand, fcn, private_data); + if (err < 0) { + SNDERR("Expand error (walk): %s", snd_strerror(err)); + return err; + } + *result = res; + return 1; +} + +/** + * \brief Expands a configuration node, applying arguments and functions. + * \param[in] config Handle to the configuration node. + * \param[in] root Handle to the root configuration node. * \param[in] args Arguments string, can be \c NULL. * \param[in] private_data Handle to the private data node for functions. * \param[out] result The function puts the handle to the result @@ -5170,7 +5690,7 @@ SNDERR("Args evaluate error: %s", snd_strerror(err)); goto _end; } - err = snd_config_walk(config, root, &res, _snd_config_expand, subs); + err = snd_config_walk(config, root, &res, _snd_config_expand, _snd_config_expand_vars, subs); if (err < 0) { SNDERR("Expand error (walk): %s", snd_strerror(err)); goto _end; diff -Nru alsa-lib-1.2.2/src/confeval.c alsa-lib-1.2.6.1/src/confeval.c --- alsa-lib-1.2.2/src/confeval.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/confeval.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,273 @@ +/** + * \file confeval.c + * \ingroup Configuration + * \brief Configuration helper functions + * \author Jaroslav Kysela + * \date 2021 + * + * Configuration string evaluation. + * + * See the \ref confarg_math page for more details. + */ +/* + * Configuration string evaluation + * Copyright (c) 2000 by Abramo Bagnara , + * Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include "local.h" + +typedef long long value_type_t; + +static const char *_find_end_of_expression(const char *s, char begin, char end) +{ + int count = 1; + while (*s) { + if (*s == begin) { + count++; + } else if (*s == end) { + count--; + if (count == 0) + return s + 1; + } + s++; + } + return NULL; +} + +static int _parse_integer(value_type_t *val, const char **s) +{ + long long v; + char *end; + + errno = 0; + v = strtoll(*s, &end, 0); + if (errno) + return -errno; + *val = v; + if (((long long)*val) != v) + return -ERANGE; + *s = end; + return 0; +} + +static int _to_integer(value_type_t *val, snd_config_t *c) +{ + int err; + + switch(snd_config_get_type(c)) { + case SND_CONFIG_TYPE_INTEGER: + { + long v; + err = snd_config_get_integer(c, &v); + if (err >= 0) + *val = v; + } + break; + case SND_CONFIG_TYPE_INTEGER64: + { + long long v; + err = snd_config_get_integer64(c, &v); + if (err >= 0) { + *val = v; + if (((long long)*val) != v) + return -ERANGE; + return 0; + } + } + break; + case SND_CONFIG_TYPE_STRING: + { + const char *s; + long long v; + err = snd_config_get_string(c, &s); + if (err >= 0) { + err = safe_strtoll(s, &v); + if (err >= 0) { + *val = v; + if (((long long)*val) != v) + return -ERANGE; + return 0; + } + } + } + break; + default: + return -EINVAL; + } + return err; +} + +int _snd_eval_string(snd_config_t **dst, const char *s, + snd_config_expand_fcn_t fcn, void *private_data) +{ + snd_config_t *tmp; + const char *save, *e; + char *m; + value_type_t left, right; + int err, c, op, off; + enum { + LEFT, + OP, + RIGHT, + END + } pos; + + while (*s && *s <= ' ') s++; + save = s; + pos = LEFT; + op = 0; + while (*s) { + while (*s && *s <= ' ') s++; + c = *s; + if (c == '\0') + break; + if (pos == END) { + SNDERR("unexpected expression tail '%s'", s); + return -EINVAL; + } + if (pos == OP) { + switch (c) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '|': + case '&': op = c; break; + default: + SNDERR("unknown operation '%c'", c); + return -EINVAL; + } + pos = RIGHT; + s++; + continue; + } + if (c == '(') { + e = _find_end_of_expression(s + 1, '(', ')'); + off = 1; + goto _expr; + } else if (c == '$') { + if (s[1] == '[') { + e = _find_end_of_expression(s + 2, '[', ']'); + off = 2; + _expr: + if (e == NULL) + return -EINVAL; + m = malloc(e - s - (off - 1)); + if (m == NULL) + return -ENOMEM; + memcpy(m, s + off, e - s - off); + m[e - s - (off + 1)] = '\0'; + err = _snd_eval_string(&tmp, m, fcn, private_data); + free(m); + if (err < 0) + return err; + s = e; + if (*s) + s++; + } else { + e = s + 1; + while (*e) { + if (!isalnum(*e) && *e != '_') + break; + e++; + } + m = malloc(e - s); + if (m == NULL) + return -ENOMEM; + memcpy(m, s + 1, e - s - 1); + m[e - s - 1] = '\0'; + err = fcn(&tmp, m, private_data); + free(m); + if (err < 0) + return err; + if (tmp == NULL) { + err = snd_config_imake_integer(&tmp, NULL, 0); + if (err < 0) + return err; + } + s = e; + } + err = _to_integer(op == LEFT ? &left : &right, tmp); + snd_config_delete(tmp); + } else if (c == '-' || (c >= '0' && c <= '9')) { + err = _parse_integer(op == LEFT ? &left : &right, &s); + } else { + return -EINVAL; + } + if (err < 0) + return err; + pos = op == LEFT ? OP : END; + } + if (pos != OP && pos != END) { + SNDERR("incomplete expression '%s'", save); + return -EINVAL; + } + + if (pos == END) { + switch (op) { + case '+': left = left + right; break; + case '-': left = left - right; break; + case '*': left = left * right; break; + case '/': left = left / right; break; + case '%': left = left % right; break; + case '|': left = left | right; break; + case '&': left = left & right; break; + default: return -EINVAL; + } + } + + if (left > INT_MAX || left < INT_MIN) + return snd_config_imake_integer64(dst, NULL, left); + else + return snd_config_imake_integer(dst, NULL, left); +} + +/** + * \brief Evaluate an math expression in the string + * \param[out] dst The function puts the handle to the new configuration + * node at the address specified by \a dst. + * \param[in] s A string to evaluate + * \param[in] fcn A function to get the variable contents + * \param[in] private_value A private value for the variable contents function + * \return 0 if successful, otherwise a negative error code. + */ +int snd_config_evaluate_string(snd_config_t **dst, const char *s, + snd_config_expand_fcn_t fcn, void *private_data) +{ + assert(dst && s); + int err; + + if (*s != '$') + return -EINVAL; + if (s[1] == '[') { + err = _snd_eval_string(dst, s, fcn, private_data); + if (err < 0) + SNDERR("wrong expression '%s'", s); + } else { + err = fcn(dst, s + 1, private_data); + } + return err; +} diff -Nru alsa-lib-1.2.2/src/confmisc.c alsa-lib-1.2.6.1/src/confmisc.c --- alsa-lib-1.2.2/src/confmisc.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/confmisc.c 2021-12-09 13:17:59.000000000 +0000 @@ -78,6 +78,7 @@ #include #include #include +#include #include "local.h" /** @@ -143,6 +144,35 @@ } /** + * \brief Gets the card number from a configuration node. + * \param conf Handle to the configuration node to be parsed. + * \return The card number if successful, otherwise a negative error code. + */ +int snd_config_get_card(const snd_config_t *conf) +{ + const char *str, *id; + long v; + int err; + + if (snd_config_get_integer(conf, &v) < 0) { + if (snd_config_get_string(conf, &str)) { + if (snd_config_get_id(conf, &id) >= 0) + SNDERR("Invalid field %s", id); + return -EINVAL; + } + err = snd_card_get_index(str); + if (err < 0) { + SNDERR("Cannot get card index for %s", str); + return err; + } + v = err; + } + if (v < 0 || v > INT_MAX) + return -EINVAL; + return v; +} + +/** * \brief Gets the control interface index from the given ASCII string. * \param ascii The string to be parsed. * \return The control interface index if successful, otherwise a negative error code. @@ -419,7 +449,6 @@ tmp = realloc(res, len + len1 + 1); if (tmp == NULL) { free(ptr); - free(res); err = -ENOMEM; goto __error; } @@ -440,8 +469,8 @@ err = snd_config_get_id(src, &id); if (err >= 0) err = snd_config_imake_string(dst, id, res); - free(res); __error: + free(res); return err; } #ifndef DOC_HIDDEN @@ -616,6 +645,28 @@ } #endif +int _snd_func_private_data(snd_config_t **dst, snd_config_t *src, + snd_config_t **private_data, const char *id) +{ + int err; + + if (*private_data == NULL) + return snd_config_copy(dst, src); + if (snd_config_get_type(*private_data) == SND_CONFIG_TYPE_COMPOUND) { + err = snd_config_search(*private_data, id, private_data); + if (err) + goto notfound; + } + err = snd_config_test_id(*private_data, id); + if (err) { +notfound: + SNDERR("field %s not found", id); + return -EINVAL; + } + return 0; +} + + /** * \brief Returns the string from \c private_data. * \param dst The function puts the handle to the result configuration node @@ -639,13 +690,9 @@ int err; const char *str, *id; - if (private_data == NULL) - return snd_config_copy(dst, src); - err = snd_config_test_id(private_data, "string"); - if (err) { - SNDERR("field string not found"); - return -EINVAL; - } + err = _snd_func_private_data(dst, src, &private_data, "string"); + if (err) + return err; err = snd_config_get_string(private_data, &str); if (err < 0) { SNDERR("field string is not a string"); @@ -660,6 +707,47 @@ SND_DLSYM_BUILD_VERSION(snd_func_private_string, SND_CONFIG_DLSYM_VERSION_EVALUATE); #endif +/** + * \brief Returns the integer from \c private_data. + * \param dst The function puts the handle to the result configuration node + * (with type integer) at the address specified by \p dst. + * \param root Handle to the root source node. + * \param src Handle to the source node. + * \param private_data Handle to the \c private_data node (type integer, + * id "integer"). + * \return A non-negative value if successful, otherwise a negative error code. + * + * Example: +\code + { + @func private_integer + } +\endcode + */ +int snd_func_private_integer(snd_config_t **dst, snd_config_t *root ATTRIBUTE_UNUSED, + snd_config_t *src, snd_config_t *private_data) +{ + int err; + const char *id; + long val; + + err = _snd_func_private_data(dst, src, &private_data, "integer"); + if (err) + return err; + err = snd_config_get_integer(private_data, &val); + if (err < 0) { + SNDERR("field integer is not a string"); + return err; + } + err = snd_config_get_id(src, &id); + if (err >= 0) + err = snd_config_imake_integer(dst, id, val); + return err; +} +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(snd_func_private_integer, SND_CONFIG_DLSYM_VERSION_EVALUATE); +#endif + #ifndef DOC_HIDDEN int snd_determine_driver(int card, char **driver) { diff -Nru alsa-lib-1.2.2/src/control/Makefile.am alsa-lib-1.2.6.1/src/control/Makefile.am --- alsa-lib-1.2.2/src/control/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -1,8 +1,12 @@ EXTRA_LTLIBRARIES = libcontrol.la libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \ - control.c control_hw.c setup.c ctlparse.c \ - control_symbols.c + control.c control_hw.c control_empty.c \ + setup.c ctlparse.c \ + control_plugin.c control_symbols.c +if BUILD_CTL_PLUGIN_REMAP +libcontrol_la_SOURCES += control_remap.c +endif if BUILD_CTL_PLUGIN_SHM libcontrol_la_SOURCES += control_shm.c endif diff -Nru alsa-lib-1.2.2/src/control/Makefile.in alsa-lib-1.2.6.1/src/control/Makefile.in --- alsa-lib-1.2.2/src/control/Makefile.in 2020-02-19 10:25:25.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/Makefile.in 2021-12-09 14:53:51.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -88,8 +88,9 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ -@BUILD_CTL_PLUGIN_SHM_TRUE@am__append_1 = control_shm.c -@BUILD_CTL_PLUGIN_EXT_TRUE@am__append_2 = control_ext.c +@BUILD_CTL_PLUGIN_REMAP_TRUE@am__append_1 = control_remap.c +@BUILD_CTL_PLUGIN_SHM_TRUE@am__append_2 = control_shm.c +@BUILD_CTL_PLUGIN_EXT_TRUE@am__append_3 = control_ext.c subdir = src/control ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ @@ -107,13 +108,16 @@ CONFIG_CLEAN_VPATH_FILES = libcontrol_la_LIBADD = am__libcontrol_la_SOURCES_DIST = cards.c tlv.c namehint.c hcontrol.c \ - control.c control_hw.c setup.c ctlparse.c control_symbols.c \ + control.c control_hw.c control_empty.c setup.c ctlparse.c \ + control_plugin.c control_symbols.c control_remap.c \ control_shm.c control_ext.c -@BUILD_CTL_PLUGIN_SHM_TRUE@am__objects_1 = control_shm.lo -@BUILD_CTL_PLUGIN_EXT_TRUE@am__objects_2 = control_ext.lo +@BUILD_CTL_PLUGIN_REMAP_TRUE@am__objects_1 = control_remap.lo +@BUILD_CTL_PLUGIN_SHM_TRUE@am__objects_2 = control_shm.lo +@BUILD_CTL_PLUGIN_EXT_TRUE@am__objects_3 = control_ext.lo am_libcontrol_la_OBJECTS = cards.lo tlv.lo namehint.lo hcontrol.lo \ - control.lo control_hw.lo setup.lo ctlparse.lo \ - control_symbols.lo $(am__objects_1) $(am__objects_2) + control.lo control_hw.lo control_empty.lo setup.lo ctlparse.lo \ + control_plugin.lo control_symbols.lo $(am__objects_1) \ + $(am__objects_2) $(am__objects_3) libcontrol_la_OBJECTS = $(am_libcontrol_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -135,11 +139,12 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/cards.Plo ./$(DEPDIR)/control.Plo \ - ./$(DEPDIR)/control_ext.Plo ./$(DEPDIR)/control_hw.Plo \ - ./$(DEPDIR)/control_shm.Plo ./$(DEPDIR)/control_symbols.Plo \ - ./$(DEPDIR)/ctlparse.Plo ./$(DEPDIR)/hcontrol.Plo \ - ./$(DEPDIR)/namehint.Plo ./$(DEPDIR)/setup.Plo \ - ./$(DEPDIR)/tlv.Plo + ./$(DEPDIR)/control_empty.Plo ./$(DEPDIR)/control_ext.Plo \ + ./$(DEPDIR)/control_hw.Plo ./$(DEPDIR)/control_plugin.Plo \ + ./$(DEPDIR)/control_remap.Plo ./$(DEPDIR)/control_shm.Plo \ + ./$(DEPDIR)/control_symbols.Plo ./$(DEPDIR)/ctlparse.Plo \ + ./$(DEPDIR)/hcontrol.Plo ./$(DEPDIR)/namehint.Plo \ + ./$(DEPDIR)/setup.Plo ./$(DEPDIR)/tlv.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -310,6 +315,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -320,8 +326,9 @@ top_srcdir = @top_srcdir@ EXTRA_LTLIBRARIES = libcontrol.la libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c control.c \ - control_hw.c setup.c ctlparse.c control_symbols.c \ - $(am__append_1) $(am__append_2) + control_hw.c control_empty.c setup.c ctlparse.c \ + control_plugin.c control_symbols.c $(am__append_1) \ + $(am__append_2) $(am__append_3) noinst_HEADERS = control_local.h AM_CPPFLAGS = -I$(top_srcdir)/include all: all-am @@ -369,8 +376,11 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cards.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_empty.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_ext.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_hw.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_remap.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_shm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_symbols.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctlparse.Plo@am__quote@ # am--include-marker @@ -538,8 +548,11 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/cards.Plo -rm -f ./$(DEPDIR)/control.Plo + -rm -f ./$(DEPDIR)/control_empty.Plo -rm -f ./$(DEPDIR)/control_ext.Plo -rm -f ./$(DEPDIR)/control_hw.Plo + -rm -f ./$(DEPDIR)/control_plugin.Plo + -rm -f ./$(DEPDIR)/control_remap.Plo -rm -f ./$(DEPDIR)/control_shm.Plo -rm -f ./$(DEPDIR)/control_symbols.Plo -rm -f ./$(DEPDIR)/ctlparse.Plo @@ -594,8 +607,11 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/cards.Plo -rm -f ./$(DEPDIR)/control.Plo + -rm -f ./$(DEPDIR)/control_empty.Plo -rm -f ./$(DEPDIR)/control_ext.Plo -rm -f ./$(DEPDIR)/control_hw.Plo + -rm -f ./$(DEPDIR)/control_plugin.Plo + -rm -f ./$(DEPDIR)/control_remap.Plo -rm -f ./$(DEPDIR)/control_shm.Plo -rm -f ./$(DEPDIR)/control_symbols.Plo -rm -f ./$(DEPDIR)/ctlparse.Plo diff -Nru alsa-lib-1.2.2/src/control/cards.c alsa-lib-1.2.6.1/src/control/cards.c --- alsa-lib-1.2.2/src/control/cards.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/cards.c 2021-12-09 13:17:59.000000000 +0000 @@ -77,8 +77,8 @@ /** * \brief Try to load the driver for a card. - * \param card Card number. - * \return 1 if driver is present, zero if driver is not present + * \param card Card index. + * \return 1 if driver is present, zero if driver is not present. */ int snd_card_load(int card) { @@ -86,14 +86,24 @@ } /** - * \brief Try to determine the next card. - * \param rcard pointer to card number - * \result zero if success, otherwise a negative error code + * \brief Iterate over physical sound cards. + * + * This function takes the index of a physical sound card and sets it to the + * index of the next card. If index is -1, it is set to the index of the first + * card. After the last card, the index is set to -1. + * + * For example, if you have 2 sound cards (with index 0 and 1), the index will + * be modified as follows: + * + * - -1 --> 0 + * - 0 --> 1 + * - 1 --> -1 + * + * This does not work for virtual sound cards. * - * Tries to determine the next card from given card number. - * If card number is -1, then the first available card is - * returned. If the result card number is -1, no more cards - * are available. + * \param rcard Index of current card. The index of the next card is stored + * here. + * \result zero if success, otherwise a negative error code. */ int snd_card_next(int *rcard) { @@ -114,13 +124,18 @@ } /** - * \brief Convert card string to an integer value. - * \param string String containing card identifier - * \return zero if success, otherwise a negative error code - * - * The accepted format is an integer value in ASCII representation - * or the card identifier (the id parameter for sound-card drivers). - * The control device name like /dev/snd/controlC0 is accepted, too. + * \brief Convert a card string to the card index. + * + * This works only for physical sound cards, not for virtual cards. + * + * \param string A string identifying the card. + * \return The index of the card. On error, a a negative error code + * is returned. + * + * The accepted formats for "string" are: + * - The index of the card (as listed in /proc/asound/cards), given as string + * - The ID of the card (as listed in /proc/asound/cards) + * - The control device name (like /dev/snd/controlC0) */ int snd_card_get_index(const char *string) { @@ -132,6 +147,7 @@ return -EINVAL; if ((isdigit(*string) && *(string + 1) == 0) || (isdigit(*string) && isdigit(*(string + 1)) && *(string + 2) == 0)) { + /* We got an index */ if (sscanf(string, "%i", &card) != 1) return -EINVAL; if (card < 0 || card >= SND_MAX_CARDS) @@ -141,8 +157,10 @@ return card; return err; } - if (string[0] == '/') /* device name */ + if (string[0] == '/') + /* We got a device name */ return snd_card_load2(string); + /* We got in ID */ for (card = 0; card < SND_MAX_CARDS; card++) { #ifdef SUPPORT_ALOAD if (! snd_card_load(card)) @@ -163,8 +181,9 @@ /** * \brief Obtain the card name. - * \param card Card number - * \param name Result - card name corresponding to card number + * + * \param card The index of the card. + * \param name Result - card name corresponding to card index. * \result zero if success, otherwise a negative error code * * The value returned in name is allocated with strdup and should be @@ -193,9 +212,9 @@ /** * \brief Obtain the card long name. - * \param card Card number - * \param name Result - card long name corresponding to card number - * \result zero if success, otherwise a negative error code + * \param card Index of the card. + * \param name Result - card long name corresponding to card index. + * \result Zero if success, otherwise a negative error code. * * The value returned in name is allocated with strdup and should be * freed when no longer used. diff -Nru alsa-lib-1.2.2/src/control/control.c alsa-lib-1.2.6.1/src/control/control.c --- alsa-lib-1.2.2/src/control/control.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control.c 2021-12-09 13:17:59.000000000 +0000 @@ -31,32 +31,135 @@ /*! \page control Control interface

Control interface is designed to access primitive controls. There is -also interface notifying about control and structure changes. +also an interface for notifying about control and structure changes. + \section control_general_overview General overview +In Alsa, there are physical sound cards, such as USB headsets, and +virtual sound cards, such as "pulse", which represents the PulseAudio +Sound system. Each sound card offers a control interface, making its +settings (e.g. volume knobs) available. The complete list of available +control interfaces can be obtained using snd_device_name_hint(), +giving -1 as card index and "ctl" as interface type. Each returned +NAME hint identifies a control interface. + +Sound cards have an ID (a string), an index (an int, sometimes called +the "card number"), a name, a longname, a mixername and a "components" +property. The file /proc/asound/cards lists most of these properties +for physical sound cards. Virtual sound cards are not listed in that +file. The format is: + +\verbatim +index [ID ] Driver - name + longname +\endverbatim + +Note that the mixername and components are not listed. + + +\subsection control_cards_id Identifying and Opening Control Interfaces + +To work with a control interface, is must be opened first, using +snd_ctl_open(). This function takes the interface name. + +For physical sound cards, the control interface can be identified +using the string "hw:" (e.g. `hw:2`). The NAME hint - which is +"hw:CARD=" - can also be used. Further, its device file (something +like `/dev/snd/controlC0`) is also acceptable. Either of them can be +given to snd_ctl_open(). + +For virtual sound cards, the NAME hint is given to snd_ctl_open(). + +The functions snd_card_get_index(), snd_card_get_name() and +snd_card_get_longname() can be used to find an identifying property +when another one is already known. + +\section control_elements Elements + In ALSA control feature, each sound card can have control elements. The elements are managed according to below model. - - element set + - Element set + - A set of elements with the same attribute (i.e. name, get/put operations). Some element sets can be added to a sound card by drivers in kernel and userspace applications. - - element - - An element can be identified by userspace applications. Each element has - own identical information. - - member - - An element includes some members to have a value. The value of each member - can be changed by both of userspace applications and drivers in kernel. -Each element can be identified by two ways; a combination of name and index, or -numerical number (numid). + - Element + + - A control element might be a master volume control, for example, or a + read-only indicator, such as a sync status. An element has a type (e.g. + SNDRV_CTL_ELEM_TYPE_INTEGER or SNDRV_CTL_ELEM_TYPE_BOOLEAN) and - depending + on the type - min/max values, a step size, a set of possible values (for + enums), etc. + + - Member + + - An element usually includes one or more member(s) to have a value. For + example, a stereo volume control element has two members (for left/right), + while a mono volume has only one member. The member count can be obtained + using snd_ctl_elem_info_get_count(). Elements of type + "SNDRV_CTL_ELEM_TYPE_BYTES" or "SNDRV_CTL_ELEM_TYPE_IEC958" have no members + at all (and thus no member count), they have just a single value. The + members share the same properties (e.g. both volume control members have + the same min/max values). The value of each member can be changed by both + of userspace applications and drivers in kernel. + + +\subsection identifying_elements Identifying Elements + +Each element has the following identifying properties: + + - The numid (a numeric identifier, assigned when the sound card is + detected, constant while the sound card is kept connected) + - The interface type (e.g. MIXER, CARD or PCM) + - The device + - The subdevice + - Its name + - Its index + +An element can be identified either by its short numid or by the full +set of fields (interface type, device, subdevice, name, index). +This set of fields is always the same (driver updates can change it, +but in practice this is rare). The numid can change on each boot. +In case of an USB sound card, the numid can also change when it +is reconnected. The short numid is used to reduce the lookup time. + +\subsection element_lists Element Lists + +An element list can be used to obtain a list of all elements of the +sound card. The list contains generic information (e.g. how many +elements the card has), and the identifying properties of the elements +(numid, card, name, ...). See #snd_ctl_elem_list_t to learn more about +element lists. + + +\subsection working_with_elements Working with Elements + +It is possible to obtain information about an element using the +snd_ctl_elem_info_*() functions. For enums, the allowed values can be +obtained, for integers, the min/max values can be obtained, and so +on. In addition, these functions can report the identifying +properties. E.g. when the element is addressed using its numid, the +functions complements the name, index, etc. + +To access the members (i.e. values) of a control, use the +snd_ctl_elem_value*() functions. These allow to get and set the +actual values or settings. It is also possible to get and set the ID +values (such as the numid or the name). + -The type of element set is one of integer, integerr64, boolean, enumerators, +\subsection element_sets Element Sets + +The type of element set is one of integer, integer64, boolean, enumerators, bytes and IEC958 structure. This indicates the type of value for each member in elements included in the element set. -When the value of member is changed, corresponding events are transferred to + +\section events Events + +When the value of a member is changed, corresponding events are transferred to userspace applications. The applications should subscribe any events in advance. \section tlv_blob Supplemental data for elements in an element set @@ -92,6 +195,7 @@ #include #include #include +#include #include "control_local.h" /** @@ -267,10 +371,15 @@ /** - * \brief Get card related information - * \param ctl CTL handle - * \param info Card info pointer - * \return 0 on success otherwise a negative error code + * \brief Get information about the sound card. + * + * Obtain information about the sound card previously opened using + * snd_ctl_open(). The object "info" must be allocated prior to calling this + * function. See snd_ctl_card_info_t for details. + * + * \param ctl The CTL handle. + * \param info The card information is stored here. + * \return 0 on success, otherwise a negative error code. */ int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) { @@ -280,6 +389,21 @@ /** * \brief Get a list of element identifiers + * + * Before calling this function, memoru must be allocated using + * snd_ctl_elem_list_malloc(). + * + * This function obtains data from the sound card driver and puts it + * into the list. + * + * If there was space allocated for the element identifiers (using + * snd_ctl_elem_list_alloc_space()), information will be filled in. If + * too little space was allocated, only a part of the elements will be + * queried. If there was too much space allocated, some of it remains + * unused. Use snd_ctl_elem_list_get_count() and + * snd_ctl_elem_list_get_used() to obtain information about space + * usage. See #snd_ctl_elem_list_t to learn more. + * * \param ctl CTL handle * \param list CTL element identifiers list pointer * \return 0 on success otherwise a negative error code @@ -333,10 +457,55 @@ #define validate_element_member_dimension(info) true #endif /* deprecated */ +#define USER_ACCESS_DEFAULT (\ + SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_USER) + +#define USER_ACCESS_SETTABLE (\ + SNDRV_CTL_ELEM_ACCESS_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE |\ + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |\ + SNDRV_CTL_ELEM_ACCESS_INACTIVE |\ + SNDRV_CTL_ELEM_ACCESS_USER) + +static inline int set_user_access(snd_ctl_elem_info_t *info) +{ + if (info->access == 0) { + info->access = USER_ACCESS_DEFAULT; + } else { + if ((info->access & SNDRV_CTL_ELEM_ACCESS_READWRITE) == 0) + return -1; + if (info->access & ~USER_ACCESS_SETTABLE) + return -1; + info->access |= SNDRV_CTL_ELEM_ACCESS_USER; + } + return 0; +} + +int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, + unsigned int element_count, + unsigned int member_count) +{ + if (ctl == NULL || info->id.name[0] == '\0') + return -EINVAL; + + if (set_user_access(info)) + return -EINVAL; + + info->owner = element_count; + info->count = member_count; + + if (!validate_element_member_dimension(info)) + return -EINVAL; + + return ctl->ops->element_add(ctl, info); +} + /** * \brief Create and add some user-defined control elements of integer type. * \param ctl A handle of backend module for control interface. - * \param info Common iformation for a new element set, with ID of the first new + * \param info Common information for a new element set, with ID of the first new * element. * \param element_count The number of elements added by this operation. * \param member_count The number of members which a element has to @@ -386,23 +555,15 @@ unsigned int numid; int err; - if (ctl == NULL || info == NULL || info->id.name[0] == '\0') + if (info == NULL) return -EINVAL; info->type = SND_CTL_ELEM_TYPE_INTEGER; - info->access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | - SNDRV_CTL_ELEM_ACCESS_USER; - info->owner = element_count; - info->count = member_count; info->value.integer.min = min; info->value.integer.max = max; info->value.integer.step = step; - if (!validate_element_member_dimension(info)) - return -EINVAL; - - err = ctl->ops->element_add(ctl, info); + err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count); if (err < 0) return err; numid = snd_ctl_elem_id_get_numid(&info->id); @@ -426,7 +587,7 @@ /** * \brief Create and add some user-defined control elements of integer64 type. * \param ctl A handle of backend module for control interface. - * \param info Common iformation for a new element set, with ID of the first new + * \param info Common information for a new element set, with ID of the first new * element. * \param element_count The number of elements added by this operation. * \param member_count The number of members which a element has to @@ -476,23 +637,15 @@ unsigned int numid; int err; - if (ctl == NULL || info == NULL || info->id.name[0] == '\0') + if (info == NULL) return -EINVAL; info->type = SND_CTL_ELEM_TYPE_INTEGER64; - info->access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | - SNDRV_CTL_ELEM_ACCESS_USER; - info->owner = element_count; - info->count = member_count; info->value.integer64.min = min; info->value.integer64.max = max; info->value.integer64.step = step; - if (!validate_element_member_dimension(info)) - return -EINVAL; - - err = ctl->ops->element_add(ctl, info); + err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count); if (err < 0) return err; numid = snd_ctl_elem_id_get_numid(&info->id); @@ -516,7 +669,7 @@ /** * \brief Create and add some user-defined control elements of boolean type. * \param ctl A handle of backend module for control interface. - * \param info Common iformation for a new element set, with ID of the first new + * \param info Common information for a new element set, with ID of the first new * element. * \param element_count The number of elements added by this operation. * \param member_count The number of members which a element has to @@ -555,28 +708,20 @@ unsigned int element_count, unsigned int member_count) { - if (ctl == NULL || info == NULL || info->id.name[0] == '\0') + if (info == NULL) return -EINVAL; info->type = SND_CTL_ELEM_TYPE_BOOLEAN; - info->access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | - SNDRV_CTL_ELEM_ACCESS_USER; - info->owner = element_count; - info->count = member_count; info->value.integer.min = 0; info->value.integer.max = 1; - if (!validate_element_member_dimension(info)) - return -EINVAL; - - return ctl->ops->element_add(ctl, info); + return __snd_ctl_add_elem_set(ctl, info, element_count, member_count); } /** * \brief Create and add some user-defined control elements of enumerated type. * \param ctl A handle of backend module for control interface. - * \param info Common iformation for a new element set, with ID of the first new + * \param info Common information for a new element set, with ID of the first new * element. * \param element_count The number of elements added by this operation. * \param member_count The number of members which a element has to @@ -626,14 +771,10 @@ char *buf, *p; int err; - if (ctl == NULL || info == NULL || info->id.name[0] == '\0' || - labels == NULL) + if (info == NULL || labels == NULL) return -EINVAL; info->type = SND_CTL_ELEM_TYPE_ENUMERATED; - info->access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | - SNDRV_CTL_ELEM_ACCESS_USER; info->owner = element_count; info->count = member_count; info->value.enumerated.items = items; @@ -654,10 +795,7 @@ p += strlen(labels[i]) + 1; } - if (!validate_element_member_dimension(info)) - return -EINVAL; - - err = ctl->ops->element_add(ctl, info); + err = __snd_ctl_add_elem_set(ctl, info, element_count, member_count); free(buf); @@ -667,7 +805,7 @@ /** * \brief Create and add some user-defined control elements of bytes type. * \param ctl A handle of backend module for control interface. - * \param info Common iformation for a new element set, with ID of the first new + * \param info Common information for a new element set, with ID of the first new * element. * \param element_count The number of elements added by this operation. * \param member_count The number of members which a element has to @@ -707,20 +845,12 @@ unsigned int element_count, unsigned int member_count) { - if (ctl == NULL || info == NULL || info->id.name[0] == '\0') + if (info == NULL) return -EINVAL; info->type = SND_CTL_ELEM_TYPE_BYTES; - info->access = SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | - SNDRV_CTL_ELEM_ACCESS_USER; - info->owner = element_count; - info->count = member_count; - if (!validate_element_member_dimension(info)) - return -EINVAL; - - return ctl->ops->element_add(ctl, info); + return __snd_ctl_add_elem_set(ctl, info, element_count, member_count); } /** @@ -862,10 +992,19 @@ } /** - * \brief Get CTL element value - * \param ctl CTL handle - * \param data Data of an element. - * \return 0 on success otherwise a negative error code + * \brief Get CTL element value. + * + * Read information from sound card. You must set the ID of the + * element before calling this function. + * + * See snd_ctl_elem_value_t for details. + * + * \param ctl CTL handle. + * \param data The element value. The ID must be set before calling + * the function, and the actual value will be returned + * here. + * + * \return 0 on success otherwise a negative error code. */ int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *data) { @@ -874,9 +1013,16 @@ } /** - * \brief Set CTL element value - * \param ctl CTL handle - * \param data Data of an element. + * \brief Set CTL element value. + * + * Write new value(s) to the sound card. You must set the ID and the + * value of the element before calling this function. + * + * See snd_ctl_elem_value_t for details. + * + * \param ctl CTL handle. + * \param data The new value. + * * \retval 0 on success * \retval >0 on success when value was changed * \retval <0 a negative error code @@ -1246,7 +1392,7 @@ } static const char *const build_in_ctls[] = { - "hw", "shm", NULL + "hw", "empty", "remap", "shm", NULL }; static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name, @@ -1337,13 +1483,13 @@ build_in++; } if (*build_in == NULL) { - buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32); + buf1 = malloc(strlen(str) + 32); if (buf1 == NULL) { err = -ENOMEM; goto _err; } lib = buf1; - sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str); + sprintf(buf1, "libasound_module_ctl_%s.so", str); } } #ifndef PIC @@ -1370,26 +1516,52 @@ return err; } -static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode) +static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, + const char *name, int mode, int hop) { int err; snd_config_t *ctl_conf; + const char *str; + err = snd_config_search_definition(root, "ctl", name, &ctl_conf); if (err < 0) { SNDERR("Invalid CTL %s", name); return err; } - err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode); + if (snd_config_get_string(ctl_conf, &str) >= 0) + err = snd_ctl_open_noupdate(ctlp, root, str, mode, hop + 1); + else { + snd_config_set_hop(ctl_conf, hop); + err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode); + } snd_config_delete(ctl_conf); return err; } +#ifndef DOC_HIDDEN +int _snd_ctl_open_named_child(snd_ctl_t **pctl, const char *name, + snd_config_t *root, snd_config_t *conf, + int mode, snd_config_t *parent_conf) +{ + const char *str; + int hop; + + if ((hop = snd_config_check_hop(parent_conf)) < 0) + return hop; + if (snd_config_get_string(conf, &str) >= 0) + return snd_ctl_open_noupdate(pctl, root, str, mode, hop + 1); + return snd_ctl_open_conf(pctl, name, root, conf, mode); +} +#endif + /** - * \brief Opens a CTL - * \param ctlp Returned CTL handle - * \param name ASCII identifier of the CTL handle - * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC) - * \return 0 on success otherwise a negative error code + * \brief Opens a sound card. + * + * \param ctlp Returned CTL handle. + * \param name A string identifying the card (See \ref control_cards_id). + * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC). + * + * \return 0 on success otherwise a negative error code. */ int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode) { @@ -1397,10 +1569,16 @@ int err; assert(ctlp && name); - err = snd_config_update_ref(&top); - if (err < 0) - return err; - err = snd_ctl_open_noupdate(ctlp, top, name, mode); + if (_snd_is_ucm_device(name)) { + name = uc_mgr_alibcfg_by_device(&top, name); + if (name == NULL) + return -ENODEV; + } else { + err = snd_config_update_ref(&top); + if (err < 0) + return err; + } + err = snd_ctl_open_noupdate(ctlp, top, name, mode, 0); snd_config_unref(top); return err; } @@ -1417,7 +1595,7 @@ int mode, snd_config_t *lconf) { assert(ctlp && name && lconf); - return snd_ctl_open_noupdate(ctlp, lconf, name, mode); + return snd_ctl_open_noupdate(ctlp, lconf, name, mode, 0); } /** @@ -1434,7 +1612,7 @@ { int err; assert(ctlp && name && root); - err = snd_ctl_open_noupdate(ctlp, root, name, mode); + err = snd_ctl_open_noupdate(ctlp, root, name, mode, 0); if (err >= 0) { free((*ctlp)->name); (*ctlp)->name = orig_name ? strdup(orig_name) : NULL; @@ -1508,9 +1686,14 @@ /** * \brief allocate space for CTL element identifiers list - * \param obj CTL element identifiers list - * \param entries Entries to allocate - * \return 0 on success otherwise a negative error code + * + * The space can be released with snd_ctl_elem_list_free_space(). + * + * \param obj CTL element identifiers list. + * \param entries How many entries to allocate. See + * #snd_ctl_elem_list_t to learn how to obtain + * this number in advance. + * \return 0 on success otherwise a negative error code. */ int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries) { @@ -1526,6 +1709,10 @@ /** * \brief free previously allocated space for CTL element identifiers list + * + * Releases space previously allocated using + * snd_ctl_elem_list_alloc_space(). + * * \param obj CTL element identifiers list */ void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj) @@ -1692,6 +1879,71 @@ } /** + * \brief compare one #snd_ctl_elem_id_t to another using numid + * \param id1 pointer to first id + * \param id2 pointer to second id + * \retval zero when values are identical, other value on a difference (like strcmp) + * + * This comparison ignores the set of fields part. + * + * The return value can be used for sorting like qsort(). It gives persistent + * results. + */ +int snd_ctl_elem_id_compare_numid(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2) +{ + int64_t d; + + assert(id1 && id2); + d = (int64_t)id1->numid - (int64_t)id2->numid; + if (d & ((int64_t)INT_MAX + 1)) { /* fast path */ + if (d > INT_MAX) + d = INT_MAX; + else if (d < INT_MIN) + d = INT_MIN; + } + return d; +} + +/** + * \brief compare one #snd_ctl_elem_id_t to another + * \param id1 pointer to first id + * \param id2 pointer to second id + * \retval zero when values are identical, other value on a difference (like strcmp) + * + * This comparison ignores the numid part. The numid comparison can be easily + * implemented using snd_ctl_elem_id_get_numid() calls. + * + * The identifier set fields are compared in this order: interface, device, + * subdevice, name, index. + * + * The return value can be used for sorting like qsort(). It gives persistent + * results. + */ +int snd_ctl_elem_id_compare_set(const snd_ctl_elem_id_t *id1, const snd_ctl_elem_id_t *id2) +{ + int d; + + assert(id1 && id2); + /* although those values are unsigned integer, practically, */ + /* the useable limit is really much lower */ + assert((id1->iface | id1->device | id1->subdevice | id1->index) <= INT_MAX); + assert((id2->iface | id2->device | id2->subdevice | id1->index) <= INT_MAX); + d = id1->iface - id2->iface; + if (d != 0) + return d; + d = id1->device - id2->device; + if (d != 0) + return d; + d = id1->subdevice - id2->subdevice; + if (d != 0) + return d; + d = strcmp((const char *)id1->name, (const char *)id2->name); + if (d != 0) + return d; + return id1->index - id2->index; +} + +/** * \brief Get numeric identifier from a CTL element identifier * \param obj CTL element identifier * \return CTL element numeric identifier @@ -1824,8 +2076,8 @@ } /** - * \brief get size of #snd_ctl_card_info_t - * \return size in bytes + * \brief get size of #snd_ctl_card_info_t. + * \return Size in bytes. */ size_t snd_ctl_card_info_sizeof() { @@ -1833,9 +2085,16 @@ } /** - * \brief allocate an invalid #snd_ctl_card_info_t using standard malloc - * \param ptr returned pointer - * \return 0 on success otherwise negative error code + * \brief Allocate an invalid #snd_ctl_card_info_t on the heap. + * + * Allocate space for a card info object on the heap. The allocated memory + * must be freed using snd_ctl_card_info_free(). + * + * See snd_ctl_card_info_t for details. + * + * \param ptr Pointer to a snd_ctl_card_info_t pointer. The address + * of the allocated space will be returned here. + * \return 0 on success, otherwise a negative error code. */ int snd_ctl_card_info_malloc(snd_ctl_card_info_t **ptr) { @@ -1847,8 +2106,10 @@ } /** - * \brief frees a previously allocated #snd_ctl_card_info_t - * \param obj pointer to object to free + * \brief Free an #snd_ctl_card_info_t previously allocated using + * snd_ctl_card_info_malloc(). + * + * \param obj Pointer to the snd_ctl_card_info_t. */ void snd_ctl_card_info_free(snd_ctl_card_info_t *obj) { @@ -1856,8 +2117,11 @@ } /** - * \brief clear given #snd_ctl_card_info_t object - * \param obj pointer to object to clear + * \brief Clear given card info object. + * + * See snd_ctl_elem_value_t for details. + * + * \param obj Card info object. */ void snd_ctl_card_info_clear(snd_ctl_card_info_t *obj) { @@ -1865,9 +2129,10 @@ } /** - * \brief copy one #snd_ctl_card_info_t to another - * \param dst pointer to destination - * \param src pointer to source + * \brief Bitwise copy of a #snd_ctl_card_info_t object. + * + * \param dst Pointer to destination. + * \param src Pointer to source. */ void snd_ctl_card_info_copy(snd_ctl_card_info_t *dst, const snd_ctl_card_info_t *src) { @@ -1876,9 +2141,12 @@ } /** - * \brief Get card number from a CTL card info - * \param obj CTL card info - * \return card number + * \brief Get the sound card index from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound card index. */ int snd_ctl_card_info_get_card(const snd_ctl_card_info_t *obj) { @@ -1887,9 +2155,12 @@ } /** - * \brief Get card identifier from a CTL card info - * \param obj CTL card info - * \return card identifier + * \brief Get the sound card ID from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound card ID. */ const char *snd_ctl_card_info_get_id(const snd_ctl_card_info_t *obj) { @@ -1898,9 +2169,12 @@ } /** - * \brief Get card driver name from a CTL card info - * \param obj CTL card info - * \return card driver name + * \brief Get the sound card driver from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return The sound card driver. */ const char *snd_ctl_card_info_get_driver(const snd_ctl_card_info_t *obj) { @@ -1909,9 +2183,12 @@ } /** - * \brief Get card name from a CTL card info - * \param obj CTL card info - * \return card name + * \brief Get the sound card name from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound card name. */ const char *snd_ctl_card_info_get_name(const snd_ctl_card_info_t *obj) { @@ -1920,9 +2197,12 @@ } /** - * \brief Get card long name from a CTL card info - * \param obj CTL card info - * \return card long name + * \brief Get the sound cards long name from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound cards long name. */ const char *snd_ctl_card_info_get_longname(const snd_ctl_card_info_t *obj) { @@ -1931,9 +2211,12 @@ } /** - * \brief Get card mixer name from a CTL card info - * \param obj CTL card info - * \return card mixer name + * \brief Get the sound card mixer name from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound card mixer name. */ const char *snd_ctl_card_info_get_mixername(const snd_ctl_card_info_t *obj) { @@ -1942,9 +2225,12 @@ } /** - * \brief Get card component list from a CTL card info - * \param obj CTL card info - * \return card mixer identifier + * \brief Get the sound cards "components" property from the given info object. + * + * See snd_ctl_card_info_t for more details. + * + * \param obj The card info object. + * \return Sound cards "components" property. */ const char *snd_ctl_card_info_get_components(const snd_ctl_card_info_t *obj) { @@ -2016,7 +2302,7 @@ } /** - * \brief get size of #snd_ctl_elem_list_t + * \brief get size of #snd_ctl_elem_list_t. * \return size in bytes */ size_t snd_ctl_elem_list_sizeof() @@ -2025,7 +2311,10 @@ } /** - * \brief allocate an invalid #snd_ctl_elem_list_t using standard malloc + * \brief allocate a #snd_ctl_elem_list_t using standard malloc. + * + * The memory can be released using snd_ctl_elem_list_free(). + * * \param ptr returned pointer * \return 0 on success otherwise negative error code */ @@ -2039,7 +2328,15 @@ } /** - * \brief frees a previously allocated #snd_ctl_elem_list_t + * \brief frees a previously allocated #snd_ctl_elem_list_t. + * + * Release memory previously allocated using + * snd_ctl_elem_list_malloc(). + * + * If you used snd_ctl_elem_list_alloc_space() on the list, you must + * use snd_ctl_elem_list_free_space() \em before calling this + * function. + * * \param obj pointer to object to free */ void snd_ctl_elem_list_free(snd_ctl_elem_list_t *obj) @@ -2048,7 +2345,15 @@ } /** - * \brief clear given #snd_ctl_elem_list_t object + * \brief Clear given #snd_ctl_elem_list_t object. + * + * This will make the stored identifiers inaccessible without freeing + * their space. + * + * \warning The element identifier space cannot be freed after calling + * this function. Therefore, snd_ctl_elem_list_free_space() + * must be called in advance. + * * \param obj pointer to object to clear */ void snd_ctl_elem_list_clear(snd_ctl_elem_list_t *obj) @@ -2057,7 +2362,11 @@ } /** - * \brief copy one #snd_ctl_elem_list_t to another + * \brief copy one #snd_ctl_elem_list_t to another. + * + * This performs a shallow copy. That means the both lists will share + * the same space for the elements. The elements will not be copied. + * * \param dst pointer to destination * \param src pointer to source */ @@ -2080,6 +2389,12 @@ /** * \brief Get number of used entries in CTL element identifiers list + * + * This function returns how many entries are actually filled with + * useful information. + * + * See also snd_ctl_elem_list_get_count(). + * * \param obj CTL element identifier list * \return number of used entries */ @@ -2090,7 +2405,14 @@ } /** - * \brief Get total count of elements present in CTL device (information present in every filled CTL element identifiers list) + * \brief Get total count of elements present in CTL device + * + * This function returns how many entries were allocated using + * snd_ctl_elem_list_alloc_space(). This information is present after + * snd_ctl_elem_list() was called. + * + * See also snd_ctl_elem_list_get_used(). + * * \param obj CTL element identifier list * \return total number of elements */ @@ -2140,7 +2462,7 @@ } /** - * \brief Get device part of CTL element identifier for an entry of a CTL element identifiers list + * \brief Get the device part of CTL element identifier for an entry of a CTL element identifiers list * \param obj CTL element identifier list * \param idx Index of entry * \return CTL element related device @@ -2754,6 +3076,46 @@ } /** + * \brief Set readability/writeability parameter of a CTL element id/info + * \param obj CTL element id/info + * \param rval readability part of element identifier + * \param wval writeability part of element identifier + */ +void snd_ctl_elem_info_set_read_write(snd_ctl_elem_info_t *obj, int rval, int wval) +{ + assert(obj); + obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_READWRITE) | + (rval ? SNDRV_CTL_ELEM_ACCESS_READ : 0) | + (wval ? SNDRV_CTL_ELEM_ACCESS_WRITE : 0); +} + +/** + * \brief Set TLV readability/writeability parameter of a CTL element id/info + * \param obj CTL element id/info + * \param rval TLV readability part of element identifier + * \param wval TLV writeability part of element identifier + */ +void snd_ctl_elem_info_set_tlv_read_write(snd_ctl_elem_info_t *obj, int rval, int wval) +{ + assert(obj); + obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) | + (rval ? SNDRV_CTL_ELEM_ACCESS_TLV_READ : 0) | + (wval ? SNDRV_CTL_ELEM_ACCESS_TLV_WRITE : 0); +} + +/** + * \brief Set inactive parameter of a CTL element id/info + * \param obj CTL element id/info + * \param val inactive part of element identifier + */ +void snd_ctl_elem_info_set_inactive(snd_ctl_elem_info_t *obj, int val) +{ + assert(obj); + obj->access = (obj->access & ~SNDRV_CTL_ELEM_ACCESS_INACTIVE) | + (val ? SNDRV_CTL_ELEM_ACCESS_INACTIVE : 0); +} + +/** * \brief Get size of data structure for an element. * \return Size in bytes. */ @@ -2763,9 +3125,16 @@ } /** - * \brief Allocate an invalid #snd_ctl_elem_value_t using standard malloc(3). - * \param ptr Returned pointer for data of an element. - * \return 0 on success otherwise negative error code. + * \brief Allocate an invalid #snd_ctl_elem_value_t on the heap. + * + * Allocate space for a value object on the heap. The allocated memory + * must be freed using snd_ctl_elem_value_free(). + * + * See snd_ctl_elem_value_t for details. + * + * \param ptr Pointer to a snd_ctl_elem_value_t pointer. The address + * of the allocated space will be returned here. + * \return 0 on success, otherwise a negative error code. */ int snd_ctl_elem_value_malloc(snd_ctl_elem_value_t **ptr) { @@ -2777,8 +3146,10 @@ } /** - * \brief Frees a previously allocated data of an element. - * \param obj Data of an element. + * \brief Free an #snd_ctl_elem_value_t previously allocated using + * snd_ctl_elem_value_malloc(). + * + * \param obj Pointer to the snd_ctl_elem_value_t. */ void snd_ctl_elem_value_free(snd_ctl_elem_value_t *obj) { @@ -2787,6 +3158,9 @@ /** * \brief Clear given data of an element. + * + * See snd_ctl_elem_value_t for details. + * * \param obj Data of an element. */ void snd_ctl_elem_value_clear(snd_ctl_elem_value_t *obj) @@ -2795,7 +3169,7 @@ } /** - * \brief Copy two data of elements. + * \brief Bitwise copy of a snd_ctl_elem_value_t value. * \param dst Pointer to destination. * \param src Pointer to source. */ @@ -2807,9 +3181,10 @@ } /** - * \brief Compare one data of an element to the other. - * \param left Pointer to first data. - * \param right Pointer to second data. + * \brief Compare two snd_ctl_elem_value_t values, bytewise. + * + * \param left First value. + * \param right Second value. * \return 0 on match, less than or greater than otherwise, see memcmp(3). */ int snd_ctl_elem_value_compare(snd_ctl_elem_value_t *left, @@ -2820,9 +3195,13 @@ } /** - * \brief Get element identifier from given data of an element. - * \param obj Data of an element. - * \param ptr Pointer for element identifier. + * \brief Get the element identifier from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param ptr Pointer to an identifier object. The identifier is + * stored there. */ void snd_ctl_elem_value_get_id(const snd_ctl_elem_value_t *obj, snd_ctl_elem_id_t *ptr) { @@ -2831,9 +3210,12 @@ } /** - * \brief Get element numeric identifier from given data of an element. - * \param obj Data of an element. - * \return Element numeric identifier. + * \brief Get the identifiers 'numid' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The numid. */ unsigned int snd_ctl_elem_value_get_numid(const snd_ctl_elem_value_t *obj) { @@ -2842,10 +3224,12 @@ } /** - * \brief Get interface part of element identifier from given data of an - * element. - * \param obj Data of an element. - * \return Interface part of element identifier. + * \brief Get the identifiers 'interface' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The interface part of element identifier. */ snd_ctl_elem_iface_t snd_ctl_elem_value_get_interface(const snd_ctl_elem_value_t *obj) { @@ -2854,9 +3238,12 @@ } /** - * \brief Get device part of element identifier from given data of an element. - * \param obj Data of an element. - * \return Device part of element identifier. + * \brief Get the identifiers 'device' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The device part of element identifier. */ unsigned int snd_ctl_elem_value_get_device(const snd_ctl_elem_value_t *obj) { @@ -2865,10 +3252,12 @@ } /** - * \brief Get subdevice part of element identifier from given data of an - * element. - * \param obj Data of an element. - * \return Subdevice part of element identifier. + * \brief Get the identifiers 'subdevice' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The subdevice part of element identifier. */ unsigned int snd_ctl_elem_value_get_subdevice(const snd_ctl_elem_value_t *obj) { @@ -2877,9 +3266,12 @@ } /** - * \brief Get name part of element identifier from given data of an element. - * \param obj Data of an element. - * \return Name part of element identifier. + * \brief Get the identifiers 'name' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The "name" part of element identifier. */ const char *snd_ctl_elem_value_get_name(const snd_ctl_elem_value_t *obj) { @@ -2888,9 +3280,12 @@ } /** - * \brief Get index part of element identifier from given data of an element. - * \param obj Data of an element. - * \return Index part of element identifier. + * \brief Get the identifiers 'index' part from the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \return The index part of element identifier. */ unsigned int snd_ctl_elem_value_get_index(const snd_ctl_elem_value_t *obj) { @@ -2898,10 +3293,14 @@ return obj->id.index; } + /** - * \brief Set element identifier to given data of an element. - * \param obj Data of an element. - * \param ptr Pointer to an element identifier. + * \brief Set the element identifier within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param ptr The new identifier. */ void snd_ctl_elem_value_set_id(snd_ctl_elem_value_t *obj, const snd_ctl_elem_id_t *ptr) { @@ -2910,9 +3309,12 @@ } /** - * \brief Set numeric identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for numeric identifier. + * \brief Set the identifiers 'numid' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new numid. */ void snd_ctl_elem_value_set_numid(snd_ctl_elem_value_t *obj, unsigned int val) { @@ -2921,9 +3323,12 @@ } /** - * \brief Set interface part of element identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for interface part of element identifier. + * \brief Set the identifiers 'interface' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new interface. */ void snd_ctl_elem_value_set_interface(snd_ctl_elem_value_t *obj, snd_ctl_elem_iface_t val) { @@ -2932,9 +3337,12 @@ } /** - * \brief Set device part of element identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for device part of element identifier. + * \brief Set the identifiers 'device' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new device. */ void snd_ctl_elem_value_set_device(snd_ctl_elem_value_t *obj, unsigned int val) { @@ -2943,9 +3351,12 @@ } /** - * \brief Set subdevice part of element identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for subdevice part of element identifier. + * \brief Set the identifiers 'subdevice' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new subdevice. */ void snd_ctl_elem_value_set_subdevice(snd_ctl_elem_value_t *obj, unsigned int val) { @@ -2954,9 +3365,12 @@ } /** - * \brief Set name part of element identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for name part of element identifier, + * \brief Set the identifiers 'name' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new name. */ void snd_ctl_elem_value_set_name(snd_ctl_elem_value_t *obj, const char *val) { @@ -2965,9 +3379,12 @@ } /** - * \brief Set index part of element identifier to given data of an element. - * \param obj Data of an element. - * \param val Value for index part of element identifier. + * \brief Set the identifiers 'index' part within the given element value. + * + * See snd_ctl_elem_value_t for more details. + * + * \param obj The element value. + * \param val The new index. */ void snd_ctl_elem_value_set_index(snd_ctl_elem_value_t *obj, unsigned int val) { @@ -2976,12 +3393,16 @@ } /** - * \brief Get value of a specified member from given data as an element of - * boolean type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \return Value for the member. - */ + * \brief Get an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BOOLEAN. It + * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref + * control for more details. + * + * \param obj The element value object + * \param idx The index of the member. + * \return The members value. + */ int snd_ctl_elem_value_get_boolean(const snd_ctl_elem_value_t *obj, unsigned int idx) { assert(obj); @@ -2990,12 +3411,16 @@ } /** - * \brief Get value of a specified member from given data as an element of - * integer type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \return Value for the member. - */ + * \brief Get an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER. It + * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref + * control for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \return The members value. + */ long snd_ctl_elem_value_get_integer(const snd_ctl_elem_value_t *obj, unsigned int idx) { assert(obj); @@ -3004,12 +3429,16 @@ } /** - * \brief Get value of a specified member from given data as an element of - * integer64 type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \return Value for the member. - */ + * \brief Get an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER64. It + * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref + * control for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \return The members value. + */ long long snd_ctl_elem_value_get_integer64(const snd_ctl_elem_value_t *obj, unsigned int idx) { assert(obj); @@ -3018,12 +3447,16 @@ } /** - * \brief Get value of a specified member from given data as an element of - * enumerated type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \return Value for the member. This is an index of name set in the element. - */ + * \brief Get an element members value. + * + * Use this function if the element is of type + * SNDRV_CTL_ELEM_TYPE_ENUMERATED. It returns the index of the active item. See + * \ref snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \param idx The index of the requested member. + * \return The index of the active item. + */ unsigned int snd_ctl_elem_value_get_enumerated(const snd_ctl_elem_value_t *obj, unsigned int idx) { assert(obj); @@ -3032,12 +3465,16 @@ } /** - * \brief Get value of a specified member from given data as an element of - * bytes type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \return Value for the member. - */ + * \brief Get an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTE. It + * returns the value of one member. See \ref snd_ctl_elem_value_t and \ref + * control for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \return The members value. + */ unsigned char snd_ctl_elem_value_get_byte(const snd_ctl_elem_value_t *obj, unsigned int idx) { assert(obj); @@ -3046,12 +3483,16 @@ } /** - * \brief Set value of a specified member to given data as an element of - * boolean type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \param val Value for the member. - */ + * \brief Set an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BOOLEAN. It + * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control + * for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \param val The new value. + */ void snd_ctl_elem_value_set_boolean(snd_ctl_elem_value_t *obj, unsigned int idx, long val) { assert(obj); @@ -3060,12 +3501,16 @@ } /** - * \brief Set value of a specified member to given data as an element of - * integer type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \param val Value for the member. - */ + * \brief Set an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER. It + * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control + * for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \param val The new value. + */ void snd_ctl_elem_value_set_integer(snd_ctl_elem_value_t *obj, unsigned int idx, long val) { assert(obj); @@ -3074,12 +3519,16 @@ } /** - * \brief Set value of a specified member to given data as an element of - * integer64 type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \param val Value for the member. - */ + * \brief Set an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_INTEGER64. It + * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control + * for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \param val The new value. + */ void snd_ctl_elem_value_set_integer64(snd_ctl_elem_value_t *obj, unsigned int idx, long long val) { assert(obj); @@ -3088,12 +3537,16 @@ } /** - * \brief Set value of a specified member to given data as an element of - * enumerated type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \param val Value for the member. - */ + * \brief Set an element members value. + * + * Use this function if the element is of type + * SNDRV_CTL_ELEM_TYPE_ENUMERATED. It activates the specified item. See \ref + * snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \param idx The index of the requested member. + * \param val The new index of the item to be activated. + */ void snd_ctl_elem_value_set_enumerated(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned int val) { assert(obj); @@ -3102,12 +3555,16 @@ } /** - * \brief Set value for a specified member to given data as an element of byte - * type. - * \param obj Data of an element. - * \param idx Index of member in the element. - * \param val Value for the member. - */ + * \brief Set an element members value. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTE. It + * sets the value of one member. See \ref snd_ctl_elem_value_t and \ref control + * for more details. + * + * \param obj The element value object. + * \param idx The index of the member. + * \param val The new value. + */ void snd_ctl_elem_value_set_byte(snd_ctl_elem_value_t *obj, unsigned int idx, unsigned char val) { assert(obj); @@ -3116,10 +3573,17 @@ } /** - * \brief Set values to given data as an element of bytes type. - * \param obj Data of an element. - * \param data Pointer for byte array. - * \param size The number of bytes included in the memory block. + * \brief Replace the data stored within the element. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTES. It + * replaces the data stored in the element. Note that "bytes" elements don't + * have members. They have only one single block of data. + * + * See \ref snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \param data Pointer to the new data. + * \param size The size of the new data, in bytes. */ void snd_ctl_elem_set_bytes(snd_ctl_elem_value_t *obj, void *data, size_t size) { @@ -3129,10 +3593,17 @@ } /** - * \brief Get memory block from given data as an element of bytes type. - * \param obj Data of an element. - * \return Pointer for byte array. - */ + * \brief Get the data stored within the element. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_BYTES. It + * returns the data stored in the element. Note that "bytes" elements don't have + * members. They have only one single block of data. + * + * See \ref snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \return Pointer to the elements data. + */ const void * snd_ctl_elem_value_get_bytes(const snd_ctl_elem_value_t *obj) { assert(obj); @@ -3140,11 +3611,17 @@ } /** - * \brief Get value from given data to given pointer as an element of IEC958 - * type. - * \param obj Data of an element. - * \param ptr Pointer to IEC958 data. - */ + * \brief Get an elements IEC958 data. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_IEC958. Note that + * "IEC958" elements don't have members. They have only one single + * IEC958 information block. + * + * See \ref snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \param ptr Pointer to an IEC958 structure. The data is stored there. + */ void snd_ctl_elem_value_get_iec958(const snd_ctl_elem_value_t *obj, snd_aes_iec958_t *ptr) { assert(obj && ptr); @@ -3152,14 +3629,19 @@ } /** - * \brief Set value from given pointer to given data as an element of IEC958 - * type. - * \param obj Data of an element. - * \param ptr Pointer to IEC958 data. - */ + * \brief Set an elements IEC958 data. + * + * Use this function if the element is of type SNDRV_CTL_ELEM_TYPE_IEC958. Note + * that "IEC958" elements don't have members. They have only one single IEC958 + * information block. + * + * See \ref snd_ctl_elem_value_t and \ref control for more details. + * + * \param obj The element value object. + * \param ptr Pointer to the new IEC958 data. + */ void snd_ctl_elem_value_set_iec958(snd_ctl_elem_value_t *obj, const snd_aes_iec958_t *ptr) { assert(obj && ptr); memcpy(&obj->value.iec958, ptr, sizeof(obj->value.iec958)); } - diff -Nru alsa-lib-1.2.2/src/control/control_empty.c alsa-lib-1.2.6.1/src/control/control_empty.c --- alsa-lib-1.2.2/src/control/control_empty.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_empty.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,101 @@ +/** + * \file control/control_empty.c + * \ingroup Control_Plugins + * \brief Control Empty Plugin Interface + * \author Jaroslav Kysela + * \date 2021 + */ +/* + * Control - Empty plugin + * Copyright (c) 2021 by Jaroslav Kysela + * + * + * 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 "control_local.h" + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_control_empty = ""; +#endif + +/*! \page control_plugins + +\section control_plugins_empty Plugin: Empty + +This plugin just redirects the control device to another plugin. + +\code +ctl.name { + type empty # Empty Control + child STR # Slave name + # or + child { # Child definition + ... + } +} +\endcode + +\subsection control_plugins_empty_funcref Function reference + +

    +
  • _snd_ctl_empty_open() +
+ +*/ + +/** + * \brief Creates a new Empty Control + * \param handlep Returns created Control handle + * \param name Name of Control + * \param root Root configuration node + * \param conf Configuration node with empty Control description + * \param mode Control mode + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int _snd_ctl_empty_open(snd_ctl_t **handlep, const char *name ATTRIBUTE_UNUSED, + snd_config_t *root, snd_config_t *conf, int mode) +{ + snd_config_t *child = NULL; + snd_config_iterator_t i, next; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (_snd_conf_generic_id(id)) + continue; + if (strcmp(id, "child") == 0) { + child = n; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!child) { + SNDERR("child is not defined"); + return -EINVAL; + } + return _snd_ctl_open_named_child(handlep, name, root, child, mode, conf); +} +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(_snd_ctl_empty_open, SND_CONTROL_DLSYM_VERSION); +#endif diff -Nru alsa-lib-1.2.2/src/control/control_hw.c alsa-lib-1.2.6.1/src/control/control_hw.c --- alsa-lib-1.2.2/src/control/control_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -1,3 +1,9 @@ +/** + * \file control/control_hw.c + * \brief CTL HW Plugin Interface + * \author Jaroslav Kysela + * \date 2000 + */ /* * Control Interface - Hardware * Copyright (c) 1998,1999,2000 by Jaroslav Kysela @@ -375,6 +381,17 @@ .read = snd_ctl_hw_read, }; +/** + * \brief Creates a new hw control + * \param handle Returns created control handle + * \param name Name of control device + * \param card Number of card + * \param mode Control mode + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) { int fd, ver; @@ -437,11 +454,44 @@ return 0; } +/*! \page control_plugins + +\section control_plugins_hw Plugin: hw + +This plugin communicates directly with the ALSA kernel driver. It is a raw +communication without any conversions. + +\code +control.name { + type hw # Kernel PCM + card INT/STR # Card name (string) or number (integer) +} +\endcode + +\subsection control_plugins_hw_funcref Function reference + +
    +
  • snd_ctl_hw_open() +
  • _snd_ctl_hw_open() +
+ +*/ + +/** + * \brief Creates a new hw control handle + * \param handlep Returns created control handle + * \param name Name of control device + * \param root Root configuration node + * \param conf Configuration node with hw PCM description + * \param mode Control Mode + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ int _snd_ctl_hw_open(snd_ctl_t **handlep, char *name, snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf, int mode) { snd_config_iterator_t i, next; long card = -1; - const char *str; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -451,15 +501,10 @@ if (_snd_conf_generic_id(id)) continue; if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) - return -EINVAL; - card = snd_card_get_index(str); - if (card < 0) - return card; - } + err = snd_config_get_card(n); + if (err < 0) + return err; + card = err; continue; } return -EINVAL; @@ -468,4 +513,6 @@ return -EINVAL; return snd_ctl_hw_open(handlep, name, card, mode); } +#ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(_snd_ctl_hw_open, SND_CONTROL_DLSYM_VERSION); +#endif diff -Nru alsa-lib-1.2.2/src/control/control_local.h alsa-lib-1.2.6.1/src/control/control_local.h --- alsa-lib-1.2.2/src/control/control_local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_local.h 2021-12-09 13:17:59.000000000 +0000 @@ -106,3 +106,21 @@ int INTERNAL(snd_ctl_elem_info_get_dimensions)(const snd_ctl_elem_info_t *obj); int INTERNAL(snd_ctl_elem_info_get_dimension)(const snd_ctl_elem_info_t *obj, unsigned int idx); #endif /* INTERNAL */ + +int _snd_ctl_open_named_child(snd_ctl_t **pctl, const char *name, + snd_config_t *root, snd_config_t *conf, + int mode, snd_config_t *parent_conf); +static inline int +_snd_ctl_open_child(snd_ctl_t **pctl, snd_config_t *root, + snd_config_t *conf, int mode, snd_config_t *parent_conf) +{ + return _snd_ctl_open_named_child(pctl, NULL, root, conf, mode, parent_conf); +} + +int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, + unsigned int element_count, + unsigned int member_count); + +int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, + const char *str, + const char **ret_ptr); diff -Nru alsa-lib-1.2.2/src/control/control_plugin.c alsa-lib-1.2.6.1/src/control/control_plugin.c --- alsa-lib-1.2.2/src/control/control_plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_plugin.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,64 @@ +/** + * \file control/control_plugin.c + * \ingroup Control + * \brief Control Interface + * \author Jaroslav Kysela + * \date 2021 + */ +/* + * Control - Common plugin code + * Copyright (c) 2021 by Jaroslav Kysela + * + * + * 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 + * + */ + +/*! + +\page control_plugins Primitive control plugins + +Control plugins extends functionality and features of control devices. +The plugins take care about various control mapping or so. + +The child configuration (in one compound): + +\code +ctl.test { + type remap + child "hw:0" + ... map/remap configuration ... +} +\endcode + +The child may be defined as compound containing the full specification: + +\code +ctl.test { + type remap + child { + type hw + card 0 + } + ... map/remap configuration ... +} +\endcode + +*/ + +#include "control_local.h" +#include "control_plugin.h" + +/* move the common plugin code from control_remap.c here on demand */ diff -Nru alsa-lib-1.2.2/src/control/control_remap.c alsa-lib-1.2.6.1/src/control/control_remap.c --- alsa-lib-1.2.2/src/control/control_remap.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_remap.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,1329 @@ +/** + * \file control/control_remap.c + * \brief CTL Remap Plugin Interface + * \author Jaroslav Kysela + * \date 2021 + */ +/* + * Control - Remap Controls + * Copyright (c) 2021 by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#include "control_local.h" + +#if 0 +#define REMAP_DEBUG 1 +#define debug(format, args...) fprintf(stderr, format, ##args) +#define debug_id(id, format, args...) do { \ + char *s = snd_ctl_ascii_elem_id_get(id); \ + fprintf(stderr, "%s: ", s); free(s); \ + fprintf(stderr, format, ##args); \ +} while (0) +#else +#define REMAP_DEBUG 0 +#define debug(format, args...) do { } while (0) +#define debug_id(id, format, args...) do { } while (0) +#endif + +#define EREMAPNOTFOUND (888899) + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_control_remap = ""; +#endif + +#ifndef DOC_HIDDEN +typedef struct { + unsigned int numid_child; + unsigned int numid_app; +} snd_ctl_numid_t; + +typedef struct { + snd_ctl_elem_id_t id_child; + snd_ctl_elem_id_t id_app; +} snd_ctl_remap_id_t; + +typedef struct { + snd_ctl_elem_id_t map_id; + snd_ctl_elem_type_t type; + size_t controls_items; + size_t controls_alloc; + struct snd_ctl_map_ctl { + snd_ctl_elem_id_t id_child; + size_t channel_map_items; + size_t channel_map_alloc; + long *channel_map; + } *controls; + unsigned int event_mask; +} snd_ctl_map_t; + +typedef struct { + snd_ctl_t *child; + int numid_remap_active; + unsigned int numid_app_last; + size_t numid_items; + size_t numid_alloc; + snd_ctl_numid_t *numid; + snd_ctl_numid_t numid_temp; + size_t remap_items; + size_t remap_alloc; + snd_ctl_remap_id_t *remap; + size_t map_items; + size_t map_alloc; + snd_ctl_map_t *map; + size_t map_read_queue_head; + size_t map_read_queue_tail; + snd_ctl_map_t **map_read_queue; +} snd_ctl_remap_t; +#endif + +static snd_ctl_numid_t *remap_numid_temp(snd_ctl_remap_t *priv, unsigned int numid) +{ + priv->numid_temp.numid_child = numid; + priv->numid_temp.numid_app = numid; + return &priv->numid_temp; +} + +static snd_ctl_numid_t *remap_find_numid_app(snd_ctl_remap_t *priv, unsigned int numid_app) +{ + snd_ctl_numid_t *numid; + size_t count; + + if (!priv->numid_remap_active) + return remap_numid_temp(priv, numid_app); + numid = priv->numid; + for (count = priv->numid_items; count > 0; count--, numid++) + if (numid_app == numid->numid_app) + return numid; + return NULL; +} + +static snd_ctl_numid_t *remap_numid_new(snd_ctl_remap_t *priv, unsigned int numid_child, + unsigned int numid_app) +{ + snd_ctl_numid_t *numid; + + if (priv->numid_alloc == priv->numid_items) { + numid = realloc(priv->numid, (priv->numid_alloc + 16) * sizeof(*numid)); + if (numid == NULL) + return NULL; + memset(numid + priv->numid_alloc, 0, sizeof(*numid) * 16); + priv->numid_alloc += 16; + priv->numid = numid; + } + numid = &priv->numid[priv->numid_items++]; + numid->numid_child = numid_child; + numid->numid_app = numid_app; + debug("new numid: child %u app %u\n", numid->numid_child, numid->numid_app); + return numid; +} + +static snd_ctl_numid_t *remap_numid_child_new(snd_ctl_remap_t *priv, unsigned int numid_child) +{ + unsigned int numid_app; + + if (numid_child == 0) + return NULL; + if (remap_find_numid_app(priv, numid_child)) { + while (remap_find_numid_app(priv, priv->numid_app_last)) + priv->numid_app_last++; + numid_app = priv->numid_app_last; + } else { + numid_app = numid_child; + } + return remap_numid_new(priv, numid_child, numid_app); +} + +static snd_ctl_numid_t *remap_find_numid_child(snd_ctl_remap_t *priv, unsigned int numid_child) +{ + snd_ctl_numid_t *numid; + size_t count; + + if (!priv->numid_remap_active) + return remap_numid_temp(priv, numid_child); + numid = priv->numid; + for (count = priv->numid_items; count > 0; count--, numid++) + if (numid_child == numid->numid_child) + return numid; + return remap_numid_child_new(priv, numid_child); +} + +static snd_ctl_remap_id_t *remap_find_id_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count; + snd_ctl_remap_id_t *rid; + + if (id->numid > 0) { + rid = priv->remap; + for (count = priv->remap_items; count > 0; count--, rid++) + if (id->numid == rid->id_child.numid) + return rid; + } + rid = priv->remap; + for (count = priv->remap_items; count > 0; count--, rid++) + if (snd_ctl_elem_id_compare_set(id, &rid->id_child) == 0) + return rid; + return NULL; +} + +static snd_ctl_remap_id_t *remap_find_id_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count; + snd_ctl_remap_id_t *rid; + + if (id->numid > 0) { + rid = priv->remap; + for (count = priv->remap_items; count > 0; count--, rid++) + if (id->numid == rid->id_app.numid) + return rid; + } + rid = priv->remap; + for (count = priv->remap_items; count > 0; count--, rid++) + if (snd_ctl_elem_id_compare_set(id, &rid->id_app) == 0) + return rid; + return NULL; +} + +static snd_ctl_map_t *remap_find_map_numid(snd_ctl_remap_t *priv, unsigned int numid) +{ + size_t count; + snd_ctl_map_t *map; + + if (numid == 0) + return NULL; + map = priv->map; + for (count = priv->map_items; count > 0; count--, map++) { + if (numid == map->map_id.numid) + return map; + } + return NULL; +} +static snd_ctl_map_t *remap_find_map_id(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id) +{ + size_t count; + snd_ctl_map_t *map; + + if (id->numid > 0) + return remap_find_map_numid(priv, id->numid); + map = priv->map; + for (count = priv->map_items; count > 0; count--, map++) + if (snd_ctl_elem_id_compare_set(id, &map->map_id) == 0) + return map; + return NULL; +} + +static int remap_id_to_child(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl_remap_id_t **_rid) +{ + snd_ctl_remap_id_t *rid; + snd_ctl_numid_t *numid; + + debug_id(id, "%s enter\n", __func__); + rid = remap_find_id_app(priv, id); + if (rid) { + if (rid->id_app.numid == 0) { + numid = remap_find_numid_app(priv, id->numid); + if (numid) { + rid->id_child.numid = numid->numid_child; + rid->id_app.numid = numid->numid_app; + } + } + *id = rid->id_child; + } else { + if (remap_find_id_child(priv, id)) + return -ENOENT; + numid = remap_find_numid_app(priv, id->numid); + if (numid) + id->numid = numid->numid_child; + else + id->numid = 0; + } + *_rid = rid; + debug_id(id, "%s leave\n", __func__); + return 0; +} + +static int remap_id_to_app(snd_ctl_remap_t *priv, snd_ctl_elem_id_t *id, snd_ctl_remap_id_t *rid, int err) +{ + snd_ctl_numid_t *numid; + + if (rid) { + if (err >= 0 && rid->id_app.numid == 0) { + numid = remap_numid_child_new(priv, id->numid); + if (numid == NULL) + return -EIO; + rid->id_child.numid = numid->numid_child; + rid->id_app.numid = numid->numid_app; + } + *id = rid->id_app; + } else { + if (err >= 0) { + numid = remap_find_numid_child(priv, id->numid); + if (numid == NULL) + return -EIO; + id->numid = numid->numid_app; + } + } + return err; +} + +static void remap_free(snd_ctl_remap_t *priv) +{ + size_t idx1, idx2; + snd_ctl_map_t *map; + + for (idx1 = 0; idx1 < priv->map_items; idx1++) { + map = &priv->map[idx1]; + for (idx2 = 0; idx2 < map->controls_items; idx2++) + free(map->controls[idx2].channel_map); + free(map->controls); + } + free(priv->map_read_queue); + free(priv->map); + free(priv->remap); + free(priv->numid); + free(priv); +} + +static int snd_ctl_remap_close(snd_ctl_t *ctl) +{ + snd_ctl_remap_t *priv = ctl->private_data; + int err = snd_ctl_close(priv->child); + remap_free(priv); + return err; +} + +static int snd_ctl_remap_nonblock(snd_ctl_t *ctl, int nonblock) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_nonblock(priv->child, nonblock); +} + +static int snd_ctl_remap_async(snd_ctl_t *ctl, int sig, pid_t pid) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_async(priv->child, sig, pid); +} + +static int snd_ctl_remap_subscribe_events(snd_ctl_t *ctl, int subscribe) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_subscribe_events(priv->child, subscribe); +} + +static int snd_ctl_remap_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_card_info(priv->child, info); +} + +static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_elem_id_t *id; + snd_ctl_remap_id_t *rid; + snd_ctl_numid_t *numid; + snd_ctl_map_t *map; + unsigned int index; + size_t index2; + int err; + + err = snd_ctl_elem_list(priv->child, list); + if (err < 0) + return err; + for (index = 0; index < list->used; index++) { + id = &list->pids[index]; + rid = remap_find_id_child(priv, id); + if (rid) { + rid->id_app.numid = id->numid; + *id = rid->id_app; + } + numid = remap_find_numid_child(priv, id->numid); + if (numid == NULL) + return -EIO; + id->numid = numid->numid_app; + } + if (list->offset >= list->count + priv->map_items) + return 0; + index2 = 0; + if (list->offset > list->count) + index2 = list->offset - list->count; + for ( ; index < list->space && index2 < priv->map_items; index2++, index++) { + id = &list->pids[index]; + map = &priv->map[index2]; + *id = map->map_id; + list->used++; + } + list->count += priv->map_items; + return 0; +} + +#define ACCESS_BITS(bits) \ + (bits & (SNDRV_CTL_ELEM_ACCESS_READWRITE|\ + SNDRV_CTL_ELEM_ACCESS_VOLATILE|\ + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) + +static int remap_map_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info) +{ + snd_ctl_map_t *map; + snd_ctl_elem_info_t info2, info3; + size_t item; + unsigned int access; + size_t count; + int owner, err; + + map = remap_find_map_id(priv, &info->id); + if (map == NULL) + return -EREMAPNOTFOUND; + debug_id(&info->id, "%s\n", __func__); + assert(map->controls_items > 0); + snd_ctl_elem_info_clear(&info2); + info2.id = map->controls[0].id_child; + debug_id(&info2.id, "%s controls[0]\n", __func__); + err = snd_ctl_elem_info(priv->child, &info2); + if (err < 0) + return err; + if (info2.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN && + info2.type != SNDRV_CTL_ELEM_TYPE_INTEGER && + info2.type != SNDRV_CTL_ELEM_TYPE_INTEGER64 && + info2.type != SNDRV_CTL_ELEM_TYPE_BYTES) + return -EIO; + map->controls[0].id_child.numid = info2.id.numid; + map->type = info2.type; + access = info2.access; + owner = info2.owner; + count = map->controls[0].channel_map_items; + for (item = 1; item < map->controls_items; item++) { + snd_ctl_elem_info_clear(&info3); + info3.id = map->controls[item].id_child; + debug_id(&info3.id, "%s controls[%zd]\n", __func__, item); + err = snd_ctl_elem_info(priv->child, &info3); + if (err < 0) + return err; + if (info2.type != info3.type) + return -EIO; + if (ACCESS_BITS(info2.access) != ACCESS_BITS(info3.access)) + return -EIO; + if (info2.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + if (memcmp(&info2.value.integer, &info3.value.integer, sizeof(info2.value.integer))) + return -EIO; + } else if (info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { + if (memcmp(&info2.value.integer64, &info3.value.integer64, sizeof(info2.value.integer64))) + return -EIO; + } + access |= info3.access; + if (owner == 0) + owner = info3.owner; + if (count < map->controls[item].channel_map_items) + count = map->controls[item].channel_map_items; + } + snd_ctl_elem_info_clear(info); + info->id = map->map_id; + info->type = info2.type; + info->access = access; + info->count = count; + if (info2.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER) + info->value.integer = info2.value.integer; + else if (info2.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) + info->value.integer64 = info2.value.integer64; + if (access & SNDRV_CTL_ELEM_ACCESS_LOCK) + info->owner = owner; + return 0; +} + +static int snd_ctl_remap_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + int err; + + debug_id(&info->id, "%s\n", __func__); + err = remap_map_elem_info(priv, info); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_id_to_child(priv, &info->id, &rid); + if (err < 0) + return err; + err = snd_ctl_elem_info(priv->child, info); + return remap_id_to_app(priv, &info->id, rid, err); +} + +static int remap_map_elem_read(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control) +{ + snd_ctl_map_t *map; + struct snd_ctl_map_ctl *mctl; + snd_ctl_elem_value_t control2; + size_t item, index; + int err; + + map = remap_find_map_id(priv, &control->id); + if (map == NULL) + return -EREMAPNOTFOUND; + debug_id(&control->id, "%s\n", __func__); + snd_ctl_elem_value_clear(control); + control->id = map->map_id; + for (item = 0; item < map->controls_items; item++) { + mctl = &map->controls[item]; + snd_ctl_elem_value_clear(&control2); + control2.id = mctl->id_child; + debug_id(&control2.id, "%s controls[%zd]\n", __func__, item); + err = snd_ctl_elem_read(priv->child, &control2); + if (err < 0) + return err; + if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + for (index = 0; index < mctl->channel_map_items; index++) { + long src = mctl->channel_map[index]; + if ((unsigned long)src < ARRAY_SIZE(control->value.integer.value)) + control->value.integer.value[index] = control2.value.integer.value[src]; + } + } else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { + for (index = 0; index < mctl->channel_map_items; index++) { + long src = mctl->channel_map[index]; + if ((unsigned long)src < ARRAY_SIZE(control->value.integer64.value)) + control->value.integer64.value[index] = control2.value.integer64.value[src]; + } + } else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) { + for (index = 0; index < mctl->channel_map_items; index++) { + long src = mctl->channel_map[index]; + if ((unsigned long)src < ARRAY_SIZE(control->value.bytes.data)) + control->value.bytes.data[index] = control2.value.bytes.data[src]; + } + } + } + return 0; +} + +static int snd_ctl_remap_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + int err; + + debug_id(&control->id, "%s\n", __func__); + err = remap_map_elem_read(priv, control); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_id_to_child(priv, &control->id, &rid); + if (err < 0) + return err; + err = snd_ctl_elem_read(priv->child, control); + return remap_id_to_app(priv, &control->id, rid, err); +} + +static int remap_map_elem_write(snd_ctl_remap_t *priv, snd_ctl_elem_value_t *control) +{ + snd_ctl_map_t *map; + struct snd_ctl_map_ctl *mctl; + snd_ctl_elem_value_t control2; + size_t item, index; + int err, changes; + + map = remap_find_map_id(priv, &control->id); + if (map == NULL) + return -EREMAPNOTFOUND; + debug_id(&control->id, "%s\n", __func__); + control->id = map->map_id; + for (item = 0; item < map->controls_items; item++) { + mctl = &map->controls[item]; + snd_ctl_elem_value_clear(&control2); + control2.id = mctl->id_child; + debug_id(&control2.id, "%s controls[%zd]\n", __func__, item); + err = snd_ctl_elem_read(priv->child, &control2); + if (err < 0) + return err; + changes = 0; + if (map->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || + map->type == SNDRV_CTL_ELEM_TYPE_INTEGER) { + for (index = 0; index < mctl->channel_map_items; index++) { + long dst = mctl->channel_map[index]; + if ((unsigned long)dst < ARRAY_SIZE(control->value.integer.value)) { + changes |= control2.value.integer.value[dst] != control->value.integer.value[index]; + control2.value.integer.value[dst] = control->value.integer.value[index]; + } + } + } else if (map->type == SNDRV_CTL_ELEM_TYPE_INTEGER64) { + for (index = 0; index < mctl->channel_map_items; index++) { + long dst = mctl->channel_map[index]; + if ((unsigned long)dst < ARRAY_SIZE(control->value.integer64.value)) { + changes |= control2.value.integer64.value[dst] != control->value.integer64.value[index]; + control2.value.integer64.value[dst] = control->value.integer64.value[index]; + } + } + } else if (map->type == SNDRV_CTL_ELEM_TYPE_BYTES) { + for (index = 0; index < mctl->channel_map_items; index++) { + long dst = mctl->channel_map[index]; + if ((unsigned long)dst < ARRAY_SIZE(control->value.bytes.data)) { + changes |= control2.value.bytes.data[dst] != control->value.bytes.data[index]; + control2.value.bytes.data[dst] = control->value.bytes.data[index]; + } + } + } + debug_id(&control2.id, "%s changes %d\n", __func__, changes); + if (changes > 0) { + err = snd_ctl_elem_write(priv->child, &control2); + if (err < 0) + return err; + } + } + return 0; +} + +static int snd_ctl_remap_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + int err; + + debug_id(&control->id, "%s\n", __func__); + err = remap_map_elem_write(priv, control); + if (err != -EREMAPNOTFOUND) + return err; + err = remap_id_to_child(priv, &control->id, &rid); + if (err < 0) + return err; + err = snd_ctl_elem_write(priv->child, control); + return remap_id_to_app(priv, &control->id, rid, err); +} + +static int snd_ctl_remap_elem_lock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + int err; + + debug_id(id, "%s\n", __func__); + err = remap_id_to_child(priv, id, &rid); + if (err < 0) + return err; + err = snd_ctl_elem_lock(priv->child, id); + return remap_id_to_app(priv, id, rid, err); +} + +static int snd_ctl_remap_elem_unlock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + int err; + + debug_id(id, "%s\n", __func__); + err = remap_id_to_child(priv, id, &rid); + if (err < 0) + return err; + err = snd_ctl_elem_unlock(priv->child, id); + return remap_id_to_app(priv, id, rid, err); +} + +static int remap_get_map_numid(snd_ctl_remap_t *priv, struct snd_ctl_map_ctl *mctl) +{ + snd_ctl_elem_info_t info; + snd_ctl_numid_t *numid; + int err; + + if (mctl->id_child.numid > 0) + return 0; + debug_id(&mctl->id_child, "%s get numid\n", __func__); + snd_ctl_elem_info_clear(&info); + info.id = mctl->id_child; + err = snd_ctl_elem_info(priv->child, &info); + if (err < 0) + return err; + numid = remap_find_numid_child(priv, info.id.numid); + if (numid == NULL) + return -EIO; + mctl->id_child.numid = info.id.numid; + return 0; +} + +static int remap_map_elem_tlv(snd_ctl_remap_t *priv, int op_flag, unsigned int numid, + unsigned int *tlv, unsigned int tlv_size) +{ + snd_ctl_map_t *map; + struct snd_ctl_map_ctl *mctl; + size_t item; + unsigned int *tlv2; + int err; + + map = remap_find_map_numid(priv, numid); + if (map == NULL) + return -EREMAPNOTFOUND; + if (op_flag != 0) /* read only */ + return -ENXIO; + debug("%s numid %d\n", __func__, numid); + mctl = &map->controls[0]; + err = remap_get_map_numid(priv, mctl); + if (err < 0) + return err; + memset(tlv, 0, tlv_size); + err = priv->child->ops->element_tlv(priv->child, op_flag, mctl->id_child.numid, tlv, tlv_size); + if (err < 0) + return err; + tlv2 = malloc(tlv_size); + if (tlv2 == NULL) + return -ENOMEM; + for (item = 1; item < map->controls_items; item++) { + mctl = &map->controls[item]; + err = remap_get_map_numid(priv, mctl); + if (err < 0) { + free(tlv2); + return err; + } + memset(tlv2, 0, tlv_size); + err = priv->child->ops->element_tlv(priv->child, op_flag, mctl->id_child.numid, tlv2, tlv_size); + if (err < 0) { + free(tlv2); + return err; + } + if (memcmp(tlv, tlv2, tlv_size) != 0) { + free(tlv2); + return -EIO; + } + } + free(tlv2); + return 0; +} + +static int snd_ctl_remap_elem_tlv(snd_ctl_t *ctl, int op_flag, + unsigned int numid, + unsigned int *tlv, unsigned int tlv_size) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_numid_t *map_numid; + int err; + + debug("%s: numid = %d, op_flag = %d\n", __func__, numid, op_flag); + err = remap_map_elem_tlv(priv, op_flag, numid, tlv, tlv_size); + if (err != -EREMAPNOTFOUND) + return err; + map_numid = remap_find_numid_app(priv, numid); + if (map_numid == NULL) + return -ENOENT; + return priv->child->ops->element_tlv(priv->child, op_flag, map_numid->numid_child, tlv, tlv_size); +} + +static int snd_ctl_remap_hwdep_next_device(snd_ctl_t *ctl, int * device) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_hwdep_next_device(priv->child, device); +} + +static int snd_ctl_remap_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_hwdep_info(priv->child, info); +} + +static int snd_ctl_remap_pcm_next_device(snd_ctl_t *ctl, int * device) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_pcm_next_device(priv->child, device); +} + +static int snd_ctl_remap_pcm_info(snd_ctl_t *ctl, snd_pcm_info_t * info) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_pcm_info(priv->child, info); +} + +static int snd_ctl_remap_pcm_prefer_subdevice(snd_ctl_t *ctl, int subdev) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_pcm_prefer_subdevice(priv->child, subdev); +} + +static int snd_ctl_remap_rawmidi_next_device(snd_ctl_t *ctl, int * device) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_rawmidi_next_device(priv->child, device); +} + +static int snd_ctl_remap_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_rawmidi_info(priv->child, info); +} + +static int snd_ctl_remap_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_rawmidi_prefer_subdevice(priv->child, subdev); +} + +static int snd_ctl_remap_set_power_state(snd_ctl_t *ctl, unsigned int state) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_set_power_state(priv->child, state); +} + +static int snd_ctl_remap_get_power_state(snd_ctl_t *ctl, unsigned int *state) +{ + snd_ctl_remap_t *priv = ctl->private_data; + return snd_ctl_get_power_state(priv->child, state); +} + +static void _next_ptr(size_t *ptr, size_t count) +{ + *ptr = (*ptr + 1) % count; +} + +static void remap_event_for_all_map_controls(snd_ctl_remap_t *priv, + snd_ctl_elem_id_t *id, + unsigned int event_mask) +{ + size_t count, index, head; + snd_ctl_map_t *map; + struct snd_ctl_map_ctl *mctl; + int found; + + if (event_mask == SNDRV_CTL_EVENT_MASK_REMOVE) + event_mask = SNDRV_CTL_EVENT_MASK_INFO; + map = priv->map; + for (count = priv->map_items; count > 0; count--, map++) { + for (index = 0; index < map->controls_items; index++) { + mctl = &map->controls[index]; + if (mctl->id_child.numid == 0) { + if (snd_ctl_elem_id_compare_set(id, &mctl->id_child)) + continue; + mctl->id_child.numid = id->numid; + } + if (id->numid != mctl->id_child.numid) + continue; + debug_id(&map->map_id, "%s found (all)\n", __func__); + map->event_mask |= event_mask; + found = 0; + for (head = priv->map_read_queue_head; + head != priv->map_read_queue_tail; + _next_ptr(&head, priv->map_items)) + if (priv->map_read_queue[head] == map) { + found = 1; + break; + } + if (found) + continue; + debug_id(&map->map_id, "%s marking for read\n", __func__); + priv->map_read_queue[priv->map_read_queue_tail] = map; + _next_ptr(&priv->map_read_queue_tail, priv->map_items); + } + } +} + +static int snd_ctl_remap_read(snd_ctl_t *ctl, snd_ctl_event_t *event) +{ + snd_ctl_remap_t *priv = ctl->private_data; + snd_ctl_remap_id_t *rid; + snd_ctl_numid_t *numid; + snd_ctl_map_t *map; + int err; + + if (priv->map_read_queue_head != priv->map_read_queue_tail) { + map = priv->map_read_queue[priv->map_read_queue_head]; + _next_ptr(&priv->map_read_queue_head, priv->map_items); + memset(event, 0, sizeof(*event)); + event->type = SNDRV_CTL_EVENT_ELEM; + event->data.elem.mask = map->event_mask; + event->data.elem.id = map->map_id; + map->event_mask = 0; + debug_id(&map->map_id, "%s queue read\n", __func__); + return 1; + } + err = snd_ctl_read(priv->child, event); + if (err < 0 || event->type != SNDRV_CTL_EVENT_ELEM) + return err; + if (event->data.elem.mask == SNDRV_CTL_EVENT_MASK_REMOVE || + (event->data.elem.mask & (SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO | + SNDRV_CTL_EVENT_MASK_ADD | SNDRV_CTL_EVENT_MASK_TLV)) != 0) { + debug_id(&event->data.elem.id, "%s event mask 0x%x\n", __func__, event->data.elem.mask); + remap_event_for_all_map_controls(priv, &event->data.elem.id, event->data.elem.mask); + rid = remap_find_id_child(priv, &event->data.elem.id); + if (rid) { + if (rid->id_child.numid == 0) { + numid = remap_find_numid_child(priv, event->data.elem.id.numid); + if (numid == NULL) + return -EIO; + rid->id_child.numid = numid->numid_child; + rid->id_app.numid = numid->numid_app; + } + event->data.elem.id = rid->id_app; + } else { + numid = remap_find_numid_child(priv, event->data.elem.id.numid); + if (numid == NULL) + return -EIO; + event->data.elem.id.numid = numid->numid_app; + } + } + return err; +} + +static const snd_ctl_ops_t snd_ctl_remap_ops = { + .close = snd_ctl_remap_close, + .nonblock = snd_ctl_remap_nonblock, + .async = snd_ctl_remap_async, + .subscribe_events = snd_ctl_remap_subscribe_events, + .card_info = snd_ctl_remap_card_info, + .element_list = snd_ctl_remap_elem_list, + .element_info = snd_ctl_remap_elem_info, + .element_read = snd_ctl_remap_elem_read, + .element_write = snd_ctl_remap_elem_write, + .element_lock = snd_ctl_remap_elem_lock, + .element_unlock = snd_ctl_remap_elem_unlock, + .element_tlv = snd_ctl_remap_elem_tlv, + .hwdep_next_device = snd_ctl_remap_hwdep_next_device, + .hwdep_info = snd_ctl_remap_hwdep_info, + .pcm_next_device = snd_ctl_remap_pcm_next_device, + .pcm_info = snd_ctl_remap_pcm_info, + .pcm_prefer_subdevice = snd_ctl_remap_pcm_prefer_subdevice, + .rawmidi_next_device = snd_ctl_remap_rawmidi_next_device, + .rawmidi_info = snd_ctl_remap_rawmidi_info, + .rawmidi_prefer_subdevice = snd_ctl_remap_rawmidi_prefer_subdevice, + .set_power_state = snd_ctl_remap_set_power_state, + .get_power_state = snd_ctl_remap_get_power_state, + .read = snd_ctl_remap_read, +}; + +static int add_to_remap(snd_ctl_remap_t *priv, + snd_ctl_elem_id_t *child, + snd_ctl_elem_id_t *app) +{ + snd_ctl_remap_id_t *rid; + + if (priv->remap_alloc == priv->remap_items) { + rid = realloc(priv->remap, (priv->remap_alloc + 16) * sizeof(*rid)); + if (rid == NULL) + return -ENOMEM; + memset(rid + priv->remap_alloc, 0, sizeof(*rid) * 16); + priv->remap_alloc += 16; + priv->remap = rid; + } + rid = &priv->remap[priv->remap_items++]; + rid->id_child = *child; + rid->id_app = *app; + debug_id(&rid->id_child, "%s remap child\n", __func__); + debug_id(&rid->id_app, "%s remap app\n", __func__); + return 0; +} + +static int parse_remap(snd_ctl_remap_t *priv, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + snd_ctl_elem_id_t child, app; + int err; + + if (conf == NULL) + return 0; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id, *str; + if (snd_config_get_id(n, &id) < 0) + continue; + if (snd_config_get_string(n, &str) < 0) { + SNDERR("expected string with the target control id!"); + return -EINVAL; + } + snd_ctl_elem_id_clear(&app); + err = snd_ctl_ascii_elem_id_parse(&app, str); + if (err < 0) { + SNDERR("unable to parse target id '%s'!", str); + return -EINVAL; + } + if (remap_find_id_app(priv, &app)) { + SNDERR("duplicate target id '%s'!", id); + return -EINVAL; + } + snd_ctl_elem_id_clear(&child); + err = snd_ctl_ascii_elem_id_parse(&child, id); + if (err < 0) { + SNDERR("unable to parse source id '%s'!", id); + return -EINVAL; + } + if (remap_find_id_child(priv, &app)) { + SNDERR("duplicate source id '%s'!", id); + return -EINVAL; + } + err = add_to_remap(priv, &child, &app); + if (err < 0) + return err; + } + + return 0; +} + +static int new_map(snd_ctl_remap_t *priv, snd_ctl_map_t **_map, snd_ctl_elem_id_t *id) +{ + snd_ctl_map_t *map; + snd_ctl_numid_t *numid; + + if (priv->map_alloc == priv->map_items) { + map = realloc(priv->map, (priv->map_alloc + 16) * sizeof(*map)); + if (map == NULL) + return -ENOMEM; + memset(map + priv->map_alloc, 0, sizeof(*map) * 16); + priv->map_alloc += 16; + priv->map = map; + } + map = &priv->map[priv->map_items++]; + map->map_id = *id; + numid = remap_numid_new(priv, 0, ++priv->numid_app_last); + if (numid == NULL) + return -ENOMEM; + map->map_id.numid = numid->numid_app; + debug_id(&map->map_id, "%s created\n", __func__); + *_map = map; + return 0; +} + +static int add_ctl_to_map(snd_ctl_map_t *map, struct snd_ctl_map_ctl **_mctl, snd_ctl_elem_id_t *id) +{ + struct snd_ctl_map_ctl *mctl; + + if (map->controls_alloc == map->controls_items) { + mctl = realloc(map->controls, (map->controls_alloc + 4) * sizeof(*mctl)); + if (mctl == NULL) + return -ENOMEM; + memset(mctl + map->controls_alloc, 0, sizeof(*mctl) * 4); + map->controls_alloc += 4; + map->controls = mctl; + } + mctl = &map->controls[map->controls_items++]; + mctl->id_child = *id; + *_mctl = mctl; + return 0; +} + +static int add_chn_to_map(struct snd_ctl_map_ctl *mctl, long idx, long val) +{ + size_t off; + long *map; + + if (mctl->channel_map_alloc <= (size_t)idx) { + map = realloc(mctl->channel_map, (idx + 4) * sizeof(*map)); + if (map == NULL) + return -ENOMEM; + mctl->channel_map = map; + off = mctl->channel_map_alloc; + mctl->channel_map_alloc = idx + 4; + for ( ; off < mctl->channel_map_alloc; off++) + map[off] = -1; + } + if ((size_t)idx >= mctl->channel_map_items) + mctl->channel_map_items = idx + 1; + mctl->channel_map[idx] = val; + return 0; +} + +static int parse_map_vindex(struct snd_ctl_map_ctl *mctl, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + long idx = -1, chn = -1; + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (safe_strtol(id, &idx) || snd_config_get_integer(n, &chn)) { + SNDERR("Wrong channel mapping (%ld -> %ld)", idx, chn); + return -EINVAL; + } + err = add_chn_to_map(mctl, idx, chn); + if (err < 0) + return err; + } + + return 0; +} + +static int parse_map_config(struct snd_ctl_map_ctl *mctl, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "vindex") == 0) { + err = parse_map_vindex(mctl, n); + if (err < 0) + return err; + } + } + return 0; +} + +static int parse_map1(snd_ctl_map_t *map, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + snd_ctl_elem_id_t cid; + struct snd_ctl_map_ctl *mctl; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + snd_ctl_elem_id_clear(&cid); + err = snd_ctl_ascii_elem_id_parse(&cid, id); + if (err < 0) { + SNDERR("unable to parse control id '%s'!", id); + return -EINVAL; + } + err = add_ctl_to_map(map, &mctl, &cid); + if (err < 0) + return err; + err = parse_map_config(mctl, n); + if (err < 0) + return err; + } + + return 0; +} + +static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf) +{ + snd_config_iterator_t i, next; + snd_ctl_elem_id_t eid; + snd_ctl_map_t *map; + int err; + + if (conf == NULL) + return 0; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + snd_ctl_elem_id_clear(&eid); + err = snd_ctl_ascii_elem_id_parse(&eid, id); + if (err < 0) { + SNDERR("unable to parse id '%s'!", id); + return -EINVAL; + } + err = new_map(priv, &map, &eid); + if (err < 0) + return 0; + err = parse_map1(map, n); + if (err < 0) + return err; + } + + return 0; +} + +/** + * \brief Creates a new remap & map control handle + * \param handlep Returns created control handle + * \param name Name of control device + * \param remap Remap configuration + * \param map Map configuration + * \param mode Control handle mode + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *remap, + snd_config_t *map, snd_ctl_t *child, int mode ATTRIBUTE_UNUSED) +{ + snd_ctl_remap_t *priv; + snd_ctl_t *ctl; + int result, err; + + /* no-op, remove the plugin */ + if (!remap && !map) + goto _noop; + + priv = calloc(1, sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; + + err = parse_remap(priv, remap); + if (err < 0) { + result = err; + goto _err; + } + + err = parse_map(priv, map); + if (err < 0) { + result = err; + goto _err; + } + + /* no-op check, remove the plugin */ + if (priv->map_items == 0 && priv->remap_items == 0) { + remap_free(priv); + _noop: + free(child->name); + child->name = name ? strdup(name) : NULL; + if (name && !child->name) + return -ENOMEM; + *handlep = child; + return 0; + } + + priv->map_read_queue = calloc(priv->map_items, sizeof(priv->map_read_queue[0])); + if (priv->map_read_queue == NULL) { + result = -ENOMEM; + goto _err; + } + + priv->numid_remap_active = priv->map_items > 0; + + priv->child = child; + err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name); + if (err < 0) { + result = err; + goto _err; + } + ctl->ops = &snd_ctl_remap_ops; + ctl->private_data = priv; + ctl->poll_fd = child->poll_fd; + + *handlep = ctl; + return 0; + + _err: + remap_free(priv); + return result; +} + +/*! \page control_plugins + +\section control_plugins_remap Plugin: Remap & map + +This plugin can remap (rename) identifiers (except the numid part) for +a child control to another. The plugin can also merge the multiple +child controls to one or split one control to more. + +\code +ctl.name { + type remap # Route & Volume conversion PCM + child STR # Slave name + # or + child { # Slave definition + type STR + ... + } + remap { + # the ID strings are parsed in the amixer style like 'name="Headphone Playback Switch",index=2' + SRC_ID1_STR DST_ID1_STR + SRC_ID2_STR DST_ID2_STR + ... + } + map { + # join two stereo controls to one + CREATE_ID1_STR { + SRC_ID1_STR { + vindex.0 0 # source channel 0 to merged channel 0 + vindex.1 1 + } + SRC_ID2_STR { + vindex.2 0 + vindex.3 1 # source channel 1 to merged channel 3 + } + } + # split stereo to mono + CREATE_ID2_STR { + SRC_ID3_STR { + vindex.0 0 # stereo to mono (first channel) + } + } + CREATE_ID3_STR { + SRC_ID4_STR { + vindex.0 1 # stereo to mono (second channel) + } + } + } +} +\endcode + +\subsection control_plugins_route_funcref Function reference + +
    +
  • snd_ctl_remap_open() +
  • _snd_ctl_remap_open() +
+ +*/ + +/** + * \brief Creates a new remap & map control plugin + * \param handlep Returns created control handle + * \param name Name of control + * \param root Root configuration node + * \param conf Configuration node with Route & Volume PCM description + * \param mode Control handle mode + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd_config_t *conf, int mode) +{ + snd_config_iterator_t i, next; + snd_config_t *child = NULL; + snd_config_t *remap = NULL; + snd_config_t *map = NULL; + snd_ctl_t *cctl; + int err; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (_snd_conf_generic_id(id)) + continue; + if (strcmp(id, "remap") == 0) { + remap = n; + continue; + } + if (strcmp(id, "map") == 0) { + map = n; + continue; + } + if (strcmp(id, "child") == 0) { + child = n; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!child) { + SNDERR("child is not defined"); + return -EINVAL; + } + err = _snd_ctl_open_child(&cctl, root, child, mode, conf); + if (err < 0) + return err; + err = snd_ctl_remap_open(handlep, name, remap, map, cctl, mode); + if (err < 0) + snd_ctl_close(cctl); + return err; +} +SND_DLSYM_BUILD_VERSION(_snd_ctl_remap_open, SND_CONTROL_DLSYM_VERSION); diff -Nru alsa-lib-1.2.2/src/control/control_shm.c alsa-lib-1.2.6.1/src/control/control_shm.c --- alsa-lib-1.2.2/src/control/control_shm.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_shm.c 2021-12-09 13:17:59.000000000 +0000 @@ -542,6 +542,7 @@ if (snd_config_get_id(n, &id) < 0) continue; if (_snd_conf_generic_id(id)) + continue; if (strcmp(id, "server") == 0) { err = snd_config_get_string(n, &server); if (err < 0) { diff -Nru alsa-lib-1.2.2/src/control/control_symbols.c alsa-lib-1.2.6.1/src/control/control_symbols.c --- alsa-lib-1.2.2/src/control/control_symbols.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/control_symbols.c 2021-12-09 13:17:59.000000000 +0000 @@ -21,11 +21,14 @@ #ifndef PIC extern const char *_snd_module_control_hw; +extern const char *_snd_module_control_empty; +extern const char *_snd_module_control_remap; extern const char *_snd_module_control_shm; extern const char *_snd_module_control_ext; static const char **snd_control_open_objects[] = { &_snd_module_control_hw, + &_snd_module_control_empty, #include "ctl_symbols_list.c" }; diff -Nru alsa-lib-1.2.2/src/control/ctlparse.c alsa-lib-1.2.6.1/src/control/ctlparse.c --- alsa-lib-1.2.2/src/control/ctlparse.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/ctlparse.c 2021-12-09 13:17:59.000000000 +0000 @@ -33,17 +33,20 @@ /* Function to convert from percentage to volume. val = percentage */ -#ifdef HAVE_SOFT_FLOAT -static inline long int convert_prange1(long val, long min, long max) +static inline long int convert_prange1(long perc, long min, long max) { - long temp = val * (max - min); - return temp / 100 + min + ((temp % 100) == 0 ? 0 : 1); -} -#else + long tmp; -#define convert_prange1(val, min, max) \ - ceil((val) * ((max) - (min)) * 0.01 + (min)) +#ifdef HAVE_SOFT_FLOAT + tmp = perc * (max - min); + tmp = tmp / 100 + ((tmp % 100) < 50 ? 0 : 1); +#else + tmp = rint((double)perc * (double)(max - min) * 0.01); #endif + if (tmp == 0 && perc > 0) + tmp++; + return tmp + min; +} #define check_range(val, min, max) \ ((val < min) ? (min) : ((val > max) ? (max) : (val))) @@ -113,14 +116,19 @@ */ char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id) { - unsigned int index, device, subdevice; + unsigned int numid, index, device, subdevice; char buf[256], buf1[32]; + const char *iface; - snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'", - snd_ctl_elem_id_get_numid(id), - snd_ctl_elem_iface_name( - snd_ctl_elem_id_get_interface(id)), - snd_ctl_elem_id_get_name(id)); + numid = snd_ctl_elem_id_get_numid(id); + iface = snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)); + if (numid > 0) { + snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'", + numid, iface, snd_ctl_elem_id_get_name(id)); + } else { + snprintf(buf, sizeof(buf), "iface=%s,name='%s'", + iface, snd_ctl_elem_id_get_name(id)); + } buf[sizeof(buf)-1] = '\0'; index = snd_ctl_elem_id_get_index(id); device = snd_ctl_elem_id_get_device(id); @@ -279,28 +287,51 @@ if (items <= 0) return -1; + end = *ptr; + if (end == '\'' || end == '"') + ptr++; + else + end = '\0'; + for (i = 0; i < items; i++) { snd_ctl_elem_info_set_item(info, i); if (snd_ctl_elem_info(handle, info) < 0) return -1; name = snd_ctl_elem_info_get_item_name(info); - end = *ptr; - if (end == '\'' || end == '"') - ptr++; - else - end = '\0'; len = strlen(name); - if (strncmp(name, ptr, len) == 0) { - if (ptr[len] == end || ptr[len] == ',' || ptr[len] == '\n') { - ptr += len; - *ptrp = ptr; - return i; - } + if (strncmp(name, ptr, len)) + continue; + if (end == '\0' && (ptr[len] == '\0' || ptr[len] == ',' || ptr[len] == '\n')) { + *ptrp = ptr + len; + return i; + } + if (end != '\0' && ptr[len] == end) { + *ptrp = ptr + len + 1; + return i; } } return -1; } +static unsigned int get_ctl_type_max_elements(snd_ctl_elem_type_t type) +{ + struct snd_ctl_elem_value value; + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + case SND_CTL_ELEM_TYPE_INTEGER: + return ARRAY_SIZE(value.value.integer.value); + case SND_CTL_ELEM_TYPE_INTEGER64: + return ARRAY_SIZE(value.value.integer64.value); + case SND_CTL_ELEM_TYPE_ENUMERATED: + return ARRAY_SIZE(value.value.enumerated.item); + case SND_CTL_ELEM_TYPE_BYTES: + return ARRAY_SIZE(value.value.bytes.data); + default: + return 0; + } +} + /** * \brief parse ASCII string as CTL element value * \param handle CTL handle @@ -328,8 +359,11 @@ type = snd_ctl_elem_info_get_type(info); count = snd_ctl_elem_info_get_count(info); snd_ctl_elem_value_set_id(dst, &myid); + + if (count > get_ctl_type_max_elements(type)) + count = get_ctl_type_max_elements(type); - for (idx = 0; idx < count && idx < 128 && ptr && *ptr; idx++) { + for (idx = 0; idx < count && ptr && *ptr; idx++) { if (*ptr == ',') goto skip; switch (type) { diff -Nru alsa-lib-1.2.2/src/control/namehint.c alsa-lib-1.2.6.1/src/control/namehint.c --- alsa-lib-1.2.2/src/control/namehint.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/namehint.c 2021-12-09 13:17:59.000000000 +0000 @@ -78,6 +78,31 @@ return 0; } +/** + * Add a namehint from string given in a user configuration file + */ +static int hint_list_add_custom(struct hint_list *list, + const char *entry) +{ + int err; + const char *sep; + char *name; + + assert(entry); + + sep = strchr(entry, '|'); + if (sep == NULL) + return hint_list_add(list, entry, NULL); + + name = strndup(entry, sep - entry); + if (name == NULL) + return -ENOMEM; + + err = hint_list_add(list, name, sep + 1); + free(name); + return err; +} + static void zero_handler(const char *file ATTRIBUTE_UNUSED, int line ATTRIBUTE_UNUSED, const char *function ATTRIBUTE_UNUSED, @@ -270,8 +295,6 @@ if (snd_config_search(cfg1, "type", &cfg) >= 0 && snd_config_get_string(cfg, &str) >= 0 && strcmp(str, "hw") == 0) { - list->device_input = -1; - list->device_output = -1; if (snd_config_search(cfg1, "device", &cfg) >= 0) { if (snd_config_get_integer(cfg, &dev) < 0) { SNDERR("(%s) device must be an integer", buf); @@ -287,10 +310,14 @@ err = -EINVAL; goto __cleanup; } + if (list->card < 0 && + snd_config_search(cfg, "omit_noargs", &n) >= 0 && + snd_config_get_bool(n) > 0) + goto __skip_add; if (level == 1 && snd_config_search(cfg, "show", &n) >= 0 && snd_config_get_bool(n) <= 0) - goto __skip_add; + goto __skip_add; if (buf1 == NULL && snd_config_search(cfg, "description", &n) >= 0 && snd_config_get_string(n, &str) >= 0) { @@ -541,10 +568,10 @@ * User-defined hints are gathered from namehint.IFACE tree like: * * - * namehint.pcm {
+ * namehint.pcm [
* myfile "file:FILE=/tmp/soundwave.raw|Save sound output to /tmp/soundwave.raw"
- * myplug "plug:front:Do all conversions for front speakers"
- * } + * myplug "plug:front|Do all conversions for front speakers"
+ * ] *
* * Note: The device description is separated with '|' char. @@ -624,7 +651,7 @@ if (snd_config_get_string(snd_config_iterator_entry(i), &str) < 0) continue; - err = hint_list_add(&list, str, NULL); + err = hint_list_add_custom(&list, str); if (err < 0) goto __error; } diff -Nru alsa-lib-1.2.2/src/control/tlv.c alsa-lib-1.2.6.1/src/control/tlv.c --- alsa-lib-1.2.2/src/control/tlv.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/control/tlv.c 2021-12-09 13:17:59.000000000 +0000 @@ -286,7 +286,8 @@ * \param db_gain the dB gain to convert (in 0.01dB unit) * \param value the pointer to store the converted raw volume value * \param xdir the direction for round-up. The value is round up - * when this is positive. + * when this is positive. A negative value means round down. + * Zero means round-up to nearest. * \return 0 if successful, or a negative error code */ int snd_tlv_convert_from_dB(unsigned int *tlv, long rangemin, long rangemax, @@ -346,6 +347,8 @@ long v = (db_gain - min) * (rangemax - rangemin); if (xdir > 0) v += (max - min) - 1; + else if (xdir == 0) + v += ((max - min) + 1) / 2; v = v / (max - min) + rangemin; *value = v; } @@ -368,6 +371,8 @@ long v = (db_gain - min) * (rangemax - rangemin); if (xdir > 0) v += (max - min) - 1; + else if (xdir == 0) + v += ((max - min) + 1) / 2; v = v / (max - min) + rangemin; *value = v; } @@ -392,6 +397,8 @@ v = (v - vmin) * (rangemax - rangemin) / (vmax - vmin); if (xdir > 0) v = ceil(v); + else if (xdir == 0) + v = lrint(v); *value = (long)v + rangemin; } return 0; diff -Nru alsa-lib-1.2.2/src/dlmisc.c alsa-lib-1.2.6.1/src/dlmisc.c --- alsa-lib-1.2.2/src/dlmisc.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/dlmisc.c 2021-12-09 13:17:59.000000000 +0000 @@ -32,12 +32,78 @@ #ifdef HAVE_LIBPTHREAD #include #endif +#include + +#if defined(HAVE_LIBDL) && defined(__GLIBC__) && !defined(__UCLIBC__) +#define DL_ORIGIN_AVAILABLE 1 +#endif #ifndef DOC_HIDDEN #ifndef PIC struct snd_dlsym_link *snd_dlsym_start = NULL; #endif +static int snd_plugin_dir_set = 0; +static char *snd_plugin_dir = NULL; +#endif + +#ifdef HAVE_LIBPTHREAD +static pthread_mutex_t snd_dlpath_mutex = PTHREAD_MUTEX_INITIALIZER; + +static inline void snd_dlpath_lock(void) +{ + pthread_mutex_lock(&snd_dlpath_mutex); +} + +static inline void snd_dlpath_unlock(void) +{ + pthread_mutex_unlock(&snd_dlpath_mutex); +} +#else +static inline void snd_dlpath_lock(void) {} +static inline void snd_dlpath_unlock(void) {} +#endif + +static void snd_dlinfo_origin(char *path, size_t path_len) +{ +#ifdef DL_ORIGIN_AVAILABLE + struct link_map *links; + Dl_info info; + char origin[PATH_MAX]; + if (dladdr1(&snd_dlpath, &info, (void**)&links, RTLD_DL_LINKMAP) == 0) + return; + if (dlinfo(links, RTLD_DI_ORIGIN, origin)) + return; + snprintf(path, path_len, "%s/alsa-lib", origin); + if (access(path, X_OK) == 0) + snd_plugin_dir = strdup(path); #endif +} + +/** + * + * \brief Compose the dynamic path + * \param path Returned path (string) + * \param path_len Returned path max size (with trailing zero) + * \param name Plugin name (relative) + * \return Zero on success, otherwise a negative error code + */ +int snd_dlpath(char *path, size_t path_len, const char *name) +{ + snd_dlpath_lock(); + if (!snd_plugin_dir_set) { + const char *env = getenv("ALSA_PLUGIN_DIR"); + if (env) { + snd_plugin_dir = strdup(env); + } else { + snd_dlinfo_origin(path, path_len); + } + snd_plugin_dir_set = 1; + } + snprintf(path, path_len, "%s/%s", + snd_plugin_dir ? snd_plugin_dir : ALSA_PLUGIN_DIR, name); + snd_dlpath_unlock(); + return 0; +} /** * \brief Opens a dynamic library - ALSA wrapper for \c dlopen. @@ -79,38 +145,26 @@ * via ld.so.conf. */ void *handle = NULL; - char *filename = NULL; + const char *filename = name; + char path[PATH_MAX]; if (name && name[0] != '/') { - filename = alloca(sizeof(ALSA_PLUGIN_DIR) + 1 + strlen(name) + 1); - if (filename) { - strcpy(filename, ALSA_PLUGIN_DIR); - strcat(filename, "/"); - strcat(filename, name); - handle = dlopen(filename, mode); - if (!handle) { - /* if the filename exists and cannot be opened */ - /* return immediately */ - if (access(filename, X_OK) == 0) - goto errpath; - } - } - } - if (!handle) { - handle = dlopen(name, mode); - if (!handle) - goto errpath; + if (snd_dlpath(path, sizeof(path), name) == 0) + filename = path; } + handle = dlopen(filename, mode); + if (!handle) + goto errpath; return handle; errpath: if (errbuf) - snprintf(errbuf, errbuflen, "%s: %s", filename, dlerror()); + snprintf(errbuf, errbuflen, "%s", dlerror()); #endif return NULL; } #ifndef DOXYGEN -void *INTERNAL(snd_dlopen_old)(const char *name, int mode) +EXPORT_SYMBOL void *INTERNAL(snd_dlopen_old)(const char *name, int mode) { return INTERNAL(snd_dlopen)(name, mode, NULL, 0); } @@ -305,7 +359,6 @@ free(c); __err: snd_dlclose(dlobj); - snd_dlobj_unlock(); return NULL; } c->dlobj = dlobj; @@ -385,7 +438,11 @@ free((void *)c->lib); /* shut up gcc warning */ free(c); } - snd_dlobj_unlock(); + snd_dlpath_lock(); + snd_plugin_dir_set = 0; + free(snd_plugin_dir); + snd_plugin_dir = NULL; + snd_dlpath_unlock(); } #endif diff -Nru alsa-lib-1.2.2/src/hwdep/Makefile.in alsa-lib-1.2.6.1/src/hwdep/Makefile.in --- alsa-lib-1.2.2/src/hwdep/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/hwdep/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -297,6 +297,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/hwdep/hwdep_hw.c alsa-lib-1.2.6.1/src/hwdep/hwdep_hw.c --- alsa-lib-1.2.2/src/hwdep/hwdep_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/hwdep/hwdep_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -151,7 +151,6 @@ { snd_config_iterator_t i, next; long card = -1, device = 0; - const char *str; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -161,15 +160,10 @@ if (_snd_conf_generic_id(id)) continue; if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) - return -EINVAL; - card = snd_card_get_index(str); - if (card < 0) - return card; - } + err = snd_config_get_card(n); + if (err < 0) + return err; + card = err; continue; } if (strcmp(id, "device") == 0) { diff -Nru alsa-lib-1.2.2/src/mixer/Makefile.in alsa-lib-1.2.6.1/src/mixer/Makefile.in --- alsa-lib-1.2.2/src/mixer/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/mixer/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -303,6 +303,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/mixer/simple.c alsa-lib-1.2.6.1/src/mixer/simple.c --- alsa-lib-1.2.2/src/mixer/simple.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/mixer/simple.c 2021-12-09 13:17:59.000000000 +0000 @@ -376,7 +376,8 @@ * \brief Return corresponding integer playback volume for given dB value for a mixer simple element * \param elem Mixer simple element handle * \param value value to be converted to dB range - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \param dBvalue pointer to returned dB value * \return 0 on success otherwise a negative error code */ @@ -454,7 +455,8 @@ * \param elem Mixer simple element handle * \param channel mixer simple element channel identifier * \param value control value in dB * 100 - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \return 0 on success otherwise a negative error code */ int snd_mixer_selem_set_playback_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir) @@ -491,7 +493,8 @@ * \brief Set value in dB of playback volume control for all channels of a mixer simple element * \param elem Mixer simple element handle * \param value control value in dB * 100 - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \return 0 on success otherwise a negative error code */ int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem, long value, int dir) @@ -706,7 +709,8 @@ * \param elem Mixer simple element handle * \param dBvalue dB value to be converted to integer range * \param value pointer to returned integer value - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \return 0 on success otherwise a negative error code */ int snd_mixer_selem_ask_capture_dB_vol(snd_mixer_elem_t *elem, long dBvalue, int dir, long *value) @@ -777,7 +781,8 @@ * \param elem Mixer simple element handle * \param channel mixer simple element channel identifier * \param value control value in dB * 100 - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \return 0 on success otherwise a negative error code */ int snd_mixer_selem_set_capture_dB(snd_mixer_elem_t *elem, snd_mixer_selem_channel_id_t channel, long value, int dir) @@ -814,7 +819,8 @@ * \brief Set value in dB of capture volume control for all channels of a mixer simple element * \param elem Mixer simple element handle * \param value control value in dB * 100 - * \param dir rounding mode - rounds up if dir > 0, otherwise rounds down + * \param dir rounding mode - rounds up if dir > 0, round to nearest if dir == 0, + * rounds down if dir < 0 * \return 0 on success otherwise a negative error code */ int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem, long value, int dir) diff -Nru alsa-lib-1.2.2/src/mixer/simple_abst.c alsa-lib-1.2.6.1/src/mixer/simple_abst.c --- alsa-lib-1.2.2/src/mixer/simple_abst.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/mixer/simple_abst.c 2021-12-09 13:17:59.000000000 +0000 @@ -40,7 +40,7 @@ #ifndef DOC_HIDDEN -#define SO_PATH ALSA_PLUGIN_DIR "/smixer" +#define SO_PATH "smixer" typedef struct _class_priv { char *device; diff -Nru alsa-lib-1.2.2/src/mixer/simple_none.c alsa-lib-1.2.6.1/src/mixer/simple_none.c --- alsa-lib-1.2.2/src/mixer/simple_none.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/mixer/simple_none.c 2021-12-09 13:17:59.000000000 +0000 @@ -907,13 +907,23 @@ }; #endif -/* Return base length or 0 on failure */ +/* Return base length */ static int base_len(const char *name, selem_ctl_type_t *type) { const struct suf *p; size_t nlen = strlen(name); - p = suffixes; - while (p->suffix) { + + /* exception: "Capture Volume" and "Capture Switch" */ + if (!strcmp(name, "Capture Volume")) { + *type = CTL_CAPTURE_VOLUME; + return strlen("Capture"); + } + if (!strcmp(name, "Capture Switch")) { + *type = CTL_CAPTURE_SWITCH; + return strlen("Capture"); + } + + for (p = suffixes; p->suffix; p++) { size_t slen = strlen(p->suffix); size_t l; if (nlen > slen) { @@ -924,7 +934,6 @@ return l; } } - p++; } /* Special case - handle "Input Source" as a capture route. @@ -944,7 +953,9 @@ return strlen(name); } } - return 0; + + *type = CTL_SINGLE; + return strlen(name); } @@ -1605,8 +1616,10 @@ static int simple_event_add(snd_mixer_class_t *class, snd_hctl_elem_t *helem) { const char *name = snd_hctl_elem_get_name(helem); + selem_ctl_type_t type; + char ename[128]; size_t len; - selem_ctl_type_t type = CTL_SINGLE; /* to shut up warning */ + if (snd_hctl_elem_get_interface(helem) != SND_CTL_ELEM_IFACE_MIXER) return 0; if (strcmp(name, "Capture Source") == 0) { @@ -1633,22 +1646,14 @@ } return 0; } + len = base_len(name, &type); - if (len == 0) { - return simple_add1(class, name, helem, CTL_SINGLE, 0); - } else { - char ename[128]; - if (len >= sizeof(ename)) - len = sizeof(ename) - 1; - memcpy(ename, name, len); - ename[len] = 0; - /* exception: Capture Volume and Capture Switch */ - if (type == CTL_GLOBAL_VOLUME && !strcmp(ename, "Capture")) - type = CTL_CAPTURE_VOLUME; - else if (type == CTL_GLOBAL_SWITCH && !strcmp(ename, "Capture")) - type = CTL_CAPTURE_SWITCH; - return simple_add1(class, ename, helem, type, 0); - } + if (len >= sizeof(ename)) + len = sizeof(ename) - 1; + memcpy(ename, name, len); + ename[len] = 0; + + return simple_add1(class, ename, helem, type, 0); } static int simple_event_remove(snd_hctl_elem_t *helem, diff -Nru alsa-lib-1.2.2/src/output.c alsa-lib-1.2.6.1/src/output.c --- alsa-lib-1.2.2/src/output.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/output.c 2021-12-09 13:17:59.000000000 +0000 @@ -252,6 +252,9 @@ size_t alloc; unsigned char *buf; + /* use 'size++' to allow to add the '\0' string terminator */ + /* without reallocation */ + size++; if (_free >= size) return _free; if (buffer->alloc == 0) @@ -350,6 +353,28 @@ } /** + * \brief Returns the address of the buffer of a #SND_OUTPUT_BUFFER output handle. + * \param output The output handle. + * \param buf The functions puts the current address of the buffer at the + * address specified by \p buf. + * \return The current size of valid data in the buffer. + * + * The internal buffer is empty after this call. The caller has the responsibility + * to clean the buffer using the free() call. + */ +size_t snd_output_buffer_steal(snd_output_t *output, char **buf) +{ + snd_output_buffer_t *buffer = output->private_data; + size_t size; + *buf = (char *)buffer->buf; + size = buffer->size; + buffer->buf = NULL; + buffer->alloc = 0; + buffer->size = 0; + return size; +} + +/** * \brief Creates a new output object with an auto-extending memory buffer. * \param outputp The function puts the pointer to the new output object * at the address specified by \p outputp. @@ -377,4 +402,3 @@ *outputp = output; return 0; } - diff -Nru alsa-lib-1.2.2/src/pcm/Makefile.in alsa-lib-1.2.6.1/src/pcm/Makefile.in --- alsa-lib-1.2.2/src/pcm/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -440,6 +440,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/pcm/pcm.c alsa-lib-1.2.6.1/src/pcm/pcm.c --- alsa-lib-1.2.2/src/pcm/pcm.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm.c 2021-12-09 13:17:59.000000000 +0000 @@ -284,7 +284,7 @@ \par -EBADFD This error means that the device is in a bad state. It means that -the handskahe between application and alsa-lib is corrupted. +the handshake between application and alsa-lib is corrupted. \par -ENOTTY, -ENODEV @@ -292,6 +292,19 @@ some hotplug devices like USB or PCMCIA, CardBus or ExpressCard can be removed on the fly). +\par -ENODATA + +This error can happen if the device data transfer is dependent on +an external condition and that condition is not met. For example, +PCM device for echo reference as described by SND_USE_CASE_MOD_ECHO_REF +UCM token, may return -ENODATA if the linked playback stream has not been +started. + +There is no defined recovery or event mechanism to signal the data / link +availability at the moment. The PCM must be completely restarted until +the mechanism is designed. The #snd_pcm_recover() function cannot be +used for this. + \section pcm_params Managing parameters The ALSA PCM device uses two groups of PCM related parameters. The hardware @@ -680,7 +693,8 @@ P_STATE(DRAINING)) /* check whether the PCM is in the unexpected state */ -static int bad_pcm_state(snd_pcm_t *pcm, unsigned int supported_states) +static int bad_pcm_state(snd_pcm_t *pcm, unsigned int supported_states, + unsigned int noop_states) { snd_pcm_state_t state; int err; @@ -688,6 +702,8 @@ if (pcm->own_state_check) return 0; /* don't care, the plugin checks by itself */ state = snd_pcm_state(pcm); + if (noop_states & (1U << state)) + return 1; /* OK, return immediately */ if (supported_states & (1U << state)) return 0; /* OK */ err = pcm_state_to_error(state); @@ -1043,6 +1059,10 @@ * This is a faster way to obtain only the PCM state without calling * \link ::snd_pcm_status() \endlink. * + * Note that this function always returns one of the + * #snd_pcm_state_t enum variants. + * It will never return a negative error code. + * * The function is thread-safe when built with the proper option. */ snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm) @@ -1206,7 +1226,7 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, ~P_STATE(DISCONNECTED)); + err = bad_pcm_state(pcm, ~P_STATE(DISCONNECTED), 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1261,7 +1281,7 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, P_STATE(PREPARED)); + err = bad_pcm_state(pcm, P_STATE(PREPARED), 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1293,7 +1313,7 @@ return -EIO; } err = bad_pcm_state(pcm, P_STATE_RUNNABLE | P_STATE(SETUP) | - P_STATE(SUSPENDED)); + P_STATE(SUSPENDED), 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1329,9 +1349,11 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE | P_STATE(SETUP), P_STATE(SETUP)); if (err < 0) return err; + if (err == 1) + return 0; /* lock handled in the callback */ if (pcm->fast_ops->drain) err = pcm->fast_ops->drain(pcm->fast_op_arg); @@ -1361,7 +1383,7 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1394,7 +1416,7 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1427,7 +1449,7 @@ } if (frames == 0) return 0; - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1460,7 +1482,7 @@ SNDMSG("PCM not set up"); return -EIO; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1497,7 +1519,7 @@ } if (frames == 0) return 0; - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -1543,7 +1565,7 @@ SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access)); return -EINVAL; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; return _snd_pcm_writei(pcm, buffer, size); @@ -1582,7 +1604,7 @@ SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access)); return -EINVAL; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; return _snd_pcm_writen(pcm, bufs, size); @@ -1621,7 +1643,7 @@ SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access)); return -EINVAL; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; return _snd_pcm_readi(pcm, buffer, size); @@ -1660,7 +1682,7 @@ SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access)); return -EINVAL; } - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; return _snd_pcm_readn(pcm, bufs, size); @@ -2573,13 +2595,13 @@ build_in++; } if (*build_in == NULL) { - buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32); + buf1 = malloc(strlen(str) + 32); if (buf1 == NULL) { err = -ENOMEM; goto _err; } lib = buf1; - sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str); + sprintf(buf1, "libasound_module_pcm_%s.so", str); } } #ifndef PIC @@ -2668,9 +2690,15 @@ int err; assert(pcmp && name); - err = snd_config_update_ref(&top); - if (err < 0) - return err; + if (_snd_is_ucm_device(name)) { + name = uc_mgr_alibcfg_by_device(&top, name); + if (name == NULL) + return -ENODEV; + } else { + err = snd_config_update_ref(&top); + if (err < 0) + return err; + } err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0); snd_config_unref(top); return err; @@ -2745,6 +2773,7 @@ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); #endif pthread_mutex_init(&pcm->lock, &attr); + pthread_mutexattr_destroy(&attr); /* use locking as default; * each plugin may suppress this in its open call */ @@ -6475,6 +6504,9 @@ * \param params Software configuration container * \param val returned minimum available frames to consider PCM ready * \return 0 otherwise a negative error code + * + * This is a threshold value when the PCM stream is considered as ready for + * another read/write operation or poll event. */ #ifndef DOXYGEN EXPORT_SYMBOL int INTERNAL(snd_pcm_sw_params_get_avail_min)(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val) @@ -6935,6 +6967,8 @@ /** * \brief Get maximum number of frames available from a PCM status container after last #snd_pcm_status call * \return Maximum number of frames ready to be read/written + * + * This value returns the peak for the available frames between #snd_pcm_status calls. */ snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj) { @@ -7189,7 +7223,7 @@ { int err; - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -7199,9 +7233,8 @@ } #ifndef DOC_HIDDEN -/* locked version */ -int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, - snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) +int __snd_pcm_mmap_begin_generic(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) { snd_pcm_uframes_t cont; snd_pcm_uframes_t f; @@ -7210,9 +7243,6 @@ assert(pcm && areas && offset && frames); - if (pcm->fast_ops->mmap_begin) - return pcm->fast_ops->mmap_begin(pcm->fast_op_arg, areas, offset, frames); - /* fallback for plugins that do not specify new callback */ xareas = snd_pcm_mmap_areas(pcm); if (xareas == NULL) @@ -7231,6 +7261,18 @@ *frames = f; return 0; } + +/* locked version */ +int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) +{ + assert(pcm && areas && offset && frames); + + if (pcm->fast_ops->mmap_begin) + return pcm->fast_ops->mmap_begin(pcm->fast_op_arg, areas, offset, frames); + + return __snd_pcm_mmap_begin_generic(pcm, areas, offset, frames); +} #endif /** @@ -7294,7 +7336,7 @@ snd_pcm_sframes_t result; int err; - err = bad_pcm_state(pcm, P_STATE_RUNNABLE); + err = bad_pcm_state(pcm, P_STATE_RUNNABLE, 0); if (err < 0) return err; snd_pcm_lock(pcm->fast_op_arg); @@ -7815,7 +7857,7 @@ #endif /* USE_VERSIONED_SYMBOLS */ #define __P_OLD_GET(pfx, name, val_type, ret_type) \ -ret_type pfx##name(const snd_pcm_hw_params_t *params) \ +EXPORT_SYMBOL ret_type pfx##name(const snd_pcm_hw_params_t *params) \ { \ val_type val; \ if (INTERNAL(name)(params, &val) < 0) \ @@ -7824,7 +7866,7 @@ } #define __P_OLD_GET1(pfx, name, val_type, ret_type) \ -ret_type pfx##name(const snd_pcm_hw_params_t *params, int *dir) \ +EXPORT_SYMBOL ret_type pfx##name(const snd_pcm_hw_params_t *params, int *dir) \ { \ val_type val; \ if (INTERNAL(name)(params, &val, dir) < 0) \ @@ -7866,7 +7908,7 @@ __OLD_GET1(snd_pcm_hw_params_get_tick_time_max, unsigned int, unsigned int); #define __P_OLD_NEAR(pfx, name, ret_type) \ -ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val) \ +EXPORT_SYMBOL ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val) \ { \ if (INTERNAL(name)(pcm, params, &val) < 0) \ return 0; \ @@ -7874,7 +7916,7 @@ } #define __P_OLD_NEAR1(pfx, name, ret_type) \ -ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val, int *dir) \ +EXPORT_SYMBOL ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val, int *dir) \ { \ if (INTERNAL(name)(pcm, params, &val, dir) < 0) \ return 0; \ @@ -7894,7 +7936,7 @@ __OLD_NEAR1(snd_pcm_hw_params_set_tick_time_near, unsigned int); #define __P_OLD_SET_FL(pfx, name, ret_type) \ -ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) \ +EXPORT_SYMBOL ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) \ { \ ret_type val; \ if (INTERNAL(name)(pcm, params, &val) < 0) \ @@ -7903,7 +7945,7 @@ } #define __P_OLD_SET_FL1(pfx, name, ret_type) \ -ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir) \ +EXPORT_SYMBOL ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir) \ { \ ret_type val; \ if (INTERNAL(name)(pcm, params, &val, dir) < 0) \ @@ -7939,7 +7981,7 @@ __OLD_SET_FL1(snd_pcm_hw_params_set_tick_time_last, unsigned int); #define __P_OLD_GET_SW(pfx, name, ret_type) \ -ret_type pfx##name(snd_pcm_sw_params_t *params) \ +EXPORT_SYMBOL ret_type pfx##name(snd_pcm_sw_params_t *params) \ { \ ret_type val; \ if (INTERNAL(name)(params, &val) < 0) \ diff -Nru alsa-lib-1.2.2/src/pcm/pcm_direct.c alsa-lib-1.2.6.1/src/pcm/pcm_direct.c --- alsa-lib-1.2.2/src/pcm/pcm_direct.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_direct.c 2021-12-09 13:17:59.000000000 +0000 @@ -82,7 +82,13 @@ return 0; } -#define SND_PCM_DIRECT_MAGIC (0xa15ad300 + sizeof(snd_pcm_direct_share_t)) +static unsigned int snd_pcm_direct_magic(snd_pcm_direct_t *dmix) +{ + if (!dmix->direct_memory_access) + return 0xa15ad300 + sizeof(snd_pcm_direct_share_t); + else + return 0xb15ad300 + sizeof(snd_pcm_direct_share_t); +} /* * global shared memory area @@ -132,10 +138,10 @@ buf.shm_perm.gid = dmix->ipc_gid; shmctl(dmix->shmid, IPC_SET, &buf); } - dmix->shmptr->magic = SND_PCM_DIRECT_MAGIC; + dmix->shmptr->magic = snd_pcm_direct_magic(dmix); return 1; } else { - if (dmix->shmptr->magic != SND_PCM_DIRECT_MAGIC) { + if (dmix->shmptr->magic != snd_pcm_direct_magic(dmix)) { snd_pcm_direct_shm_discard(dmix); return -EINVAL; } @@ -929,10 +935,14 @@ return err; if (dshare->var_periodsize) { /* more tolerant settings... */ - if (dshare->shmptr->hw.buffer_size.max / 2 > period_size.max) + if (dshare->shmptr->hw.buffer_size.max / 2 > period_size.max) { period_size.max = dshare->shmptr->hw.buffer_size.max / 2; - if (dshare->shmptr->hw.buffer_time.max / 2 > period_time.max) + period_size.openmax = dshare->shmptr->hw.buffer_size.openmax; + } + if (dshare->shmptr->hw.buffer_time.max / 2 > period_time.max) { period_time.max = dshare->shmptr->hw.buffer_time.max / 2; + period_time.openmax = dshare->shmptr->hw.buffer_time.openmax; + } } err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_PERIOD_SIZE, @@ -991,8 +1001,11 @@ return 0; } -int snd_pcm_direct_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params ATTRIBUTE_UNUSED) +int snd_pcm_direct_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { + if (params->tstamp_type != pcm->tstamp_type) + return -EINVAL; + /* values are cached in the pcm structure */ return 0; } @@ -1318,6 +1331,15 @@ return ret; } + if (dmix->tstamp_type != -1) { + ret = snd_pcm_sw_params_set_tstamp_type(spcm, &sw_params, + dmix->tstamp_type); + if (ret < 0) { + SNDERR("unable to set tstamp type"); + return ret; + } + } + if (dmix->type != SND_PCM_TYPE_DMIX && dmix->type != SND_PCM_TYPE_DSHARE) goto __skip_silencing; @@ -1605,43 +1627,37 @@ int snd_pcm_direct_check_interleave(snd_pcm_direct_t *dmix, snd_pcm_t *pcm) { unsigned int chn, channels; - int bits, interleaved = 1; + int bits; const snd_pcm_channel_area_t *dst_areas; const snd_pcm_channel_area_t *src_areas; bits = snd_pcm_format_physical_width(pcm->format); if ((bits % 8) != 0) - interleaved = 0; + goto __nointerleaved; channels = dmix->channels; + if (channels != dmix->spcm->channels) + goto __nointerleaved; dst_areas = snd_pcm_mmap_areas(dmix->spcm); src_areas = snd_pcm_mmap_areas(pcm); for (chn = 1; chn < channels; chn++) { - if (dst_areas[chn-1].addr != dst_areas[chn].addr) { - interleaved = 0; - break; - } - if (src_areas[chn-1].addr != src_areas[chn].addr) { - interleaved = 0; - break; - } + if (dst_areas[chn-1].addr != dst_areas[chn].addr) + goto __nointerleaved; + if (src_areas[chn-1].addr != src_areas[chn].addr) + goto __nointerleaved; } for (chn = 0; chn < channels; chn++) { - if (dmix->bindings && dmix->bindings[chn] != chn) { - interleaved = 0; - break; - } + if (dmix->bindings && dmix->bindings[chn] != chn) + goto __nointerleaved; if (dst_areas[chn].first != chn * bits || - dst_areas[chn].step != channels * bits) { - interleaved = 0; - break; - } + dst_areas[chn].step != channels * bits) + goto __nointerleaved; if (src_areas[chn].first != chn * bits || - src_areas[chn].step != channels * bits) { - interleaved = 0; - break; - } + src_areas[chn].step != channels * bits) + goto __nointerleaved; } - return dmix->interleaved = interleaved; + return dmix->interleaved = 1; +__nointerleaved: + return dmix->interleaved = 0; } /* @@ -1812,19 +1828,10 @@ continue; } if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - card = snd_card_get_index(str); - if (card < 0) { - SNDERR("Invalid value for %s", id); - return card; - } - } + err = snd_config_get_card(n); + if (err < 0) + return err; + card = err; continue; } if (strcmp(id, "device") == 0) { @@ -1844,8 +1851,6 @@ continue; } } - if (card < 0) - card = 0; if (device < 0) device = 0; if (subdevice < 0) @@ -1876,8 +1881,13 @@ rec->slowptr = 1; rec->max_periods = 0; rec->var_periodsize = 0; +#ifdef LOCKLESS_DMIX_DEFAULT rec->direct_memory_access = 1; +#else + rec->direct_memory_access = 0; +#endif rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_AUTO; + rec->tstamp_type = -1; /* read defaults */ if (snd_config_search(root, "defaults.pcm.dmix_max_periods", &n) >= 0) { @@ -1941,6 +1951,27 @@ continue; } + if (strcmp(id, "tstamp_type") == 0) { + const char *str; + err = snd_config_get_string(n, &str); + if (err < 0) { + SNDERR("Invalid type for %s", id); + return -EINVAL; + } + if (strcmp(str, "default") == 0) + rec->tstamp_type = -1; + else if (strcmp(str, "gettimeofday") == 0) + rec->tstamp_type = SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY; + else if (strcmp(str, "monotonic") == 0) + rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; + else if (strcmp(str, "monotonic_raw") == 0) + rec->tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW; + else { + SNDERR("The field tstamp_type is invalid : %s", str); + return -EINVAL; + } + continue; + } if (strcmp(id, "ipc_gid") == 0) { char *group; char *endp; @@ -2058,3 +2089,69 @@ ((dmix->slave_hw_ptr / dmix->slave_period_size) * dmix->slave_period_size); } + +int _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type, + const char *name, struct snd_pcm_direct_open_conf *opts, + struct slave_params *params, snd_pcm_stream_t stream, int mode) +{ + snd_pcm_direct_t *dmix; + int fail_sem_loop = 10; + int ret; + + dmix = calloc(1, sizeof(snd_pcm_direct_t)); + if (!dmix) + return -ENOMEM; + + ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings); + if (ret < 0) { + free(dmix); + return ret; + } + + dmix->ipc_key = opts->ipc_key; + dmix->ipc_perm = opts->ipc_perm; + dmix->ipc_gid = opts->ipc_gid; + dmix->tstamp_type = opts->tstamp_type; + dmix->semid = -1; + dmix->shmid = -1; + dmix->shmptr = (void *) -1; + dmix->type = type; + + ret = snd_pcm_new(pcmp, type, name, stream, mode); + if (ret < 0) + goto _err_nosem; + + while (1) { + ret = snd_pcm_direct_semaphore_create_or_connect(dmix); + if (ret < 0) { + SNDERR("unable to create IPC semaphore"); + goto _err_nosem_free; + } + ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); + if (ret < 0) { + snd_pcm_direct_semaphore_discard(dmix); + if (--fail_sem_loop <= 0) + goto _err_nosem_free; + continue; + } + break; + } + + ret = snd_pcm_direct_shm_create_or_connect(dmix); + if (ret < 0) { + SNDERR("unable to create IPC shm instance"); + snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); + goto _err_nosem_free; + } else { + *_dmix = dmix; + } + + return ret; +_err_nosem_free: + snd_pcm_free(*pcmp); + *pcmp = NULL; +_err_nosem: + free(dmix->bindings); + free(dmix); + return ret; +} diff -Nru alsa-lib-1.2.2/src/pcm/pcm_direct.h alsa-lib-1.2.6.1/src/pcm/pcm_direct.h --- alsa-lib-1.2.2/src/pcm/pcm_direct.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_direct.h 2021-12-09 13:17:59.000000000 +0000 @@ -173,6 +173,7 @@ unsigned int recoveries; /* mirror of executed recoveries on slave */ int direct_memory_access; /* use arch-optimized buffer RW */ snd_pcm_direct_hw_ptr_alignment_t hw_ptr_alignment; + int tstamp_type; /* cached from conf, can be -1(default) on top of real types */ union { struct { int shmid_sum; /* IPC global sum ring buffer memory identification */ @@ -185,6 +186,7 @@ mix_areas_32_t *remix_areas_32; mix_areas_24_t *remix_areas_24; mix_areas_u8_t *remix_areas_u8; + unsigned int use_sem; } dmix; struct { unsigned long long chn_mask; @@ -357,8 +359,13 @@ int var_periodsize; int direct_memory_access; snd_pcm_direct_hw_ptr_alignment_t hw_ptr_alignment; + int tstamp_type; snd_config_t *slave; snd_config_t *bindings; }; int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf, int stream, struct snd_pcm_direct_open_conf *rec); + +int _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type, + const char *name, struct snd_pcm_direct_open_conf *opts, + struct slave_params *params, snd_pcm_stream_t stream, int mode); diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix.c alsa-lib-1.2.6.1/src/pcm/pcm_dmix.c --- alsa-lib-1.2.2/src/pcm/pcm_dmix.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix.c 2021-12-09 13:17:59.000000000 +0000 @@ -212,10 +212,10 @@ do_mix_areas(size, ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step, ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step, - dmix->u.dmix.sum_buffer + channels * dst_ofs + chn, + dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn, dst_step, src_step, - channels * sizeof(signed int)); + dmix->shmptr->s.channels * sizeof(signed int)); } } @@ -280,10 +280,10 @@ do_remix_areas(size, ((unsigned char *)dst_areas[dchn].addr + dst_areas[dchn].first / 8) + dst_ofs * dst_step, ((unsigned char *)src_areas[chn].addr + src_areas[chn].first / 8) + src_ofs * src_step, - dmix->u.dmix.sum_buffer + channels * dst_ofs + chn, + dmix->u.dmix.sum_buffer + dmix->shmptr->s.channels * dst_ofs + dchn, dst_step, src_step, - channels * sizeof(signed int)); + dmix->shmptr->s.channels * sizeof(signed int)); } } @@ -292,13 +292,17 @@ * the area via semaphore */ #ifndef DOC_HIDDEN -#ifdef NO_CONCURRENT_ACCESS -#define dmix_down_sem(dmix) snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT) -#define dmix_up_sem(dmix) snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT) -#else -#define dmix_down_sem(dmix) -#define dmix_up_sem(dmix) -#endif +static void dmix_down_sem(snd_pcm_direct_t *dmix) +{ + if (dmix->u.dmix.use_sem) + snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); +} + +static void dmix_up_sem(snd_pcm_direct_t *dmix) +{ + if (dmix->u.dmix.use_sem) + snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); +} #endif /* @@ -315,18 +319,13 @@ /* check the available size in the local buffer * last_appl_ptr keeps the last updated position */ - size = dmix->appl_ptr - dmix->last_appl_ptr; + size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary); if (! size) return; - if (size >= pcm->boundary / 2) - size = pcm->boundary - size; /* the slave_app_ptr can be far behind the slave_hw_ptr */ /* reduce mixing and errors here - just skip not catched writes */ - if (dmix->slave_hw_ptr <= dmix->slave_appl_ptr) - slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr; - else - slave_size = dmix->slave_appl_ptr + (dmix->slave_boundary - dmix->slave_hw_ptr); + slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, dmix->slave_boundary); if (slave_size > dmix->slave_buffer_size) { transfer = dmix->slave_buffer_size - slave_size; if (transfer > size) @@ -335,11 +334,9 @@ dmix->last_appl_ptr %= pcm->boundary; dmix->slave_appl_ptr += transfer; dmix->slave_appl_ptr %= dmix->slave_boundary; - size = dmix->appl_ptr - dmix->last_appl_ptr; + size = pcm_frame_diff2(dmix->appl_ptr, dmix->last_appl_ptr, pcm->boundary); if (! size) return; - if (size >= pcm->boundary / 2) - size = pcm->boundary - size; } /* check the available size in the slave PCM buffer */ @@ -351,10 +348,7 @@ slave_hw_ptr += dmix->slave_buffer_size; if (slave_hw_ptr >= dmix->slave_boundary) slave_hw_ptr -= dmix->slave_boundary; - if (slave_hw_ptr < dmix->slave_appl_ptr) - slave_size = slave_hw_ptr + (dmix->slave_boundary - dmix->slave_appl_ptr); - else - slave_size = slave_hw_ptr - dmix->slave_appl_ptr; + slave_size = pcm_frame_diff(slave_hw_ptr, dmix->slave_appl_ptr, dmix->slave_boundary); if (slave_size < size) size = slave_size; if (! size) @@ -399,17 +393,13 @@ old_slave_hw_ptr = dmix->slave_hw_ptr; dmix->slave_hw_ptr = slave_hw_ptr; - diff = slave_hw_ptr - old_slave_hw_ptr; + diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dmix->slave_boundary); if (diff == 0) /* fast path */ return 0; if (dmix->state != SND_PCM_STATE_RUNNING && dmix->state != SND_PCM_STATE_DRAINING) /* not really started yet - don't update hw_ptr */ return 0; - if (diff < 0) { - slave_hw_ptr += dmix->slave_boundary; - diff = slave_hw_ptr - old_slave_hw_ptr; - } dmix->hw_ptr += diff; dmix->hw_ptr %= pcm->boundary; if (pcm->stop_threshold >= pcm->boundary) /* don't care */ @@ -494,14 +484,15 @@ case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_RUNNING: snd_pcm_dmix_sync_ptr0(pcm, status->hw_ptr); - status->delay += snd_pcm_mmap_playback_delay(pcm) - + status->avail - dmix->spcm->buffer_size; + status->delay = snd_pcm_mmap_playback_delay(pcm); break; default: break; } status->state = snd_pcm_dmix_state(pcm); + status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ status->trigger_tstamp = dmix->trigger_tstamp; status->avail = snd_pcm_mmap_playback_avail(pcm); status->avail_max = status->avail > dmix->avail_max ? status->avail : dmix->avail_max; @@ -524,7 +515,7 @@ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: case STATE_RUN_PENDING: - *delayp = snd_pcm_mmap_playback_hw_avail(pcm); + *delayp = snd_pcm_mmap_playback_delay(pcm); return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; @@ -722,10 +713,7 @@ * So they can be remixed. */ - if (dmix->last_appl_ptr < dmix->appl_ptr) - size = dmix->appl_ptr - dmix->last_appl_ptr; - else - size = dmix->appl_ptr + (pcm->boundary - dmix->last_appl_ptr); + size = pcm_frame_diff(dmix->last_appl_ptr, dmix->appl_ptr, pcm->boundary); if (frames < size) size = frames; snd_pcm_mmap_appl_backward(pcm, size); @@ -737,16 +725,10 @@ /* Always at this point last_appl_ptr == appl_ptr * So (appl_ptr - hw_ptr) indicates the frames which can be remixed */ - if (dmix->hw_ptr < dmix->appl_ptr) - size = dmix->appl_ptr - dmix->hw_ptr; - else - size = dmix->appl_ptr + (pcm->boundary - dmix->hw_ptr); + size = pcm_frame_diff(dmix->appl_ptr, dmix->hw_ptr, pcm->boundary); if (size > frames) size = frames; - if (dmix->slave_hw_ptr < dmix->slave_appl_ptr) - slave_size = dmix->slave_appl_ptr - dmix->slave_hw_ptr; - else - slave_size = dmix->slave_appl_ptr + (pcm->boundary - dmix->slave_hw_ptr); + slave_size = pcm_frame_diff(dmix->slave_appl_ptr, dmix->slave_hw_ptr, pcm->boundary); if (slave_size < size) size = slave_size; @@ -1013,10 +995,9 @@ snd_config_t *root, snd_config_t *sconf, snd_pcm_stream_t stream, int mode) { - snd_pcm_t *pcm = NULL, *spcm = NULL; - snd_pcm_direct_t *dmix = NULL; + snd_pcm_t *pcm, *spcm = NULL; + snd_pcm_direct_t *dmix; int ret, first_instance; - int fail_sem_loop = 10; assert(pcmp); @@ -1025,49 +1006,11 @@ return -EINVAL; } - dmix = calloc(1, sizeof(snd_pcm_direct_t)); - if (!dmix) { - ret = -ENOMEM; - goto _err_nosem; - } - - ret = snd_pcm_direct_parse_bindings(dmix, params, opts->bindings); - if (ret < 0) - goto _err_nosem; - - dmix->ipc_key = opts->ipc_key; - dmix->ipc_perm = opts->ipc_perm; - dmix->ipc_gid = opts->ipc_gid; - dmix->semid = -1; - dmix->shmid = -1; - - ret = snd_pcm_new(&pcm, dmix->type = SND_PCM_TYPE_DMIX, name, stream, mode); + ret = _snd_pcm_direct_new(&pcm, &dmix, SND_PCM_TYPE_DMIX, name, opts, params, stream, mode); if (ret < 0) - goto _err; + return ret; + first_instance = ret; - - while (1) { - ret = snd_pcm_direct_semaphore_create_or_connect(dmix); - if (ret < 0) { - SNDERR("unable to create IPC semaphore"); - goto _err_nosem; - } - ret = snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); - if (ret < 0) { - snd_pcm_direct_semaphore_discard(dmix); - if (--fail_sem_loop <= 0) - goto _err_nosem; - continue; - } - break; - } - - first_instance = ret = snd_pcm_direct_shm_create_or_connect(dmix); - if (ret < 0) { - SNDERR("unable to create IPC shm instance"); - goto _err; - } - pcm->ops = &snd_pcm_dmix_ops; pcm->fast_ops = &snd_pcm_dmix_fast_ops; pcm->private_data = dmix; @@ -1208,12 +1151,9 @@ } else snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); _err_nosem: - if (dmix) { - free(dmix->bindings); - free(dmix); - } - if (pcm) - snd_pcm_free(pcm); + free(dmix->bindings); + free(dmix); + snd_pcm_free(pcm); return ret; } @@ -1237,6 +1177,9 @@ # roundup # rounddown # auto (default) + tstamp_type STR # timestamp type + # STR can be one of the below strings : + # default, gettimeofday, monotonic, monotonic_raw slave STR # or slave { # Slave definition diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix_generic.c alsa-lib-1.2.6.1/src/pcm/pcm_dmix_generic.c --- alsa-lib-1.2.2/src/pcm/pcm_dmix_generic.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix_generic.c 2021-12-09 13:17:59.000000000 +0000 @@ -43,7 +43,6 @@ #ifndef ARCH_ADD #define ARCH_ADD(p,a) (*(p) += (a)) #define ARCH_CMPXCHG(p,a,b) (*(p)) /* fake */ -#define NO_CONCURRENT_ACCESS /* use semaphore to avoid race */ #define IS_CONCURRENT 0 /* no race check */ #endif @@ -530,6 +529,7 @@ dmix->u.dmix.mix_areas_u8 = generic_mix_areas_u8; dmix->u.dmix.remix_areas_24 = generic_remix_areas_24; dmix->u.dmix.remix_areas_u8 = generic_remix_areas_u8; + dmix->u.dmix.use_sem = 1; } #endif diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix_i386.c alsa-lib-1.2.6.1/src/pcm/pcm_dmix_i386.c --- alsa-lib-1.2.2/src/pcm/pcm_dmix_i386.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix_i386.c 2021-12-09 13:17:59.000000000 +0000 @@ -135,4 +135,5 @@ dmix->u.dmix.mix_areas_24 = smp > 1 ? mix_areas_24_smp: mix_areas_24; dmix->u.dmix.remix_areas_24 = smp > 1 ? remix_areas_24_smp: remix_areas_24; } + dmix->u.dmix.use_sem = 0; } diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix_i386.h alsa-lib-1.2.6.1/src/pcm/pcm_dmix_i386.h --- alsa-lib-1.2.2/src/pcm/pcm_dmix_i386.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix_i386.h 2021-12-09 13:17:59.000000000 +0000 @@ -26,6 +26,10 @@ * */ +#if defined(__GNUC__) && __GNUC__ < 5 && defined(__PIC__) +# define BOUNDED_EBX +#endif + /* * for plain i386 */ @@ -34,8 +38,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_EBX unsigned int old_ebx; - +#endif /* * ESI - src * EDI - dst @@ -46,15 +51,16 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovl %%ebx, %7\n" /* ebx is GOT pointer (-fPIC) */ +#ifdef BOUNDED_EBX + "\tmovl %%ebx, %[old_ebx]\n" /* ebx is GOT pointer (-fPIC) */ +#endif /* * initialization, load ESI, EDI, EBX registers */ - "\tmovl %1, %%edi\n" - "\tmovl %2, %%esi\n" - "\tmovl %3, %%ebx\n" - "\tcmpl $0, %0\n" + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" + "\tcmpl $0, %[size]\n" "\tjnz 2f\n" "\tjmp 7f\n" @@ -64,9 +70,9 @@ */ "\t.p2align 4,,15\n" "1:" - "\tadd %4, %%edi\n" - "\tadd %5, %%esi\n" - "\tadd %6, %%ebx\n" + "\tadd %[dst_step], %%edi\n" + "\tadd %[src_step], %%esi\n" + "\tadd %[sum_step], %%ebx\n" /* * sample = *src; @@ -108,7 +114,7 @@ /* * while (size-- > 0) */ - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjnz 1b\n" "\tjmp 7f\n" @@ -122,7 +128,7 @@ "\tmovw $0x7fff, (%%edi)\n" "\tcmpl %%ecx,(%%ebx)\n" "\tjnz 4b\n" - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjnz 1b\n" "\tjmp 7f\n" @@ -136,17 +142,24 @@ "\tmovw $-0x8000, (%%edi)\n" "\tcmpl %%ecx, (%%ebx)\n" "\tjnz 4b\n" - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjnz 1b\n" - - "7:" - "\tmovl %7, %%ebx\n" /* ebx is GOT pointer (-fPIC) */ - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_ebx) - : "esi", "edi", "edx", "ecx", "eax" + "7:" +#ifdef BOUNDED_EBX + "\tmovl %[old_ebx], %%ebx\n" /* ebx is GOT pointer (-fPIC) */ +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_EBX + , [old_ebx] "=m" (old_ebx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "esi", "edi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_EBX + , "ebx" +#endif ); } @@ -158,8 +171,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_EBX unsigned int old_ebx; - +#endif /* * ESI - src * EDI - dst @@ -170,23 +184,24 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovl %%ebx, %7\n" /* ebx is GOT pointer (-fPIC) */ +#ifdef BOUNDED_EBX + "\tmovl %%ebx, %[old_ebx]\n" /* ebx is GOT pointer (-fPIC) */ +#endif /* * initialization, load ESI, EDI, EBX registers */ - "\tmovl %1, %%edi\n" - "\tmovl %2, %%esi\n" - "\tmovl %3, %%ebx\n" - "\tcmpl $0, %0\n" + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" + "\tcmpl $0, %[size]\n" "\tjnz 2f\n" "\tjmp 5f\n" "\t.p2align 4,,15\n" "1:" - "\tadd %4, %%edi\n" - "\tadd %5, %%esi\n" - "\tadd %6, %%ebx\n" + "\tadd %[dst_step], %%edi\n" + "\tadd %[src_step], %%esi\n" + "\tadd %[sum_step], %%ebx\n" "2:" /* @@ -226,17 +241,30 @@ /* * while (size-- > 0) */ - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjnz 1b\n" "\temms\n" "5:" - "\tmovl %7, %%ebx\n" /* ebx is GOT pointer (-fPIC) */ - - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_ebx) - : "esi", "edi", "edx", "ecx", "eax" +#ifdef BOUNDED_EBX + "\tmovl %[old_ebx], %%ebx\n" /* ebx is GOT pointer (-fPIC) */ +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_EBX + , [old_ebx] "=m" (old_ebx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "esi", "edi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_EBX + , "ebx" +#endif +#ifdef HAVE_MMX + , "mm0" +#else + , "st", "st(1)", "st(2)", "st(3)", + "st(4)", "st(5)", "st(6)", "st(7)" +#endif ); } @@ -248,8 +276,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_EBX unsigned int old_ebx; - +#endif /* * ESI - src * EDI - dst @@ -260,15 +289,16 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovl %%ebx, %7\n" /* ebx is GOT pointer (-fPIC) */ +#ifdef BOUNDED_EBX + "\tmovl %%ebx, %[old_ebx]\n" /* ebx is GOT pointer (-fPIC) */ +#endif /* * initialization, load ESI, EDI, EBX registers */ - "\tmovl %1, %%edi\n" - "\tmovl %2, %%esi\n" - "\tmovl %3, %%ebx\n" - "\tcmpl $0, %0\n" + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" + "\tcmpl $0, %[size]\n" "\tjnz 1f\n" "\tjmp 6f\n" @@ -335,21 +365,28 @@ /* * while (size-- > 0) */ - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjz 6f\n" - "\tadd %4, %%edi\n" - "\tadd %5, %%esi\n" - "\tadd %6, %%ebx\n" + "\tadd %[dst_step], %%edi\n" + "\tadd %[src_step], %%esi\n" + "\tadd %[sum_step], %%ebx\n" "\tjmp 1b\n" - - "6:" - "\tmovl %7, %%ebx\n" /* ebx is GOT pointer (-fPIC) */ - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_ebx) - : "esi", "edi", "edx", "ecx", "eax" + "6:" +#ifdef BOUNDED_EBX + "\tmovl %[old_ebx], %%ebx\n" /* ebx is GOT pointer (-fPIC) */ +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_EBX + , [old_ebx] "=m" (old_ebx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "esi", "edi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_EBX + , "ebx" +#endif ); } @@ -361,8 +398,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_EBX unsigned int old_ebx; - +#endif /* * ESI - src * EDI - dst @@ -373,15 +411,16 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovl %%ebx, %7\n" /* ebx is GOT pointer (-fPIC) */ +#ifdef BOUNDED_EBX + "\tmovl %%ebx, %[old_ebx]\n" /* ebx is GOT pointer (-fPIC) */ +#endif /* * initialization, load ESI, EDI, EBX registers */ - "\tmovl %1, %%edi\n" - "\tmovl %2, %%esi\n" - "\tmovl %3, %%ebx\n" - "\tcmpl $0, %0\n" + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" + "\tcmpl $0, %[size]\n" "\tjnz 1f\n" "\tjmp 6f\n" @@ -441,21 +480,28 @@ /* * while (size-- > 0) */ - "\tdecl %0\n" + "\tdecl %[size]\n" "\tjz 6f\n" - "\tadd %4, %%edi\n" - "\tadd %5, %%esi\n" - "\tadd %6, %%ebx\n" + "\tadd %[dst_step], %%edi\n" + "\tadd %[src_step], %%esi\n" + "\tadd %[sum_step], %%ebx\n" "\tjmp 1b\n" - - "6:" - "\tmovl %7, %%ebx\n" /* ebx is GOT pointer (-fPIC) */ - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_ebx) - : "esi", "edi", "edx", "ecx", "eax" + "6:" +#ifdef BOUNDED_EBX + "\tmovl %[old_ebx], %%ebx\n" /* ebx is GOT pointer (-fPIC) */ +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_EBX + , [old_ebx] "=m" (old_ebx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "esi", "edi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_EBX + , "ebx" +#endif ); } @@ -467,8 +513,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_EBX unsigned int old_ebx; - +#endif /* * ESI - src * EDI - dst @@ -479,15 +526,16 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovl %%ebx, %7\n" /* ebx is GOT pointer (-fPIC) */ +#ifdef BOUNDED_EBX + "\tmovl %%ebx, %[old_ebx]\n" /* ebx is GOT pointer (-fPIC) */ +#endif /* * initialization, load ESI, EDI, EBX registers */ - "\tmovl %1, %%edi\n" - "\tmovl %2, %%esi\n" - "\tmovl %3, %%ebx\n" - "\tcmpl $0, %0\n" + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" + "\tcmpl $0, %[size]\n" "\tjz 6f\n" "\t.p2align 4,,15\n" @@ -541,19 +589,30 @@ /* * while (size-- > 0) */ - "\tadd %4, %%edi\n" - "\tadd %5, %%esi\n" - "\tadd %6, %%ebx\n" - "\tdecl %0\n" + "\tadd %[dst_step], %%edi\n" + "\tadd %[src_step], %%esi\n" + "\tadd %[sum_step], %%ebx\n" + "\tdecl %[size]\n" "\tjnz 1b\n" - - "6:" - "\tmovl %7, %%ebx\n" /* ebx is GOT pointer (-fPIC) */ - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_ebx) - : "esi", "edi", "edx", "ecx", "eax" + "6:" +#ifdef BOUNDED_EBX + "\tmovl %[old_ebx], %%ebx\n" /* ebx is GOT pointer (-fPIC) */ +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_EBX + , [old_ebx] "=m" (old_ebx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "esi", "edi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_EBX + , "ebx" +#endif ); } + +#ifdef BOUNDED_EBX +# undef BOUNDED_EBX +#endif diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix_x86_64.c alsa-lib-1.2.6.1/src/pcm/pcm_dmix_x86_64.c --- alsa-lib-1.2.2/src/pcm/pcm_dmix_x86_64.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix_x86_64.c 2021-12-09 13:17:59.000000000 +0000 @@ -102,4 +102,5 @@ dmix->u.dmix.remix_areas_32 = smp > 1 ? remix_areas_32_smp : remix_areas_32; dmix->u.dmix.mix_areas_24 = smp > 1 ? mix_areas_24_smp : mix_areas_24; dmix->u.dmix.remix_areas_24 = smp > 1 ? remix_areas_24_smp : remix_areas_24; + dmix->u.dmix.use_sem = 0; } diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dmix_x86_64.h alsa-lib-1.2.6.1/src/pcm/pcm_dmix_x86_64.h --- alsa-lib-1.2.2/src/pcm/pcm_dmix_x86_64.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dmix_x86_64.h 2021-12-09 13:17:59.000000000 +0000 @@ -27,6 +27,10 @@ * */ +#if defined(__GNUC__) && __GNUC__ < 5 && defined(__PIC__) +# define BOUNDED_RBX +#endif + /* * MMX optimized */ @@ -35,8 +39,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_RBX unsigned long long old_rbx; - +#endif /* * RSI - src * RDI - dst @@ -47,19 +52,26 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovq %%rbx, %7\n" +#ifdef BOUNDED_RBX + "\tmovq %%rbx, %[old_rbx]\n" +#endif /* * initialization, load RSI, RDI, RBX registers */ - "\tmovq %1, %%rdi\n" - "\tmovq %2, %%rsi\n" - "\tmovq %3, %%rbx\n" +#ifndef _ILP32 + "\tmovq %[dst], %%rdi\n" + "\tmovq %[src], %%rsi\n" + "\tmovq %[sum], %%rbx\n" +#else + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" +#endif /* * while (size-- > 0) { */ - "\tcmpl $0, %0\n" + "\tcmpl $0, %[size]\n" "jz 6f\n" "\t.p2align 4,,15\n" @@ -103,22 +115,41 @@ /* * while (size-- > 0) */ - "\tadd %4, %%rdi\n" - "\tadd %5, %%rsi\n" - "\tadd %6, %%rbx\n" - "\tdecl %0\n" +#ifndef _ILP32 + "\taddq %[dst_step], %%rdi\n" + "\taddq %[src_step], %%rsi\n" + "\taddq %[sum_step], %%rbx\n" +#else + "\taddl %[dst_step], %%edi\n" + "\taddl %[src_step], %%esi\n" + "\taddl %[sum_step], %%ebx\n" +#endif + "\tdecl %[size]\n" "\tjnz 1b\n" "6:" - - "\temms\n" - "\tmovq %7, %%rbx\n" - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_rbx) - : "rsi", "rdi", "edx", "ecx", "eax" + "\temms\n" +#ifdef BOUNDED_RBX + "\tmovq %[old_rbx], %%rbx\n" +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_RBX + , [old_rbx] "=m" (old_rbx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "rsi", "rdi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_RBX + , "rbx" +#endif +#ifdef HAVE_MMX + , "mm0" +#else + , "st", "st(1)", "st(2)", "st(3)", + "st(4)", "st(5)", "st(6)", "st(7)" +#endif ); } @@ -130,8 +161,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_RBX unsigned long long old_rbx; - +#endif /* * RSI - src * RDI - dst @@ -142,19 +174,26 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovq %%rbx, %7\n" +#ifdef BOUNDED_RBX + "\tmovq %%rbx, %[old_rbx]\n" +#endif /* - * initialization, load ESI, EDI, EBX registers + * initialization, load RSI, RDI, RBX registers */ - "\tmovq %1, %%rdi\n" - "\tmovq %2, %%rsi\n" - "\tmovq %3, %%rbx\n" +#ifndef _ILP32 + "\tmovq %[dst], %%rdi\n" + "\tmovq %[src], %%rsi\n" + "\tmovq %[sum], %%rbx\n" +#else + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" +#endif /* * while (size-- > 0) { */ - "\tcmpl $0, %0\n" + "\tcmpl $0, %[size]\n" "jz 6f\n" "\t.p2align 4,,15\n" @@ -220,20 +259,33 @@ /* * while (size-- > 0) */ - "\tadd %4, %%rdi\n" - "\tadd %5, %%rsi\n" - "\tadd %6, %%rbx\n" - "\tdecl %0\n" +#ifndef _ILP32 + "\taddq %[dst_step], %%rdi\n" + "\taddq %[src_step], %%rsi\n" + "\taddq %[sum_step], %%rbx\n" +#else + "\taddl %[dst_step], %%edi\n" + "\taddl %[src_step], %%esi\n" + "\taddl %[sum_step], %%ebx\n" +#endif + "\tdecl %[size]\n" "\tjnz 1b\n" - - "6:" - "\tmovq %7, %%rbx\n" - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_rbx) - : "rsi", "rdi", "edx", "ecx", "eax" + "6:" +#ifdef BOUNDED_RBX + "\tmovq %[old_rbx], %%rbx\n" +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_RBX + , [old_rbx] "=m" (old_rbx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "rsi", "rdi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_RBX + , "rbx" +#endif ); } @@ -245,8 +297,9 @@ volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) { +#ifdef BOUNDED_RBX unsigned long long old_rbx; - +#endif /* * RSI - src * RDI - dst @@ -257,19 +310,26 @@ */ __asm__ __volatile__ ( "\n" - - "\tmovq %%rbx, %7\n" +#ifdef BOUNDED_RBX + "\tmovq %%rbx, %[old_rbx]\n" +#endif /* - * initialization, load ESI, EDI, EBX registers + * initialization, load RSI, RDI, RBX registers */ - "\tmovq %1, %%rdi\n" - "\tmovq %2, %%rsi\n" - "\tmovq %3, %%rbx\n" +#ifndef _ILP32 + "\tmovq %[dst], %%rdi\n" + "\tmovq %[src], %%rsi\n" + "\tmovq %[sum], %%rbx\n" +#else + "\tmovl %[dst], %%edi\n" + "\tmovl %[src], %%esi\n" + "\tmovl %[sum], %%ebx\n" +#endif /* * while (size-- > 0) { */ - "\tcmpl $0, %0\n" + "\tcmpl $0, %[size]\n" "jz 6f\n" "\t.p2align 4,,15\n" @@ -316,26 +376,43 @@ "\tmovw %%ax, (%%rdi)\n" "\tshrl $16, %%eax\n" "\tmovb %%al, 2(%%rdi)\n" - + "\tcmpl %%ecx, (%%rbx)\n" "\tjnz 3b\n" /* * while (size-- > 0) */ - "\tadd %4, %%rdi\n" - "\tadd %5, %%rsi\n" - "\tadd %6, %%rbx\n" - "\tdecl %0\n" +#ifndef _ILP32 + "\taddq %[dst_step], %%rdi\n" + "\taddq %[src_step], %%rsi\n" + "\taddq %[sum_step], %%rbx\n" +#else + "\taddl %[dst_step], %%edi\n" + "\taddl %[src_step], %%esi\n" + "\taddl %[sum_step], %%ebx\n" +#endif + "\tdecl %[size]\n" "\tjnz 1b\n" - - "6:" - "\tmovq %7, %%rbx\n" - : /* no output regs */ - : "m" (size), "m" (dst), "m" (src), - "m" (sum), "m" (dst_step), "m" (src_step), - "m" (sum_step), "m" (old_rbx) - : "rsi", "rdi", "edx", "ecx", "eax" + "6:" +#ifdef BOUNDED_RBX + "\tmovq %[old_rbx], %%rbx\n" +#endif + : [size] "+&rm" (size) +#ifdef BOUNDED_RBX + , [old_rbx] "=m" (old_rbx) +#endif + : [dst] "m" (dst), [src] "m" (src), [sum] "m" (sum), + [dst_step] "im" (dst_step), [src_step] "im" (src_step), + [sum_step] "im" (sum_step) + : "rsi", "rdi", "edx", "ecx", "eax", "memory", "cc" +#ifndef BOUNDED_RBX + , "rbx" +#endif ); } + +#ifdef BOUNDED_RBX +# undef BOUNDED_RBX +#endif diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dshare.c alsa-lib-1.2.6.1/src/pcm/pcm_dshare.c --- alsa-lib-1.2.2/src/pcm/pcm_dshare.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dshare.c 2021-12-09 13:17:59.000000000 +0000 @@ -112,7 +112,7 @@ const snd_pcm_channel_area_t *src_areas, *dst_areas; /* calculate the size to transfer */ - size = dshare->appl_ptr - dshare->last_appl_ptr; + size = pcm_frame_diff(dshare->appl_ptr, dshare->last_appl_ptr, pcm->boundary); if (! size) return; slave_hw_ptr = dshare->slave_hw_ptr; @@ -123,10 +123,7 @@ slave_hw_ptr += dshare->slave_buffer_size; if (slave_hw_ptr >= dshare->slave_boundary) slave_hw_ptr -= dshare->slave_boundary; - if (slave_hw_ptr < dshare->slave_appl_ptr) - slave_size = slave_hw_ptr + (dshare->slave_boundary - dshare->slave_appl_ptr); - else - slave_size = slave_hw_ptr - dshare->slave_appl_ptr; + slave_size = pcm_frame_diff(slave_hw_ptr, dshare->slave_appl_ptr, dshare->slave_boundary); if (slave_size < size) size = slave_size; if (! size) @@ -169,17 +166,13 @@ old_slave_hw_ptr = dshare->slave_hw_ptr; dshare->slave_hw_ptr = slave_hw_ptr; - diff = slave_hw_ptr - old_slave_hw_ptr; + diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dshare->slave_boundary); if (diff == 0) /* fast path */ return 0; if (dshare->state != SND_PCM_STATE_RUNNING && dshare->state != SND_PCM_STATE_DRAINING) /* not really started yet - don't update hw_ptr */ return 0; - if (diff < 0) { - slave_hw_ptr += dshare->slave_boundary; - diff = slave_hw_ptr - old_slave_hw_ptr; - } dshare->hw_ptr += diff; dshare->hw_ptr %= pcm->boundary; // printf("sync ptr diff = %li\n", diff); @@ -244,13 +237,14 @@ case SNDRV_PCM_STATE_DRAINING: case SNDRV_PCM_STATE_RUNNING: snd_pcm_dshare_sync_ptr0(pcm, status->hw_ptr); - status->delay += snd_pcm_mmap_playback_delay(pcm) - + status->avail - dshare->spcm->buffer_size; + status->delay += snd_pcm_mmap_playback_delay(pcm); break; default: break; } status->state = snd_pcm_dshare_state(pcm); + status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ status->trigger_tstamp = dshare->trigger_tstamp; status->avail = snd_pcm_mmap_playback_avail(pcm); status->avail_max = status->avail > dshare->avail_max ? status->avail : dshare->avail_max; @@ -297,7 +291,7 @@ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: case STATE_RUN_PENDING: - *delayp = snd_pcm_mmap_playback_hw_avail(pcm); + *delayp = snd_pcm_mmap_playback_delay(pcm); return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; @@ -697,11 +691,10 @@ snd_config_t *root, snd_config_t *sconf, snd_pcm_stream_t stream, int mode) { - snd_pcm_t *pcm = NULL, *spcm = NULL; - snd_pcm_direct_t *dshare = NULL; + snd_pcm_t *pcm, *spcm = NULL; + snd_pcm_direct_t *dshare; int ret, first_instance; unsigned int chn; - int fail_sem_loop = 10; assert(pcmp); @@ -710,48 +703,10 @@ return -EINVAL; } - dshare = calloc(1, sizeof(snd_pcm_direct_t)); - if (!dshare) { - ret = -ENOMEM; - goto _err_nosem; - } - - ret = snd_pcm_direct_parse_bindings(dshare, params, opts->bindings); - if (ret < 0) - goto _err_nosem; - - dshare->ipc_key = opts->ipc_key; - dshare->ipc_perm = opts->ipc_perm; - dshare->ipc_gid = opts->ipc_gid; - dshare->semid = -1; - dshare->shmid = -1; - - ret = snd_pcm_new(&pcm, dshare->type = SND_PCM_TYPE_DSHARE, name, stream, mode); + ret = _snd_pcm_direct_new(&pcm, &dshare, SND_PCM_TYPE_DSHARE, name, opts, params, stream, mode); if (ret < 0) - goto _err_nosem; - - while (1) { - ret = snd_pcm_direct_semaphore_create_or_connect(dshare); - if (ret < 0) { - SNDERR("unable to create IPC semaphore"); - goto _err_nosem; - } - - ret = snd_pcm_direct_semaphore_down(dshare, DIRECT_IPC_SEM_CLIENT); - if (ret < 0) { - snd_pcm_direct_semaphore_discard(dshare); - if (--fail_sem_loop <= 0) - goto _err_nosem; - continue; - } - break; - } - - first_instance = ret = snd_pcm_direct_shm_create_or_connect(dshare); - if (ret < 0) { - SNDERR("unable to create IPC shm instance"); - goto _err; - } + return ret; + first_instance = ret; if (!dshare->bindings) { pcm->ops = &snd_pcm_dshare_dummy_ops; @@ -881,7 +836,7 @@ return 0; _err: - if (dshare->shmptr) + if (dshare->shmptr != (void *) -1) dshare->shmptr->u.dshare.chn_mask &= ~dshare->u.dshare.chn_mask; if (dshare->timer) snd_timer_close(dshare->timer); @@ -897,12 +852,9 @@ } else snd_pcm_direct_semaphore_up(dshare, DIRECT_IPC_SEM_CLIENT); _err_nosem: - if (dshare) { - free(dshare->bindings); - free(dshare); - } - if (pcm) - snd_pcm_free(pcm); + free(dshare->bindings); + free(dshare); + snd_pcm_free(pcm); return ret; } @@ -929,6 +881,9 @@ # roundup # rounddown # auto (default) + tstamp_type STR # timestamp type + # STR can be one of the below strings : + # default, gettimeofday, monotonic, monotonic_raw slave STR # or slave { # Slave definition diff -Nru alsa-lib-1.2.2/src/pcm/pcm_dsnoop.c alsa-lib-1.2.6.1/src/pcm/pcm_dsnoop.c --- alsa-lib-1.2.2/src/pcm/pcm_dsnoop.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_dsnoop.c 2021-12-09 13:17:59.000000000 +0000 @@ -152,20 +152,16 @@ old_slave_hw_ptr = dsnoop->slave_hw_ptr; snoop_timestamp(pcm); slave_hw_ptr = dsnoop->slave_hw_ptr; - diff = slave_hw_ptr - old_slave_hw_ptr; + diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dsnoop->slave_boundary); if (diff == 0) /* fast path */ return 0; - if (diff < 0) { - slave_hw_ptr += dsnoop->slave_boundary; - diff = slave_hw_ptr - old_slave_hw_ptr; - } snd_pcm_dsnoop_sync_area(pcm, old_slave_hw_ptr, diff); dsnoop->hw_ptr += diff; dsnoop->hw_ptr %= pcm->boundary; // printf("sync ptr diff = %li\n", diff); if (pcm->stop_threshold >= pcm->boundary) /* don't care */ return 0; - if ((avail = snd_pcm_mmap_capture_hw_avail(pcm)) >= pcm->stop_threshold) { + if ((avail = snd_pcm_mmap_capture_avail(pcm)) >= pcm->stop_threshold) { gettimestamp(&dsnoop->trigger_tstamp, pcm->tstamp_type); dsnoop->state = SND_PCM_STATE_XRUN; dsnoop->avail_max = avail; @@ -197,6 +193,8 @@ snd_pcm_status(dsnoop->spcm, status); state = snd_pcm_state(dsnoop->spcm); status->state = state == SND_PCM_STATE_RUNNING ? dsnoop->state : state; + status->hw_ptr = *pcm->hw.ptr; /* boundary may be different */ + status->appl_ptr = *pcm->appl.ptr; /* slave PCM doesn't set appl_ptr */ status->trigger_tstamp = dsnoop->trigger_tstamp; status->avail = snd_pcm_mmap_capture_avail(pcm); status->avail_max = status->avail > dsnoop->avail_max ? status->avail : dsnoop->avail_max; @@ -241,7 +239,7 @@ /* Fall through */ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: - *delayp = snd_pcm_mmap_capture_hw_avail(pcm); + *delayp = snd_pcm_mmap_capture_avail(pcm); return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; @@ -352,7 +350,7 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_rewindable(snd_pcm_t *pcm) { - return snd_pcm_mmap_capture_hw_avail(pcm); + return snd_pcm_mmap_capture_hw_rewindable(pcm); } static snd_pcm_sframes_t snd_pcm_dsnoop_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) @@ -567,9 +565,9 @@ snd_config_t *root, snd_config_t *sconf, snd_pcm_stream_t stream, int mode) { - snd_pcm_t *pcm = NULL, *spcm = NULL; - snd_pcm_direct_t *dsnoop = NULL; - int ret, first_instance, fail_sem_loop = 10; + snd_pcm_t *pcm, *spcm = NULL; + snd_pcm_direct_t *dsnoop; + int ret, first_instance; assert(pcmp); @@ -578,49 +576,11 @@ return -EINVAL; } - dsnoop = calloc(1, sizeof(snd_pcm_direct_t)); - if (!dsnoop) { - ret = -ENOMEM; - goto _err_nosem; - } - - ret = snd_pcm_direct_parse_bindings(dsnoop, params, opts->bindings); - if (ret < 0) - goto _err_nosem; - - dsnoop->ipc_key = opts->ipc_key; - dsnoop->ipc_perm = opts->ipc_perm; - dsnoop->ipc_gid = opts->ipc_gid; - dsnoop->semid = -1; - dsnoop->shmid = -1; - - ret = snd_pcm_new(&pcm, dsnoop->type = SND_PCM_TYPE_DSNOOP, name, stream, mode); + ret = _snd_pcm_direct_new(&pcm, &dsnoop, SND_PCM_TYPE_DSNOOP, name, opts, params, stream, mode); if (ret < 0) - goto _err_nosem; + return ret; + first_instance = ret; - while (1) { - ret = snd_pcm_direct_semaphore_create_or_connect(dsnoop); - if (ret < 0) { - SNDERR("unable to create IPC semaphore"); - goto _err_nosem; - } - - ret = snd_pcm_direct_semaphore_down(dsnoop, DIRECT_IPC_SEM_CLIENT); - if (ret < 0) { - snd_pcm_direct_semaphore_discard(dsnoop); - if (--fail_sem_loop <= 0) - goto _err_nosem; - continue; - } - break; - } - - first_instance = ret = snd_pcm_direct_shm_create_or_connect(dsnoop); - if (ret < 0) { - SNDERR("unable to create IPC shm instance"); - goto _err; - } - pcm->ops = &snd_pcm_dsnoop_ops; pcm->fast_ops = &snd_pcm_dsnoop_fast_ops; pcm->private_data = dsnoop; @@ -749,12 +709,9 @@ snd_pcm_direct_semaphore_up(dsnoop, DIRECT_IPC_SEM_CLIENT); _err_nosem: - if (dsnoop) { - free(dsnoop->bindings); - free(dsnoop); - } - if (pcm) - snd_pcm_free(pcm); + free(dsnoop->bindings); + free(dsnoop); + snd_pcm_free(pcm); return ret; } @@ -780,6 +737,9 @@ # roundup # rounddown # auto (default) + tstamp_type STR # timestamp type + # STR can be one of the below strings : + # default, gettimeofday, monotonic, monotonic_raw slave STR # or slave { # Slave definition diff -Nru alsa-lib-1.2.2/src/pcm/pcm_empty.c alsa-lib-1.2.6.1/src/pcm/pcm_empty.c --- alsa-lib-1.2.2/src/pcm/pcm_empty.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_empty.c 2021-12-09 13:17:59.000000000 +0000 @@ -1,12 +1,12 @@ /** * \file pcm/pcm_empty.c * \ingroup PCM_Plugins - * \brief PCM Null Plugin Interface + * \brief PCM Empty Plugin Interface * \author Jaroslav Kysela * \date 2006 */ /* - * PCM - Null plugin + * PCM - Empty plugin * Copyright (c) 2006 by Jaroslav Kysela * * @@ -36,21 +36,26 @@ /*! \page pcm_plugins -\section pcm_plugins_null Plugin: Null +\section pcm_plugins_empty Plugin: Empty -This plugin discards contents of a PCM stream or creates a stream with zero -samples. - -Note: This implementation uses devices /dev/null (playback, must be writable) -and /dev/full (capture, must be readable). +This plugin just redirects the PCM stream to another plugin. \code pcm.name { - type null # Null PCM + type empty # Null PCM + slave STR # Slave name + # or + slave { # Slave definition + pcm STR # Slave PCM name + # or + pcm { } # Slave PCM definition + [format STR] # Slave format + [channels INT] # Slave channels + } } \endcode -\subsection pcm_plugins_null_funcref Function reference +\subsection pcm_plugins_empty_funcref Function reference
  • _snd_pcm_empty_open() diff -Nru alsa-lib-1.2.2/src/pcm/pcm_extplug.c alsa-lib-1.2.6.1/src/pcm/pcm_extplug.c --- alsa-lib-1.2.2/src/pcm/pcm_extplug.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_extplug.c 2021-12-09 13:17:59.000000000 +0000 @@ -732,6 +732,7 @@ pcm->private_data = ext; pcm->poll_fd = spcm->poll_fd; pcm->poll_events = spcm->poll_events; + pcm->tstamp_type = spcm->tstamp_type; snd_pcm_set_hw_ptr(pcm, &ext->plug.hw_ptr, -1, 0); snd_pcm_set_appl_ptr(pcm, &ext->plug.appl_ptr, -1, 0); diff -Nru alsa-lib-1.2.2/src/pcm/pcm_file.c alsa-lib-1.2.6.1/src/pcm/pcm_file.c --- alsa-lib-1.2.2/src/pcm/pcm_file.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_file.c 2021-12-09 13:17:59.000000000 +0000 @@ -100,6 +100,21 @@ #define TO_LE16(x) bswap_16(x) #endif +static ssize_t safe_write(int fd, const void *buf, size_t len) +{ + while (1) { + ssize_t r = write(fd, buf, len); + if (r < 0) { + if (errno == EINTR) + continue; + if (errno == EPIPE) + return -EIO; + return -errno; + } + return r; + } +} + static int snd_pcm_file_append_value(char **string_p, char **index_ch_p, int *len_p, const char *value) { @@ -339,15 +354,15 @@ setup_wav_header(pcm, &file->wav_header); - res = write(file->fd, header, sizeof(header)); + res = safe_write(file->fd, header, sizeof(header)); if (res != sizeof(header)) goto write_error; - res = write(file->fd, &file->wav_header, sizeof(file->wav_header)); + res = safe_write(file->fd, &file->wav_header, sizeof(file->wav_header)); if (res != sizeof(file->wav_header)) goto write_error; - res = write(file->fd, header2, sizeof(header2)); + res = safe_write(file->fd, header2, sizeof(header2)); if (res != sizeof(header2)) goto write_error; @@ -381,7 +396,7 @@ len = (file->filelen + 0x24) > 0x7fffffff ? 0x7fffffff : (int)(file->filelen + 0x24); len = TO_LE32(len); - ret = write(file->fd, &len, 4); + ret = safe_write(file->fd, &len, 4); if (ret < 0) return; } @@ -390,7 +405,7 @@ len = file->filelen > 0x7fffffff ? 0x7fffffff : (int)file->filelen; len = TO_LE32(len); - ret = write(file->fd, &len, 4); + ret = safe_write(file->fd, &len, 4); if (ret < 0) return; } @@ -421,9 +436,8 @@ size_t cont = file->wbuf_size_bytes - file->file_ptr_bytes; if (n > cont) n = cont; - err = write(file->fd, file->wbuf + file->file_ptr_bytes, n); + err = safe_write(file->fd, file->wbuf + file->file_ptr_bytes, n); if (err < 0) { - err = -errno; file->wbuf_used_bytes = 0; file->file_ptr_bytes = 0; SYSERR("%s write failed, file data may be corrupt", file->fname); diff -Nru alsa-lib-1.2.2/src/pcm/pcm_hw.c alsa-lib-1.2.6.1/src/pcm/pcm_hw.c --- alsa-lib-1.2.2/src/pcm/pcm_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -600,9 +600,10 @@ static snd_pcm_state_t snd_pcm_hw_state(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; - int err = query_status_data(hw); - if (err < 0) - return err; + /* the -ENODEV may come from the snd_disconnect_ioctl() or + snd_power_wait() in kernel */ + if (query_status_data(hw) == -ENODEV) + return SND_PCM_STATE_DISCONNECTED; return (snd_pcm_state_t) hw->mmap_status->state; } @@ -1816,21 +1817,10 @@ if (snd_pcm_conf_generic_id(id)) continue; if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) { - SNDERR("Invalid type for %s", id); - err = -EINVAL; - goto fail; - } - card = snd_card_get_index(str); - if (card < 0) { - SNDERR("Invalid value for %s", id); - err = card; - goto fail; - } - } + err = snd_config_get_card(n); + if (err < 0) + goto fail; + card = err; continue; } if (strcmp(id, "device") == 0) { diff -Nru alsa-lib-1.2.2/src/pcm/pcm_iec958.c alsa-lib-1.2.6.1/src/pcm/pcm_iec958.c --- alsa-lib-1.2.2/src/pcm/pcm_iec958.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_iec958.c 2021-12-09 13:17:59.000000000 +0000 @@ -63,6 +63,7 @@ unsigned int byteswap; unsigned char preamble[3]; /* B/M/W or Z/X/Y */ snd_pcm_fast_ops_t fops; + int hdmi_mode; }; enum { PREAMBLE_Z, PREAMBLE_X, PREAMBLE_Y }; @@ -193,6 +194,10 @@ unsigned int channel; int32_t sample = 0; int counter = iec->counter; + int single_stream = iec->hdmi_mode && + (iec->status[0] & IEC958_AES0_NONAUDIO) && + (channels == 8); + int counter_step = single_stream ? ((channels + 1) >> 1) : 1; for (channel = 0; channel < channels; ++channel) { const char *src; uint32_t *dst; @@ -205,7 +210,12 @@ src_step = snd_pcm_channel_area_step(src_area); dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(uint32_t); frames1 = frames; - iec->counter = counter; + + if (single_stream) + iec->counter = (counter + (channel >> 1)) % 192; + else + iec->counter = counter; + while (frames1-- > 0) { goto *get; #define GET32_END after @@ -217,9 +227,11 @@ *dst = sample; src += src_step; dst += dst_step; - iec->counter++; + iec->counter += counter_step; iec->counter %= 192; } + if (single_stream) /* set counter to ch0 value for next iteration */ + iec->counter = (counter + frames * counter_step) % 192; } } #endif /* DOC_HIDDEN */ @@ -353,9 +365,80 @@ iec->byteswap = format != SND_PCM_FORMAT_IEC958_SUBFRAME; } } - /* FIXME: needs to adjust status_bits according to the format - * and sample rate - */ + + if ((iec->status[0] & IEC958_AES0_PROFESSIONAL) == 0) { + if ((iec->status[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) { + unsigned int rate = 0; + unsigned char fs; + + err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &rate, 0); + if (err < 0) + rate = 0; + + switch (rate) { + case 22050: + fs = IEC958_AES3_CON_FS_22050; + break; + case 24000: + fs = IEC958_AES3_CON_FS_24000; + break; + case 32000: + fs = IEC958_AES3_CON_FS_32000; + break; + case 44100: + fs = IEC958_AES3_CON_FS_44100; + break; + case 48000: + fs = IEC958_AES3_CON_FS_48000; + break; + case 88200: + fs = IEC958_AES3_CON_FS_88200; + break; + case 96000: + fs = IEC958_AES3_CON_FS_96000; + break; + case 176400: + fs = IEC958_AES3_CON_FS_176400; + break; + case 192000: + fs = IEC958_AES3_CON_FS_192000; + break; + case 768000: + fs = IEC958_AES3_CON_FS_768000; + break; + default: + fs = IEC958_AES3_CON_FS_NOTID; + break; + } + + iec->status[3] &= ~IEC958_AES3_CON_FS; + iec->status[3] |= fs; + } + + if ((iec->status[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) { + unsigned char ws; + switch (snd_pcm_format_width(format)) { + case 16: + ws = IEC958_AES4_CON_WORDLEN_20_16; + break; + case 18: + ws = IEC958_AES4_CON_WORDLEN_22_18; + break; + case 20: + ws = IEC958_AES4_CON_WORDLEN_20_16 | IEC958_AES4_CON_MAX_WORDLEN_24; + break; + case 24: + case 32: /* Assume 24-bit width for 32-bit samples. */ + ws = IEC958_AES4_CON_WORDLEN_24_20 | IEC958_AES4_CON_MAX_WORDLEN_24; + break; + default: + ws = IEC958_AES4_CON_WORDLEN_NOTID; + break; + } + iec->status[4] &= ~(IEC958_AES4_CON_MAX_WORDLEN_24 | IEC958_AES4_CON_WORDLEN); + iec->status[4] |= ws; + } + } return 0; } @@ -473,6 +556,7 @@ * \param close_slave When set, the slave PCM handle is closed with copy PCM * \param status_bits The IEC958 status bits * \param preamble_vals The preamble byte values + * \param hdmi_mode When set, enable HDMI compliant formatting * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense * of compatibility reasons. The prototype might be freely @@ -481,7 +565,8 @@ int snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave, const unsigned char *status_bits, - const unsigned char *preamble_vals) + const unsigned char *preamble_vals, + int hdmi_mode) { snd_pcm_t *pcm; snd_pcm_iec958_t *iec; @@ -490,7 +575,8 @@ IEC958_AES0_CON_EMPHASIS_NONE, IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER, 0, - IEC958_AES3_CON_FS_48000 + IEC958_AES3_CON_FS_NOTID, /* will be set in hwparams */ + IEC958_AES4_CON_WORDLEN_NOTID /* will be set in hwparams */ }; assert(pcmp && slave); @@ -519,6 +605,8 @@ memcpy(iec->preamble, preamble_vals, 3); + iec->hdmi_mode = hdmi_mode; + err = snd_pcm_new(&pcm, SND_PCM_TYPE_IEC958, name, slave->stream, slave->mode); if (err < 0) { free(iec); @@ -566,9 +654,14 @@ [preamble.z or preamble.b val] [preamble.x or preamble.m val] [preamble.y or preamble.w val] + [hdmi_mode true] } \endcode +When hdmi_mode is true, 8-channel compressed data is +formatted as 4 contiguous frames of a single IEC958 stream as required +by the HDMI HBR specification. + \subsection pcm_plugins_iec958_funcref Function reference
      @@ -605,6 +698,7 @@ unsigned char preamble_vals[3] = { 0x08, 0x02, 0x04 /* Z, X, Y */ }; + int hdmi_mode = 0; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -633,6 +727,13 @@ preamble = n; continue; } + if (strcmp(id, "hdmi_mode") == 0) { + err = snd_config_get_bool(n); + if (err < 0) + continue; + hdmi_mode = err; + continue; + } SNDERR("Unknown field %s", id); return -EINVAL; } @@ -707,7 +808,7 @@ return err; err = snd_pcm_iec958_open(pcmp, name, sformat, spcm, 1, status ? status_bits : NULL, - preamble_vals); + preamble_vals, hdmi_mode); if (err < 0) snd_pcm_close(spcm); return err; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_ioplug.c alsa-lib-1.2.6.1/src/pcm/pcm_ioplug.c --- alsa-lib-1.2.2/src/pcm/pcm_ioplug.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_ioplug.c 2021-12-09 13:17:59.000000000 +0000 @@ -107,16 +107,37 @@ return snd_pcm_channel_info_shm(pcm, info, -1); } +static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) +{ + ioplug_priv_t *io = pcm->private_data; + + if (io->data->version >= 0x010001 && + io->data->callback->delay) + return io->data->callback->delay(io->data, delayp); + else { + snd_pcm_ioplug_hw_ptr_update(pcm); + *delayp = snd_pcm_mmap_delay(pcm); + } + return 0; +} + static int snd_pcm_ioplug_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { ioplug_priv_t *io = pcm->private_data; + snd_pcm_sframes_t sd; memset(status, 0, sizeof(*status)); snd_pcm_ioplug_hw_ptr_update(pcm); status->state = io->data->state; status->trigger_tstamp = io->trigger_tstamp; + gettimestamp(&status->tstamp, pcm->tstamp_type); status->avail = snd_pcm_mmap_avail(pcm); status->avail_max = io->avail_max; + status->appl_ptr = *pcm->appl.ptr; + status->hw_ptr = *pcm->hw.ptr; + if (snd_pcm_ioplug_delay(pcm, &sd) < 0) + sd = snd_pcm_mmap_delay(pcm); + status->delay = sd; return 0; } @@ -132,20 +153,6 @@ return 0; } -static int snd_pcm_ioplug_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) -{ - ioplug_priv_t *io = pcm->private_data; - - if (io->data->version >= 0x010001 && - io->data->callback->delay) - return io->data->callback->delay(io->data, delayp); - else { - snd_pcm_ioplug_hw_ptr_update(pcm); - *delayp = snd_pcm_mmap_hw_avail(pcm); - } - return 0; -} - static int snd_pcm_ioplug_reset(snd_pcm_t *pcm) { ioplug_priv_t *io = pcm->private_data; @@ -690,6 +697,38 @@ } } +static int snd_pcm_ioplug_mmap_begin_capture(snd_pcm_t *pcm, + const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, + snd_pcm_uframes_t *frames) +{ + ioplug_priv_t *io = pcm->private_data; + int err; + + err = __snd_pcm_mmap_begin_generic(pcm, areas, offset, frames); + if (err < 0) + return err; + + if (io->data->callback->transfer && + pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && + pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { + snd_pcm_sframes_t result; + result = io->data->callback->transfer(io->data, *areas, *offset, *frames); + if (result < 0) + return result; + } + + return err; +} + +static int snd_pcm_ioplug_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames) +{ + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + return __snd_pcm_mmap_begin_generic(pcm, areas, offset, frames); + return snd_pcm_ioplug_mmap_begin_capture(pcm, areas, offset, frames); +} + static snd_pcm_sframes_t snd_pcm_ioplug_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t size) @@ -700,7 +739,7 @@ const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t ofs, frames = size; - __snd_pcm_mmap_begin(pcm, &areas, &ofs, &frames); + __snd_pcm_mmap_begin_generic(pcm, &areas, &ofs, &frames); if (ofs != offset) return -EIO; return ioplug_priv_transfer_areas(pcm, areas, offset, frames); @@ -720,31 +759,6 @@ return -EPIPE; avail = snd_pcm_mmap_avail(pcm); - if (pcm->stream == SND_PCM_STREAM_CAPTURE && - pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && - pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { - if (io->data->callback->transfer) { - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t offset, size = UINT_MAX; - snd_pcm_sframes_t result; - - __snd_pcm_mmap_begin(pcm, &areas, &offset, &size); - result = io->data->callback->transfer(io->data, areas, offset, size); - if (result < 0) - return result; - - /* If the available data doesn't fit in the - contiguous area at the end of the mmap we - must transfer the remaining data to the - beginning of the mmap. */ - if (size < avail) { - result = io->data->callback->transfer(io->data, areas, - 0, avail - size); - if (result < 0) - return result; - } - } - } if (avail > io->avail_max) io->avail_max = avail; return (snd_pcm_sframes_t)avail; @@ -940,6 +954,7 @@ .poll_descriptors_count = snd_pcm_ioplug_poll_descriptors_count, .poll_descriptors = snd_pcm_ioplug_poll_descriptors, .poll_revents = snd_pcm_ioplug_poll_revents, + .mmap_begin = snd_pcm_ioplug_mmap_begin, }; #endif /* !DOC_HIDDEN */ diff -Nru alsa-lib-1.2.2/src/pcm/pcm_local.h alsa-lib-1.2.6.1/src/pcm/pcm_local.h --- alsa-lib-1.2.2/src/pcm/pcm_local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_local.h 2021-12-09 13:17:59.000000000 +0000 @@ -420,6 +420,8 @@ #define _snd_pcm_async_descriptor _snd_pcm_poll_descriptor /* FIXME */ /* locked versions */ +int __snd_pcm_mmap_begin_generic(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, + snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames); snd_pcm_sframes_t __snd_pcm_mmap_commit(snd_pcm_t *pcm, @@ -444,7 +446,7 @@ static inline snd_pcm_state_t __snd_pcm_state(snd_pcm_t *pcm) { if (!pcm->fast_ops->state) - return -ENOSYS; + return SND_PCM_STATE_OPEN; return pcm->fast_ops->state(pcm->fast_op_arg); } @@ -480,6 +482,13 @@ return err; } +/** + * \retval number of frames available to the application for playback + * + * This is how far ahead the hardware position in the ring buffer is, + * compared to the application position. ie. for playback it's the + * number of frames in the empty part of the ring buffer. + */ static inline snd_pcm_uframes_t __snd_pcm_playback_avail(snd_pcm_t *pcm, const snd_pcm_uframes_t hw_ptr, const snd_pcm_uframes_t appl_ptr) @@ -498,6 +507,13 @@ return __snd_pcm_playback_avail(pcm, *pcm->hw.ptr, *pcm->appl.ptr); } +/* + * \retval number of frames available to the application for capture + * + * This is how far ahead the hardware position in the ring buffer is + * compared to the application position. ie. for capture, it's the + * number of frames in the filled part of the ring buffer. + */ static inline snd_pcm_uframes_t __snd_pcm_capture_avail(snd_pcm_t *pcm, const snd_pcm_uframes_t hw_ptr, const snd_pcm_uframes_t appl_ptr) @@ -529,11 +545,21 @@ return __snd_pcm_avail(pcm, *pcm->hw.ptr, *pcm->appl.ptr); } +/* + * \retval number of frames available to the hardware for playback + * + * ie. the filled part of the ring buffer + */ static inline snd_pcm_sframes_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm) { return pcm->buffer_size - snd_pcm_mmap_playback_avail(pcm); } +/* + * \retval number of frames available to the hardware for capture + * + * ie. the empty part of the ring buffer. + */ static inline snd_pcm_sframes_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm) { return pcm->buffer_size - snd_pcm_mmap_capture_avail(pcm); @@ -582,14 +608,20 @@ return *pcm->hw.ptr % pcm->buffer_size; } +/* + * \retval number of frames pending from application to hardware + */ static inline snd_pcm_uframes_t snd_pcm_mmap_playback_delay(snd_pcm_t *pcm) { return snd_pcm_mmap_playback_hw_avail(pcm); } +/* + * \retval number of frames pending from hardware to application + */ static inline snd_pcm_uframes_t snd_pcm_mmap_capture_delay(snd_pcm_t *pcm) { - return snd_pcm_mmap_capture_hw_avail(pcm); + return snd_pcm_mmap_capture_avail(pcm); } static inline snd_pcm_sframes_t snd_pcm_mmap_delay(snd_pcm_t *pcm) @@ -600,19 +632,6 @@ return snd_pcm_mmap_capture_delay(pcm); } -static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset) -{ - unsigned int bitofs = area->first + area->step * offset; - assert(bitofs % 8 == 0); - return (char *) area->addr + bitofs / 8; -} - -static inline unsigned int snd_pcm_channel_area_step(const snd_pcm_channel_area_t *area) -{ - assert(area->step % 8 == 0); - return area->step / 8; -} - static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size) { /* lock handled in the callback */ @@ -928,6 +947,8 @@ int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_t val); int INTERNAL(snd_pcm_sw_params_get_tstamp_mode)(const snd_pcm_sw_params_t *params, snd_pcm_tstamp_t *val); +int snd_pcm_sw_params_set_tstamp_type(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_type_t val); +int snd_pcm_sw_params_get_tstamp_type(const snd_pcm_sw_params_t *params, snd_pcm_tstamp_type_t *val); int snd_pcm_sw_params_set_avail_min(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); int INTERNAL(snd_pcm_sw_params_get_avail_min)(const snd_pcm_sw_params_t *params, snd_pcm_uframes_t *val); int snd_pcm_sw_params_set_start_threshold(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); @@ -1138,6 +1159,26 @@ #define PCMINABORT(pcm) (((pcm)->mode & SND_PCM_ABORT) != 0) +static inline snd_pcm_sframes_t pcm_frame_diff(snd_pcm_uframes_t ptr1, + snd_pcm_uframes_t ptr2, + snd_pcm_uframes_t boundary) +{ + if (ptr1 < ptr2) + return ptr1 + (boundary - ptr2); + else + return ptr1 - ptr2; +} + +static inline snd_pcm_sframes_t pcm_frame_diff2(snd_pcm_uframes_t ptr1, + snd_pcm_uframes_t ptr2, + snd_pcm_uframes_t boundary) +{ + snd_pcm_sframes_t r = ptr1 - ptr2; + if (r >= (snd_pcm_sframes_t)boundary / 2) + return boundary - r; + return r; +} + #ifdef THREAD_SAFE_API /* * __snd_pcm_lock() and __snd_pcm_unlock() are used to lock/unlock the plugin diff -Nru alsa-lib-1.2.2/src/pcm/pcm_meter.c alsa-lib-1.2.6.1/src/pcm/pcm_meter.c --- alsa-lib-1.2.2/src/pcm/pcm_meter.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_meter.c 2021-12-09 13:17:59.000000000 +0000 @@ -79,6 +79,8 @@ snd_pcm_uframes_t frames) { snd_pcm_meter_t *meter = pcm->private_data; + if (frames > pcm->buffer_size) + frames = pcm->buffer_size; while (frames > 0) { snd_pcm_uframes_t n = frames; snd_pcm_uframes_t dst_offset = ptr % meter->buf_size; @@ -1100,6 +1102,8 @@ size = meter->now - s16->old; if (size < 0) size += spcm->boundary; + if (size > (snd_pcm_sframes_t)s16->pcm->buffer_size) + size = s16->pcm->buffer_size; offset = s16->old % meter->buf_size; while (size > 0) { snd_pcm_uframes_t frames = size; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_misc.c alsa-lib-1.2.6.1/src/pcm/pcm_misc.c --- alsa-lib-1.2.2/src/pcm/pcm_misc.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_misc.c 2021-12-09 13:17:59.000000000 +0000 @@ -763,6 +763,7 @@ * \param hwctlp the pointer to store the h/w control flag (optional) * \return 0 if successful, or a negative error code * + * \deprecated Since 1.2.5 * This function parses the given config tree to retrieve the control element id * and the card index. It's used by softvol. External PCM plugins can use this * function for creating or assigining their controls. @@ -793,21 +794,10 @@ if (strcmp(id, "comment") == 0) continue; if (strcmp(id, "card") == 0) { - const char *str; - long v; - if ((err = snd_config_get_integer(n, &v)) < 0) { - if ((err = snd_config_get_string(n, &str)) < 0) { - SNDERR("Invalid field %s", id); - goto _err; - } - *cardp = snd_card_get_index(str); - if (*cardp < 0) { - SNDERR("Cannot get index for %s", str); - err = *cardp; - goto _err; - } - } else - *cardp = v; + err = snd_config_get_card(n); + if (err < 0) + goto _err; + *cardp = err; continue; } if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) { diff -Nru alsa-lib-1.2.2/src/pcm/pcm_mmap.c alsa-lib-1.2.6.1/src/pcm/pcm_mmap.c --- alsa-lib-1.2.2/src/pcm/pcm_mmap.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_mmap.c 2021-12-09 13:17:59.000000000 +0000 @@ -183,7 +183,7 @@ * \brief Read interleaved frames from a PCM using direct buffer (mmap) * \param pcm PCM handle * \param buffer frames containing buffer - * \param size frames to be written + * \param size frames to be read * \return a positive number of frames actually read otherwise a * negative error code * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) diff -Nru alsa-lib-1.2.2/src/pcm/pcm_multi.c alsa-lib-1.2.6.1/src/pcm/pcm_multi.c --- alsa-lib-1.2.2/src/pcm/pcm_multi.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_multi.c 2021-12-09 13:17:59.000000000 +0000 @@ -669,6 +669,7 @@ return -EIO; } } + snd_pcm_mmap_appl_backward(pcm, frames); return frames; } @@ -699,6 +700,7 @@ return -EIO; } } + snd_pcm_mmap_appl_forward(pcm, frames); return frames; } @@ -782,8 +784,7 @@ if ((snd_pcm_uframes_t)result != size) return -EIO; } - multi->appl_ptr += size; - multi->appl_ptr %= pcm->boundary; + snd_pcm_mmap_appl_forward(pcm, size); return size; } @@ -1323,7 +1324,6 @@ err = -ENOMEM; goto _free; } - idx = 0; for (idx = 0; idx < channels_count; ++idx) channels_sidx[idx] = -1; idx = 0; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_null.c alsa-lib-1.2.6.1/src/pcm/pcm_null.c --- alsa-lib-1.2.2/src/pcm/pcm_null.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_null.c 2021-12-09 13:17:59.000000000 +0000 @@ -96,6 +96,8 @@ memset(status, 0, sizeof(*status)); status->state = null->state; status->trigger_tstamp = null->trigger_tstamp; + status->appl_ptr = *pcm->appl.ptr; + status->hw_ptr = *pcm->hw.ptr; gettimestamp(&status->tstamp, pcm->tstamp_type); status->avail = snd_pcm_null_avail_update(pcm); status->avail_max = pcm->buffer_size; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_plugin.c alsa-lib-1.2.6.1/src/pcm/pcm_plugin.c --- alsa-lib-1.2.2/src/pcm/pcm_plugin.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_plugin.c 2021-12-09 13:17:59.000000000 +0000 @@ -142,25 +142,18 @@ int err = snd_pcm_delay(plugin->gen.slave, &sd); if (err < 0) return err; - if (pcm->stream == SND_PCM_STREAM_CAPTURE && - pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && - pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) { - sd += snd_pcm_mmap_capture_avail(pcm); - } - *delayp = sd; return 0; } -static int snd_pcm_plugin_prepare(snd_pcm_t *pcm) +static int snd_pcm_plugin_call_init_cb(snd_pcm_t *pcm, snd_pcm_plugin_t *plugin) { - snd_pcm_plugin_t *plugin = pcm->private_data; + snd_pcm_t *slave = plugin->gen.slave; int err; - err = snd_pcm_prepare(plugin->gen.slave); - if (err < 0) - return err; - *pcm->hw.ptr = 0; - *pcm->appl.ptr = 0; + + assert(pcm->boundary == slave->boundary); + *pcm->hw.ptr = *slave->hw.ptr; + *pcm->appl.ptr = *slave->appl.ptr; if (plugin->init) { err = plugin->init(pcm); if (err < 0) @@ -169,6 +162,16 @@ return 0; } +static int snd_pcm_plugin_prepare(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private_data; + int err; + err = snd_pcm_prepare(plugin->gen.slave); + if (err < 0) + return err; + return snd_pcm_plugin_call_init_cb(pcm, plugin); +} + static int snd_pcm_plugin_reset(snd_pcm_t *pcm) { snd_pcm_plugin_t *plugin = pcm->private_data; @@ -176,14 +179,7 @@ err = snd_pcm_reset(plugin->gen.slave); if (err < 0) return err; - *pcm->hw.ptr = 0; - *pcm->appl.ptr = 0; - if (plugin->init) { - err = plugin->init(pcm); - if (err < 0) - return err; - } - return 0; + return snd_pcm_plugin_call_init_cb(pcm, plugin); } static snd_pcm_sframes_t snd_pcm_plugin_rewindable(snd_pcm_t *pcm) @@ -460,97 +456,121 @@ return xfer > 0 ? xfer : err; } -static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) +static snd_pcm_sframes_t +snd_pcm_plugin_sync_hw_ptr_capture(snd_pcm_t *pcm, + snd_pcm_sframes_t slave_size) { snd_pcm_plugin_t *plugin = pcm->private_data; snd_pcm_t *slave = plugin->gen.slave; - snd_pcm_sframes_t slave_size; + const snd_pcm_channel_area_t *areas; + snd_pcm_uframes_t xfer, hw_offset, size; int err; - slave_size = snd_pcm_avail_update(slave); + xfer = snd_pcm_mmap_capture_avail(pcm); + size = pcm->buffer_size - xfer; + areas = snd_pcm_mmap_areas(pcm); + hw_offset = snd_pcm_mmap_hw_offset(pcm); + while (size > 0 && slave_size > 0) { + snd_pcm_uframes_t frames = size; + snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; + const snd_pcm_channel_area_t *slave_areas; + snd_pcm_uframes_t slave_offset; + snd_pcm_uframes_t slave_frames = ULONG_MAX; + snd_pcm_sframes_t result; + /* As mentioned in the ALSA API (see pcm/pcm.c:942): + * The function #snd_pcm_avail_update() + * have to be called before any mmap begin+commit operation. + * Otherwise the snd_pcm_areas_copy will not called a second time. + * But this is needed, if the ring buffer wrap is reached and + * there is more data available. + */ + slave_size = snd_pcm_avail_update(slave); + result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); + if (result < 0) { + err = result; + goto error; + } + if (frames > cont) + frames = cont; + frames = (plugin->read)(pcm, areas, hw_offset, frames, + slave_areas, slave_offset, &slave_frames); + result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); + if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { + snd_pcm_sframes_t res; + res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result); + if (res < 0) { + err = res; + goto error; + } + frames -= res; + } + if (result <= 0) { + err = result; + goto error; + } + snd_pcm_mmap_hw_forward(pcm, frames); + if (frames == cont) + hw_offset = 0; + else + hw_offset += frames; + size -= frames; + slave_size -= slave_frames; + xfer += frames; + } + return (snd_pcm_sframes_t)xfer; +error: + return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; +} + +static snd_pcm_sframes_t snd_pcm_plugin_sync_hw_ptr(snd_pcm_t *pcm, + snd_pcm_uframes_t slave_hw_ptr, + snd_pcm_sframes_t slave_size) +{ if (pcm->stream == SND_PCM_STREAM_CAPTURE && pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && pcm->access != SND_PCM_ACCESS_RW_NONINTERLEAVED) - goto _capture; - *pcm->hw.ptr = *slave->hw.ptr; + return snd_pcm_plugin_sync_hw_ptr_capture(pcm, slave_size); + *pcm->hw.ptr = slave_hw_ptr; return slave_size; - _capture: - { - const snd_pcm_channel_area_t *areas; - snd_pcm_uframes_t xfer, hw_offset, size; - - xfer = snd_pcm_mmap_capture_avail(pcm); - size = pcm->buffer_size - xfer; - areas = snd_pcm_mmap_areas(pcm); - hw_offset = snd_pcm_mmap_hw_offset(pcm); - while (size > 0 && slave_size > 0) { - snd_pcm_uframes_t frames = size; - snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset; - const snd_pcm_channel_area_t *slave_areas; - snd_pcm_uframes_t slave_offset; - snd_pcm_uframes_t slave_frames = ULONG_MAX; - snd_pcm_sframes_t result; - /* As mentioned in the ALSA API (see pcm/pcm.c:942): - * The function #snd_pcm_avail_update() - * have to be called before any mmap begin+commit operation. - * Otherwise the snd_pcm_areas_copy will not called a second time. - * But this is needed, if the ring buffer wrap is reached and - * there is more data available. - */ - slave_size = snd_pcm_avail_update(slave); - result = snd_pcm_mmap_begin(slave, &slave_areas, &slave_offset, &slave_frames); - if (result < 0) { - err = result; - goto error; - } - if (frames > cont) - frames = cont; - frames = (plugin->read)(pcm, areas, hw_offset, frames, - slave_areas, slave_offset, &slave_frames); - result = snd_pcm_mmap_commit(slave, slave_offset, slave_frames); - if (result > 0 && (snd_pcm_uframes_t)result != slave_frames) { - snd_pcm_sframes_t res; - - res = plugin->undo_read(slave, areas, hw_offset, frames, slave_frames - result); - if (res < 0) { - err = res; - goto error; - } - frames -= res; - } - if (result <= 0) { - err = result; - goto error; - } - snd_pcm_mmap_hw_forward(pcm, frames); - if (frames == cont) - hw_offset = 0; - else - hw_offset += frames; - size -= frames; - slave_size -= slave_frames; - xfer += frames; - } - return (snd_pcm_sframes_t)xfer; +} - error: - return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; - } +static snd_pcm_sframes_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private_data; + snd_pcm_t *slave = plugin->gen.slave; + snd_pcm_sframes_t slave_size; + + slave_size = snd_pcm_avail_update(slave); + return snd_pcm_plugin_sync_hw_ptr(pcm, *slave->hw.ptr, slave_size); } static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) { snd_pcm_plugin_t *plugin = pcm->private_data; - snd_pcm_sframes_t err; - - /* sync with the latest hw and appl ptrs */ - snd_pcm_plugin_avail_update(pcm); + snd_pcm_sframes_t err, diff; err = snd_pcm_status(plugin->gen.slave, status); if (err < 0) return err; - status->appl_ptr = *pcm->appl.ptr; - status->hw_ptr = *pcm->hw.ptr; + snd_pcm_plugin_sync_hw_ptr(pcm, status->hw_ptr, status->avail); + /* + * For capture stream, the situation is more complicated, because + * snd_pcm_plugin_avail_update() commits the data to the slave pcm. + * It means that the slave appl_ptr is updated. Calculate diff and + * update the delay and avail. + * + * This resolves the data inconsistency for immediate calls: + * snd_pcm_avail_update() + * snd_pcm_status() + */ + if (pcm->stream == SND_PCM_STREAM_CAPTURE) { + diff = pcm_frame_diff(status->appl_ptr, *pcm->appl.ptr, pcm->boundary); + status->appl_ptr = *pcm->appl.ptr; + status->avail += diff; + status->delay += diff; + } else { + assert(status->appl_ptr == *pcm->appl.ptr); + } return 0; } diff -Nru alsa-lib-1.2.2/src/pcm/pcm_rate.c alsa-lib-1.2.6.1/src/pcm/pcm_rate.c --- alsa-lib-1.2.2/src/pcm/pcm_rate.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_rate.c 2021-12-09 13:17:59.000000000 +0000 @@ -62,20 +62,63 @@ void *open_func; void *obj; snd_pcm_rate_ops_t ops; - unsigned int get_idx; - unsigned int put_idx; - int16_t *src_buf; - int16_t *dst_buf; + unsigned int src_conv_idx; + unsigned int dst_conv_idx; + snd_pcm_channel_area_t *src_buf; + snd_pcm_channel_area_t *dst_buf; int start_pending; /* start is triggered but not commited to slave */ snd_htimestamp_t trigger_tstamp; unsigned int plugin_version; unsigned int rate_min, rate_max; + snd_pcm_format_t orig_in_format; + snd_pcm_format_t orig_out_format; + uint64_t in_formats; + uint64_t out_formats; + unsigned int format_flags; }; #define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */ - #endif /* DOC_HIDDEN */ +/* allocate a channel area and a temporary buffer for the given size */ +static snd_pcm_channel_area_t * +rate_alloc_tmp_buf(snd_pcm_format_t format, + unsigned int channels, unsigned int frames) +{ + snd_pcm_channel_area_t *ap; + int width = snd_pcm_format_physical_width(format); + unsigned int i; + + ap = malloc(sizeof(*ap) * channels); + if (!ap) + return NULL; + ap->addr = malloc(frames * channels * width / 8); + if (!ap->addr) { + free(ap); + return NULL; + } + + /* set up in interleaved format */ + for (i = 0; i < channels; i++) { + ap[i].addr = ap[0].addr + (i * width) / 8; + ap[i].first = 0; + ap[i].step = width * channels; + } + + return ap; +} + +static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr) +{ + snd_pcm_channel_area_t *c = *ptr; + + if (c) { + free(c->addr); + free(c); + *ptr = NULL; + } +} + static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) { snd_pcm_rate_t *rate = pcm->private_data; @@ -235,12 +278,84 @@ snd_pcm_generic_hw_refine); } +/* evaluate the best matching available format to the given format */ +static int get_best_format(uint64_t mask, snd_pcm_format_t orig) +{ + int pwidth = snd_pcm_format_physical_width(orig); + int width = snd_pcm_format_width(orig); + int signd = snd_pcm_format_signed(orig); + int best_score = -1; + int match = -1; + int f, score; + + for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) { + if (!(mask & (1ULL << f))) + continue; + score = 0; + if (snd_pcm_format_linear(f)) { + if (snd_pcm_format_physical_width(f) == pwidth) + score++; + if (snd_pcm_format_physical_width(f) >= pwidth) + score++; + if (snd_pcm_format_width(f) == width) + score++; + if (snd_pcm_format_signed(f) == signd) + score++; + } + if (score > best_score) { + match = f; + best_score = score; + } + } + + return match; +} + +/* set up the input and output formats from the available lists */ +static int choose_preferred_format(snd_pcm_rate_t *rate) +{ + uint64_t in_mask = rate->in_formats; + uint64_t out_mask = rate->out_formats; + int in, out; + + if (!in_mask || !out_mask) + return 0; + + if (rate->orig_in_format == rate->orig_out_format) + if (in_mask & out_mask & (1ULL << rate->orig_in_format)) + return 0; /* nothing changed */ + + repeat: + in = get_best_format(in_mask, rate->orig_in_format); + out = get_best_format(out_mask, rate->orig_out_format); + if (in < 0 || out < 0) + return -ENOENT; + + if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) && + in != out) { + if (out_mask & (1ULL << in)) + out = in; + else if (in_mask & (1ULL << out)) + in = out; + else { + in_mask &= ~(1ULL << in); + out_mask &= ~(1ULL << out); + goto repeat; + } + } + + rate->info.in.format = in; + rate->info.out.format = out; + return 0; +} + static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) { snd_pcm_rate_t *rate = pcm->private_data; snd_pcm_t *slave = rate->gen.slave; snd_pcm_rate_side_info_t *sinfo, *cinfo; - unsigned int channels, cwidth, swidth, chn; + unsigned int channels, acc; + int need_src_buf, need_dst_buf; int err = snd_pcm_hw_params_slave(pcm, params, snd_pcm_rate_hw_refine_cchange, snd_pcm_rate_hw_refine_sprepare, @@ -271,6 +386,9 @@ err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels); if (err < 0) return err; + err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc); + if (err < 0) + return err; rate->info.channels = channels; sinfo->format = slave->format; @@ -282,70 +400,92 @@ SNDMSG("rate plugin already in use"); return -EBUSY; } + + rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels, + cinfo->period_size); + rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels, + sinfo->period_size); + if (!rate->pareas || !rate->sareas) { + err = -ENOMEM; + goto error_pareas; + } + + rate->orig_in_format = rate->info.in.format; + rate->orig_out_format = rate->info.out.format; + if (choose_preferred_format(rate) < 0) { + SNDERR("No matching format in rate plugin"); + err = -EINVAL; + goto error_pareas; + } + err = rate->ops.init(rate->obj, &rate->info); if (err < 0) - return err; + goto error_init; + + rate_free_tmp_buf(&rate->src_buf); + rate_free_tmp_buf(&rate->dst_buf); + + need_src_buf = need_dst_buf = 0; - rate->pareas = malloc(2 * channels * sizeof(*rate->pareas)); - if (rate->pareas == NULL) - goto error; - - cwidth = snd_pcm_format_physical_width(cinfo->format); - swidth = snd_pcm_format_physical_width(sinfo->format); - rate->pareas[0].addr = malloc(((cwidth * channels * cinfo->period_size) / 8) + - ((swidth * channels * sinfo->period_size) / 8)); - if (rate->pareas[0].addr == NULL) - goto error; - - rate->sareas = rate->pareas + channels; - rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((cwidth * channels * cinfo->period_size) / 8); - for (chn = 0; chn < channels; chn++) { - rate->pareas[chn].addr = (char *)rate->pareas[0].addr + (cwidth * chn * cinfo->period_size) / 8; - rate->pareas[chn].first = 0; - rate->pareas[chn].step = cwidth; - rate->sareas[chn].addr = (char *)rate->sareas[0].addr + (swidth * chn * sinfo->period_size) / 8; - rate->sareas[chn].first = 0; - rate->sareas[chn].step = swidth; - } - - if (rate->ops.convert_s16) { - rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16); - rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format); - free(rate->src_buf); - rate->src_buf = malloc(channels * rate->info.in.period_size * 2); - free(rate->dst_buf); - rate->dst_buf = malloc(channels * rate->info.out.period_size * 2); - if (! rate->src_buf || ! rate->dst_buf) + if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) && + !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED || + acc == SND_PCM_ACCESS_RW_INTERLEAVED)) { + need_src_buf = need_dst_buf = 1; + } else { + if (rate->orig_in_format != rate->info.in.format) + need_src_buf = 1; + if (rate->orig_out_format != rate->info.out.format) + need_dst_buf = 1; + } + + if (need_src_buf) { + rate->src_conv_idx = + snd_pcm_linear_convert_index(rate->orig_in_format, + rate->info.in.format); + rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format, + channels, rate->info.in.period_size); + if (!rate->src_buf) { + err = -ENOMEM; + goto error; + } + } + + if (need_dst_buf) { + rate->dst_conv_idx = + snd_pcm_linear_convert_index(rate->info.out.format, + rate->orig_out_format); + rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format, + channels, rate->info.out.period_size); + if (!rate->dst_buf) { + err = -ENOMEM; goto error; + } } return 0; error: - if (rate->pareas) { - free(rate->pareas[0].addr); - free(rate->pareas); - rate->pareas = NULL; - } + rate_free_tmp_buf(&rate->src_buf); + rate_free_tmp_buf(&rate->dst_buf); + error_init: if (rate->ops.free) rate->ops.free(rate->obj); - return -ENOMEM; + error_pareas: + rate_free_tmp_buf(&rate->pareas); + rate_free_tmp_buf(&rate->sareas); + return err; } static int snd_pcm_rate_hw_free(snd_pcm_t *pcm) { snd_pcm_rate_t *rate = pcm->private_data; - if (rate->pareas) { - free(rate->pareas[0].addr); - free(rate->pareas); - rate->pareas = NULL; - rate->sareas = NULL; - } + + rate_free_tmp_buf(&rate->pareas); + rate_free_tmp_buf(&rate->sareas); if (rate->ops.free) rate->ops.free(rate->obj); - free(rate->src_buf); - free(rate->dst_buf); - rate->src_buf = rate->dst_buf = NULL; + rate_free_tmp_buf(&rate->src_buf); + rate_free_tmp_buf(&rate->dst_buf); return snd_pcm_hw_free(rate->gen.slave); } @@ -426,82 +566,6 @@ return 0; } -static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, unsigned int frames, - unsigned int channels) -{ -#ifndef DOC_HIDDEN -#define GET16_LABELS -#include "plugin_ops.h" -#undef GET16_LABELS -#endif /* DOC_HIDDEN */ - void *get = get16_labels[rate->get_idx]; - const char *src; - int16_t sample; - const char *srcs[channels]; - int src_step[channels]; - unsigned int c; - - for (c = 0; c < channels; c++) { - srcs[c] = snd_pcm_channel_area_addr(areas + c, offset); - src_step[c] = snd_pcm_channel_area_step(areas + c); - } - - while (frames--) { - for (c = 0; c < channels; c++) { - src = srcs[c]; - goto *get; -#ifndef DOC_HIDDEN -#define GET16_END after_get -#include "plugin_ops.h" -#undef GET16_END -#endif /* DOC_HIDDEN */ - after_get: - *buf++ = sample; - srcs[c] += src_step[c]; - } - } -} - -static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, unsigned int frames, - unsigned int channels) -{ -#ifndef DOC_HIDDEN -#define PUT16_LABELS -#include "plugin_ops.h" -#undef PUT16_LABELS -#endif /* DOC_HIDDEN */ - void *put = put16_labels[rate->put_idx]; - char *dst; - int16_t sample; - char *dsts[channels]; - int dst_step[channels]; - unsigned int c; - - for (c = 0; c < channels; c++) { - dsts[c] = snd_pcm_channel_area_addr(areas + c, offset); - dst_step[c] = snd_pcm_channel_area_step(areas + c); - } - - while (frames--) { - for (c = 0; c < channels; c++) { - dst = dsts[c]; - sample = *buf++; - goto *put; -#ifndef DOC_HIDDEN -#define PUT16_END after_put -#include "plugin_ops.h" -#undef PUT16_END -#endif /* DOC_HIDDEN */ - after_put: - dsts[c] += dst_step[c]; - } - } -} - static void do_convert(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset, unsigned int dst_frames, const snd_pcm_channel_area_t *src_areas, @@ -509,28 +573,40 @@ unsigned int channels, snd_pcm_rate_t *rate) { - if (rate->ops.convert_s16) { - const int16_t *src; - int16_t *dst; - if (! rate->src_buf) - src = (int16_t *)src_areas->addr + src_offset * channels; - else { - convert_to_s16(rate, rate->src_buf, src_areas, src_offset, - src_frames, channels); - src = rate->src_buf; - } - if (! rate->dst_buf) - dst = (int16_t *)dst_areas->addr + dst_offset * channels; - else - dst = rate->dst_buf; - rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames); - if (dst == rate->dst_buf) - convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset, - dst_frames, channels); + const snd_pcm_channel_area_t *out_areas; + snd_pcm_uframes_t out_offset; + + if (rate->dst_buf) { + out_areas = rate->dst_buf; + out_offset = 0; } else { - rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames, - src_areas, src_offset, src_frames); + out_areas = dst_areas; + out_offset = dst_offset; } + + if (rate->src_buf) { + snd_pcm_linear_convert(rate->src_buf, 0, + src_areas, src_offset, + channels, src_frames, + rate->src_conv_idx); + src_areas = rate->src_buf; + src_offset = 0; + } + + if (rate->ops.convert) + rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames, + src_areas, src_offset, src_frames); + else + rate->ops.convert_s16(rate->obj, + snd_pcm_channel_area_addr(out_areas, out_offset), + dst_frames, + snd_pcm_channel_area_addr(src_areas, src_offset), + src_frames); + if (rate->dst_buf) + snd_pcm_linear_convert(dst_areas, dst_offset, + rate->dst_buf, 0, + channels, dst_frames, + rate->dst_conv_idx); } static inline void @@ -561,17 +637,16 @@ static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr) { - snd_pcm_rate_t *rate = pcm->private_data; - - snd_pcm_sframes_t slave_hw_ptr_diff = slave_hw_ptr - rate->last_slave_hw_ptr; + snd_pcm_rate_t *rate; + snd_pcm_sframes_t slave_hw_ptr_diff; snd_pcm_sframes_t last_slave_hw_ptr_frac; if (pcm->stream != SND_PCM_STREAM_PLAYBACK) return; - if (slave_hw_ptr_diff < 0) - slave_hw_ptr_diff += rate->gen.slave->boundary; /* slave boundary wraparound */ - else if (slave_hw_ptr_diff == 0) + rate = pcm->private_data; + slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary); + if (slave_hw_ptr_diff == 0) return; last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size; /* While handling fraction part fo slave period, rounded value will be @@ -612,11 +687,7 @@ { snd_pcm_rate_t *rate = pcm->private_data; - if (rate->appl_ptr < rate->last_commit_ptr) { - return rate->appl_ptr - rate->last_commit_ptr + pcm->boundary; - } else { - return rate->appl_ptr - rate->last_commit_ptr; - } + return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary); } static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) @@ -637,7 +708,7 @@ + snd_pcm_rate_playback_internal_delay(pcm); } else { *delayp = rate->ops.output_frames(rate->obj, slave_delay) - + snd_pcm_mmap_capture_hw_avail(pcm); + + snd_pcm_mmap_capture_delay(pcm); } return 0; } @@ -746,7 +817,6 @@ if (result < 0) return result; __partial: - xfer = 0; cont = slave_frames; if (cont > slave_size) cont = slave_size; @@ -846,7 +916,6 @@ if (result < 0) return result; __partial: - xfer = 0; cont = slave_frames; if (cont > rate->gen.slave->period_size) cont = rate->gen.slave->period_size; @@ -928,10 +997,7 @@ if (slave_size < 0) return slave_size; - if (appl_ptr < rate->last_commit_ptr) - xfer = appl_ptr - rate->last_commit_ptr + pcm->boundary; - else - xfer = appl_ptr - rate->last_commit_ptr; + xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary); while (xfer >= pcm->period_size && (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) { err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size); @@ -966,29 +1032,18 @@ return size; } -static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm) +static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm, + snd_pcm_sframes_t slave_size) { snd_pcm_rate_t *rate = pcm->private_data; snd_pcm_t *slave = rate->gen.slave; - snd_pcm_sframes_t slave_size; - - slave_size = snd_pcm_avail_update(slave); - if (slave_size < 0) - return slave_size; - - if (pcm->stream == SND_PCM_STREAM_CAPTURE) - goto _capture; - snd_pcm_rate_sync_hwptr(pcm); - snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr); - return snd_pcm_mmap_avail(pcm); - _capture: { snd_pcm_uframes_t xfer, hw_offset, size; xfer = snd_pcm_mmap_capture_avail(pcm); size = pcm->buffer_size - xfer; hw_offset = snd_pcm_mmap_hw_offset(pcm); while (size >= pcm->period_size && - (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) { + (snd_pcm_uframes_t)slave_size >= slave->period_size) { int err = snd_pcm_rate_grab_next_period(pcm, hw_offset); if (err < 0) return err; @@ -996,13 +1051,29 @@ return (snd_pcm_sframes_t)xfer; xfer += pcm->period_size; size -= pcm->period_size; - slave_size -= rate->gen.slave->period_size; + slave_size -= slave->period_size; hw_offset += pcm->period_size; hw_offset %= pcm->buffer_size; snd_pcm_mmap_hw_forward(pcm, pcm->period_size); } return (snd_pcm_sframes_t)xfer; - } +} + +static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_rate_t *rate = pcm->private_data; + snd_pcm_sframes_t slave_size; + + slave_size = snd_pcm_avail_update(rate->gen.slave); + if (slave_size < 0) + return slave_size; + + if (pcm->stream == SND_PCM_STREAM_CAPTURE) + return snd_pcm_rate_avail_update_capture(pcm, slave_size); + + snd_pcm_rate_sync_hwptr(pcm); + snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr); + return snd_pcm_mmap_avail(pcm); } static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm, @@ -1051,6 +1122,7 @@ /* commit the remaining fraction (if any) */ snd_pcm_uframes_t size, ofs, saved_avail_min; snd_pcm_sw_params_t sw_params; + int commit_err = 0; __snd_pcm_lock(pcm); /* temporarily set avail_min to one */ @@ -1059,7 +1131,7 @@ sw_params.avail_min = 1; snd_pcm_sw_params(rate->gen.slave, &sw_params); - size = rate->appl_ptr - rate->last_commit_ptr; + size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary); ofs = rate->last_commit_ptr % pcm->buffer_size; while (size > 0) { snd_pcm_uframes_t psize, spsize; @@ -1077,14 +1149,29 @@ if (! spsize) break; } - snd_pcm_rate_commit_area(pcm, rate, ofs, + commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs, psize, spsize); + if (commit_err == 1) { + rate->last_commit_ptr += psize; + if (rate->last_commit_ptr >= pcm->boundary) + rate->last_commit_ptr = 0; + } else if (commit_err == 0) { + if (pcm->mode & SND_PCM_NONBLOCK) { + commit_err = -EAGAIN; + break; + } + continue; + } else + break; + ofs = (ofs + psize) % pcm->buffer_size; size -= psize; } sw_params.avail_min = saved_avail_min; snd_pcm_sw_params(rate->gen.slave, &sw_params); __snd_pcm_unlock(pcm); + if (commit_err < 0) + return commit_err; } return snd_pcm_drain(rate->gen.slave); } @@ -1146,12 +1233,8 @@ status->avail = snd_pcm_mmap_playback_avail(pcm); status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max); } else { - /* FIXME: Maybe possible to somthing similar to - * snd_pcm_rate_playback_internal_delay() - * for the capture case. - */ status->delay = rate->ops.output_frames(rate->obj, status->delay) - + snd_pcm_mmap_capture_hw_avail(pcm); + + snd_pcm_mmap_capture_delay(pcm); status->avail = snd_pcm_mmap_capture_avail(pcm); status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max); } @@ -1251,6 +1334,30 @@ return NULL; } +static void rate_initial_setup(snd_pcm_rate_t *rate) +{ + if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION) + rate->plugin_version = rate->ops.version; + + if (rate->plugin_version >= 0x010002 && + rate->ops.get_supported_rates) + rate->ops.get_supported_rates(rate->obj, + &rate->rate_min, + &rate->rate_max); + + if (rate->plugin_version >= 0x010003 && + rate->ops.get_supported_formats) { + rate->ops.get_supported_formats(rate->obj, + &rate->in_formats, + &rate->out_formats, + &rate->format_flags); + } else if (!rate->ops.convert && rate->ops.convert_s16) { + rate->in_formats = rate->out_formats = + 1ULL << SND_PCM_FORMAT_S16; + rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED; + } +} + #ifdef PIC static int is_builtin_plugin(const char *type) { @@ -1263,7 +1370,7 @@ static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose) { - char open_name[64], open_conf_name[64], lib_name[128], *lib = NULL; + char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL; snd_pcm_rate_open_func_t open_func; snd_pcm_rate_open_conf_func_t open_conf_func; int err; @@ -1272,24 +1379,15 @@ snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type); if (!is_builtin_plugin(type)) { snprintf(lib_name, sizeof(lib_name), - "%s/libasound_module_rate_%s.so", ALSA_PLUGIN_DIR, type); + "libasound_module_rate_%s.so", type); lib = lib_name; } - rate->rate_min = SND_PCM_PLUGIN_RATE_MIN; - rate->rate_max = SND_PCM_PLUGIN_RATE_MAX; - rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION; - open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL); if (open_conf_func) { err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops, converter_conf); if (!err) { - rate->plugin_version = rate->ops.version; - if (rate->ops.get_supported_rates) - rate->ops.get_supported_rates(rate->obj, - &rate->rate_min, - &rate->rate_max); rate->open_func = open_conf_func; return 0; } else { @@ -1305,23 +1403,18 @@ rate->open_func = open_func; err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops); - if (!err) { - rate->plugin_version = rate->ops.version; - if (rate->ops.get_supported_rates) - rate->ops.get_supported_rates(rate->obj, - &rate->rate_min, - &rate->rate_max); + if (!err) return 0; - } /* try to open with the old protocol version */ rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD; err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD, &rate->obj, &rate->ops); - if (err) { - snd_dlobj_cache_put(open_func); - rate->open_func = NULL; - } + if (!err) + return 0; + + snd_dlobj_cache_put(open_func); + rate->open_func = NULL; return err; } #endif @@ -1392,6 +1485,10 @@ rate->srate = srate; rate->sformat = sformat; + rate->rate_min = SND_PCM_PLUGIN_RATE_MIN; + rate->rate_max = SND_PCM_PLUGIN_RATE_MAX; + rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION; + err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode); if (err < 0) { free(rate); @@ -1471,6 +1568,8 @@ return err; } + rate_initial_setup(rate); + pcm->ops = &snd_pcm_rate_ops; pcm->fast_ops = &snd_pcm_rate_fast_ops; pcm->private_data = rate; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_route.c alsa-lib-1.2.6.1/src/pcm/pcm_route.c --- alsa-lib-1.2.2/src/pcm/pcm_route.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_route.c 2021-12-09 13:17:59.000000000 +0000 @@ -104,6 +104,7 @@ int schannels; snd_pcm_route_params_t params; snd_pcm_chmap_t *chmap; + snd_pcm_chmap_query_t **chmap_override; } snd_pcm_route_t; #endif /* DOC_HIDDEN */ @@ -441,6 +442,7 @@ free(params->dsts); } free(route->chmap); + snd_pcm_free_chmaps(route->chmap_override); return snd_pcm_generic_close(pcm); } @@ -634,6 +636,9 @@ snd_pcm_chmap_t *map, *slave_map; unsigned int src, dst, nsrcs; + if (route->chmap_override) + return _snd_pcm_choose_fixed_chmap(pcm, route->chmap_override); + slave_map = snd_pcm_generic_get_chmap(pcm); if (!slave_map) return NULL; @@ -660,8 +665,14 @@ static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm) { + snd_pcm_route_t *route = pcm->private_data; snd_pcm_chmap_query_t **maps; - snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm); + snd_pcm_chmap_t *map; + + if (route->chmap_override) + return _snd_pcm_copy_chmap_query(route->chmap_override); + + map = snd_pcm_route_get_chmap(pcm); if (!map) return NULL; maps = _snd_pcm_make_single_query_chmaps(map); @@ -818,10 +829,10 @@ return -EINVAL; } -static int find_matching_chmap(snd_pcm_t *spcm, snd_pcm_chmap_t *tt_chmap, +static int find_matching_chmap(snd_pcm_chmap_query_t **chmaps, + snd_pcm_chmap_t *tt_chmap, snd_pcm_chmap_t **found_chmap, int *schannels) { - snd_pcm_chmap_query_t** chmaps = snd_pcm_query_chmaps(spcm); int i; *found_chmap = NULL; @@ -854,7 +865,6 @@ int size = sizeof(snd_pcm_chmap_t) + c->channels * sizeof(unsigned int); *found_chmap = malloc(size); if (!*found_chmap) { - snd_pcm_free_chmaps(chmaps); return -ENOMEM; } memcpy(*found_chmap, c, size); @@ -863,8 +873,6 @@ } } - snd_pcm_free_chmaps(chmaps); - if (*found_chmap == NULL) { SNDERR("Found no matching channel map"); return -EINVAL; @@ -1252,6 +1260,7 @@ SCHANNEL REAL # route value (0.0 - 1.0) } } + [chmap MAP] # Override channel maps; MAP is a string array } \endcode @@ -1292,6 +1301,7 @@ snd_pcm_route_ttable_entry_t *ttable = NULL; unsigned int csize, ssize; unsigned int cused, sused; + snd_pcm_chmap_query_t **chmaps = NULL; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); const char *id; @@ -1306,31 +1316,45 @@ if (strcmp(id, "ttable") == 0) { if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { SNDERR("Invalid type for %s", id); + snd_pcm_free_chmaps(chmaps); return -EINVAL; } tt = n; continue; } + if (strcmp(id, "chmap") == 0) { + chmaps = _snd_pcm_parse_config_chmaps(n); + if (!chmaps) { + SNDERR("Invalid channel map for %s", id); + return -EINVAL; + } + continue; + } SNDERR("Unknown field %s", id); return -EINVAL; } if (!slave) { SNDERR("slave is not defined"); + snd_pcm_free_chmaps(chmaps); return -EINVAL; } if (!tt) { SNDERR("ttable is not defined"); + snd_pcm_free_chmaps(chmaps); return -EINVAL; } err = snd_pcm_slave_conf(root, slave, &sconf, 2, SND_PCM_HW_PARAM_FORMAT, 0, &sformat, SND_PCM_HW_PARAM_CHANNELS, 0, &schannels); - if (err < 0) + if (err < 0) { + snd_pcm_free_chmaps(chmaps); return err; + } if (sformat != SND_PCM_FORMAT_UNKNOWN && snd_pcm_format_linear(sformat) != 1) { snd_config_delete(sconf); SNDERR("slave format is not linear"); + snd_pcm_free_chmaps(chmaps); return -EINVAL; } @@ -1345,13 +1369,19 @@ if (err < 0) { free(tt_chmap); free(ttable); + snd_pcm_free_chmaps(chmaps); return err; } if (tt_chmap) { - err = find_matching_chmap(spcm, tt_chmap, &chmap, &schannels); + if (!chmaps) + chmaps = snd_pcm_query_chmaps(spcm); + if (chmaps) + err = find_matching_chmap(chmaps, tt_chmap, &chmap, + &schannels); free(tt_chmap); - if (err < 0) { + if (chmaps && err < 0) { + snd_pcm_free_chmaps(chmaps); snd_pcm_close(spcm); return err; } @@ -1360,12 +1390,14 @@ err = _snd_pcm_route_determine_ttable(tt, &csize, &ssize, chmap); if (err < 0) { free(chmap); + snd_pcm_free_chmaps(chmaps); snd_pcm_close(spcm); return err; } ttable = malloc(csize * ssize * sizeof(snd_pcm_route_ttable_entry_t)); if (ttable == NULL) { free(chmap); + snd_pcm_free_chmaps(chmaps); snd_pcm_close(spcm); return -ENOMEM; } @@ -1374,6 +1406,7 @@ if (err < 0) { free(chmap); free(ttable); + snd_pcm_free_chmaps(chmaps); snd_pcm_close(spcm); return err; } @@ -1385,9 +1418,13 @@ free(ttable); if (err < 0) { free(chmap); + snd_pcm_free_chmaps(chmaps); snd_pcm_close(spcm); } else { - ((snd_pcm_route_t*) (*pcmp)->private_data)->chmap = chmap; + snd_pcm_route_t *route = (*pcmp)->private_data; + + route->chmap = chmap; + route->chmap_override = chmaps; } return err; diff -Nru alsa-lib-1.2.2/src/pcm/pcm_share.c alsa-lib-1.2.6.1/src/pcm/pcm_share.c --- alsa-lib-1.2.2/src/pcm/pcm_share.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_share.c 2021-12-09 13:17:59.000000000 +0000 @@ -711,6 +711,8 @@ _notrunning: status->delay = sd + d; status->state = share->state; + status->appl_ptr = *pcm->appl.ptr; + status->hw_ptr = *pcm->hw.ptr; status->trigger_tstamp = share->trigger_tstamp; _end: Pthread_mutex_unlock(&slave->mutex); diff -Nru alsa-lib-1.2.2/src/pcm/pcm_softvol.c alsa-lib-1.2.6.1/src/pcm/pcm_softvol.c --- alsa-lib-1.2.2/src/pcm/pcm_softvol.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/pcm_softvol.c 2021-12-09 13:17:59.000000000 +0000 @@ -707,7 +707,8 @@ snd_pcm_dump(svol->plug.gen.slave, out); } -static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo) +static int add_tlv_info(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo, + unsigned int *old_tlv, size_t old_tlv_size) { unsigned int tlv[4]; tlv[SNDRV_CTL_TLVO_TYPE] = SND_CTL_TLVT_DB_SCALE; @@ -715,6 +716,8 @@ tlv[SNDRV_CTL_TLVO_DB_SCALE_MIN] = (int)(svol->min_dB * 100); tlv[SNDRV_CTL_TLVO_DB_SCALE_MUTE_AND_STEP] = (int)((svol->max_dB - svol->min_dB) * 100 / svol->max_val); + if (sizeof(tlv) <= old_tlv_size && memcmp(tlv, old_tlv, sizeof(tlv)) == 0) + return 0; return snd_ctl_elem_tlv_write(svol->ctl, &cinfo->id, tlv); } @@ -725,17 +728,19 @@ int i; unsigned int def_val; - if (svol->max_val == 1) + if (svol->max_val == 1) { + snd_ctl_elem_info_set_read_write(cinfo, 1, 1); err = snd_ctl_add_boolean_elem_set(svol->ctl, cinfo, 1, count); - else + } else { err = snd_ctl_add_integer_elem_set(svol->ctl, cinfo, 1, count, 0, svol->max_val, 0); + } if (err < 0) return err; if (svol->max_val == 1) def_val = 1; else { - add_tlv_info(svol, cinfo); + add_tlv_info(svol, cinfo, NULL, 0); /* set zero dB value as default, or max_val if there is no 0 dB setting */ def_val = svol->zero_dB_val ? svol->zero_dB_val : svol->max_val; @@ -811,13 +816,18 @@ cinfo.type != SND_CTL_ELEM_TYPE_BOOLEAN) || cinfo.count != (unsigned int)cchannels || cinfo.value.integer.min != 0 || - cinfo.value.integer.max != resolution - 1) { + cinfo.value.integer.max != svol->max_val || + (svol->max_val > 1 && + (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || + (svol->max_val < 2 && + (cinfo.access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) != 0)) { err = snd_ctl_elem_remove(svol->ctl, &cinfo.id); if (err < 0) { SNDERR("Control %s mismatch", tmp_name); return err; } - /* reset numid */ + /* clear cinfo including numid */ + snd_ctl_elem_info_clear(&cinfo); snd_ctl_elem_info_set_id(&cinfo, ctl_id); if ((err = add_user_ctl(svol, &cinfo, cchannels)) < 0) { SNDERR("Cannot add a control"); @@ -828,8 +838,7 @@ unsigned int tlv[4]; err = snd_ctl_elem_tlv_read(svol->ctl, &cinfo.id, tlv, sizeof(tlv)); - if (err < 0) - add_tlv_info(svol, &cinfo); + add_tlv_info(svol, &cinfo, tlv, err < 0 ? 0 : sizeof(tlv)); } } @@ -974,9 +983,107 @@ return 0; } -/* in pcm_misc.c */ -int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp, - int *cchannelsp, int *hwctlp); +static int _snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, + int *cardp, int *cchannels) +{ + snd_config_iterator_t i, next; + int iface = SND_CTL_ELEM_IFACE_MIXER; + const char *name = NULL; + long index = 0; + long device = -1; + long subdevice = -1; + int err; + + assert(ctl_id && cardp && cchannels); + + *cardp = -1; + *cchannels = 2; + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "comment") == 0) + continue; + if (strcmp(id, "card") == 0) { + err = snd_config_get_card(n); + if (err < 0) + goto _err; + *cardp = err; + continue; + } + if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) { + err = snd_config_get_ctl_iface(n); + if (err < 0) + goto _err; + iface = err; + continue; + } + if (strcmp(id, "name") == 0) { + if ((err = snd_config_get_string(n, &name)) < 0) { + SNDERR("field %s is not a string", id); + goto _err; + } + continue; + } + if (strcmp(id, "index") == 0) { + if ((err = snd_config_get_integer(n, &index)) < 0) { + SNDERR("field %s is not an integer", id); + goto _err; + } + continue; + } + if (strcmp(id, "device") == 0) { + if ((err = snd_config_get_integer(n, &device)) < 0) { + SNDERR("field %s is not an integer", id); + goto _err; + } + continue; + } + if (strcmp(id, "subdevice") == 0) { + if ((err = snd_config_get_integer(n, &subdevice)) < 0) { + SNDERR("field %s is not an integer", id); + goto _err; + } + continue; + } + if (strcmp(id, "count") == 0) { + long v; + if ((err = snd_config_get_integer(n, &v)) < 0) { + SNDERR("field %s is not an integer", id); + goto _err; + } + if (v < 1 || v > 2) { + SNDERR("Invalid count %ld", v); + goto _err; + } + *cchannels = v; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (name == NULL) { + SNDERR("Missing control name"); + err = -EINVAL; + goto _err; + } + if (device < 0) + device = 0; + if (subdevice < 0) + subdevice = 0; + + snd_ctl_elem_id_set_interface(ctl_id, iface); + snd_ctl_elem_id_set_name(ctl_id, name); + snd_ctl_elem_id_set_index(ctl_id, index); + snd_ctl_elem_id_set_device(ctl_id, device); + snd_ctl_elem_id_set_subdevice(ctl_id, subdevice); + + return 0; + + _err: + return err; +} /*! \page pcm_plugins @@ -1149,8 +1256,7 @@ snd_config_delete(sconf); if (err < 0) return err; - err = snd_pcm_parse_control_id(control, &ctl_id, &card, - &cchannels, NULL); + err = _snd_pcm_parse_control_id(control, &ctl_id, &card, &cchannels); if (err < 0) { snd_pcm_close(spcm); return err; diff -Nru alsa-lib-1.2.2/src/pcm/scopes/Makefile.in alsa-lib-1.2.6.1/src/pcm/scopes/Makefile.in --- alsa-lib-1.2.2/src/pcm/scopes/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/pcm/scopes/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -327,6 +327,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/rawmidi/Makefile.in alsa-lib-1.2.6.1/src/rawmidi/Makefile.in --- alsa-lib-1.2.2/src/rawmidi/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/rawmidi/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -303,6 +303,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/rawmidi/rawmidi.c alsa-lib-1.2.6.1/src/rawmidi/rawmidi.c --- alsa-lib-1.2.2/src/rawmidi/rawmidi.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/rawmidi/rawmidi.c 2021-12-09 13:17:59.000000000 +0000 @@ -118,6 +118,15 @@ hw:DEV=1,CARD=soundwave,SUBDEV=2 \endcode +\section read_mode Read mode + +Optionally, incoming rawmidi bytes can be marked with timestamps. The library hides +the kernel implementation (linux kernel 5.14+) and exports +the \link ::snd_rawmidi_tread() \endlink function which returns the +midi bytes marked with the identical timestamp in one iteration. + +The timestamping is available only on input streams. + \section rawmidi_examples Examples The full featured examples with cross-links: @@ -154,6 +163,8 @@ params->buffer_size = page_size(); params->avail_min = 1; params->no_active_sensing = 1; + params->mode = 0; + memset(params->reserved, 0, sizeof(params->reserved)); return 0; } @@ -304,9 +315,15 @@ int err; assert((inputp || outputp) && name); - err = snd_config_update_ref(&top); - if (err < 0) - return err; + if (_snd_is_ucm_device(name)) { + name = uc_mgr_alibcfg_by_device(&top, name); + if (name == NULL) + return -ENODEV; + } else { + err = snd_config_update_ref(&top); + if (err < 0) + return err; + } err = snd_rawmidi_open_noupdate(inputp, outputp, top, name, mode); snd_config_unref(top); return err; @@ -806,6 +823,95 @@ } /** + * \brief set read mode + * \param rawmidi RawMidi handle + * \param params pointer to snd_rawmidi_params_t structure + * \param val type of read_mode + * \return 0 on success, otherwise a negative error code. + * + * Notable error codes: + * -EINVAL - "val" is invalid + * -ENOTSUP - mode is not supported + * + */ +int snd_rawmidi_params_set_read_mode(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_read_mode_t val) +{ + unsigned int framing; + assert(rawmidi && params); + + switch (val) { + case SND_RAWMIDI_READ_STANDARD: + framing = SNDRV_RAWMIDI_MODE_FRAMING_NONE; + break; + case SND_RAWMIDI_READ_TSTAMP: + if (rawmidi->ops->tread == NULL) + return -ENOTSUP; + framing = SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP; + break; + default: + return -EINVAL; + } + + if (framing != SNDRV_RAWMIDI_MODE_FRAMING_NONE && + (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT)) + return -ENOTSUP; + params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_FRAMING_MASK) | framing; + return 0; +} + +/** + * \brief get current read mode + * \param params pointer to snd_rawmidi_params_t structure + * \return the current read mode (see enum) + */ +snd_rawmidi_read_mode_t snd_rawmidi_params_get_read_mode(const snd_rawmidi_params_t *params) +{ + unsigned int framing; + + assert(params); + framing = params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK; + if (framing == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) + return SND_RAWMIDI_READ_TSTAMP; + return SND_RAWMIDI_READ_STANDARD; +} + +/** + * \brief sets clock type for tstamp type framing + * \param rawmidi RawMidi handle + * \param params pointer to snd_rawmidi_params_t structure + * \param val one of the SND_RAWMIDI_CLOCK_* constants + * \return 0 on success, otherwise a negative error code. + * + * Notable error codes: + * -EINVAL - "val" is invalid + * -ENOTSUP - Kernel is too old to support framing. + * + */ +int snd_rawmidi_params_set_clock_type(const snd_rawmidi_t *rawmidi, snd_rawmidi_params_t *params, snd_rawmidi_clock_t val) +{ + assert(rawmidi && params); + if (val > SNDRV_RAWMIDI_MODE_CLOCK_MASK >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT) + return -EINVAL; + if (val != SNDRV_RAWMIDI_MODE_CLOCK_NONE && + (rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) || rawmidi->stream != SND_RAWMIDI_STREAM_INPUT)) + return -ENOTSUP; + params->mode = (params->mode & ~SNDRV_RAWMIDI_MODE_CLOCK_MASK) + (val << SNDRV_RAWMIDI_MODE_CLOCK_SHIFT); + return 0; +} + +/** + * \brief get current clock type (for tstamp type framing) + * \param params pointer to snd_rawmidi_params_t structure + * \return the current clock type (one of the SND_RAWMIDI_CLOCK_* constants) + */ +snd_rawmidi_clock_t snd_rawmidi_params_get_clock_type(const snd_rawmidi_params_t *params) +{ + assert(params); + return (params->mode & SNDRV_RAWMIDI_MODE_CLOCK_MASK) >> SNDRV_RAWMIDI_MODE_CLOCK_SHIFT; +} + + +/** * \brief set parameters about rawmidi stream * \param rawmidi RawMidi handle * \param params pointer to a snd_rawmidi_params_t structure to be filled @@ -822,6 +928,7 @@ rawmidi->buffer_size = params->buffer_size; rawmidi->avail_min = params->avail_min; rawmidi->no_active_sensing = params->no_active_sensing; + rawmidi->params_mode = rawmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 2) ? 0 : params->mode; return 0; } @@ -838,6 +945,7 @@ params->buffer_size = rawmidi->buffer_size; params->avail_min = rawmidi->avail_min; params->no_active_sensing = rawmidi->no_active_sensing; + params->mode = rawmidi->params_mode; return 0; } @@ -981,11 +1089,34 @@ * \param rawmidi RawMidi handle * \param buffer buffer to store the input MIDI bytes * \param size input buffer size in bytes + * \retval count of MIDI bytes otherwise a negative error code */ ssize_t snd_rawmidi_read(snd_rawmidi_t *rawmidi, void *buffer, size_t size) { assert(rawmidi); assert(rawmidi->stream == SND_RAWMIDI_STREAM_INPUT); + if ((rawmidi->params_mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) + size &= ~(sizeof(struct snd_rawmidi_framing_tstamp) - 1); assert(buffer || size == 0); return (rawmidi->ops->read)(rawmidi, buffer, size); } + +/** + * \brief read MIDI bytes from MIDI stream with timestamp + * \param rawmidi RawMidi handle + * \param[out] tstamp timestamp for the returned MIDI bytes + * \param buffer buffer to store the input MIDI bytes + * \param size input buffer size in bytes + * \retval count of MIDI bytes otherwise a negative error code + */ +ssize_t snd_rawmidi_tread(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void *buffer, size_t size) +{ + assert(rawmidi); + assert(rawmidi->stream == SND_RAWMIDI_STREAM_INPUT); + assert(buffer || size == 0); + if ((rawmidi->params_mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP) + return -EINVAL; + if (rawmidi->ops->tread == NULL) + return -ENOTSUP; + return (rawmidi->ops->tread)(rawmidi, tstamp, buffer, size); +} diff -Nru alsa-lib-1.2.2/src/rawmidi/rawmidi_hw.c alsa-lib-1.2.6.1/src/rawmidi/rawmidi_hw.c --- alsa-lib-1.2.2/src/rawmidi/rawmidi_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/rawmidi/rawmidi_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -42,9 +42,21 @@ int open; int fd; int card, device, subdevice; + unsigned char *buf; + size_t buf_size; /* total buffer size in bytes */ + size_t buf_fill; /* filled buffer size in bytes */ + size_t buf_pos; /* offset to frame in the read buffer (bytes) */ + size_t buf_fpos; /* offset to the frame data array (bytes 0-16) */ } snd_rawmidi_hw_t; #endif +static void buf_reset(snd_rawmidi_hw_t *hw) +{ + hw->buf_fill = 0; + hw->buf_pos = 0; + hw->buf_fpos = 0; +} + static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi) { snd_rawmidi_hw_t *hw = rmidi->private_data; @@ -57,6 +69,7 @@ err = -errno; SYSERR("close failed\n"); } + free(hw->buf); free(hw); return err; } @@ -95,11 +108,33 @@ static int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params) { snd_rawmidi_hw_t *hw = rmidi->private_data; + int tstamp; params->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_PARAMS, params) < 0) { SYSERR("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); return -errno; } + buf_reset(hw); + tstamp = (params->mode & SNDRV_RAWMIDI_MODE_FRAMING_MASK) == SNDRV_RAWMIDI_MODE_FRAMING_TSTAMP; + if (hw->buf && !tstamp) { + free(hw->buf); + hw->buf = NULL; + hw->buf_size = 0; + } else if (tstamp) { + size_t alloc_size; + void *buf; + + alloc_size = page_size(); + if (params->buffer_size > alloc_size) + alloc_size = params->buffer_size; + if (alloc_size != hw->buf_size) { + buf = realloc(hw->buf, alloc_size); + if (buf == NULL) + return -ENOMEM; + hw->buf = buf; + hw->buf_size = alloc_size; + } + } return 0; } @@ -122,6 +157,7 @@ SYSERR("SNDRV_RAWMIDI_IOCTL_DROP failed"); return -errno; } + buf_reset(hw); return 0; } @@ -156,6 +192,86 @@ return result; } +static ssize_t read_from_ts_buf(snd_rawmidi_hw_t *hw, struct timespec *tstamp, + void *buffer, size_t size) +{ + struct snd_rawmidi_framing_tstamp *f; + size_t flen; + ssize_t result = 0; + + f = (struct snd_rawmidi_framing_tstamp *)(hw->buf + hw->buf_pos); + while (hw->buf_fill >= sizeof(*f)) { + if (f->frame_type == 0) { + tstamp->tv_sec = f->tv_sec; + tstamp->tv_nsec = f->tv_nsec; + break; + } + hw->buf_pos += sizeof(*f); + hw->buf_fill -= sizeof(*f); + f++; + } + while (size > 0 && hw->buf_fill >= sizeof(*f)) { + /* skip other frames */ + if (f->frame_type != 0) + goto __next; + if (f->length == 0 || f->length > SNDRV_RAWMIDI_FRAMING_DATA_LENGTH) + return -EINVAL; + if (tstamp->tv_sec != (time_t)f->tv_sec || + tstamp->tv_nsec != f->tv_nsec) + break; + flen = f->length - hw->buf_fpos; + if (size < flen) { + /* partial copy */ + memcpy(buffer, f->data + hw->buf_fpos, size); + hw->buf_fpos += size; + result += size; + break; + } + memcpy(buffer, f->data + hw->buf_fpos, flen); + hw->buf_fpos = 0; + buffer += flen; + size -= flen; + result += flen; + __next: + hw->buf_pos += sizeof(*f); + hw->buf_fill -= sizeof(*f); + f++; + } + return result; +} + +static ssize_t snd_rawmidi_hw_tread(snd_rawmidi_t *rmidi, struct timespec *tstamp, + void *buffer, size_t size) +{ + snd_rawmidi_hw_t *hw = rmidi->private_data; + ssize_t result = 0, ret; + + /* no timestamp */ + tstamp->tv_sec = tstamp->tv_nsec = 0; + + /* copy buffered frames */ + if (hw->buf_fill > 0) { + result = read_from_ts_buf(hw, tstamp, buffer, size); + if (result < 0 || size == (size_t)result || + hw->buf_fill >= sizeof(struct snd_rawmidi_framing_tstamp)) + return result; + buffer += result; + size -= result; + } + + buf_reset(hw); + ret = read(hw->fd, hw->buf, hw->buf_size); + if (ret < 0) + return result > 0 ? result : -errno; + if (ret < (ssize_t)sizeof(struct snd_rawmidi_framing_tstamp)) + return result; + hw->buf_fill = ret; + ret = read_from_ts_buf(hw, tstamp, buffer, size); + if (ret < 0 && result > 0) + return result; + return ret + result; +} + static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { .close = snd_rawmidi_hw_close, .nonblock = snd_rawmidi_hw_nonblock, @@ -166,6 +282,7 @@ .drain = snd_rawmidi_hw_drain, .write = snd_rawmidi_hw_write, .read = snd_rawmidi_hw_read, + .tread = snd_rawmidi_hw_tread }; @@ -248,6 +365,11 @@ snd_ctl_close(ctl); return -SND_ERROR_INCOMPATIBLE_VERSION; } + if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= ver) { + /* inform the protocol version we're supporting */ + unsigned int user_ver = SNDRV_RAWMIDI_VERSION; + ioctl(fd, SNDRV_RAWMIDI_IOCTL_USER_PVERSION, &user_ver); + } if (subdevice >= 0) { memset(&info, 0, sizeof(info)); info.stream = outputp ? SNDRV_RAWMIDI_STREAM_OUTPUT : SNDRV_RAWMIDI_STREAM_INPUT; @@ -285,6 +407,7 @@ rmidi->poll_fd = fd; rmidi->ops = &snd_rawmidi_hw_ops; rmidi->private_data = hw; + rmidi->version = ver; hw->open++; *inputp = rmidi; } @@ -300,6 +423,7 @@ rmidi->poll_fd = fd; rmidi->ops = &snd_rawmidi_hw_ops; rmidi->private_data = hw; + rmidi->version = ver; hw->open++; *outputp = rmidi; } @@ -321,7 +445,6 @@ { snd_config_iterator_t i, next; long card = -1, device = 0, subdevice = -1; - const char *str; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -331,15 +454,10 @@ if (snd_rawmidi_conf_generic_id(id)) continue; if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) - return -EINVAL; - card = snd_card_get_index(str); - if (card < 0) - return card; - } + err = snd_config_get_card(n); + if (err < 0) + return err; + card = err; continue; } if (strcmp(id, "device") == 0) { diff -Nru alsa-lib-1.2.2/src/rawmidi/rawmidi_local.h alsa-lib-1.2.6.1/src/rawmidi/rawmidi_local.h --- alsa-lib-1.2.2/src/rawmidi/rawmidi_local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/rawmidi/rawmidi_local.h 2021-12-09 13:17:59.000000000 +0000 @@ -34,6 +34,7 @@ int (*drain)(snd_rawmidi_t *rawmidi); ssize_t (*write)(snd_rawmidi_t *rawmidi, const void *buffer, size_t size); ssize_t (*read)(snd_rawmidi_t *rawmidi, void *buffer, size_t size); + ssize_t (*tread)(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void *buffer, size_t size); } snd_rawmidi_ops_t; struct _snd_rawmidi { @@ -42,12 +43,14 @@ snd_rawmidi_type_t type; snd_rawmidi_stream_t stream; int mode; + int version; int poll_fd; const snd_rawmidi_ops_t *ops; void *private_data; size_t buffer_size; size_t avail_min; unsigned int no_active_sensing: 1; + int params_mode; }; int snd_rawmidi_hw_open(snd_rawmidi_t **input, snd_rawmidi_t **output, diff -Nru alsa-lib-1.2.2/src/rawmidi/rawmidi_virt.c alsa-lib-1.2.6.1/src/rawmidi/rawmidi_virt.c --- alsa-lib-1.2.2/src/rawmidi/rawmidi_virt.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/rawmidi/rawmidi_virt.c 2021-12-09 13:17:59.000000000 +0000 @@ -315,7 +315,7 @@ int merge, int mode) { int err; - snd_rawmidi_t *rmidi; + snd_rawmidi_t *rmidi = NULL; snd_rawmidi_virtual_t *virt = NULL; struct pollfd pfd; @@ -392,6 +392,7 @@ free(*inputp); if (outputp) free(*outputp); + free(rmidi); return err; } diff -Nru alsa-lib-1.2.2/src/seq/Makefile.in alsa-lib-1.2.6.1/src/seq/Makefile.in --- alsa-lib-1.2.2/src/seq/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/seq/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -304,6 +304,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/seq/seq.c alsa-lib-1.2.6.1/src/seq/seq.c --- alsa-lib-1.2.2/src/seq/seq.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/seq/seq.c 2021-12-09 13:17:59.000000000 +0000 @@ -978,9 +978,15 @@ int err; assert(seqp && name); - err = snd_config_update_ref(&top); - if (err < 0) - return err; + if (_snd_is_ucm_device(name)) { + name = uc_mgr_alibcfg_by_device(&top, name); + if (name == NULL) + return -ENODEV; + } else { + err = snd_config_update_ref(&top); + if (err < 0) + return err; + } err = snd_seq_open_noupdate(seqp, top, name, streams, mode, 0); snd_config_unref(top); return err; diff -Nru alsa-lib-1.2.2/src/seq/seqmid.c alsa-lib-1.2.6.1/src/seq/seqmid.c --- alsa-lib-1.2.2/src/seq/seqmid.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/seq/seqmid.c 2021-12-09 13:17:59.000000000 +0000 @@ -388,25 +388,43 @@ */ int snd_seq_parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, const char *arg) { - char *p; - int client, port; + char *p, *buf; + const char *s; + char c; + long client, port = 0; int len; assert(addr && arg); - if ((p = strpbrk(arg, ":.")) != NULL) { - if ((port = atoi(p + 1)) < 0) - return -EINVAL; - len = (int)(p - arg); /* length of client name */ + c = *arg; + if (c == '"' || c == '\'') { + s = ++arg; + while (*s && *s != c) s++; + len = s - arg; + if (*s) + s++; + if (*s) { + if (*s != '.' && *s != ':') + return -EINVAL; + if ((port = atoi(s + 1)) < 0) + return -EINVAL; + } } else { - port = 0; - len = strlen(arg); + if ((p = strpbrk(arg, ":.")) != NULL) { + if ((port = atoi(p + 1)) < 0) + return -EINVAL; + len = (int)(p - arg); /* length of client name */ + } else { + len = strlen(arg); + } } + if (len == 0) + return -EINVAL; + buf = alloca(len + 1); + strncpy(buf, arg, len); + buf[len] = '\0'; addr->port = port; - if (isdigit(*arg)) { - client = atoi(arg); - if (client < 0) - return -EINVAL; + if (safe_strtol(buf, &client) == 0) { addr->client = client; } else { /* convert from the name */ diff -Nru alsa-lib-1.2.2/src/timer/Makefile.in alsa-lib-1.2.6.1/src/timer/Makefile.in --- alsa-lib-1.2.2/src/timer/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/timer/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -299,6 +299,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/timer/timer.c alsa-lib-1.2.6.1/src/timer/timer.c --- alsa-lib-1.2.2/src/timer/timer.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/timer/timer.c 2021-12-09 13:17:59.000000000 +0000 @@ -205,9 +205,15 @@ int err; assert(timer && name); - err = snd_config_update_ref(&top); - if (err < 0) - return err; + if (_snd_is_ucm_device(name)) { + name = uc_mgr_alibcfg_by_device(&top, name); + if (name == NULL) + return -ENODEV; + } else { + err = snd_config_update_ref(&top); + if (err < 0) + return err; + } err = snd_timer_open_noupdate(timer, top, name, mode); snd_config_unref(top); return err; diff -Nru alsa-lib-1.2.2/src/timer/timer_hw.c alsa-lib-1.2.6.1/src/timer/timer_hw.c --- alsa-lib-1.2.2/src/timer/timer_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/timer/timer_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -288,7 +288,6 @@ snd_config_iterator_t i, next; long dev_class = SND_TIMER_CLASS_GLOBAL, dev_sclass = SND_TIMER_SCLASS_NONE; long card = 0, device = 0, subdevice = 0; - const char *str; int err; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -310,15 +309,10 @@ continue; } if (strcmp(id, "card") == 0) { - err = snd_config_get_integer(n, &card); - if (err < 0) { - err = snd_config_get_string(n, &str); - if (err < 0) - return -EINVAL; - card = snd_card_get_index(str); - if (card < 0) - return card; - } + err = snd_config_get_card(n); + if (err < 0) + return err; + card = err; continue; } if (strcmp(id, "device") == 0) { @@ -336,8 +330,6 @@ SNDERR("Unexpected field %s", id); return -EINVAL; } - if (card < 0) - return -EINVAL; return snd_timer_hw_open(timer, name, dev_class, dev_sclass, card, device, subdevice, mode); } SND_DLSYM_BUILD_VERSION(_snd_timer_hw_open, SND_TIMER_DLSYM_VERSION); diff -Nru alsa-lib-1.2.2/src/timer/timer_query_hw.c alsa-lib-1.2.6.1/src/timer/timer_query_hw.c --- alsa-lib-1.2.2/src/timer/timer_query_hw.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/timer/timer_query_hw.c 2021-12-09 13:17:59.000000000 +0000 @@ -104,7 +104,7 @@ close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } - tmr = (snd_timer_query_t *) calloc(1, sizeof(snd_timer_t)); + tmr = (snd_timer_query_t *) calloc(1, sizeof(snd_timer_query_t)); if (tmr == NULL) { close(fd); return -ENOMEM; diff -Nru alsa-lib-1.2.2/src/topology/Makefile.in alsa-lib-1.2.6.1/src/topology/Makefile.in --- alsa-lib-1.2.2/src/topology/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -336,6 +336,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/src/topology/builder.c alsa-lib-1.2.6.1/src/topology/builder.c --- alsa-lib-1.2.2/src/topology/builder.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/builder.c 2021-12-09 13:17:59.000000000 +0000 @@ -55,7 +55,7 @@ " offset 0x%zx is %s by %ld bytes", tplg->next_hdr_pos, tplg->bin_pos, tplg->bin_pos > tplg->next_hdr_pos ? "ahead" : "behind", - labs(tplg->bin_pos - tplg->next_hdr_pos)); + tplg->bin_pos - tplg->next_hdr_pos); return -EINVAL; } diff -Nru alsa-lib-1.2.2/src/topology/channel.c alsa-lib-1.2.6.1/src/topology/channel.c --- alsa-lib-1.2.2/src/topology/channel.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/channel.c 2021-12-09 13:17:59.000000000 +0000 @@ -138,7 +138,8 @@ int tplg_save_channels(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct snd_soc_tplg_channel *channel, - unsigned int count, char **dst, const char *pfx) + unsigned int count, struct tplg_buf *dst, + const char *pfx) { struct snd_soc_tplg_channel *c; const char *s; diff -Nru alsa-lib-1.2.2/src/topology/ctl.c alsa-lib-1.2.6.1/src/topology/ctl.c --- alsa-lib-1.2.2/src/topology/ctl.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/ctl.c 2021-12-09 13:17:59.000000000 +0000 @@ -105,8 +105,8 @@ /* Save Access */ static int tplg_save_access(snd_tplg_t *tplg ATTRIBUTE_UNUSED, - struct snd_soc_tplg_ctl_hdr *hdr, char **dst, - const char *pfx) + struct snd_soc_tplg_ctl_hdr *hdr, + struct tplg_buf *dst, const char *pfx) { const char *last; unsigned int j, count, access, cval; @@ -399,7 +399,7 @@ /* save TLV data */ int tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_ctl_tlv *tlv = elem->tlv; struct snd_soc_tplg_tlv_dbscale *scale; @@ -557,7 +557,7 @@ /* save control bytes */ int tplg_save_control_bytes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_bytes_control *be = elem->bytes_ext; char pfx2[16]; @@ -697,7 +697,7 @@ /* save control eunm */ int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_enum_control *ec = elem->enum_ctrl; char pfx2[16]; @@ -858,8 +858,8 @@ } int tplg_save_control_mixer(snd_tplg_t *tplg ATTRIBUTE_UNUSED, - struct tplg_elem *elem, char **dst, - const char *pfx) + struct tplg_elem *elem, + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_mixer_control *mc = elem->mixer_ctrl; char pfx2[16]; @@ -879,9 +879,9 @@ if (err >= 0 && mc->max > 0) err = tplg_save_printf(dst, pfx, "\tmax %u\n", mc->max); if (err >= 0 && mc->invert > 0) - err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max); + err = tplg_save_printf(dst, pfx, "\tinvert 1\n"); if (err >= 0 && mc->invert > 0) - err = tplg_save_printf(dst, pfx, "\tinvert 1\n", mc->max); + err = tplg_save_printf(dst, pfx, "\tinvert 1\n"); if (err >= 0) err = tplg_save_ops(tplg, &mc->hdr, dst, pfx2); if (err >= 0) @@ -1088,11 +1088,19 @@ } if (enum_ctl->texts != NULL) { + struct tplg_elem *texts = tplg_elem_new_common(tplg, NULL, + enum_ctl->hdr.name, SND_TPLG_TYPE_TEXT); + + texts->texts->num_items = num_items; for (i = 0; i < num_items; i++) { - if (enum_ctl->texts[i] != NULL) - snd_strlcpy(ec->texts[i], enum_ctl->texts[i], - SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (!enum_ctl->texts[i]) + continue; + snd_strlcpy(ec->texts[i], enum_ctl->texts[i], + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + snd_strlcpy(texts->texts->items[i], enum_ctl->texts[i], + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); } + tplg_ref_add(elem, SND_TPLG_TYPE_TEXT, enum_ctl->hdr.name); } if (enum_ctl->values != NULL) { @@ -1327,23 +1335,10 @@ struct list_head *heap, struct snd_tplg_enum_template *et, size_t pos, - void *bin, size_t size) + struct snd_soc_tplg_enum_control *ec) { - struct snd_soc_tplg_enum_control *ec = bin; - struct snd_tplg_channel_map_template cmt; int i; - if (size < sizeof(*ec)) { - SNDERR("enum: small size %d", size); - return -EINVAL; - } - - tplg_log(tplg, 'D', pos, "enum: size %d private size %d", - ec->size, ec->priv.size); - if (size != ec->size + ec->priv.size) { - SNDERR("enum: unexpected element size %d", size); - return -EINVAL; - } if (ec->num_channels > SND_TPLG_MAX_CHAN || ec->num_channels > SND_SOC_TPLG_MAX_CHAN) { SNDERR("enum: unexpected channel count %d", ec->num_channels); @@ -1368,18 +1363,17 @@ et->texts = tplg_calloc(heap, sizeof(char *) * ec->items); if (!et->texts) return -ENOMEM; - for (i = 0; ec->items; i++) { - unsigned int j = i * sizeof(int) * ENUM_VAL_SIZE; + for (i = 0; (unsigned int)i < ec->items; i++) et->texts[i] = ec->texts[i]; - et->values[i] = (int *)&ec->values[j]; - } } - et->map = &cmt; - memset(&cmt, 0, sizeof(cmt)); - cmt.num_channels = ec->num_channels; - for (i = 0; i < cmt.num_channels; i++) { - struct snd_tplg_channel_elem *channel = &cmt.channel[i]; + et->map = tplg_calloc(heap, sizeof(struct snd_tplg_channel_map_template)); + if (!et->map) + return -ENOMEM; + et->map->num_channels = ec->num_channels; + for (i = 0; i < et->map->num_channels; i++) { + struct snd_tplg_channel_elem *channel = &et->map->channel[i]; + tplg_log(tplg, 'D', pos + ((void *)&ec->channel[i] - (void *)ec), "enum: channel size %d", ec->channel[i].size); channel->reg = ec->channel[i].reg; @@ -1421,7 +1415,10 @@ return -EINVAL; } - err = tplg_decode_control_enum1(tplg, &heap, &et, pos, bin, size); + tplg_log(tplg, 'D', pos, "enum: size %d private size %d", + ec->size, ec->priv.size); + + err = tplg_decode_control_enum1(tplg, &heap, &et, pos, ec); if (err >= 0) { t.enum_ctl = &et; err = snd_tplg_add_object(tplg, &t); diff -Nru alsa-lib-1.2.2/src/topology/dapm.c alsa-lib-1.2.6.1/src/topology/dapm.c --- alsa-lib-1.2.2/src/topology/dapm.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/dapm.c 2021-12-09 13:17:59.000000000 +0000 @@ -43,6 +43,7 @@ {"effect", SND_SOC_TPLG_DAPM_EFFECT}, {"siggen", SND_SOC_TPLG_DAPM_SIGGEN}, {"src", SND_SOC_TPLG_DAPM_SRC}, + {"asrc", SND_SOC_TPLG_DAPM_ASRC}, {"encoder", SND_SOC_TPLG_DAPM_ENCODER}, {"decoder", SND_SOC_TPLG_DAPM_DECODER}, }; @@ -415,7 +416,8 @@ } /* save DAPM graph */ -int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, char **dst, const char *pfx) +int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_dapm_graph_elem *route; struct list_head *pos; @@ -481,7 +483,7 @@ } if (first) { first = 0; - err = tplg_save_printf(dst, pfx, "\t\tlines [\n", elem->index); + err = tplg_save_printf(dst, pfx, "\t\tlines [\n"); if (err < 0) return err; } @@ -594,7 +596,8 @@ } if (strcmp(id, "invert") == 0) { - if (tplg_get_integer(n, &ival, 0)) + ival = snd_config_get_bool(n); + if (ival < 0) return -EINVAL; widget->invert = ival; @@ -667,7 +670,7 @@ /* save DAPM widget */ int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_dapm_widget *widget = elem->widget; const char *s; @@ -833,6 +836,7 @@ default: SNDERR("widget %s: invalid type %d for ctl %d", wt->name, ct->type, i); + ret = -EINVAL; break; } @@ -970,8 +974,7 @@ err = -EINVAL; goto retval; } - err = tplg_decode_control_enum1(tplg, &heap, et, pos, - bin, size2); + err = tplg_decode_control_enum1(tplg, &heap, et, pos, ec); break; case SND_SOC_TPLG_TYPE_BYTES: bt = tplg_calloc(&heap, sizeof(*bt)); diff -Nru alsa-lib-1.2.2/src/topology/data.c alsa-lib-1.2.6.1/src/topology/data.c --- alsa-lib-1.2.2/src/topology/data.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/data.c 2021-12-09 13:17:59.000000000 +0000 @@ -121,7 +121,8 @@ /* save references */ int tplg_save_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, unsigned int type, - const char *id, char **dst, const char *pfx) + const char *id, struct tplg_buf *dst, + const char *pfx) { struct tplg_ref *ref, *last; struct list_head *pos; @@ -413,12 +414,8 @@ void *p = &val; errno = 0; - val = strtol(str, NULL, 16); - - if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) - || (errno != 0 && val == 0)) { + if (safe_strtol_base(str, &val, 16) < 0) return -EINVAL; - } switch (width) { case 1: @@ -858,11 +855,11 @@ goto err; } - if ((type == SND_SOC_TPLG_TUPLE_TYPE_WORD - && tuple_val > UINT_MAX) - || (type == SND_SOC_TPLG_TUPLE_TYPE_SHORT - && tuple_val > USHRT_MAX) - || (type == SND_SOC_TPLG_TUPLE_TYPE_BYTE + if (/* (type == SND_SOC_TPLG_TUPLE_TYPE_WORD + && tuple_val > UINT_MAX) || */ + (type == SND_SOC_TPLG_TUPLE_TYPE_SHORT + && tuple_val > USHRT_MAX) || + (type == SND_SOC_TPLG_TUPLE_TYPE_BYTE && tuple_val > UCHAR_MAX)) { SNDERR("tuple %s: invalid value", id); goto err; @@ -890,7 +887,7 @@ /* save tuple set */ static int tplg_save_tuple_set(struct tplg_vendor_tuples *tuples, unsigned int set_index, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct tplg_tuple_set *set; struct tplg_tuple *tuple; @@ -929,6 +926,8 @@ err = tplg_save_printf(dst, pfx, "\t'%s' ", tuple->token); } + if (err < 0) + return err; switch (set->type) { case SND_SOC_TPLG_TUPLE_TYPE_UUID: err = tplg_save_printf(dst, NULL, "'" UUID_FORMAT "'\n", @@ -1014,7 +1013,7 @@ /* save tuple sets */ int tplg_save_tuple_sets(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct tplg_vendor_tuples *tuples = elem->tuples; unsigned int i; @@ -1085,7 +1084,7 @@ /* save vendor tokens */ int tplg_save_tokens(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct tplg_vendor_tokens *tokens = elem->tokens; unsigned int i; @@ -1156,7 +1155,7 @@ /* save vendor tuples */ int tplg_save_tuples(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { char pfx2[16]; int err; @@ -1242,7 +1241,7 @@ /* save manifest data */ int tplg_save_manifest_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, - struct tplg_elem *elem, char **dst, + struct tplg_elem *elem, struct tplg_buf *dst, const char *pfx) { struct list_head *pos; @@ -1275,9 +1274,9 @@ elem->id, index, ref->id); } else { err = tplg_save_printf(dst, pfx, "\t'%s'\n", ref->id); - if (err < 0) - return err; } + if (err < 0) + return err; index++; } if (count > 1) { @@ -1420,7 +1419,7 @@ /* save data element */ int tplg_save_data(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_private *priv = elem->data; struct list_head *pos; @@ -1611,7 +1610,7 @@ if (!elem) return -ENOMEM; - tplg_log(tplg, 'D', pos, "manifest: private size %d", size); + tplg_log(tplg, 'D', pos, "manifest: private size %zd", size); return tplg_add_data(tplg, elem, bin, size); } @@ -1671,7 +1670,7 @@ va = bin; if (size < sizeof(*va) || size < va->size) { - tplg_log(tplg, 'A', pos, "tuple set verify: wrong size %d", size); + tplg_log(tplg, 'A', pos, "tuple set verify: wrong size %zd", size); return -EINVAL; } @@ -1718,7 +1717,7 @@ va = bin; if (size < sizeof(*va) || size < va->size) { - SNDERR("tuples: wrong size %d", size); + SNDERR("tuples: wrong size %zd", size); return -EINVAL; } @@ -1805,14 +1804,14 @@ int err; if (size < sizeof(*va)) { - tplg_log(tplg, 'A', pos, "tuples: small size %d", size); + tplg_log(tplg, 'A', pos, "tuples: small size %zd", size); return -EINVAL; } next: va = bin; if (size < sizeof(*va)) { - tplg_log(tplg, 'A', pos, "tuples: unexpected vendor arry size %d", size); + tplg_log(tplg, 'A', pos, "tuples: unexpected vendor arry size %zd", size); return -EINVAL; } @@ -1841,14 +1840,14 @@ int err; if (size < sizeof(*va)) { - SNDERR("tuples: small size %d", size); + SNDERR("tuples: small size %zd", size); return -EINVAL; } next: va = bin; if (size < sizeof(*va)) { - SNDERR("tuples: unexpected vendor arry size %d", size); + SNDERR("tuples: unexpected vendor arry size %zd", size); return -EINVAL; } @@ -1893,7 +1892,7 @@ next: tp = bin; if (off + size < tp->size) { - SNDERR("data: unexpected element size %d", size); + SNDERR("data: unexpected element size %zd", size); return -EINVAL; } diff -Nru alsa-lib-1.2.2/src/topology/ops.c alsa-lib-1.2.6.1/src/topology/ops.c --- alsa-lib-1.2.2/src/topology/ops.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/ops.c 2021-12-09 13:17:59.000000000 +0000 @@ -34,15 +34,22 @@ static int lookup_ops(const char *c) { - unsigned int i; + int i; + long ret; - for (i = 0; i < ARRAY_SIZE(control_map); i++) { + for (i = 0; i < (int)ARRAY_SIZE(control_map); i++) { if (strcmp(control_map[i].name, c) == 0) return control_map[i].id; } /* cant find string name in our table so we use its ID number */ - return strtol(c, NULL, 0); + i = safe_strtol(c, &ret); + if (i < 0) { + SNDERR("wrong kcontrol ops value string '%s'", c); + return i; + } + + return ret; } const char *tplg_ops_name(int type) @@ -105,8 +112,8 @@ /* save control operations */ int tplg_save_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, - struct snd_soc_tplg_ctl_hdr *hdr, char **dst, - const char *pfx) + struct snd_soc_tplg_ctl_hdr *hdr, + struct tplg_buf *dst, const char *pfx) { const char *s; int err; @@ -191,7 +198,7 @@ /* save external control operations */ int tplg_save_ext_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct snd_soc_tplg_bytes_control *be, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { const char *s; int err; diff -Nru alsa-lib-1.2.2/src/topology/parser.c alsa-lib-1.2.6.1/src/topology/parser.c --- alsa-lib-1.2.2/src/topology/parser.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/parser.c 2021-12-09 13:17:59.000000000 +0000 @@ -22,6 +22,25 @@ #include "tplg_local.h" /* + * Safe strtol call + */ +int safe_strtol_base(const char *str, long *val, int base) +{ + char *end; + long v; + if (!*str) + return -EINVAL; + errno = 0; + v = strtol(str, &end, base); + if (errno) + return -errno; + if (*end) + return -EINVAL; + *val = v; + return 0; +} + +/* * Get integer value */ int tplg_get_integer(snd_config_t *n, int *val, int base) @@ -35,24 +54,23 @@ err = snd_config_get_integer(n, &lval); if (err < 0) return err; - if (lval < INT_MIN || lval > INT_MAX) - return -ERANGE; - *val = lval; - return err; + goto __retval; case SND_CONFIG_TYPE_STRING: err = snd_config_get_string(n, &str); if (err < 0) return err; - errno = 0; - *val = strtol(str, NULL, base); - if (errno == ERANGE) - return -ERANGE; - if (errno && *val == 0) - return -EINVAL; - return 0; + err = safe_strtol_base(str, &lval, base); + if (err < 0) + return err; + goto __retval; default: return -EINVAL; } + __retval: + if (lval < INT_MIN || lval > INT_MAX) + return -ERANGE; + *val = lval; + return 0; } /* @@ -427,10 +445,8 @@ static bool is_little_endian(void) { -#ifdef __BYTE_ORDER - #if __BYTE_ORDER == __LITTLE_ENDIAN - return true; - #endif +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && __SIZEOF_INT__ == 4 + return true; #endif return false; } diff -Nru alsa-lib-1.2.2/src/topology/pcm.c alsa-lib-1.2.6.1/src/topology/pcm.c --- alsa-lib-1.2.2/src/topology/pcm.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/pcm.c 2021-12-09 13:17:59.000000000 +0000 @@ -376,19 +376,19 @@ return 0; } -static int parse_unsigned(snd_config_t *n, unsigned int *dst) +static int parse_unsigned(snd_config_t *n, void *dst) { int ival; if (tplg_get_integer(n, &ival, 0) < 0) return -EINVAL; - *dst = ival; + unaligned_put32(dst, ival); #if TPLG_DEBUG { const char *id; if (snd_config_get_id(n, &id) >= 0) - tplg_dbg("\t\t%s: %d", id, *dst); + tplg_dbg("\t\t%s: %d", id, ival); } #endif return 0; @@ -538,7 +538,7 @@ /* save stream caps */ int tplg_save_stream_caps(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_stream_caps *sc = elem->stream_caps; const char *s; @@ -549,7 +549,7 @@ if (err >= 0 && sc->formats) { err = tplg_save_printf(dst, pfx, "\tformats '"); first = 1; - for (i = 0; err >= 0 && i < SND_PCM_FORMAT_LAST; i++) { + for (i = 0; err >= 0 && i <= SND_PCM_FORMAT_LAST; i++) { if (sc->formats & (1ULL << i)) { s = snd_pcm_format_name(i); err = tplg_save_printf(dst, NULL, "%s%s", @@ -563,7 +563,7 @@ if (err >= 0 && sc->rates) { err = tplg_save_printf(dst, pfx, "\trates '"); first = 1; - for (i = 0; err >= 0 && i < SND_PCM_RATE_LAST; i++) { + for (i = 0; err >= 0 && i <= SND_PCM_RATE_LAST; i++) { if (sc->rates & (1ULL << i)) { s = get_rate_name(i); err = tplg_save_printf(dst, NULL, "%s%s", @@ -604,6 +604,9 @@ if (err >= 0 && sc->buffer_size_max) err = tplg_save_printf(dst, pfx, "\tbuffer_size_max %u\n", sc->buffer_size_max); + if (err >= 0 && sc->sig_bits) + err = tplg_save_printf(dst, pfx, "\tsig_bits %u\n", + sc->sig_bits); if (err >= 0) err = tplg_save_printf(dst, pfx, "}\n"); return err; @@ -618,7 +621,7 @@ struct tplg_elem *elem = private; struct snd_soc_tplg_pcm *pcm; struct snd_soc_tplg_dai *dai; - unsigned int *playback, *capture; + void *playback, *capture; struct snd_soc_tplg_stream_caps *caps; const char *id, *value; int stream; @@ -648,10 +651,10 @@ if (strcmp(id, "playback") == 0) { stream = SND_SOC_TPLG_STREAM_PLAYBACK; - *playback = 1; + unaligned_put32(playback, 1); } else if (strcmp(id, "capture") == 0) { stream = SND_SOC_TPLG_STREAM_CAPTURE; - *capture = 1; + unaligned_put32(capture, 1); } else return -EINVAL; @@ -683,7 +686,7 @@ /* Save the caps and config of a pcm stream */ int tplg_save_streams(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { static const char *stream_ids[2] = { "playback", @@ -744,6 +747,7 @@ snd_config_iterator_t i, next; snd_config_t *n; const char *id; + unsigned int dai_id; snd_config_get_id(cfg, &id); tplg_dbg("\t\tFE DAI %s:", id); @@ -758,12 +762,13 @@ continue; if (strcmp(id, "id") == 0) { - if (tplg_get_unsigned(n, &pcm->dai_id, 0)) { + if (tplg_get_unsigned(n, &dai_id, 0)) { SNDERR("invalid fe dai ID"); return -EINVAL; } - tplg_dbg("\t\t\tindex: %d", pcm->dai_id); + unaligned_put32(&pcm->dai_id, dai_id); + tplg_dbg("\t\t\tindex: %d", dai_id); } } @@ -773,19 +778,21 @@ /* Save the caps and config of a pcm stream */ int tplg_save_fe_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_pcm *pcm = elem->pcm; int err = 0; - if (pcm->dai_id > 0) + if (strlen(pcm->dai_name)) + err = tplg_save_printf(dst, pfx, "dai.'%s'.id %u\n", pcm->dai_name, pcm->dai_id); + else if (pcm->dai_id > 0) err = tplg_save_printf(dst, pfx, "dai.0.id %u\n", pcm->dai_id); return err; } /* parse a flag bit of the given mask */ static int parse_flag(snd_config_t *n, unsigned int mask_in, - unsigned int *mask, unsigned int *flags) + void *mask, void *flags) { int ret; @@ -793,17 +800,17 @@ if (ret < 0) return ret; - *mask |= mask_in; + unaligned_put32(mask, unaligned_get32(mask) | mask_in); if (ret) - *flags |= mask_in; + unaligned_put32(flags, unaligned_get32(flags) | mask_in); else - *flags &= ~mask_in; + unaligned_put32(flags, unaligned_get32(flags) & (~mask_in)); return 0; } static int save_flags(unsigned int flags, unsigned int mask, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { static unsigned int flag_masks[3] = { SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, @@ -937,7 +944,7 @@ /* save PCM */ int tplg_save_pcm(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_pcm *pcm = elem->pcm; char pfx2[16]; @@ -1074,7 +1081,7 @@ /* save DAI */ int tplg_save_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_dai *dai = elem->dai; char pfx2[16]; @@ -1228,7 +1235,7 @@ /* save physical link */ int tplg_save_link(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_link_config *link = elem->link; char pfx2[16]; @@ -1308,7 +1315,7 @@ /* save CC */ int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_link_config *link = elem->link; char pfx2[16]; @@ -1360,10 +1367,6 @@ .name = "AC97", }, { - .type = SND_SOC_DAI_FORMAT_AC97, - .name = "AC97", - }, - { .type = SND_SOC_DAI_FORMAT_PDM, .name = "PDM", }, @@ -1404,6 +1407,7 @@ snd_config_t *n; const char *id, *val = NULL; int ret, ival; + bool provider_legacy; elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG); if (!elem) @@ -1444,8 +1448,15 @@ continue; } - if (strcmp(id, "bclk") == 0 || - strcmp(id, "bclk_master") == 0) { + provider_legacy = false; + if (strcmp(id, "bclk_master") == 0) { + SNDERR("deprecated option %s, please use 'bclk'\n", id); + provider_legacy = true; + } + + if (provider_legacy || + strcmp(id, "bclk") == 0) { + if (snd_config_get_string(n, &val) < 0) return -EINVAL; @@ -1455,11 +1466,19 @@ */ SNDERR("deprecated bclk value '%s'", val); - hw_cfg->bclk_master = SND_SOC_TPLG_BCLK_CS; + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; } else if (!strcmp(val, "codec_slave")) { - hw_cfg->bclk_master = SND_SOC_TPLG_BCLK_CS; + SNDERR("deprecated bclk value '%s', use 'codec_consumer'", val); + + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; + } else if (!strcmp(val, "codec_consumer")) { + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; } else if (!strcmp(val, "codec_master")) { - hw_cfg->bclk_master = SND_SOC_TPLG_BCLK_CM; + SNDERR("deprecated bclk value '%s', use 'codec_provider", val); + + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; + } else if (!strcmp(val, "codec_provider")) { + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; } continue; } @@ -1481,8 +1500,15 @@ continue; } - if (strcmp(id, "fsync") == 0 || - strcmp(id, "fsync_master") == 0) { + provider_legacy = false; + if (strcmp(id, "fsync_master") == 0) { + SNDERR("deprecated option %s, please use 'fsync'\n", id); + provider_legacy = true; + } + + if (provider_legacy || + strcmp(id, "fsync") == 0) { + if (snd_config_get_string(n, &val) < 0) return -EINVAL; @@ -1492,11 +1518,19 @@ */ SNDERR("deprecated fsync value '%s'", val); - hw_cfg->fsync_master = SND_SOC_TPLG_FSYNC_CS; + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; } else if (!strcmp(val, "codec_slave")) { - hw_cfg->fsync_master = SND_SOC_TPLG_FSYNC_CS; + SNDERR("deprecated fsync value '%s', use 'codec_consumer'", val); + + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; + } else if (!strcmp(val, "codec_consumer")) { + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; } else if (!strcmp(val, "codec_master")) { - hw_cfg->fsync_master = SND_SOC_TPLG_FSYNC_CM; + SNDERR("deprecated fsync value '%s', use 'codec_provider'", val); + + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; + } else if (!strcmp(val, "codec_provider")) { + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; } continue; } @@ -1604,7 +1638,7 @@ /* save hw config */ int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct snd_soc_tplg_hw_config *hc = elem->hw_cfg; int err; @@ -1616,19 +1650,19 @@ if (err >= 0 && hc->fmt) err = tplg_save_printf(dst, pfx, "\tformat '%s'\n", get_audio_hw_format_name(hc->fmt)); - if (err >= 0 && hc->bclk_master) + if (err >= 0 && hc->bclk_provider) err = tplg_save_printf(dst, pfx, "\tbclk '%s'\n", - hc->bclk_master == SND_SOC_TPLG_BCLK_CS ? - "codec_slave" : "codec_master"); + hc->bclk_provider == SND_SOC_TPLG_BCLK_CC ? + "codec_consumer" : "codec_provider"); if (err >= 0 && hc->bclk_rate) err = tplg_save_printf(dst, pfx, "\tbclk_freq %u\n", hc->bclk_rate); if (err >= 0 && hc->invert_bclk) err = tplg_save_printf(dst, pfx, "\tbclk_invert 1\n"); - if (err >= 0 && hc->fsync_master) - err = tplg_save_printf(dst, pfx, "\tfsync_master '%s'\n", - hc->fsync_master == SND_SOC_TPLG_FSYNC_CS ? - "codec_slave" : "codec_master"); + if (err >= 0 && hc->fsync_provider) + err = tplg_save_printf(dst, pfx, "\tfsync_provider '%s'\n", + hc->fsync_provider == SND_SOC_TPLG_FSYNC_CC ? + "codec_consumer" : "codec_provider"); if (err >= 0 && hc->fsync_rate) err = tplg_save_printf(dst, pfx, "\tfsync_freq %u\n", hc->fsync_rate); @@ -1784,8 +1818,8 @@ cfg->clock_gated = tpl->clock_gated; cfg->invert_bclk = tpl->invert_bclk; cfg->invert_fsync = tpl->invert_fsync; - cfg->bclk_master = tpl->bclk_master; - cfg->fsync_master = tpl->fsync_master; + cfg->bclk_provider = tpl->bclk_provider; + cfg->fsync_provider = tpl->fsync_provider; cfg->mclk_direction = tpl->mclk_direction; cfg->reserved = tpl->reserved; cfg->mclk_rate = tpl->mclk_rate; @@ -1982,7 +2016,7 @@ pt->playback = pcm->playback; pt->capture = pcm->capture; pt->compress = pcm->compress; - tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress", + tplg_log(tplg, 'D', pos, "pcm: playback %d capture %d compress %d", pt->playback, pt->capture, pt->compress); pt->num_streams = pcm->num_streams; pt->flag_mask = pcm->flag_mask; @@ -2053,20 +2087,22 @@ } /* decode dai from the binary input */ -int tplg_decode_dai(snd_tplg_t *tplg, - size_t pos, - struct snd_soc_tplg_hdr *hdr, - void *bin, size_t size) +int tplg_decode_dai(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + size_t pos ATTRIBUTE_UNUSED, + struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED, + void *bin ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED) { SNDERR("not implemented"); return -ENXIO; } /* decode cc from the binary input */ -int tplg_decode_cc(snd_tplg_t *tplg, - size_t pos, - struct snd_soc_tplg_hdr *hdr, - void *bin, size_t size) +int tplg_decode_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, + size_t pos ATTRIBUTE_UNUSED, + struct snd_soc_tplg_hdr *hdr ATTRIBUTE_UNUSED, + void *bin ATTRIBUTE_UNUSED, + size_t size ATTRIBUTE_UNUSED) { SNDERR("not implemented"); return -ENXIO; @@ -2165,8 +2201,8 @@ hw->clock_gated = link->hw_config[i].clock_gated; hw->invert_bclk = link->hw_config[i].invert_bclk; hw->invert_fsync = link->hw_config[i].invert_fsync; - hw->bclk_master = link->hw_config[i].bclk_master; - hw->fsync_master = link->hw_config[i].fsync_master; + hw->bclk_provider = link->hw_config[i].bclk_provider; + hw->fsync_provider = link->hw_config[i].fsync_provider; hw->mclk_direction = link->hw_config[i].mclk_direction; hw->mclk_rate = link->hw_config[i].mclk_rate; hw->bclk_rate = link->hw_config[i].bclk_rate; diff -Nru alsa-lib-1.2.2/src/topology/save.c alsa-lib-1.2.6.1/src/topology/save.c --- alsa-lib-1.2.2/src/topology/save.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/save.c 2021-12-09 13:17:59.000000000 +0000 @@ -19,45 +19,86 @@ #include "tplg_local.h" #define SAVE_ALLOC_SHIFT (13) /* 8192 bytes */ +#define PRINT_ALLOC_SHIFT (10) /* 1024 bytes */ +#define PRINT_BUF_SIZE_MAX (1024 * 1024) +#define NEXT_CHUNK(val, shift) ((((val) >> (shift)) + 1) << (shift)) -int tplg_save_printf(char **dst, const char *pfx, const char *fmt, ...) +void tplg_buf_init(struct tplg_buf *buf) +{ + buf->dst = NULL; + buf->dst_len = 0; + buf->printf_buf = NULL; + buf->printf_buf_size = 0; +} + +void tplg_buf_free(struct tplg_buf *buf) +{ + free(buf->dst); + free(buf->printf_buf); +} + +char *tplg_buf_detach(struct tplg_buf *buf) +{ + char *ret = buf->dst; + free(buf->printf_buf); + return ret; +} + +int tplg_save_printf(struct tplg_buf *dst, const char *pfx, const char *fmt, ...) { va_list va; - char buf[1024], *s; + char *s; size_t n, l, t, pl; + int ret = 0; if (pfx == NULL) pfx = ""; va_start(va, fmt); - n = vsnprintf(buf, sizeof(buf), fmt, va); + n = vsnprintf(dst->printf_buf, dst->printf_buf_size, fmt, va); va_end(va); - if (n >= sizeof(buf)) - return -EOVERFLOW; + if (n >= PRINT_BUF_SIZE_MAX) { + ret = -EOVERFLOW; + goto end; + } + + if (n >= dst->printf_buf_size) { + t = NEXT_CHUNK(n + 1, PRINT_ALLOC_SHIFT); + s = realloc(dst->printf_buf, t); + if (!s) { + ret = -ENOMEM; + goto end; + } + dst->printf_buf = s; + dst->printf_buf_size = t; + va_start(va, fmt); + n = vsnprintf(dst->printf_buf, n + 1, fmt, va); + va_end(va); + } pl = strlen(pfx); - l = *dst ? strlen(*dst) : 0; + l = dst->dst_len; t = l + pl + n + 1; /* allocate chunks */ - if (*dst == NULL || + if (dst->dst == NULL || (l >> SAVE_ALLOC_SHIFT) != (t >> SAVE_ALLOC_SHIFT)) { - s = realloc(*dst, ((t >> SAVE_ALLOC_SHIFT) + 1) << - SAVE_ALLOC_SHIFT); + s = realloc(dst->dst, NEXT_CHUNK(t, SAVE_ALLOC_SHIFT)); if (s == NULL) { - free(*dst); - *dst = NULL; - return -ENOMEM; + ret = -ENOMEM; + goto end; } } else { - s = *dst; + s = dst->dst; } if (pl > 0) strcpy(s + l, pfx); - strcpy(s + l + pl, buf); - *dst = s; - return 0; + strcpy(s + l + pl, dst->printf_buf); + dst->dst = s; + dst->dst_len = t - 1; +end: + return ret; } int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value) @@ -92,6 +133,8 @@ if (llval < INT_MIN || llval > UINT_MAX) return snd_config_get_ascii(n, ret); lval = llval; + } else { + lval = 0; } err = tplg_nice_value_format(buf, sizeof(buf), (unsigned int)lval); if (err < 0) @@ -102,21 +145,6 @@ return 0; } -static int tplg_check_array_item(const char *id, int index) -{ - const char *p; - long int val; - - for (p = id; *p; p++) { - if (*p < '0' || *p > '9') - return 0; - } - - errno = 0; - val = strtol(id, NULL, 10); - return errno == 0 && val == index; -} - static int _compar(const void *a, const void *b) { const snd_config_t *c1 = *(snd_config_t **)a; @@ -134,7 +162,6 @@ int index, array, count; if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { - if (snd_config_copy(&dst, src) >= 0) return dst; return NULL; @@ -145,47 +172,36 @@ a = malloc(sizeof(dst) * count); if (a == NULL) return NULL; - index = array = 0; + array = snd_config_is_array(src); + index = 0; snd_config_for_each(i, next, src) { snd_config_t *s = snd_config_iterator_entry(i); - const char *id2; a[index++] = s; - if (array < 0) - continue; - if (snd_config_get_id(s, &id2)) { - free(a); - return NULL; - } - if (array >= 0 && tplg_check_array_item(id2, array)) - array++; - else - array = -1; } - if (array < 0) + if (array <= 0) qsort(a, count, sizeof(a[0]), _compar); - if (snd_config_make_compound(&dst, id, count == 1)) { - free(a); - return NULL; - } + if (snd_config_make_compound(&dst, id, count == 1)) + goto lerr; for (index = 0; index < count; index++) { snd_config_t *s = a[index]; const char *id2; if (snd_config_get_id(s, &id2)) { snd_config_delete(dst); - free(a); - return NULL; + goto lerr; } s = sort_config(id2, s); if (s == NULL || snd_config_add(dst, s)) { if (s) snd_config_delete(s); snd_config_delete(dst); - free(a); - return NULL; + goto lerr; } } free(a); return dst; +lerr: + free(a); + return NULL; } static int tplg_check_quoted(const unsigned char *p) @@ -211,7 +227,7 @@ return 0; } -static int tplg_save_quoted(char **dst, const char *str) +static int tplg_save_quoted(struct tplg_buf *dst, const char *str) { static const char nibble[16] = "0123456789abcdef"; unsigned char *p, *d, *t; @@ -265,7 +281,7 @@ return tplg_save_printf(dst, NULL, "'%s'", d); } -static int tplg_save_string(char **dst, const char *str, int id) +static int tplg_save_string(struct tplg_buf *dst, const char *str, int id) { const unsigned char *p = (const unsigned char *)str; @@ -281,7 +297,7 @@ return tplg_save_printf(dst, NULL, "%s", str); } -static int save_config(char **dst, int level, const char *delim, snd_config_t *src) +static int save_config(struct tplg_buf *dst, int level, const char *delim, snd_config_t *src) { snd_config_iterator_t i, next; snd_config_t *s; @@ -329,7 +345,7 @@ count = 0; quoted = 0; - array = 0; + array = snd_config_is_array(src); s = NULL; snd_config_for_each(i, next, src) { s = snd_config_iterator_entry(i); @@ -338,10 +354,6 @@ return err; if (!quoted && tplg_check_quoted((unsigned char *)id)) quoted = 1; - if (array >= 0 && tplg_check_array_item(id, array)) - array++; - else - array = -1; count++; } if (count == 0) @@ -364,7 +376,7 @@ if (level > 0) { err = tplg_save_printf(dst, NULL, "%s%s\n", delim, - array >= 0 ? "[" : "{"); + array > 0 ? "[" : "{"); if (err < 0) return err; } @@ -378,15 +390,15 @@ err = tplg_save_printf(dst, pfx, ""); if (err < 0) return err; - if (array < 0) { + if (array <= 0) { delim = " "; if (quoted) { err = tplg_save_quoted(dst, id); } else { err = tplg_save_string(dst, id, 1); - if (err < 0) - return err; } + if (err < 0) + return err; } else { delim = ""; } @@ -398,7 +410,7 @@ if (level > 0) { pfx[level - 1] = '\0'; err = tplg_save_printf(dst, pfx, "%s\n", - array >= 0 ? "]" : "}"); + array > 0 ? "]" : "}"); if (err < 0) return err; } @@ -406,7 +418,8 @@ return 0; } -static int tplg_save(snd_tplg_t *tplg, char **dst, int gindex, const char *prefix) +static int tplg_save(snd_tplg_t *tplg, struct tplg_buf *dst, + int gindex, const char *prefix) { struct tplg_table *tptr; struct tplg_elem *elem; @@ -490,8 +503,6 @@ return 0; _err: - free(*dst); - *dst = NULL; return err; } @@ -546,9 +557,9 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags) { + struct tplg_buf buf, buf2; snd_input_t *in; snd_config_t *top, *top2; - char *dst2; int *indexes, *a; int err; @@ -556,35 +567,41 @@ assert(dst); *dst = NULL; + tplg_buf_init(&buf); + if (flags & SND_TPLG_SAVE_GROUPS) { err = tplg_index_groups(tplg, &indexes); if (err < 0) return err; for (a = indexes; err >= 0 && *a >= 0; a++) { - err = tplg_save_printf(dst, NULL, + err = tplg_save_printf(&buf, NULL, "IndexGroup.%d {\n", *a); if (err >= 0) - err = tplg_save(tplg, dst, *a, "\t"); + err = tplg_save(tplg, &buf, *a, "\t"); if (err >= 0) - err = tplg_save_printf(dst, NULL, "}\n"); + err = tplg_save_printf(&buf, NULL, "}\n"); } free(indexes); } else { - err = tplg_save(tplg, dst, -1, NULL); + err = tplg_save(tplg, &buf, -1, NULL); } if (err < 0) goto _err; - if (*dst == NULL) - return -EINVAL; + if (buf.dst == NULL) { + err = -EINVAL; + goto _err; + } - if (flags & SND_TPLG_SAVE_NOCHECK) + if (flags & SND_TPLG_SAVE_NOCHECK) { + *dst = tplg_buf_detach(&buf); return 0; + } /* always load configuration - check */ - err = snd_input_buffer_open(&in, *dst, strlen(*dst)); + err = snd_input_buffer_open(&in, buf.dst, strlen(buf.dst)); if (err < 0) { SNDERR("could not create input buffer"); goto _err; @@ -616,20 +633,20 @@ top = top2; } - dst2 = NULL; - err = save_config(&dst2, 0, NULL, top); + tplg_buf_init(&buf2); + err = save_config(&buf2, 0, NULL, top); snd_config_delete(top); if (err < 0) { SNDERR("could not save configuration"); goto _err; } - free(*dst); - *dst = dst2; + tplg_buf_free(&buf); + *dst = tplg_buf_detach(&buf2); return 0; _err: - free(*dst); + tplg_buf_free(&buf); *dst = NULL; return err; } diff -Nru alsa-lib-1.2.2/src/topology/text.c alsa-lib-1.2.6.1/src/topology/text.c --- alsa-lib-1.2.2/src/topology/text.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/text.c 2021-12-09 13:17:59.000000000 +0000 @@ -93,7 +93,7 @@ /* save text data */ int tplg_save_text(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, - char **dst, const char *pfx) + struct tplg_buf *dst, const char *pfx) { struct tplg_texts *texts = elem->texts; unsigned int i; @@ -103,7 +103,7 @@ return 0; err = tplg_save_printf(dst, pfx, "'%s'.values [\n", elem->id); for (i = 0; err >= 0 && i < texts->num_items; i++) - err = tplg_save_printf(dst, pfx, "\t'%s'\n", texts->items[i][0]); + err = tplg_save_printf(dst, pfx, "\t'%s'\n", texts->items[i]); if (err >= 0) err = tplg_save_printf(dst, pfx, "]\n"); return err; diff -Nru alsa-lib-1.2.2/src/topology/tplg_local.h alsa-lib-1.2.6.1/src/topology/tplg_local.h --- alsa-lib-1.2.2/src/topology/tplg_local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/topology/tplg_local.h 2021-12-09 13:17:59.000000000 +0000 @@ -16,6 +16,7 @@ #include "local.h" #include "list.h" +#include "bswap.h" #include "topology.h" #include @@ -200,6 +201,14 @@ int id; }; +/* output buffer */ +struct tplg_buf { + char *dst; + size_t dst_len; + char *printf_buf; + size_t printf_buf_size; +}; + /* mapping table */ struct tplg_table { const char *name; @@ -214,9 +223,9 @@ void (*free)(void *); int (*parse)(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); int (*save)(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *prefix); + struct tplg_buf *dst, const char *prefix); int (*gsave)(snd_tplg_t *tplg, int index, - char **dst, const char *prefix); + struct tplg_buf *dst, const char *prefix); int (*decod)(snd_tplg_t *tplg, size_t pos, struct snd_soc_tplg_hdr *hdr, void *bin, size_t size); @@ -225,6 +234,25 @@ extern struct tplg_table tplg_table[]; extern unsigned int tplg_table_items; +#if __SIZEOF_INT__ == 4 +static inline unsigned int unaligned_get32(void *src) +{ + unsigned int ret; + memcpy(&ret, src, sizeof(ret)); +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + ret = bswap_32(ret); +#endif + return ret; +} +static inline void unaligned_put32(void *dst, unsigned int val) +{ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + val = bswap_32(val); +#endif + memcpy(dst, &val, sizeof(val)); +} +#endif + #define tplg_log(tplg, type, pos, fmt, args...) do { \ if ((tplg)->verbose) \ tplg_log_((tplg), (type), (pos), (fmt), ##args); \ @@ -335,49 +363,49 @@ int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value); -int tplg_save_printf(char **dst, const char *prefix, const char *fmt, ...); +int tplg_save_printf(struct tplg_buf *dst, const char *prefix, const char *fmt, ...); int tplg_save_refs(snd_tplg_t *tplg, struct tplg_elem *elem, unsigned int type, - const char *id, char **dst, const char *pfx); + const char *id, struct tplg_buf *dst, const char *pfx); int tplg_save_channels(snd_tplg_t *tplg, struct snd_soc_tplg_channel *channel, - unsigned int channel_count, char **dst, const char *pfx); + unsigned int channel_count, struct tplg_buf *dst, const char *pfx); int tplg_save_ops(snd_tplg_t *tplg, struct snd_soc_tplg_ctl_hdr *hdr, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_ext_ops(snd_tplg_t *tplg, struct snd_soc_tplg_bytes_control *be, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_manifest_data(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_control_mixer(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_control_enum(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_control_bytes(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_tlv(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_data(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_text(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_tokens(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_tuples(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_dapm_widget(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_link(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_cc(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_pcm(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_hw_config(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_stream_caps(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_save_dai(snd_tplg_t *tplg, struct tplg_elem *elem, - char **dst, const char *pfx); + struct tplg_buf *dst, const char *pfx); int tplg_decode_template(snd_tplg_t *tplg, size_t pos, @@ -398,7 +426,7 @@ struct list_head *heap, struct snd_tplg_enum_template *et, size_t pos, - void *bin, size_t size); + struct snd_soc_tplg_enum_control *ec); int tplg_decode_control_enum(snd_tplg_t *tplg, size_t pos, struct snd_soc_tplg_hdr *hdr, void *bin, size_t size); diff -Nru alsa-lib-1.2.2/src/ucm/Makefile.am alsa-lib-1.2.6.1/src/ucm/Makefile.am --- alsa-lib-1.2.2/src/ucm/Makefile.am 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/Makefile.am 2021-12-09 13:17:59.000000000 +0000 @@ -1,8 +1,9 @@ EXTRA_LTLIBRARIES = libucm.la -libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c main.c +libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \ + ucm_regex.c ucm_exec.c main.c -noinst_HEADERS = ucm_local.h +noinst_HEADERS = ucm_local.h ucm_confdoc.h all: libucm.la diff -Nru alsa-lib-1.2.2/src/ucm/Makefile.in alsa-lib-1.2.6.1/src/ucm/Makefile.in --- alsa-lib-1.2.2/src/ucm/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -105,7 +105,7 @@ CONFIG_CLEAN_VPATH_FILES = libucm_la_LIBADD = am_libucm_la_OBJECTS = utils.lo parser.lo ucm_cond.lo ucm_subs.lo \ - main.lo + ucm_include.lo ucm_regex.lo ucm_exec.lo main.lo libucm_la_OBJECTS = $(am_libucm_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -127,8 +127,9 @@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/main.Plo ./$(DEPDIR)/parser.Plo \ - ./$(DEPDIR)/ucm_cond.Plo ./$(DEPDIR)/ucm_subs.Plo \ - ./$(DEPDIR)/utils.Plo + ./$(DEPDIR)/ucm_cond.Plo ./$(DEPDIR)/ucm_exec.Plo \ + ./$(DEPDIR)/ucm_include.Plo ./$(DEPDIR)/ucm_regex.Plo \ + ./$(DEPDIR)/ucm_subs.Plo ./$(DEPDIR)/utils.Plo am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) @@ -299,6 +300,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -308,8 +310,10 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_LTLIBRARIES = libucm.la -libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c main.c -noinst_HEADERS = ucm_local.h +libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \ + ucm_regex.c ucm_exec.c main.c + +noinst_HEADERS = ucm_local.h ucm_confdoc.h AM_CPPFLAGS = -I$(top_srcdir)/include all: all-am @@ -357,6 +361,9 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucm_cond.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucm_exec.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucm_include.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucm_regex.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ucm_subs.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ # am--include-marker @@ -520,6 +527,9 @@ -rm -f ./$(DEPDIR)/main.Plo -rm -f ./$(DEPDIR)/parser.Plo -rm -f ./$(DEPDIR)/ucm_cond.Plo + -rm -f ./$(DEPDIR)/ucm_exec.Plo + -rm -f ./$(DEPDIR)/ucm_include.Plo + -rm -f ./$(DEPDIR)/ucm_regex.Plo -rm -f ./$(DEPDIR)/ucm_subs.Plo -rm -f ./$(DEPDIR)/utils.Plo -rm -f Makefile @@ -570,6 +580,9 @@ -rm -f ./$(DEPDIR)/main.Plo -rm -f ./$(DEPDIR)/parser.Plo -rm -f ./$(DEPDIR)/ucm_cond.Plo + -rm -f ./$(DEPDIR)/ucm_exec.Plo + -rm -f ./$(DEPDIR)/ucm_include.Plo + -rm -f ./$(DEPDIR)/ucm_regex.Plo -rm -f ./$(DEPDIR)/ucm_subs.Plo -rm -f ./$(DEPDIR)/utils.Plo -rm -f Makefile diff -Nru alsa-lib-1.2.2/src/ucm/main.c alsa-lib-1.2.6.1/src/ucm/main.c --- alsa-lib-1.2.2/src/ucm/main.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/main.c 2021-12-09 13:17:59.000000000 +0000 @@ -31,20 +31,29 @@ */ #include "ucm_local.h" +#include "../control/control_local.h" +#include #include #include #include #include +#include #include /* * misc */ +static int get_value(snd_use_case_mgr_t *uc_mgr, + const char *identifier, + char **value, + const char *mod_dev_name, + const char *verb_name, + int exact); static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, - struct list_head *value_list, const char *identifier); + struct list_head *value_list, const char *identifier); static int get_value3(snd_use_case_mgr_t *uc_mgr, - char **value, + char **value, const char *identifier, struct list_head *value_list1, struct list_head *value_list2, @@ -73,30 +82,30 @@ static int list_count(struct list_head *list) { - struct list_head *pos; - int count = 0; - - list_for_each(pos, list) { - count += 1; - } - return count; + struct list_head *pos; + int count = 0; + + list_for_each(pos, list) { + count += 1; + } + return count; } static int alloc_str_list(struct list_head *list, int mult, char **result[]) { - char **res; - int cnt; - - cnt = list_count(list) * mult; - if (cnt == 0) { + char **res; + int cnt; + + cnt = list_count(list) * mult; + if (cnt == 0) { *result = NULL; - return cnt; + return cnt; } - res = calloc(mult, cnt * sizeof(char *)); - if (res == NULL) - return -ENOMEM; - *result = res; - return cnt; + res = calloc(mult, cnt * sizeof(char *)); + if (res == NULL) + return -ENOMEM; + *result = res; + return cnt; } /** @@ -132,12 +141,12 @@ */ int snd_use_case_free_list(const char *list[], int items) { - int i; + int i; if (list == NULL) return 0; - for (i = 0; i < items; i++) + for (i = 0; i < items; i++) free((void *)list[i]); - free(list); + free(list); return 0; } @@ -246,9 +255,138 @@ return err; } -extern int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, - const char *str, - const char **ret_ptr); +static const char *parse_type(const char *p, const char *prefix, size_t len, + snd_ctl_elem_info_t *info) +{ + if (strncasecmp(p, prefix, len)) + return p; + p += len; + if (info->type != SND_CTL_ELEM_TYPE_NONE) + return NULL; + if (strncasecmp(p, "bool", sizeof("bool") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_BOOLEAN; + else if (strncasecmp(p, "integer64", sizeof("integer64") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_INTEGER64; + else if (strncasecmp(p, "int64", sizeof("int64") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_INTEGER64; + else if (strncasecmp(p, "int", sizeof("int") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_INTEGER; + else if (strncasecmp(p, "enum", sizeof("enum") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_ENUMERATED; + else if (strncasecmp(p, "bytes", sizeof("bytes") - 1) == 0) + info->type = SND_CTL_ELEM_TYPE_BYTES; + else + return NULL; + while (isalpha(*p)) + p++; + return p; +} + +static const char *parse_uint(const char *p, const char *prefix, size_t len, + unsigned int min, unsigned int max, unsigned int *rval) +{ + long v; + char *end; + + if (strncasecmp(p, prefix, len)) + return p; + p += len; + v = strtol(p, &end, 0); + if (*end != '\0' && *end != ' ' && *end != ',') { + uc_error("unable to parse '%s'", prefix); + return NULL; + } + if (v < min || v > max) { + uc_error("value '%s' out of range %u-%u %(%ld)", min, max, v); + return NULL; + } + *rval = v; + return end; +} + +static const char *parse_labels(const char *p, const char *prefix, size_t len, + snd_ctl_elem_info_t *info) +{ + const char *s; + char *buf, *bp; + size_t l; + int c; + + if (info->type != SND_CTL_ELEM_TYPE_ENUMERATED) + return NULL; + if (strncasecmp(p, prefix, len)) + return p; + p += len; + s = p; + c = *s; + l = 0; + if (c == '\'' || c == '\"') { + s++; + while (*s && *s != c) { + s++, l++; + } + if (*s == c) + s++; + } else { + while (*s && *s != ',') + l++; + } + if (l == 0) + return NULL; + buf = malloc(l + 1); + if (buf == NULL) + return NULL; + memcpy(buf, p + ((c == '\'' || c == '\"') ? 1 : 0), l); + buf[l] = '\0'; + info->value.enumerated.items = 1; + for (bp = buf; *bp; bp++) { + if (*bp == ';') { + if (bp == buf || bp[1] == ';') { + free(buf); + return NULL; + } + info->value.enumerated.items++; + *bp = '\0'; + } + } + info->value.enumerated.names_ptr = (uintptr_t)buf; + info->value.enumerated.names_length = l + 1; + return s; +} + +static int parse_cset_new_info(snd_ctl_elem_info_t *info, const char *s, const char **pos) +{ + const char *p = s, *op; + + info->count = 1; + while (*s) { + op = p; + p = parse_type(p, "type=", sizeof("type=") - 1, info); + if (p != op) + goto next; + p = parse_uint(p, "elements=", sizeof("elements=") - 1, 1, 128, (unsigned int *)&info->owner); + if (p != op) + goto next; + p = parse_uint(p, "count=", sizeof("count=") - 1, 1, 128, &info->count); + if (p != op) + goto next; + p = parse_labels(p, "labels=", sizeof("labels=") - 1, info); +next: + if (p == NULL) + goto er; + if (*p == ',') + p++; + if (isspace(*p)) + break; + if (op == p) + goto er; + } + *pos = p; + return 0; +er: + uc_error("unknown syntax '%s'", p); + return -EINVAL; +} static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) { @@ -256,7 +394,7 @@ int err; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *value; - snd_ctl_elem_info_t *info; + snd_ctl_elem_info_t *info, *info2 = NULL; unsigned int *res = NULL; snd_ctl_elem_id_malloc(&id); @@ -268,14 +406,53 @@ goto __fail; while (*pos && isspace(*pos)) pos++; + if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) { + snd_ctl_elem_info_malloc(&info2); + snd_ctl_elem_info_set_id(info2, id); + err = parse_cset_new_info(info2, pos, &pos); + if (err < 0 || !*pos) { + uc_error("undefined or wrong id config for cset-new", cset); + err = -EINVAL; + goto __fail; + } + while (*pos && isspace(*pos)) + pos++; + } if (!*pos) { - uc_error("undefined value for cset >%s<", cset); + if (type != SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) { + uc_error("undefined value for cset >%s<", cset); + err = -EINVAL; + goto __fail; + } + } else if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) { + uc_error("extra value for ctl-remove >%s<", cset); err = -EINVAL; goto __fail; } + snd_ctl_elem_info_set_id(info, id); err = snd_ctl_elem_info(ctl, info); - if (err < 0) + if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW || + type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) { + if (err >= 0) { + err = snd_ctl_elem_remove(ctl, id); + if (err < 0) { + uc_error("unable to remove control"); + err = -EINVAL; + goto __fail; + } + } + if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) + goto __ok; + err = __snd_ctl_add_elem_set(ctl, info2, info2->owner, info2->count); + if (err < 0) { + uc_error("unable to create new control"); + goto __fail; + } + /* new id copy */ + snd_ctl_elem_info_get_id(info2, id); + snd_ctl_elem_info_set_id(info, id); + } else if (err < 0) goto __fail; if (type == SEQUENCE_ELEMENT_TYPE_CSET_TLV) { if (!snd_ctl_elem_info_is_tlv_writable(info)) { @@ -302,21 +479,187 @@ err = snd_ctl_elem_write(ctl, value); if (err < 0) goto __fail; + if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) { + unsigned int idx; + for (idx = 1; idx < (unsigned int)info2->owner; idx++) { + value->id.numid += 1; + err = snd_ctl_elem_write(ctl, value); + if (err < 0) + goto __fail; + } + } } + __ok: err = 0; __fail: - if (id != NULL) - free(id); - if (value != NULL) - free(value); - if (info != NULL) - free(info); - if (res != NULL) - free(res); + free(id); + free(value); + if (info2) { + if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED) + free((void *)info2->value.enumerated.names_ptr); + free(info2); + } + free(info); + free(res); return err; } +static int execute_sysw(const char *sysw) +{ + char path[PATH_MAX]; + const char *e; + char *s, *value; + ssize_t wlen; + size_t len; + int fd, myerrno; + bool ignore_error = false; + + if (sysw == NULL || *sysw == '\0') + return 0; + + if (sysw[0] == '-') { + ignore_error = true; + sysw++; + } + + if (sysw[0] == ':') + return -EINVAL; + + s = strdup(sysw[0] != '/' ? sysw : sysw + 1); + if (s == NULL) + return -ENOMEM; + + value = strchr(s, ':'); + if (!value) { + free(s); + return -EINVAL; + } + *value = '\0'; + value++; + len = strlen(value); + if (len < 1) { + free(s); + return -EINVAL; + } + + e = uc_mgr_sysfs_root(); + if (e == NULL) { + free(s); + return -EINVAL; + } + snprintf(path, sizeof(path), "%s/%s", e, s); + + fd = open(path, O_WRONLY|O_CLOEXEC); + if (fd < 0) { + free(s); + if (ignore_error) + return 0; + uc_error("unable to open '%s' for write", path); + return -EINVAL; + } + wlen = write(fd, value, len); + myerrno = errno; + close(fd); + free(s); + + if (ignore_error) + return 0; + + if (wlen != (ssize_t)len) { + uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno)); + return -EINVAL; + } + + return 0; +} + +int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out, unsigned int level); + +static int execute_cfgsave(snd_use_case_mgr_t *uc_mgr, const char *filename) +{ + snd_config_t *config = uc_mgr->local_config; + char *file, *root; + snd_output_t *out; + bool with_root = false; + int err = 0; + + file = strdup(filename); + if (!file) + return -ENOMEM; + root = strchr(file, ':'); + if (config && root) { + *root++ = '\0'; + if (*root == '+') { + with_root = true; + root++; + } + err = snd_config_search(config, root, &config); + if (err < 0) { + uc_error("Unable to find subtree '%s'", root); + goto _err; + } + } + + err = snd_output_stdio_open(&out, file, "w+"); + if (err < 0) { + uc_error("unable to open file '%s': %s", file, snd_strerror(err)); + goto _err; + } + if (!config || snd_config_is_empty(config)) { + snd_output_close(out); + goto _err; + } + if (with_root) { + snd_output_printf(out, "%s ", root); + err = _snd_config_save_node_value(config, out, 0); + } else { + err = snd_config_save(config, out); + } + snd_output_close(out); + if (err < 0) { + uc_error("unable to save configuration: %s", snd_strerror(err)); + goto _err; + } +_err: + free(file); + return err; +} + +static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, char **value) +{ + char *sval; + size_t l; + static const char **s, *_prefix[] = { + "PlaybackCTL", + "CaptureCTL", + "PlaybackMixer", + "CaptureMixer", + "PlaybackPCM", + "CapturePCM", + NULL + }; + + if (!uc_mgr_has_local_config(uc_mgr)) + return 0; + for (s = _prefix; *s && *value; s++) { + if (strcmp(*s, name) != 0) + continue; + l = strlen(*value) + 9 + 1; + sval = malloc(l); + if (sval == NULL) { + free(*value); + *value = NULL; + return -ENOMEM; + } + snprintf(sval, l, "_ucm%04X.%s", uc_mgr->ucm_card_number, *value); + free(*value); + *value = sval; + break; + } + return 0; +} + /** * \brief Execute the sequence * \param uc_mgr Use case manager @@ -333,6 +676,8 @@ struct sequence_element *s; char *cdev = NULL; snd_ctl_t *ctl = NULL; + struct ctl_list *ctl_list; + bool ignore_error; int err = 0; list_for_each(pos, seq) { @@ -342,10 +687,14 @@ cdev = strdup(s->data.cdev); if (cdev == NULL) goto __fail_nomem; + if (rewrite_device_value(uc_mgr, "PlaybackCTL", &cdev)) + goto __fail_nomem; break; case SEQUENCE_ELEMENT_TYPE_CSET: case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE: case SEQUENCE_ELEMENT_TYPE_CSET_TLV: + case SEQUENCE_ELEMENT_TYPE_CSET_NEW: + case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE: if (cdev == NULL && uc_mgr->in_component_domain) { /* For sequence of a component device, use * its parent's cdev stored by ucm manager. @@ -400,11 +749,12 @@ } } if (ctl == NULL) { - err = uc_mgr_open_ctl(uc_mgr, &ctl, cdev); + err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cdev, 1); if (err < 0) { uc_error("unable to open ctl device '%s'", cdev); goto __fail; } + ctl = ctl_list->ctl; } err = execute_cset(ctl, s->data.cset, s->type); if (err < 0) { @@ -412,13 +762,44 @@ goto __fail; } break; + case SEQUENCE_ELEMENT_TYPE_SYSSET: + err = execute_sysw(s->data.sysw); + if (err < 0) + goto __fail; + break; case SEQUENCE_ELEMENT_TYPE_SLEEP: usleep(s->data.sleep); break; case SEQUENCE_ELEMENT_TYPE_EXEC: - err = system(s->data.exec); - if (err < 0) + if (s->data.exec == NULL) + break; + ignore_error = s->data.exec[0] == '-'; + err = uc_mgr_exec(s->data.exec + (ignore_error ? 1 : 0)); + if (ignore_error == false && err != 0) { + uc_error("exec '%s' failed (exit code %d)", s->data.exec, err); goto __fail; + } + break; + case SEQUENCE_ELEMENT_TYPE_SHELL: + if (s->data.exec == NULL) + break; + ignore_error = s->data.exec[0] == '-'; +shell_retry: + err = system(s->data.exec + (ignore_error ? 1 : 0)); + if (WIFSIGNALED(err)) { + err = -EINTR; + } if (WIFEXITED(err)) { + if (ignore_error == false && WEXITSTATUS(err) != 0) { + uc_error("command '%s' failed (exit code %d)", s->data.exec, WEXITSTATUS(err)); + err = -EINVAL; + goto __fail; + } + } else if (err < 0) { + if (errno == EAGAIN) + goto shell_retry; + err = -errno; + goto __fail; + } break; case SEQUENCE_ELEMENT_TYPE_CMPT_SEQ: /* Execute enable or disable sequence of a component @@ -433,6 +814,11 @@ if (err < 0) goto __fail; break; + case SEQUENCE_ELEMENT_TYPE_CFGSAVE: + err = execute_cfgsave(uc_mgr, s->data.cfgsave); + if (err < 0) + goto __fail; + break; default: uc_error("unknown sequence command %i", s->type); break; @@ -516,7 +902,7 @@ char buf[40]; int err; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list) { id = snd_ctl_card_info_get_id(ctl_list->ctl_info); snprintf(buf, sizeof(buf), "hw:%s", id); @@ -531,6 +917,27 @@ } /** + * \brief execute default commands + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +static int set_defaults(snd_use_case_mgr_t *uc_mgr) +{ + int err; + + if (uc_mgr->default_list_executed) + return 0; + err = execute_sequence(uc_mgr, &uc_mgr->default_list, + &uc_mgr->value_list, NULL, NULL); + if (err < 0) { + uc_error("Unable to execute default sequence"); + return err; + } + uc_mgr->default_list_executed = 1; + return 0; +} + +/** * \brief Import master config and execute the default sequence * \param uc_mgr Use case manager * \return zero on success, otherwise a negative error code @@ -542,14 +949,34 @@ err = uc_mgr_import_master_config(uc_mgr); if (err < 0) return err; - err = add_auto_values(uc_mgr); - if (err < 0) - return err; - err = execute_sequence(uc_mgr, &uc_mgr->default_list, - &uc_mgr->value_list, NULL, NULL); - if (err < 0) - uc_error("Unable to execute default sequence"); - return err; + return add_auto_values(uc_mgr); +} + +/** + * \brief Check, if the UCM configuration is empty + * \param uc_mgr Use case Manager + * \return zero on success, otherwise a negative error code + */ +static int check_empty_configuration(snd_use_case_mgr_t *uc_mgr) +{ + int err; + char *value; + + err = get_value(uc_mgr, "Linked", &value, NULL, NULL, 1); + if (err >= 0) { + err = strcasecmp(value, "true") == 0 || + strcmp(value, "1") == 0; + free(value); + if (err) + return 0; + } + if (!list_empty(&uc_mgr->verb_list)) + return 0; + if (!list_empty(&uc_mgr->fixedboot_list)) + return 0; + if (!list_empty(&uc_mgr->boot_list)) + return 0; + return -ENXIO; } /** @@ -590,42 +1017,42 @@ * \return count of items on success, otherwise a negative error code */ static int get_list0(struct list_head *list, - const char **result[], - unsigned long offset, - unsigned long s1offset) + const char **result[], + unsigned long offset, + unsigned long s1offset) { - char **res; - int cnt; + char **res; + int cnt; struct list_head *pos; char *ptr, *str1; cnt = alloc_str_list(list, 1, &res); if (cnt <= 0) { *result = NULL; - return cnt; + return cnt; } *result = (const char **)res; list_for_each(pos, list) { ptr = list_entry_offset(pos, char, offset); str1 = *((char **)(ptr + s1offset)); if (str1 != NULL) { - *res = strdup(str1); - if (*res == NULL) - goto __fail; - } else { - *res = NULL; - } - res++; + *res = strdup(str1); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; } return cnt; __fail: - snd_use_case_free_list((const char **)res, cnt); - return -ENOMEM; + snd_use_case_free_list(*result, cnt); + return -ENOMEM; } #define get_list(list, result, type, member, s1) \ get_list0(list, result, \ - (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->member), \ (unsigned long)(&((type *)0)->s1)) /** @@ -638,52 +1065,52 @@ * \return count of items on success, otherwise a negative error code */ static int get_list20(struct list_head *list, - const char **result[], - unsigned long offset, - unsigned long s1offset, - unsigned long s2offset) + const char **result[], + unsigned long offset, + unsigned long s1offset, + unsigned long s2offset) { - char **res; - int cnt; + char **res; + int cnt; struct list_head *pos; char *ptr, *str1, *str2; cnt = alloc_str_list(list, 2, &res); if (cnt <= 0) { *result = NULL; - return cnt; + return cnt; } - *result = (const char **)res; + *result = (const char **)res; list_for_each(pos, list) { ptr = list_entry_offset(pos, char, offset); str1 = *((char **)(ptr + s1offset)); if (str1 != NULL) { - *res = strdup(str1); - if (*res == NULL) - goto __fail; - } else { - *res = NULL; - } - res++; + *res = strdup(str1); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; str2 = *((char **)(ptr + s2offset)); if (str2 != NULL) { - *res = strdup(str2); - if (*res == NULL) - goto __fail; - } else { - *res = NULL; - } - res++; + *res = strdup(str2); + if (*res == NULL) + goto __fail; + } else { + *res = NULL; + } + res++; } return cnt; __fail: - snd_use_case_free_list((const char **)res, cnt); - return -ENOMEM; + snd_use_case_free_list(*result, cnt); + return -ENOMEM; } #define get_list2(list, result, type, member, s1, s2) \ get_list20(list, result, \ - (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->member), \ (unsigned long)(&((type *)0)->s1), \ (unsigned long)(&((type *)0)->s2)) @@ -753,7 +1180,7 @@ * \return structure on success, otherwise a NULL (not found) */ static inline struct use_case_device * - find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, const char *device_name, int check_supported) { struct use_case_device *device; @@ -781,7 +1208,7 @@ * \return structure on success, otherwise a NULL (not found) */ static struct use_case_modifier * - find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, const char *modifier_name, int check_supported) { struct use_case_modifier *modifier; @@ -803,31 +1230,31 @@ } long device_status(snd_use_case_mgr_t *uc_mgr, - const char *device_name) + const char *device_name) { - struct use_case_device *dev; - struct list_head *pos; + struct use_case_device *dev; + struct list_head *pos; - list_for_each(pos, &uc_mgr->active_devices) { - dev = list_entry(pos, struct use_case_device, active_list); - if (strcmp(dev->name, device_name) == 0) - return 1; - } - return 0; + list_for_each(pos, &uc_mgr->active_devices) { + dev = list_entry(pos, struct use_case_device, active_list); + if (strcmp(dev->name, device_name) == 0) + return 1; + } + return 0; } long modifier_status(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) + const char *modifier_name) { - struct use_case_modifier *mod; - struct list_head *pos; + struct use_case_modifier *mod; + struct list_head *pos; - list_for_each(pos, &uc_mgr->active_modifiers) { - mod = list_entry(pos, struct use_case_modifier, active_list); - if (strcmp(mod->name, modifier_name) == 0) - return 1; - } - return 0; + list_for_each(pos, &uc_mgr->active_modifiers) { + mod = list_entry(pos, struct use_case_modifier, active_list); + if (strcmp(mod->name, modifier_name) == 0) + return 1; + } + return 0; } /** @@ -845,6 +1272,9 @@ int err; if (enable) { + err = set_defaults(uc_mgr); + if (err < 0) + return err; seq = &verb->enable_list; } else { seq = &verb->disable_list; @@ -906,7 +1336,7 @@ struct list_head *seq; int err; - if (device_status(uc_mgr, device->name) == enable) + if (device_status(uc_mgr, device->name) == enable) return 0; if (enable) { @@ -943,31 +1373,58 @@ if (mgr == NULL) return -ENOMEM; INIT_LIST_HEAD(&mgr->verb_list); + INIT_LIST_HEAD(&mgr->fixedboot_list); + INIT_LIST_HEAD(&mgr->boot_list); INIT_LIST_HEAD(&mgr->default_list); INIT_LIST_HEAD(&mgr->value_list); INIT_LIST_HEAD(&mgr->active_modifiers); INIT_LIST_HEAD(&mgr->active_devices); INIT_LIST_HEAD(&mgr->ctl_list); + INIT_LIST_HEAD(&mgr->variable_list); pthread_mutex_init(&mgr->mutex, NULL); + if (card_name && *card_name == '-') { + card_name++; + mgr->suppress_nodev_errors = 1; + } + + err = uc_mgr_card_open(mgr); + if (err < 0) { + uc_mgr_free(mgr); + return err; + } + + err = snd_config_top(&mgr->local_config); + if (err < 0) + goto _err; + mgr->card_name = strdup(card_name); if (mgr->card_name == NULL) { - free(mgr); - return -ENOMEM; + err = -ENOMEM; + goto _err; } /* get info on use_cases and verify against card */ err = import_master_config(mgr); if (err < 0) { + if (err == -ENXIO && mgr->suppress_nodev_errors) + goto _err; uc_error("error: failed to import %s use case configuration %d", - card_name, err); - goto err; + card_name, err); + goto _err; + } + + err = check_empty_configuration(mgr); + if (err < 0) { + uc_error("error: failed to import %s (empty configuration)", card_name); + goto _err; } *uc_mgr = mgr; return 0; -err: +_err: + uc_mgr_card_close(mgr); uc_mgr_free(mgr); return err; } @@ -985,6 +1442,8 @@ uc_mgr_free_verb(uc_mgr); + uc_mgr->default_list_executed = 0; + /* reload all use cases */ err = import_master_config(uc_mgr); if (err < 0) { @@ -1004,6 +1463,7 @@ */ int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr) { + uc_mgr_card_close(uc_mgr); uc_mgr_free(uc_mgr); return 0; @@ -1057,7 +1517,7 @@ */ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) { - int err; + int err; pthread_mutex_lock(&uc_mgr->mutex); err = execute_sequence(uc_mgr, &uc_mgr->default_list, @@ -1076,9 +1536,9 @@ */ static int get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[]) { - return get_list2(&uc_mgr->verb_list, list, - struct use_case_verb, list, - name, comment); + return get_list2(&uc_mgr->verb_list, list, + struct use_case_verb, list, + name, comment); } /** @@ -1088,20 +1548,20 @@ * \return Number of list entries if success, otherwise a negative error code */ static int get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[], - char *verbname) + char *verbname) { - struct use_case_verb *verb; - - if (verbname) { - verb = find_verb(uc_mgr, verbname); - } else { - verb = uc_mgr->active_verb; - } - if (verb == NULL) - return -ENOENT; - return get_list2(&verb->device_list, list, - struct use_case_device, list, - name, comment); + struct use_case_verb *verb; + + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + return get_list2(&verb->device_list, list, + struct use_case_device, list, + name, comment); } /** @@ -1111,20 +1571,19 @@ * \return Number of list entries if success, otherwise a negative error code */ static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[], - char *verbname) + char *verbname) { - struct use_case_verb *verb; - - if (verbname) { - verb = find_verb(uc_mgr, verbname); - } else { - verb = uc_mgr->active_verb; - } - if (verb == NULL) - return -ENOENT; - return get_list2(&verb->modifier_list, list, - struct use_case_modifier, list, - name, comment); + struct use_case_verb *verb; + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + return get_list2(&verb->modifier_list, list, + struct use_case_modifier, list, + name, comment); } /** @@ -1311,13 +1770,13 @@ * \param source Source list with ucm_value structures */ static int add_values(struct list_head *list, - const char *identifier, - struct list_head *source) + const char *identifier, + struct list_head *source) { struct ucm_value *v; struct list_head *pos; int err; - + list_for_each(pos, source) { v = list_entry(pos, struct ucm_value, list); if (check_identifier(identifier, v->name)) { @@ -1412,51 +1871,51 @@ * \return Number of list entries if success, otherwise a negative error code */ static int get_value_list(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - const char **list[], - char *verbname) + const char *identifier, + const char **list[], + char *verbname) { struct list_head mylist, *pos; - struct use_case_verb *verb; - struct use_case_device *dev; - struct use_case_modifier *mod; - char **res; - int err; - - if (verbname) { - verb = find_verb(uc_mgr, verbname); - } else { - verb = uc_mgr->active_verb; - } - if (verb == NULL) - return -ENOENT; - INIT_LIST_HEAD(&mylist); + struct use_case_verb *verb; + struct use_case_device *dev; + struct use_case_modifier *mod; + char **res; + int err; + + if (verbname) { + verb = find_verb(uc_mgr, verbname); + } else { + verb = uc_mgr->active_verb; + } + if (verb == NULL) + return -ENOENT; + INIT_LIST_HEAD(&mylist); err = add_values(&mylist, identifier, &uc_mgr->value_list); if (err < 0) goto __fail; - err = add_values(&mylist, identifier, &verb->value_list); - if (err < 0) - goto __fail; - list_for_each(pos, &verb->device_list) { - dev = list_entry(pos, struct use_case_device, list); - err = add_values(&mylist, identifier, &dev->value_list); - if (err < 0) - goto __fail; - } - list_for_each(pos, &verb->modifier_list) { - mod = list_entry(pos, struct use_case_modifier, list); - err = add_values(&mylist, identifier, &mod->value_list); - if (err < 0) - goto __fail; - } + err = add_values(&mylist, identifier, &verb->value_list); + if (err < 0) + goto __fail; + list_for_each(pos, &verb->device_list) { + dev = list_entry(pos, struct use_case_device, list); + err = add_values(&mylist, identifier, &dev->value_list); + if (err < 0) + goto __fail; + } + list_for_each(pos, &verb->modifier_list) { + mod = list_entry(pos, struct use_case_modifier, list); + err = add_values(&mylist, identifier, &mod->value_list); + if (err < 0) + goto __fail; + } err = myvalue_to_str_list(&mylist, &res); if (err > 0) - *list = (const char **)res; + *list = (const char **)res; else if (err == 0) *list = NULL; __fail: myvalue_list_free(&mylist); - return err; + return err; } /** @@ -1466,13 +1925,13 @@ * \return Number of list entries if success, otherwise a negative error code */ static int get_enabled_device_list(snd_use_case_mgr_t *uc_mgr, - const char **list[]) + const char **list[]) { - if (uc_mgr->active_verb == NULL) - return -EINVAL; - return get_list(&uc_mgr->active_devices, list, - struct use_case_device, active_list, - name); + if (uc_mgr->active_verb == NULL) + return -EINVAL; + return get_list(&uc_mgr->active_devices, list, + struct use_case_device, active_list, + name); } /** @@ -1482,13 +1941,13 @@ * \return Number of list entries if success, otherwise a negative error code */ static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr, - const char **list[]) + const char **list[]) { - if (uc_mgr->active_verb == NULL) - return -EINVAL; - return get_list(&uc_mgr->active_modifiers, list, - struct use_case_modifier, active_list, - name); + if (uc_mgr->active_verb == NULL) + return -EINVAL; + return get_list(&uc_mgr->active_modifiers, list, + struct use_case_modifier, active_list, + name); } /** @@ -1510,24 +1969,24 @@ pthread_mutex_lock(&uc_mgr->mutex); if (strcmp(identifier, "_verbs") == 0) err = get_verb_list(uc_mgr, list); - else if (strcmp(identifier, "_enadevs") == 0) - err = get_enabled_device_list(uc_mgr, list); - else if (strcmp(identifier, "_enamods") == 0) - err = get_enabled_modifier_list(uc_mgr, list); - else { - str1 = strchr(identifier, '/'); - if (str1) { - str = strdup(str1 + 1); - if (str == NULL) { - err = -ENOMEM; - goto __end; - } - } else { - str = NULL; - } + else if (strcmp(identifier, "_enadevs") == 0) + err = get_enabled_device_list(uc_mgr, list); + else if (strcmp(identifier, "_enamods") == 0) + err = get_enabled_modifier_list(uc_mgr, list); + else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } if (check_identifier(identifier, "_devices")) err = get_device_list(uc_mgr, list, str); - else if (check_identifier(identifier, "_modifiers")) + else if (check_identifier(identifier, "_modifiers")) err = get_modifier_list(uc_mgr, list, str); else if (check_identifier(identifier, "_identifiers")) err = get_identifiers_list(uc_mgr, list, str); @@ -1550,13 +2009,14 @@ static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value, struct list_head *value_list, const char *identifier) { - struct ucm_value *val; - struct list_head *pos; - + struct ucm_value *val; + struct list_head *pos; + int err; + if (!value_list) return -ENOENT; - list_for_each(pos, value_list) { + list_for_each(pos, value_list) { val = list_entry(pos, struct ucm_value, list); if (check_identifier(identifier, val->name)) { if (uc_mgr->conf_format < 2) { @@ -1565,10 +2025,13 @@ return -ENOMEM; return 0; } - return uc_mgr_get_substituted_value(uc_mgr, value, val->data); + err = uc_mgr_get_substituted_value(uc_mgr, value, val->data); + if (err < 0) + return err; + return rewrite_device_value(uc_mgr, val->name, value); } - } - return -ENOENT; + } + return -ENOENT; } static int get_value3(snd_use_case_mgr_t *uc_mgr, @@ -1661,6 +2124,50 @@ } /** + * \brief Get private alsa-lib configuration (ASCII) + * \param uc_mgr Use case manager + * \param str Returned value string + * \return Zero on success (value is filled), otherwise a negative error code + */ +static int get_alibcfg(snd_use_case_mgr_t *uc_mgr, char **str) +{ + snd_output_t *out; + size_t size; + int err; + + err = snd_output_buffer_open(&out); + if (err < 0) + return err; + err = snd_config_save(uc_mgr->local_config, out); + if (err >= 0) { + size = snd_output_buffer_steal(out, str); + if (*str) + (*str)[size] = '\0'; + } + snd_output_close(out); + return 0; +} + +/** + * \brief Get device prefix for private alsa-lib configuration + * \param uc_mgr Use case manager + * \param str Returned value string + * \return Zero on success (value is filled), otherwise a negative error code + */ +static int get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str) +{ + const size_t l = 10; + char *s; + + s = malloc(l); + if (s == NULL) + return -ENOMEM; + snprintf(s, l, "_ucm%04X.", uc_mgr->ucm_card_number); + *str = s; + return 0; +} + +/** * \brief Get current - string * \param uc_mgr Use case manager * \param identifier @@ -1677,27 +2184,27 @@ const char *slash1, *slash2, *mod_dev_after; const char *ident, *mod_dev, *verb; int exact = 0; - int err; + int err; pthread_mutex_lock(&uc_mgr->mutex); if (identifier == NULL) { - *value = strdup(uc_mgr->card_name); - if (*value == NULL) { - err = -ENOMEM; - goto __end; - } - err = 0; - } else if (strcmp(identifier, "_verb") == 0) { - if (uc_mgr->active_verb == NULL) { - err = -ENOENT; + *value = strdup(uc_mgr->card_name); + if (*value == NULL) { + err = -ENOMEM; goto __end; } - *value = strdup(uc_mgr->active_verb->name); - if (*value == NULL) { - err = -ENOMEM; - goto __end; - } - err = 0; + err = 0; + } else if (strcmp(identifier, "_verb") == 0) { + if (uc_mgr->active_verb == NULL) { + err = -ENOENT; + goto __end; + } + *value = strdup(uc_mgr->active_verb->name); + if (*value == NULL) { + err = -ENOMEM; + goto __end; + } + err = 0; } else if (strcmp(identifier, "_file") == 0) { /* get the conf file name of the opened card */ if ((uc_mgr->card_name == NULL) || @@ -1713,10 +2220,13 @@ } err = 0; + } else if (strcmp(identifier, "_alibcfg") == 0) { + err = get_alibcfg(uc_mgr, (char **)value); + } else if (strcmp(identifier, "_alibpref") == 0) { + err = get_alibpref(uc_mgr, (char **)value); } else if (identifier[0] == '_') { err = -ENOENT; - goto __end; - } else { + } else { if (identifier[0] == '=') { exact = 1; identifier++; @@ -1749,15 +2259,15 @@ } err = get_value(uc_mgr, ident, (char **)value, mod_dev, verb, - exact); + exact); if (ident != identifier) free((void *)ident); if (mod_dev) free((void *)mod_dev); - } + } __end: pthread_mutex_unlock(&uc_mgr->mutex); - return err; + return err; } @@ -1771,29 +2281,29 @@ const char *identifier, long *value) { - char *str, *str1; - long err; + char *str, *str1; + long err; pthread_mutex_lock(&uc_mgr->mutex); - if (0) { - /* nothing here - prepared for fixed identifiers */ - } else { - str1 = strchr(identifier, '/'); - if (str1) { - str = strdup(str1 + 1); - if (str == NULL) { - err = -ENOMEM; - goto __end; - } - } else { - str = NULL; - } - if (check_identifier(identifier, "_devstatus")) { + if (0) { + /* nothing here - prepared for fixed identifiers */ + } else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + str = NULL; + } + if (check_identifier(identifier, "_devstatus")) { if (!str) { err = -EINVAL; goto __end; } - err = device_status(uc_mgr, str); + err = device_status(uc_mgr, str); if (err >= 0) { *value = err; err = 0; @@ -1803,7 +2313,7 @@ err = -EINVAL; goto __end; } - err = modifier_status(uc_mgr, str); + err = modifier_status(uc_mgr, str); if (err >= 0) { *value = err; err = 0; @@ -1817,209 +2327,259 @@ err = -ENOENT; #endif } else - err = -ENOENT; - if (str) - free(str); - } + err = -ENOENT; + if (str) + free(str); + } __end: pthread_mutex_unlock(&uc_mgr->mutex); - return err; + return err; +} + +static int set_fixedboot_user(snd_use_case_mgr_t *uc_mgr, + const char *value) +{ + int err; + + if (value != NULL && *value) { + uc_error("error: wrong value for _fboot (%s)", value); + return -EINVAL; + } + if (list_empty(&uc_mgr->fixedboot_list)) + return -ENOENT; + err = execute_sequence(uc_mgr, &uc_mgr->fixedboot_list, + &uc_mgr->value_list, NULL, NULL); + if (err < 0) { + uc_error("Unable to execute force boot sequence"); + return err; + } + return err; +} + +static int set_boot_user(snd_use_case_mgr_t *uc_mgr, + const char *value) +{ + int err; + + if (value != NULL && *value) { + uc_error("error: wrong value for _boot (%s)", value); + return -EINVAL; + } + if (list_empty(&uc_mgr->boot_list)) + return -ENOENT; + err = execute_sequence(uc_mgr, &uc_mgr->boot_list, + &uc_mgr->value_list, NULL, NULL); + if (err < 0) { + uc_error("Unable to execute boot sequence"); + return err; + } + return err; +} + +static int set_defaults_user(snd_use_case_mgr_t *uc_mgr, + const char *value) +{ + if (value != NULL && *value) { + uc_error("error: wrong value for _defaults (%s)", value); + return -EINVAL; + } + return set_defaults(uc_mgr); } static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *new_verb) + struct use_case_verb *new_verb) { - struct list_head *pos; - struct transition_sequence *trans; - int err; - - list_for_each(pos, &uc_mgr->active_verb->transition_list) { - trans = list_entry(pos, struct transition_sequence, list); - if (strcmp(trans->name, new_verb->name) == 0) { - err = execute_sequence(uc_mgr, &trans->transition_list, + struct list_head *pos; + struct transition_sequence *trans; + int err; + + list_for_each(pos, &uc_mgr->active_verb->transition_list) { + trans = list_entry(pos, struct transition_sequence, list); + if (strcmp(trans->name, new_verb->name) == 0) { + err = execute_sequence(uc_mgr, &trans->transition_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list, NULL); - if (err >= 0) - return 1; - return err; - } - } - return 0; + if (err >= 0) + return 1; + return err; + } + } + return 0; } static int set_verb_user(snd_use_case_mgr_t *uc_mgr, - const char *verb_name) + const char *verb_name) { - struct use_case_verb *verb; - int err = 0; + struct use_case_verb *verb; + int err = 0; - if (uc_mgr->active_verb && - strcmp(uc_mgr->active_verb->name, verb_name) == 0) - return 0; - if (strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE) != 0) { - verb = find_verb(uc_mgr, verb_name); - if (verb == NULL) - return -ENOENT; - } else { - verb = NULL; - } - if (uc_mgr->active_verb) { - err = handle_transition_verb(uc_mgr, verb); - if (err == 0) { - err = dismantle_use_case(uc_mgr); - if (err < 0) - return err; - } else if (err == 1) { - uc_mgr->active_verb = verb; - verb = NULL; - } else { - verb = NULL; /* show error */ - } - } - if (verb) { - err = set_verb(uc_mgr, verb, 1); - if (err < 0) - uc_error("error: failed to initialize new use case: %s", - verb_name); - } - return err; + if (uc_mgr->active_verb && + strcmp(uc_mgr->active_verb->name, verb_name) == 0) + return 0; + if (strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE) != 0) { + verb = find_verb(uc_mgr, verb_name); + if (verb == NULL) + return -ENOENT; + } else { + verb = NULL; + } + if (uc_mgr->active_verb) { + err = handle_transition_verb(uc_mgr, verb); + if (err == 0) { + err = dismantle_use_case(uc_mgr); + if (err < 0) + return err; + } else if (err == 1) { + uc_mgr->active_verb = verb; + verb = NULL; + } else { + verb = NULL; /* show error */ + } + } + if (verb) { + err = set_verb(uc_mgr, verb, 1); + if (err < 0) + uc_error("error: failed to initialize new use case: %s", + verb_name); + } + return err; } static int set_device_user(snd_use_case_mgr_t *uc_mgr, - const char *device_name, - int enable) + const char *device_name, + int enable) { - struct use_case_device *device; + struct use_case_device *device; - if (uc_mgr->active_verb == NULL) - return -ENOENT; - device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1); - if (device == NULL) - return -ENOENT; - return set_device(uc_mgr, device, enable); + if (uc_mgr->active_verb == NULL) + return -ENOENT; + device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1); + if (device == NULL) + return -ENOENT; + return set_device(uc_mgr, device, enable); } static int set_modifier_user(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name, - int enable) + const char *modifier_name, + int enable) { - struct use_case_modifier *modifier; + struct use_case_modifier *modifier; - if (uc_mgr->active_verb == NULL) - return -ENOENT; + if (uc_mgr->active_verb == NULL) + return -ENOENT; - modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1); - if (modifier == NULL) - return -ENOENT; - return set_modifier(uc_mgr, modifier, enable); + modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1); + if (modifier == NULL) + return -ENOENT; + return set_modifier(uc_mgr, modifier, enable); } static int switch_device(snd_use_case_mgr_t *uc_mgr, - const char *old_device, - const char *new_device) + const char *old_device, + const char *new_device) { - struct use_case_device *xold, *xnew; - struct transition_sequence *trans; - struct list_head *pos; - int err, seq_found = 0; - - if (uc_mgr->active_verb == NULL) - return -ENOENT; - if (device_status(uc_mgr, old_device) == 0) { - uc_error("error: device %s not enabled", old_device); - return -EINVAL; - } - if (device_status(uc_mgr, new_device) != 0) { - uc_error("error: device %s already enabled", new_device); - return -EINVAL; - } - xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1); - if (xold == NULL) - return -ENOENT; - list_del(&xold->active_list); - xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1); - list_add_tail(&xold->active_list, &uc_mgr->active_devices); - if (xnew == NULL) - return -ENOENT; - err = 0; - list_for_each(pos, &xold->transition_list) { - trans = list_entry(pos, struct transition_sequence, list); - if (strcmp(trans->name, new_device) == 0) { - err = execute_sequence(uc_mgr, &trans->transition_list, + struct use_case_device *xold, *xnew; + struct transition_sequence *trans; + struct list_head *pos; + int err, seq_found = 0; + + if (uc_mgr->active_verb == NULL) + return -ENOENT; + if (device_status(uc_mgr, old_device) == 0) { + uc_error("error: device %s not enabled", old_device); + return -EINVAL; + } + if (device_status(uc_mgr, new_device) != 0) { + uc_error("error: device %s already enabled", new_device); + return -EINVAL; + } + xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1); + if (xold == NULL) + return -ENOENT; + list_del(&xold->active_list); + xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1); + list_add_tail(&xold->active_list, &uc_mgr->active_devices); + if (xnew == NULL) + return -ENOENT; + err = 0; + list_for_each(pos, &xold->transition_list) { + trans = list_entry(pos, struct transition_sequence, list); + if (strcmp(trans->name, new_device) == 0) { + err = execute_sequence(uc_mgr, &trans->transition_list, &xold->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); - if (err >= 0) { - list_del(&xold->active_list); - list_add_tail(&xnew->active_list, &uc_mgr->active_devices); - } - seq_found = 1; - break; - } - } - if (!seq_found) { - err = set_device(uc_mgr, xold, 0); - if (err < 0) - return err; - err = set_device(uc_mgr, xnew, 1); - if (err < 0) - return err; - } - return err; + if (err >= 0) { + list_del(&xold->active_list); + list_add_tail(&xnew->active_list, &uc_mgr->active_devices); + } + seq_found = 1; + break; + } + } + if (!seq_found) { + err = set_device(uc_mgr, xold, 0); + if (err < 0) + return err; + err = set_device(uc_mgr, xnew, 1); + if (err < 0) + return err; + } + return err; } static int switch_modifier(snd_use_case_mgr_t *uc_mgr, - const char *old_modifier, - const char *new_modifier) + const char *old_modifier, + const char *new_modifier) { - struct use_case_modifier *xold, *xnew; - struct transition_sequence *trans; - struct list_head *pos; - int err, seq_found = 0; - - if (uc_mgr->active_verb == NULL) - return -ENOENT; - if (modifier_status(uc_mgr, old_modifier) == 0) { - uc_error("error: modifier %s not enabled", old_modifier); - return -EINVAL; - } - if (modifier_status(uc_mgr, new_modifier) != 0) { - uc_error("error: modifier %s already enabled", new_modifier); - return -EINVAL; - } - xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1); - if (xold == NULL) - return -ENOENT; - xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1); - if (xnew == NULL) - return -ENOENT; - err = 0; - list_for_each(pos, &xold->transition_list) { - trans = list_entry(pos, struct transition_sequence, list); - if (strcmp(trans->name, new_modifier) == 0) { - err = execute_sequence(uc_mgr, &trans->transition_list, + struct use_case_modifier *xold, *xnew; + struct transition_sequence *trans; + struct list_head *pos; + int err, seq_found = 0; + + if (uc_mgr->active_verb == NULL) + return -ENOENT; + if (modifier_status(uc_mgr, old_modifier) == 0) { + uc_error("error: modifier %s not enabled", old_modifier); + return -EINVAL; + } + if (modifier_status(uc_mgr, new_modifier) != 0) { + uc_error("error: modifier %s already enabled", new_modifier); + return -EINVAL; + } + xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1); + if (xold == NULL) + return -ENOENT; + xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1); + if (xnew == NULL) + return -ENOENT; + err = 0; + list_for_each(pos, &xold->transition_list) { + trans = list_entry(pos, struct transition_sequence, list); + if (strcmp(trans->name, new_modifier) == 0) { + err = execute_sequence(uc_mgr, &trans->transition_list, &xold->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); - if (err >= 0) { - list_del(&xold->active_list); - list_add_tail(&xnew->active_list, &uc_mgr->active_modifiers); - } - seq_found = 1; - break; - } - } - if (!seq_found) { - err = set_modifier(uc_mgr, xold, 0); - if (err < 0) - return err; - err = set_modifier(uc_mgr, xnew, 1); - if (err < 0) - return err; - } - return err; + if (err >= 0) { + list_del(&xold->active_list); + list_add_tail(&xnew->active_list, &uc_mgr->active_modifiers); + } + seq_found = 1; + break; + } + } + if (!seq_found) { + err = set_modifier(uc_mgr, xold, 0); + if (err < 0) + return err; + err = set_modifier(uc_mgr, xnew, 1); + if (err < 0) + return err; + } + return err; } /** @@ -2030,47 +2590,53 @@ * \return Zero if success, otherwise a negative error code */ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, - const char *identifier, - const char *value) + const char *identifier, + const char *value) { char *str, *str1; int err = 0; pthread_mutex_lock(&uc_mgr->mutex); - if (strcmp(identifier, "_verb") == 0) - err = set_verb_user(uc_mgr, value); - else if (strcmp(identifier, "_enadev") == 0) - err = set_device_user(uc_mgr, value, 1); - else if (strcmp(identifier, "_disdev") == 0) - err = set_device_user(uc_mgr, value, 0); - else if (strcmp(identifier, "_enamod") == 0) - err = set_modifier_user(uc_mgr, value, 1); - else if (strcmp(identifier, "_dismod") == 0) - err = set_modifier_user(uc_mgr, value, 0); - else { - str1 = strchr(identifier, '/'); - if (str1) { - str = strdup(str1 + 1); - if (str == NULL) { - err = -ENOMEM; - goto __end; - } - } else { - err = -EINVAL; - goto __end; - } - if (check_identifier(identifier, "_swdev")) - err = switch_device(uc_mgr, str, value); - else if (check_identifier(identifier, "_swmod")) - err = switch_modifier(uc_mgr, str, value); - else - err = -EINVAL; - if (str) - free(str); - } + if (strcmp(identifier, "_fboot") == 0) + err = set_fixedboot_user(uc_mgr, value); + else if (strcmp(identifier, "_boot") == 0) + err = set_boot_user(uc_mgr, value); + else if (strcmp(identifier, "_defaults") == 0) + err = set_defaults_user(uc_mgr, value); + else if (strcmp(identifier, "_verb") == 0) + err = set_verb_user(uc_mgr, value); + else if (strcmp(identifier, "_enadev") == 0) + err = set_device_user(uc_mgr, value, 1); + else if (strcmp(identifier, "_disdev") == 0) + err = set_device_user(uc_mgr, value, 0); + else if (strcmp(identifier, "_enamod") == 0) + err = set_modifier_user(uc_mgr, value, 1); + else if (strcmp(identifier, "_dismod") == 0) + err = set_modifier_user(uc_mgr, value, 0); + else { + str1 = strchr(identifier, '/'); + if (str1) { + str = strdup(str1 + 1); + if (str == NULL) { + err = -ENOMEM; + goto __end; + } + } else { + err = -EINVAL; + goto __end; + } + if (check_identifier(identifier, "_swdev")) + err = switch_device(uc_mgr, str, value); + else if (check_identifier(identifier, "_swmod")) + err = switch_modifier(uc_mgr, str, value); + else + err = -EINVAL; + if (str) + free(str); + } __end: pthread_mutex_unlock(&uc_mgr->mutex); - return err; + return err; } /** diff -Nru alsa-lib-1.2.2/src/ucm/parser.c alsa-lib-1.2.6.1/src/ucm/parser.c --- alsa-lib-1.2.2/src/ucm/parser.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/parser.c 2021-12-09 13:17:59.000000000 +0000 @@ -31,97 +31,53 @@ */ #include "ucm_local.h" +#include #include #include -/* Directories to store UCM configuration files for components, like - * off-soc codecs or embedded DSPs. Components can define their own - * devices and sequences, to be reused by sound cards/machines. UCM - * manager should not scan these component directories. - * Machine use case files can include component configratuation files - * via alsaconf syntax: - * and . - * Alsaconf will import the included files automatically. After including - * a component file, a machine device's sequence can enable or disable - * a component device via syntax: - * enadev "component_device_name" - * disdev "component_device_name" - */ -static const char * const component_dir[] = { - "codecs", /* for off-soc codecs */ - "dsps", /* for DSPs embedded in SoC */ - "platforms", /* for common platform implementations */ - NULL, /* terminator */ -}; - static int filename_filter(const struct dirent *dirent); -static int is_component_directory(const char *dir); static int parse_sequence(snd_use_case_mgr_t *uc_mgr, struct list_head *base, snd_config_t *cfg); /* - * compose configuration file + * compose the absolute ucm filename */ -static void configuration_filename2(char *fn, size_t fn_len, int format, - const char *dir, const char *file, - const char *suffix) +static void ucm_filename(char *fn, size_t fn_len, long version, + const char *dir, const char *file) { - snprintf(fn, fn_len, "%s/ucm%s/%s/%s%s", - snd_config_topdir(), format >= 2 ? "2" : "", - dir, file, suffix); + const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR); + + if (file[0] == '/') + file++; + if (env == NULL) + snprintf(fn, fn_len, "%s/%s/%s%s%s", + snd_config_topdir(), version > 1 ? "ucm2" : "ucm", + dir ?: "", dir ? "/" : "", file); + else + snprintf(fn, fn_len, "%s/%s%s%s", + env, dir ?: "", dir ? "/" : "", file); } -static void configuration_filename(snd_use_case_mgr_t *uc_mgr, - char *fn, size_t fn_len, - const char *dir, const char *file, - const char *suffix) +/* + * + */ +int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, + const char *file, snd_config_t **cfg) { - const char *env; - - if (uc_mgr->conf_format > 0) { - /* known format */ - env = getenv(uc_mgr->conf_format >= 2 ? ALSA_CONFIG_UCM2_VAR : - ALSA_CONFIG_UCM_VAR); - } else { - /* auto-detect */ - env = getenv(ALSA_CONFIG_UCM2_VAR); - if (env == NULL) { - env = getenv(ALSA_CONFIG_UCM_VAR); - if (env) - uc_mgr->conf_format = 1; - } else { - uc_mgr->conf_format = 2; - } - } - if (env) { - snprintf(fn, fn_len, "%s/%s/%s%s", env, dir, file, suffix); - return; - } - - if (uc_mgr->conf_format > 0) { - configuration_filename2(fn, fn_len, uc_mgr->conf_format, - dir, file, suffix); - return; - } - - configuration_filename2(fn, fn_len, 2, dir, file, suffix); - if (access(fn, R_OK) == 0) { - /* Found an ucm2 file, only look in the ucm2 dir from now on */ - uc_mgr->conf_format = 2; - return; - } + char filename[PATH_MAX]; + int err; - configuration_filename2(fn, fn_len, 0, dir, file, suffix); - if (access(fn, R_OK) == 0) { - /* Found an ucm1 file, only look in the ucm dir from now on */ - uc_mgr->conf_format = 1; - return; + ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, + file[0] == '/' ? NULL : uc_mgr->conf_dir_name, + file); + err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg); + if (err < 0) { + uc_error("error: failed to open file %s: %d", filename, err); + return err; } - - /* make sure that the error message refers to the new path */ - configuration_filename2(fn, fn_len, 2, dir, file, suffix); + return 0; } /* @@ -130,7 +86,7 @@ static char *replace_string(char **dst, const char *value) { free(*dst); - *dst = strdup(value); + *dst = value ? strdup(value) : NULL; return *dst; } @@ -151,6 +107,80 @@ } /* + * Parse string and substitute + */ +int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, + snd_config_t *n, char **res) +{ + const char *str; + char *s; + int err; + + err = snd_config_get_string(n, &str); + if (err < 0) + return err; + err = uc_mgr_get_substituted_value(uc_mgr, &s, str); + if (err >= 0) + *res = s; + return err; +} + +/* + * Parse string and substitute + */ +int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, + snd_config_t *n, char **res) +{ + if (uc_mgr->conf_format < 3) + return parse_string(n, res); + return parse_string_substitute(uc_mgr, n, res); +} + +/* + * Parse integer with substitution + */ +int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, + snd_config_t *n, long *res) +{ + char *s1, *s2; + int err; + + err = snd_config_get_ascii(n, &s1); + if (err < 0) + return err; + err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1); + if (err >= 0) + err = safe_strtol(s2, res); + free(s2); + free(s1); + return err; +} + +/* + * Parse integer with substitution + */ +int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, + snd_config_t *n, long *res) +{ + char *s1, *s2; + int err; + + err = snd_config_get_ascii(n, &s1); + if (err < 0) + return err; + if (uc_mgr->conf_format < 3) + s2 = s1; + else + err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1); + if (err >= 0) + err = safe_strtol(s2, res); + if (s1 != s2) + free(s2); + free(s1); + return err; +} + +/* * Parse safe ID */ int parse_is_name_safe(const char *name) @@ -162,19 +192,170 @@ return 1; } -int parse_get_safe_id(snd_config_t *n, const char **id) +int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) +{ + if (uc_mgr->conf_format < 3) { + *s = strdup(s1); + if (*s == NULL) + return -ENOMEM; + return 0; + } + return uc_mgr_get_substituted_value(uc_mgr, s, s1); +} + +int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, + const char *alt, char **name) +{ + const char *id; + int err; + + if (alt) { + id = alt; + } else { + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + } + err = get_string3(uc_mgr, id, name); + if (err < 0) + return err; + if (!parse_is_name_safe(*name)) { + free(*name); + return -EINVAL; + } + return 0; +} + +/* + * Handle 'Error' configuration node. + */ +static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + int err; + char *s; + + err = parse_string_substitute3(uc_mgr, cfg, &s); + if (err < 0) { + uc_error("error: failed to get Error string"); + return err; + } + if (!uc_mgr->suppress_nodev_errors) + uc_error("%s", s); + free(s); + return -ENXIO; +} + +/* + * Evaluate variable regex definitions (in-place delete) + */ +static int evaluate_regex(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) { + snd_config_iterator_t i, next; + snd_config_t *d, *n; + const char *id; int err; - err = snd_config_get_id(n, id); + err = snd_config_search(cfg, "DefineRegex", &d); + if (err == -ENOENT) + return 1; if (err < 0) return err; - if (!parse_is_name_safe((char *)(*id))) + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineRegex"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 3) { + uc_error("DefineRegex is supported in v3+ syntax"); return -EINVAL; + } + + snd_config_for_each(i, next, d) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + err = uc_mgr_define_regex(uc_mgr, id, n); + if (err < 0) + return err; + } + + snd_config_delete(d); return 0; } /* + * Evaluate variable definitions (in-place delete) + */ +static int evaluate_define(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *d, *n; + const char *id; + char *var, *s; + int err; + + err = snd_config_search(cfg, "Define", &d); + if (err == -ENOENT) + return evaluate_regex(uc_mgr, cfg); + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for Define"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 3) { + uc_error("Define is supported in v3+ syntax"); + return -EINVAL; + } + + snd_config_for_each(i, next, d) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + err = snd_config_get_ascii(n, &var); + if (err < 0) + return err; + err = uc_mgr_get_substituted_value(uc_mgr, &s, var); + free(var); + if (err < 0) + return err; + uc_mgr_set_variable(uc_mgr, id, s); + free(s); + } + + snd_config_delete(d); + + return evaluate_regex(uc_mgr, cfg); +} + +/* + * Evaluate include (in-place) + */ +static int evaluate_include(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_t *n; + int err; + + err = snd_config_search(cfg, "Include", &n); + if (err == -ENOENT) + return 1; + if (err < 0) + return err; + + err = uc_mgr_evaluate_include(uc_mgr, cfg, n); + snd_config_delete(n); + return err; +} + +/* * Evaluate condition (in-place) */ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, @@ -185,7 +366,7 @@ err = snd_config_search(cfg, "If", &n); if (err == -ENOENT) - return 0; + return 1; if (err < 0) return err; @@ -195,6 +376,152 @@ } /* + * In-place evaluate + */ +int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + int err1 = 0, err2 = 0, err3 = 0; + + while (err1 == 0 || err2 == 0 || err3 == 0) { + /* variables at first */ + err1 = evaluate_define(uc_mgr, cfg); + if (err1 < 0) + return err1; + /* include at second */ + err2 = evaluate_include(uc_mgr, cfg); + if (err2 < 0) + return err2; + /* include may define another variables */ + /* conditions may depend on them */ + if (err2 == 0) + continue; + err3 = evaluate_condition(uc_mgr, cfg); + if (err3 < 0) + return err3; + } + return 0; +} + +/* + * Parse one item for alsa-lib config + */ +static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n, *config = NULL; + const char *id, *file = NULL; + bool substfile = false, substconfig = false; + int err; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + return -EINVAL; + + if (strcmp(id, "File") == 0 || + strcmp(id, "SubstiFile") == 0) { + substfile = id[0] == 'S'; + err = snd_config_get_string(n, &file); + if (err < 0) + return err; + continue; + } + + if (strcmp(id, "Config") == 0 || + strcmp(id, "SubstiConfig") == 0) { + substconfig = id[0] == 'S'; + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + config = n; + continue; + } + + uc_error("unknown field %s", id); + return -EINVAL; + } + + if (file) { + if (substfile) { + snd_config_t *cfg; + err = uc_mgr_config_load(uc_mgr->conf_format, file, &cfg); + if (err < 0) + return err; + err = uc_mgr_substitute_tree(uc_mgr, cfg); + if (err < 0) { + snd_config_delete(cfg); + return err; + } + err = snd_config_merge(uc_mgr->local_config, cfg, 0); + if (err < 0) { + snd_config_delete(cfg); + return err; + } + } else { + char filename[PATH_MAX]; + + ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, + file[0] == '/' ? NULL : uc_mgr->conf_dir_name, + file); + err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config); + if (err < 0) + return err; + } + } + + if (config) { + if (substconfig) { + err = uc_mgr_substitute_tree(uc_mgr, config); + if (err < 0) + return err; + } + err = snd_config_merge(uc_mgr->local_config, config, 0); + if (err < 0) + return err; + } + + return 0; +} + +/* + * Parse alsa-lib config + */ +static int parse_libconfig(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + int err; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + err = parse_libconfig1(uc_mgr, n); + if (err < 0) + return err; + } + + return 0; +} + +/* * Parse transition */ static int parse_transition(snd_use_case_mgr_t *uc_mgr, @@ -226,10 +553,10 @@ return -ENOMEM; INIT_LIST_HEAD(&tseq->transition_list); - tseq->name = strdup(id); - if (tseq->name == NULL) { + err = get_string3(uc_mgr, id, &tseq->name); + if (err < 0) { free(tseq); - return -ENOMEM; + return err; } err = parse_sequence(uc_mgr, &tseq->transition_list, n); @@ -330,7 +657,7 @@ sdev = calloc(1, sizeof(struct dev_list_node)); if (sdev == NULL) return -ENOMEM; - err = parse_string(n, &sdev->name); + err = parse_string_substitute3(uc_mgr, n, &sdev->name); if (err < 0) { free(sdev); return err; @@ -403,21 +730,23 @@ * disable sequence is needed by its parenet device. */ static int parse_component_seq(snd_use_case_mgr_t *uc_mgr, - snd_config_t *n, int enable, - struct component_sequence *cmpt_seq) + snd_config_t *n, int enable, + struct component_sequence *cmpt_seq) { - const char *val; + char *val; int err; - err = snd_config_get_string(n, &val); + err = parse_string_substitute3(uc_mgr, n, &val); if (err < 0) return err; cmpt_seq->device = find_component_dev(uc_mgr, val); if (!cmpt_seq->device) { uc_error("error: Cannot find component device %s", val); + free(val); return -EINVAL; } + free(val); /* Parent needs its enable or disable sequence */ cmpt_seq->enable = enable; @@ -482,7 +811,7 @@ if (strcmp(cmd, "cdev") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_CDEV; - err = parse_string(n, &curr->data.cdev); + err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev); if (err < 0) { uc_error("error: cdev requires a string!"); return err; @@ -492,9 +821,10 @@ if (strcmp(cmd, "cset") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_CSET; - err = parse_string(n, &curr->data.cset); +cset: + err = parse_string_substitute3(uc_mgr, n, &curr->data.cset); if (err < 0) { - uc_error("error: cset requires a string!"); + uc_error("error: %s requires a string!", cmd); return err; } continue; @@ -526,19 +856,29 @@ if (strcmp(cmd, "cset-bin-file") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE; - err = parse_string(n, &curr->data.cset); - if (err < 0) { - uc_error("error: cset-bin-file requires a string!"); - return err; - } - continue; + goto cset; } if (strcmp(cmd, "cset-tlv") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV; - err = parse_string(n, &curr->data.cset); + goto cset; + } + + if (strcmp(cmd, "cset-new") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW; + goto cset; + } + + if (strcmp(cmd, "ctl-remove") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_CTL_REMOVE; + goto cset; + } + + if (strcmp(cmd, "sysw") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_SYSSET; + err = parse_string_substitute3(uc_mgr, n, &curr->data.sysw); if (err < 0) { - uc_error("error: cset-tlv requires a string!"); + uc_error("error: sysw requires a string!"); return err; } continue; @@ -546,7 +886,7 @@ if (strcmp(cmd, "usleep") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP; - err = snd_config_get_integer(n, &curr->data.sleep); + err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep); if (err < 0) { uc_error("error: usleep requires integer!"); return err; @@ -556,7 +896,7 @@ if (strcmp(cmd, "msleep") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP; - err = snd_config_get_integer(n, &curr->data.sleep); + err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep); if (err < 0) { uc_error("error: msleep requires integer!"); return err; @@ -567,14 +907,36 @@ if (strcmp(cmd, "exec") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_EXEC; - err = parse_string(n, &curr->data.exec); +exec: + err = parse_string_substitute3(uc_mgr, n, &curr->data.exec); if (err < 0) { uc_error("error: exec requires a string!"); return err; } continue; } - + + if (strcmp(cmd, "shell") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_SHELL; + goto exec; + } + + if (strcmp(cmd, "cfg-save") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_CFGSAVE; + err = parse_string_substitute3(uc_mgr, n, &curr->data.cfgsave); + if (err < 0) { + uc_error("error: sysw requires a string!"); + return err; + } + continue; + } + + if (strcmp(cmd, "comment") == 0) + goto skip; + + uc_error("error: sequence command '%s' is ignored", cmd); + +skip: list_del(&curr->list); uc_mgr_free_sequence_element(curr); } @@ -629,8 +991,8 @@ return -EINVAL; } - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) return err; @@ -653,7 +1015,7 @@ } break; case SND_CONFIG_TYPE_STRING: - err = parse_string(n, &s); + err = parse_string_substitute(uc_mgr, n, &s); if (err < 0) { uc_error("error: unable to parse a string for id '%s'!", id); return err; @@ -716,41 +1078,35 @@ * Both are optional. */ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, - snd_config_t *cfg, - void *data1, - void *data2) + snd_config_t *cfg, + void *data1, void *data2) { struct use_case_verb *verb = data1; struct use_case_modifier *modifier; - const char *name; + char *name; snd_config_iterator_t i, next; snd_config_t *n; int err; - if (data2) { - name = data2; - if (!parse_is_name_safe(name)) - return -EINVAL; - } - else { - if (parse_get_safe_id(cfg, &name) < 0) - return -EINVAL; - } + if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0) + return -EINVAL; /* allocate modifier */ modifier = calloc(1, sizeof(*modifier)); - if (modifier == NULL) + if (modifier == NULL) { + free(name); return -ENOMEM; + } INIT_LIST_HEAD(&modifier->enable_list); INIT_LIST_HEAD(&modifier->disable_list); INIT_LIST_HEAD(&modifier->transition_list); INIT_LIST_HEAD(&modifier->dev_list.list); INIT_LIST_HEAD(&modifier->value_list); list_add_tail(&modifier->list, &verb->modifier_list); - modifier->name = strdup(name); + modifier->name = name; - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) return err; @@ -761,7 +1117,7 @@ continue; if (strcmp(id, "Comment") == 0) { - err = parse_string(n, &modifier->comment); + err = parse_string_substitute3(uc_mgr, n, &modifier->comment); if (err < 0) { uc_error("error: failed to get modifier comment"); return err; @@ -869,39 +1225,33 @@ */ static int parse_device(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, - void *data1, - void *data2) + void *data1, void *data2) { struct use_case_verb *verb = data1; - const char *name; + char *name; struct use_case_device *device; snd_config_iterator_t i, next; snd_config_t *n; int err; - if (data2) { - name = data2; - if (!parse_is_name_safe(name)) - return -EINVAL; - } - else { - if (parse_get_safe_id(cfg, &name) < 0) - return -EINVAL; - } + if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0) + return -EINVAL; device = calloc(1, sizeof(*device)); - if (device == NULL) + if (device == NULL) { + free(name); return -ENOMEM; + } INIT_LIST_HEAD(&device->enable_list); INIT_LIST_HEAD(&device->disable_list); INIT_LIST_HEAD(&device->transition_list); INIT_LIST_HEAD(&device->dev_list.list); INIT_LIST_HEAD(&device->value_list); list_add_tail(&device->list, &verb->device_list); - device->name = strdup(name); + device->name = name; - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) return err; @@ -912,7 +1262,7 @@ continue; if (strcmp(id, "Comment") == 0) { - err = parse_string(n, &device->comment); + err = parse_string_substitute3(uc_mgr, n, &device->comment); if (err < 0) { uc_error("error: failed to get device comment"); return err; @@ -994,13 +1344,14 @@ * RenameDevice."Speaker1" "Speaker" * RemoveDevice."Speaker2" "Speaker2" */ -static int parse_dev_name_list(snd_config_t *cfg, +static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, struct list_head *list) { snd_config_t *n; snd_config_iterator_t i, next; const char *id, *name1; - char *name2; + char *name1s, *name2; struct ucm_dev_name *dev; snd_config_iterator_t pos; int err; @@ -1019,8 +1370,13 @@ if (snd_config_get_id(n, &name1) < 0) return -EINVAL; - err = parse_string(n, &name2); + err = get_string3(uc_mgr, name1, &name1s); + if (err < 0) + return err; + + err = parse_string_substitute3(uc_mgr, n, &name2); if (err < 0) { + free(name1s); uc_error("error: failed to get target device name for '%s'", name1); return err; } @@ -1028,15 +1384,20 @@ /* skip duplicates */ list_for_each(pos, list) { dev = list_entry(pos, struct ucm_dev_name, list); - if (strcmp(dev->name1, name1) == 0) { + if (strcmp(dev->name1, name1s) == 0) { free(name2); + free(name1s); return 0; } } + free(name1s); + dev = calloc(1, sizeof(*dev)); - if (dev == NULL) + if (dev == NULL) { + free(name2); return -ENOMEM; + } dev->name1 = strdup(name1); if (dev->name1 == NULL) { free(dev); @@ -1109,7 +1470,7 @@ void *data1, void *data2 ATTRIBUTE_UNUSED) { - return parse_compound(uc_mgr, cfg, parse_modifier, data1, data2); + return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1); } static int verb_dev_list_add(struct use_case_verb *verb, @@ -1176,7 +1537,7 @@ } /* remove devices */ - list_for_each(pos, &verb->rename_list) { + list_for_each(pos, &verb->remove_list) { dev = list_entry(pos, struct ucm_dev_name, list); err = uc_mgr_remove_device(verb, dev->name2); if (err < 0) { @@ -1238,8 +1599,8 @@ snd_config_t *n; int err; - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) return err; @@ -1312,7 +1673,6 @@ snd_config_t *n; struct use_case_verb *verb; snd_config_t *cfg; - char filename[PATH_MAX]; int err; /* allocate verb */ @@ -1342,19 +1702,14 @@ } /* open Verb file for reading */ - configuration_filename(uc_mgr, filename, sizeof(filename), - uc_mgr->conf_dir_name, file, ""); - err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg); - if (err < 0) { - uc_error("error: failed to open verb file %s : %d", - filename, -errno); + err = uc_mgr_config_load_file(uc_mgr, file, &cfg); + if (err < 0) return err; - } - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) - return err; + goto _err; /* parse master config sections */ snd_config_for_each(i, next, cfg) { @@ -1400,22 +1755,34 @@ /* device renames */ if (strcmp(id, "RenameDevice") == 0) { - err = parse_dev_name_list(n, &verb->rename_list); + err = parse_dev_name_list(uc_mgr, n, &verb->rename_list); if (err < 0) { uc_error("error: %s failed to parse device rename", file); goto _err; } + continue; } /* device remove */ if (strcmp(id, "RemoveDevice") == 0) { - err = parse_dev_name_list(n, &verb->remove_list); + err = parse_dev_name_list(uc_mgr, n, &verb->remove_list); if (err < 0) { uc_error("error: %s failed to parse device remove", file); goto _err; } + continue; + } + + /* alsa-lib configuration */ + if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { + err = parse_libconfig(uc_mgr, n); + if (err < 0) { + uc_error("error: failed to parse LibConfig"); + goto _err; + } + continue; } } @@ -1450,23 +1817,24 @@ { snd_config_iterator_t i, next; snd_config_t *n; - const char *use_case_name, *file = NULL, *comment = NULL; + char *use_case_name, *file = NULL, *comment = NULL; int err; - if (snd_config_get_id(cfg, &use_case_name) < 0) { - uc_error("unable to get name for use case section"); - return -EINVAL; - } - if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { uc_error("compound type expected for use case section"); return -EINVAL; } - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); - if (err < 0) + err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name); + if (err < 0) { + uc_error("unable to get name for use case section"); return err; + } + + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); + if (err < 0) + goto __error; /* parse master config sections */ snd_config_for_each(i, next, cfg) { @@ -1477,20 +1845,20 @@ /* get use case verb file name */ if (strcmp(id, "File") == 0) { - err = snd_config_get_string(n, &file); + err = parse_string_substitute3(uc_mgr, n, &file); if (err < 0) { uc_error("failed to get File"); - return err; + goto __error; } continue; } /* get optional use case comment */ if (strncmp(id, "Comment", 7) == 0) { - err = snd_config_get_string(n, &comment); + err = parse_string_substitute3(uc_mgr, n, &comment); if (err < 0) { uc_error("error: failed to get Comment"); - return err; + goto __error; } continue; } @@ -1503,11 +1871,58 @@ /* do we have both use case name and file ? */ if (!file) { uc_error("error: use case missing file"); - return -EINVAL; + err = -EINVAL; + goto __error; } /* parse verb file */ - return parse_verb_file(uc_mgr, use_case_name, comment, file); + err = parse_verb_file(uc_mgr, use_case_name, comment, file); + +__error: + free(use_case_name); + free(file); + free(comment); + return err; +} + +/* + * parse controls which should be run only at initial boot (forcefully) + */ +static int parse_controls_fixedboot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + int err; + + if (!list_empty(&uc_mgr->fixedboot_list)) { + uc_error("FixedBoot list is not empty"); + return -EINVAL; + } + err = parse_sequence(uc_mgr, &uc_mgr->fixedboot_list, cfg); + if (err < 0) { + uc_error("Unable to parse FixedBootSequence"); + return err; + } + + return 0; +} + +/* + * parse controls which should be run only at initial boot + */ +static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + int err; + + if (!list_empty(&uc_mgr->boot_list)) { + uc_error("Boot list is not empty"); + return -EINVAL; + } + err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg); + if (err < 0) { + uc_error("Unable to parse BootSequence"); + return err; + } + + return 0; } /* @@ -1516,7 +1931,7 @@ static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) { int err; - + if (!list_empty(&uc_mgr->default_list)) { uc_error("Default list is not empty"); return -EINVAL; @@ -1526,7 +1941,7 @@ uc_error("Unable to parse SectionDefaults"); return err; } - + return 0; } @@ -1558,11 +1973,16 @@ * CaptureCTL "hw:CARD=0" * } * + * # The initial boot (run once) configuration. + * + * BootSequence [ + * cset "name='Master Playback Switch',index=2 1,1" + * cset "name='Master Playback Volume',index=2 25,25" + * ] + * * # This file also stores the default sound card state. * * SectionDefaults [ - * cset "name='Master Playback Switch',index=2 1,1" - * cset "name='Master Playback Volume',index=2 25,25" * cset "name='Master Mono Playback',index=1 0" * cset "name='Master Mono Playback Volume',index=1 0" * cset "name='PCM Switch',index=2 1,1" @@ -1603,10 +2023,11 @@ } /* delete this field to avoid strcmp() call in the loop */ snd_config_delete(n); + uc_mgr->conf_format = l; } - /* in-place condition evaluation */ - err = evaluate_condition(uc_mgr, cfg); + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); if (err < 0) return err; @@ -1618,7 +2039,7 @@ continue; if (strcmp(id, "Comment") == 0) { - err = parse_string(n, &uc_mgr->comment); + err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment); if (err < 0) { uc_error("error: failed to get master comment"); return err; @@ -1636,6 +2057,22 @@ continue; } + /* find default control values section (force boot sequence only) */ + if (strcmp(id, "FixedBootSequence") == 0) { + err = parse_controls_fixedboot(uc_mgr, n); + if (err < 0) + return err; + continue; + } + + /* find default control values section (first boot only) */ + if (strcmp(id, "BootSequence") == 0) { + err = parse_controls_boot(uc_mgr, n); + if (err < 0) + return err; + continue; + } + /* find default control values section and parse it */ if (strcmp(id, "SectionDefaults") == 0) { err = parse_controls(uc_mgr, n); @@ -1654,7 +2091,21 @@ continue; } - uc_error("uknown master file field %s", id); + /* alsa-lib configuration */ + if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { + err = parse_libconfig(uc_mgr, n); + if (err < 0) { + uc_error("error: failed to parse LibraryConfig"); + return err; + } + continue; + } + + /* error */ + if (strcmp(id, "Error") == 0) + return error_node(uc_mgr, n); + + uc_error("unknown master file field %s", id); } return 0; } @@ -1662,34 +2113,24 @@ /* get the card info */ static int get_card_info(snd_use_case_mgr_t *mgr, const char *ctl_name, - snd_ctl_t **_handle, - snd_ctl_card_info_t *info) + snd_ctl_card_info_t **info) { - snd_ctl_t *handle; + struct ctl_list *ctl_list; int err; - *_handle = NULL; - - err = uc_mgr_open_ctl(mgr, &handle, ctl_name); + err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0); if (err < 0) return err; - err = snd_ctl_card_info(handle, info); - if (err < 0) { - uc_error("control hardware info (%s): %s", ctl_name, snd_strerror(err)); - } else { - *_handle = handle; - } - + if (info) + *info = ctl_list->ctl_info; return err; } -/* find the card in the local machine and store the card long name */ -static int get_card_long_name(snd_use_case_mgr_t *mgr, char *longname) +/* find the card in the local machine */ +static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name) { - const char *card_name = mgr->card_name; int card, err; - snd_ctl_t *ctl; snd_ctl_card_info_t *info; const char *_driver, *_name, *_long_name; @@ -1704,11 +2145,11 @@ while (card >= 0) { char name[32]; - /* most probably, we do not need to cache all CTL devices here */ + /* clear the list, keep the only one CTL device */ uc_mgr_free_ctl_list(mgr); sprintf(name, "hw:%d", card); - err = get_card_info(mgr, name, &ctl, info); + err = get_card_info(mgr, name, &info); if (err == 0) { _driver = snd_ctl_card_info_get_driver(info); @@ -1716,10 +2157,8 @@ _long_name = snd_ctl_card_info_get_longname(info); if (!strcmp(card_name, _driver) || !strcmp(card_name, _name) || - !strcmp(card_name, _long_name)) { - snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME); + !strcmp(card_name, _long_name)) return 0; - } } if (snd_card_next(&card) < 0) { @@ -1734,126 +2173,255 @@ } /* set the driver name and long name by the card ctl name */ -static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *longname) +static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name) { - snd_ctl_t *ctl; - snd_ctl_card_info_t *info; - const char *_driver, *_long_name; + return get_card_info(mgr, ctl_name, NULL); +} + +static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr, + char *filename, + snd_config_t *cfg) +{ + snd_config_iterator_t i, next, i2, next2; + snd_config_t *n, *n2; + const char *id; + char *dir = NULL, *file = NULL, fn[PATH_MAX]; + long version; int err; - snd_ctl_card_info_alloca(&info); + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for UseCasePath node"); + return -EINVAL; + } - err = get_card_info(mgr, ctl_name, &ctl, info); - if (err) - return err; + /* parse use case path config sections */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); - _driver = snd_ctl_card_info_get_driver(info); - if (replace_string(&mgr->conf_dir_name, _driver) == NULL) - return -ENOMEM; - _long_name = snd_ctl_card_info_get_longname(info); - snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for UseCasePath.something node"); + return -EINVAL; + } - return 0; + if (snd_config_get_id(n, &id) < 0) + continue; + + version = 2; + + /* parse use case path config sections */ + snd_config_for_each(i2, next2, n) { + + n2 = snd_config_iterator_entry(i2); + if (snd_config_get_id(n2, &id) < 0) + continue; + + if (strcmp(id, "Version") == 0) { + err = parse_integer_substitute(uc_mgr, n2, &version); + if (err < 0) { + uc_error("unable to parse UcmDirectory"); + goto __error; + } + if (version < 1 || version > 2) { + uc_error("Version must be 1 or 2"); + err = -EINVAL; + goto __error; + } + continue; + } + + if (strcmp(id, "Directory") == 0) { + err = parse_string_substitute(uc_mgr, n2, &dir); + if (err < 0) { + uc_error("unable to parse Directory"); + goto __error; + } + continue; + } + + if (strcmp(id, "File") == 0) { + err = parse_string_substitute(uc_mgr, n2, &file); + if (err < 0) { + uc_error("unable to parse File"); + goto __error; + } + continue; + } + + uc_error("unknown UseCasePath field %s", id); + } + + if (dir == NULL) { + uc_error("Directory is not defined in %s!", filename); + goto __next; + } + if (file == NULL) { + uc_error("File is not defined in %s!", filename); + goto __next; + } + + ucm_filename(fn, sizeof(fn), version, dir, file); + if (access(fn, R_OK) == 0) { + if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL) { + err = -ENOMEM; + goto __error; + } + if (replace_string(&uc_mgr->conf_file_name, file) == NULL) { + err = -ENOMEM; + goto __error; + } + strncpy(filename, fn, PATH_MAX); + uc_mgr->conf_format = version; + goto __ok; + } + +__next: + free(file); + free(dir); + dir = NULL; + file = NULL; + } + + err = -ENOENT; + goto __error; + +__ok: + err = 0; +__error: + free(file); + free(dir); + return err; } -static int load_master_config(snd_use_case_mgr_t *uc_mgr, - const char *card_name, snd_config_t **cfg, int longname) +static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, + char *filename, + snd_config_t *cfg) { - char filename[PATH_MAX]; + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + long l; int err; - if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) { - uc_error("error: invalid card name %s (at most %d chars)", - card_name, MAX_CARD_LONG_NAME - 1); + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for toplevel file"); return -EINVAL; } - uc_mgr->conf_format = 0; - if (longname) { - if (getenv(ALSA_CONFIG_UCM2_VAR) || !getenv(ALSA_CONFIG_UCM_VAR)) { - uc_mgr->conf_format = 2; - configuration_filename(uc_mgr, filename, sizeof(filename), - uc_mgr->conf_dir_name, card_name, ".conf"); - if (access(filename, R_OK) == 0) - goto __load; - } - /* try the old ucm directory */ - uc_mgr->conf_format = 1; - configuration_filename(uc_mgr, filename, sizeof(filename), - card_name, card_name, ".conf"); - if (access(filename, R_OK) != 0) - return -ENOENT; - } else { - configuration_filename(uc_mgr, filename, sizeof(filename), - card_name, card_name, ".conf"); + err = snd_config_search(cfg, "Syntax", &n); + if (err < 0) { + uc_error("Syntax field not found in %s", filename); + return -EINVAL; + } + err = snd_config_get_integer(n, &l); + if (err < 0) { + uc_error("Syntax field is invalid in %s", filename); + return err; + } + if (l < 2 || l > SYNTAX_VERSION_MAX) { + uc_error("Incompatible syntax %d in %s", l, filename); + return -EINVAL; } + /* delete this field to avoid strcmp() call in the loop */ + snd_config_delete(n); + uc_mgr->conf_format = l; + + /* in-place evaluation */ + err = uc_mgr_evaluate_inplace(uc_mgr, cfg); + if (err < 0) + return err; + + /* parse toplevel config sections */ + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "UseCasePath") == 0) { + err = parse_toplevel_path(uc_mgr, filename, n); + if (err == 0) + return err; + continue; + } + + /* alsa-lib configuration */ + if (uc_mgr->conf_format > 3 && strcmp(id, "LibraryConfig") == 0) { + err = parse_libconfig(uc_mgr, n); + if (err < 0) { + uc_error("error: failed to parse LibConfig"); + return err; + } + continue; + } + + uc_error("unknown toplevel field %s", id); + } + + return -ENOENT; +} + +static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr, + snd_config_t **cfg) +{ + char filename[PATH_MAX]; + snd_config_t *tcfg; + int err; + + ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf"); + + if (access(filename, R_OK) != 0) { + uc_error("Unable to find the top-level configuration file '%s'.", filename); + return -ENOENT; + } + + err = uc_mgr_config_load(2, filename, &tcfg); + if (err < 0) + goto __error; + + /* filename is shared for function input and output! */ + err = parse_toplevel_config(uc_mgr, filename, tcfg); + snd_config_delete(tcfg); + if (err < 0) + goto __error; -__load: err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg); if (err < 0) { uc_error("error: could not parse configuration for card %s", - card_name); - return err; + uc_mgr->card_name); + goto __error; } - if (replace_string(&uc_mgr->conf_file_name, card_name) == NULL) - return -ENOMEM; - return 0; + +__error: + return err; } -/* load master use case file for sound card - * - * The same ASoC machine driver can be shared by many different devices. - * For user space to differentiate them and get the best device-specific - * configuration, ASoC machine drivers may use the DMI info - * (vendor-product-version-board) as the card long name. And user space can - * define configuration files like longnamei/longname.conf for a specific device. - * - * This function will try to find the card in the local machine and get its - * long name, then load the file longname/longname.conf to get the best - * device-specific configuration. If the card is not found in the local - * machine or the device-specific file is not available, fall back to load - * the default configuration file name/name.conf. +/* load master use case file for sound card based on rules in ucm2/ucm.conf */ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) { snd_config_t *cfg; - const char *name = uc_mgr->card_name; - char longname[MAX_CARD_LONG_NAME]; + const char *name; int err; - if (replace_string(&uc_mgr->conf_dir_name, uc_mgr->card_name) == NULL) - return -ENOMEM; - + name = uc_mgr->card_name; if (strncmp(name, "hw:", 3) == 0) { - err = get_by_card(uc_mgr, name, longname); - if (err == 0) - goto __longname; - uc_error("card '%s' is not valid", name); - goto __error; - } else if (strncmp(name, "strict:", 7)) { - err = get_card_long_name(uc_mgr, longname); - if (err == 0) { /* load file that matches the card long name */ -__longname: - err = load_master_config(uc_mgr, longname, &cfg, 1); - } - - if (err == 0) { - /* got device-specific file that matches the card long name */ - if (uc_mgr->conf_format < 2) - snd_strlcpy(uc_mgr->conf_dir_name, longname, - sizeof(uc_mgr->conf_dir_name)); - goto __parse; + err = get_by_card(uc_mgr, name); + if (err < 0) { + uc_error("card '%s' is not valid", name); + goto __error; } + } else if (strncmp(name, "strict:", 7)) { + /* do not handle the error here */ + /* we can refer the virtual UCM config */ + get_by_card_name(uc_mgr, name); } - /* standard path */ - err = load_master_config(uc_mgr, uc_mgr->conf_dir_name, &cfg, 0); + err = load_toplevel_config(uc_mgr, &cfg); if (err < 0) goto __error; -__parse: err = parse_master_file(uc_mgr, cfg); snd_config_delete(cfg); if (err < 0) { @@ -1865,7 +2433,7 @@ __error: uc_mgr_free_ctl_list(uc_mgr); - uc_mgr->conf_dir_name[0] = '\0'; + replace_string(&uc_mgr->conf_dir_name, NULL); return err; } @@ -1886,20 +2454,6 @@ return 0; } -/* whether input dir is a predefined component directory */ -static int is_component_directory(const char *dir) -{ - int i = 0; - - while (component_dir[i]) { - if (!strncmp(dir, component_dir[i], PATH_MAX)) - return 1; - i++; - }; - - return 0; -} - /* scan all cards and comments * * Cards are defined by machines. Each card/machine installs its UCM @@ -1910,7 +2464,7 @@ */ int uc_mgr_scan_master_configs(const char **_list[]) { - char filename[PATH_MAX], dfl[PATH_MAX]; + char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX]; char *env = getenv(ALSA_CONFIG_UCM2_VAR); const char **list, *d_name; snd_config_t *cfg, *c; @@ -1920,9 +2474,9 @@ struct dirent **namelist; if (env) - snprintf(filename, sizeof(filename), "%s", env); + snprintf(filename, sizeof(filename), "%s/conf.virt.d", env); else - snprintf(filename, sizeof(filename), "%s/ucm2", + snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d", snd_config_topdir()); #if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID) @@ -1963,12 +2517,15 @@ d_name = namelist[i]->d_name; - /* Skip the directories for component devices */ - if (is_component_directory(d_name)) + snprintf(fn, sizeof(fn), "%s.conf", d_name); + ucm_filename(filename, sizeof(filename), 2, d_name, fn); +#ifdef HAVE_EACCESS + if (eaccess(filename, R_OK)) +#else + if (access(filename, R_OK)) +#endif continue; - configuration_filename2(filename, sizeof(filename), 2, - d_name, d_name, ".conf"); err = uc_mgr_config_load(2, filename, &cfg); if (err < 0) goto __err; diff -Nru alsa-lib-1.2.2/src/ucm/ucm_cond.c alsa-lib-1.2.6.1/src/ucm/ucm_cond.c --- alsa-lib-1.2.2/src/ucm/ucm_cond.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_cond.c 2021-12-09 13:17:59.000000000 +0000 @@ -44,6 +44,23 @@ char *s1, *s2; int err; + if (uc_mgr->conf_format >= 3) { + err = get_string(eval, "Empty", &string1); + if (err < 0 && err != -ENOENT) { + uc_error("String error (If.Condition.Empty)"); + return -EINVAL; + } + + if (string1) { + err = uc_mgr_get_substituted_value(uc_mgr, &s1, string1); + if (err < 0) + return err; + err = s1 == NULL || s1[0] == '\0'; + free(s1); + return err; + } + } + err = get_string(eval, "String1", &string1); if (err < 0 && err != -ENOENT) { uc_error("String error (If.Condition.String1)"); @@ -143,11 +160,12 @@ if (err < 0) return err; err = regcomp(&re, s, options); - free(s); if (err) { - uc_error("Regex '%s' compilation failed (code %d)", err); + uc_error("Regex '%s' compilation failed (code %d)", s, err); + free(s); return -EINVAL; } + free(s); err = uc_mgr_get_substituted_value(uc_mgr, &s, string); if (err < 0) { @@ -163,6 +181,7 @@ static int if_eval_control_exists(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) { snd_ctl_t *ctl; + struct ctl_list *ctl_list; const char *device = NULL, *ctldef, *enumval = NULL, *name; snd_ctl_elem_id_t *elem_id; snd_ctl_elem_info_t *elem_info; @@ -211,10 +230,11 @@ err = uc_mgr_get_substituted_value(uc_mgr, &s, device); if (err < 0) return err; - err = uc_mgr_open_ctl(uc_mgr, &ctl, s); + err = uc_mgr_open_ctl(uc_mgr, &ctl_list, s, 1); free(s); if (err < 0) return err; + ctl = ctl_list->ctl; } snd_ctl_elem_info_set_id(elem_info, elem_id); @@ -250,6 +270,51 @@ return 1; } +static int if_eval_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) +{ + const char *path, *mode = ""; + int err, amode = F_OK; + + if (uc_mgr->conf_format < 4) { + uc_error("Path condition is supported in v4+ syntax"); + return -EINVAL; + } + + err = get_string(eval, "Path", &path); + if (err < 0) { + uc_error("Path error (If.Condition.Path)"); + return -EINVAL; + } + + err = get_string(eval, "Mode", &mode); + if (err < 0 && err != -ENOENT) { + uc_error("Path error (If.Condition.Mode)"); + return -EINVAL; + } + + if (strncasecmp(mode, "exist", 5) == 0) { + amode = F_OK; + } else if (strcasecmp(mode, "read") == 0) { + amode = R_OK; + } else if (strcasecmp(mode, "write") == 0) { + amode = W_OK; + } else if (strcasecmp(mode, "exec") == 0) { + amode = X_OK; + } else { + uc_error("Path unknown mode (If.Condition.Mode)"); + return -EINVAL; + } + +#ifdef HAVE_EACCESS + if (eaccess(path, amode)) +#else + if (access(path, amode)) +#endif + return 0; + + return 1; +} + static int if_eval(snd_use_case_mgr_t *uc_mgr, snd_config_t *eval) { const char *type; @@ -266,15 +331,21 @@ return -EINVAL; } - if (strcmp(type, "ControlExists") == 0) - return if_eval_control_exists(uc_mgr, eval); + if (strcmp(type, "AlwaysTrue") == 0) + return 1; if (strcmp(type, "String") == 0) return if_eval_string(uc_mgr, eval); + if (strcmp(type, "ControlExists") == 0) + return if_eval_control_exists(uc_mgr, eval); + if (strcmp(type, "RegexMatch") == 0) return if_eval_regex_match(uc_mgr, eval); + if (strcmp(type, "Path") == 0) + return if_eval_path(uc_mgr, eval); + uc_error("unknown If.Condition.Type"); return -EINVAL; } @@ -347,72 +418,6 @@ } #endif -static int compound_merge(const char *id, - snd_config_t *dst, snd_config_t *src, - snd_config_t *before, snd_config_t *after) -{ - snd_config_iterator_t i, next; - snd_config_t *n, *_before = NULL, *_after = NULL; - const char *s; - int err; - - if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { - uc_error("compound type expected for If True/False block"); - return -EINVAL; - } - - if (before) { - err = get_string(before, id, &s); - if (err < 0 && err != -ENOENT) - return err; - if (err == 0) { - err = snd_config_search(dst, s, &_before); - if (err < 0 && err != -ENOENT) - return err; - } - } - if (after) { - err = get_string(after, id, &s); - if (err < 0 && err != -ENOENT) - return err; - if (err == 0) { - err = snd_config_search(dst, s, &_after); - if (err < 0 && err != -ENOENT) - return err; - } - } - - if (_before && _after) { - uc_error("defined both before and after identifiers in the If block"); - return -EINVAL; - } - - snd_config_for_each(i, next, src) { - n = snd_config_iterator_entry(i); - err = snd_config_remove(n); - if (err < 0) - return err; - if (_before) { - err = snd_config_add_before(_before, n); - if (err < 0) - return err; - _before = NULL; - _after = n; - } else if (_after) { - err = snd_config_add_after(_after, n); - if (err < 0) - return err; - _after = n; - } else { - err = snd_config_add(dst, n); - if (err < 0) - return err; - } - } - - return 0; -} - /* * put back the result from all conditions to the parent */ @@ -420,9 +425,8 @@ snd_config_t *parent, snd_config_t *cond) { - snd_config_iterator_t i, i2, next, next2; - snd_config_t *a, *n, *n2, *parent2, *before, *after; - const char *id; + snd_config_iterator_t i, next; + snd_config_t *a, *n, *before, *after; int err; if (uc_mgr->conf_format < 2) { @@ -443,38 +447,13 @@ return err; if (a == NULL) continue; - err = snd_config_search(a, "If", &n2); - if (err < 0 && err != -ENOENT) { - uc_error("If block error (If)"); - return -EINVAL; - } else if (err == 0) { - err = uc_mgr_evaluate_condition(uc_mgr, a, n2); - if (err < 0) - return err; - snd_config_delete(n2); - } - snd_config_for_each(i2, next2, a) { - n2 = snd_config_iterator_entry(i2); - err = snd_config_remove(n2); - if (err < 0) - return err; - err = snd_config_get_id(n2, &id); - if (err < 0) { -__add: - err = snd_config_add(parent, n2); - if (err < 0) - return err; - continue; - } else { - err = snd_config_search(parent, id, &parent2); - if (err == -ENOENT) - goto __add; - err = compound_merge(id, parent2, n2, before, after); - if (err < 0) - return err; - } - snd_config_delete(n2); - } + err = uc_mgr_evaluate_inplace(uc_mgr, a); + if (err < 0) + return err; + err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after); + if (err < 0) + return err; + snd_config_delete(a); } return 0; } diff -Nru alsa-lib-1.2.2/src/ucm/ucm_confdoc.h alsa-lib-1.2.6.1/src/ucm/ucm_confdoc.h --- alsa-lib-1.2.2/src/ucm/ucm_confdoc.h 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_confdoc.h 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,531 @@ +/* + * 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 of the License, or (at your option) any later version. + * + * This library 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 + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2021 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +/** + * \defgroup ucm_conf Use Case Configuration + * The ALSA Use Case Configuration. + * See \ref Usecase_conf page for more details. + * \{ + */ + +/*! \page Usecase_conf ALSA Use Case Configuration + +The use case configuration files use \ref conf syntax to define the +static configuration tree. This tree is evaluated (modified) at runtime +according the conditions and dynamic variables in the configuration tree. +The result is parsed and exported to the applications using \ref ucm API. + +### Configuration directory and main filename lookup + +The lookup paths are describen in *ucm.conf* file. The configuration +structure looks like: + +~~~{.html} +UseCasePath.path1 { + Directory "conf.virt.d" + File "${OpenName}.conf" +} +UseCasePath.path2 { + Directory "external" + File "${OpenName}.conf" +} +~~~ + +### UCM main configuration file + +Each sound card has a master sound card file that lists all the supported +use case verbs for that sound card. i.e.: + +~~~{.html} +# Example master file for blah sound card +# By Joe Blogs + +# Use Case name for user interface +Comment "Nice Abstracted Soundcard" + +# The file is divided into Use case sections. One section per use case verb. + +SectionUseCase."Voice Call" { + File "voice_call_blah" + Comment "Make a voice phone call." +} + +SectionUseCase."HiFi" { + File "hifi_blah" + Comment "Play and record HiFi quality Music." +} + +# Define Value defaults + +ValueDefaults { + PlaybackChannels 4 + CaptureChannels 4 +} + +# Define boot / initialization sequence +# This sequence is skipped, when the soundcard was already configured by system +# (alsactl configuration was already created). The purpose is to not alter +# ALSA card controls which may be modified by user after initial settings. + +BootSequence [ + cset "name='My control' on" +] + +# Define fixed boot sequence +# This sequence is always executed on boot (hotplug). + +FixedBootSequence [ + cset "name='Something to toggle' toggle" +] +~~~ + +### UCM verb configuration file + +The verb configuration file defines devices, modifier and initialization sequences. +It is something like a sound profile. + +~~~{.html} +# Example Use case verb section for Voice call blah +# By Joe Blogs + +# verb global section + +SectionVerb { + + # enable and disable sequences are compulsory + EnableSequence [ + cset "name='Master Playback Switch',index=2 0,0" + cset "name='Master Playback Volume',index=2 25,25" + msleep 50 + cset "name='Master Playback Switch',index=2 1,1" + cset "name='Master Playback Volume',index=2 50,50" + ] + + DisableSequence [ + cset "name='Master Playback Switch',index=2 0,0" + cset "name='Master Playback Volume',index=2 25,25" + msleep 50 + cset "name='Master Playback Switch',index=2 1,1" + cset "name='Master Playback Volume',index=2 50,50" + ] + + # Optional transition verb + TransitionSequence."ToCaseName" [ + msleep 1 + ] + + # Optional TQ and device values + Value { + TQ HiFi + PlaybackChannels 6 + } +} + +# Each device is described in new section. N devices are allowed + +SectionDevice."Headphones" { + + SupportedDevice [ + "x" + "y" + ] + + # or (not both) + + ConflictingDevice [ + "x" + "y" + ] + + EnableSequence [ + ... + ] + + DisableSequence [ + ... + ] + + TransitionSequence."ToDevice" [ + ... + ] + + Value { + PlaybackVolume "name='Master Playback Volume',index=2" + PlaybackSwitch "name='Master Playback Switch',index=2" + PlaybackPCM "hw:${CardId},4" + } +} + +# Each modifier is described in new section. N modifiers are allowed + +SectionModifier."Capture Voice" { + Comment "Record voice call" + + SupportedDevice [ + "x" + "y" + ] + + # or (not both) + + ConflictingDevice [ + "x" + "y" + ] + + EnableSequence [ + ... + ] + + DisableSequence [ + ... + ] + + TransitionSequence."ToModifierName" [ + ... + ] + + # Optional TQ and ALSA PCMs + Value { + TQ Voice + CapturePCM "hw:${CardId},11" + PlaybackVolume "name='Master Playback Volume',index=2" + PlaybackSwitch "name='Master Playback Switch',index=2" + } +} +~~~ + +### Sequence commands + +Command name | Description +---------------|---------------------------------------------- +cdev ARG | ALSA control device name for snd_ctl_open() +cset ARG | ALSA control set - snd_ctl_ascii_elem_id_parse() + snd_ctl_ascii_value_parse() +cset-new ARG | Create new ALSA user control element - snd_ctl_ascii_elem_id_parse() + description +ctl-remove ARG | Remove ALSA user control element - snd_ctl_ascii_elem_id_parse() +sysw ARG | write to sysfs tree +usleep ARG | sleep for specified amount of microseconds +msleep ARG | sleep for specified amount of milliseconds +exec ARG | execute a specific command (without shell - *man execv*) +shell ARG | execute a specific command (using shell - *man system*) +cfg-save ARG | save LibraryConfig to a file + +~~~{.html} +# Examples +cset "name='PCM Playback Volue',index=2 99" +cset-new "name='Bool2' type=bool,count=2 1,0" +cset-new "name='Enum' type=enum,labels='L1;L2;L3' 'L2'" +ctl-remove "name='Bool2'" +sysw "-/class/sound/ctl-led/speaker/card${CardNumber}/attach:Speaker Channel Switch" +usleep 10 +exec "/bin/echo hello" +shell "set" +cfg-save "/tmp/test.conf:+pcm" +~~~ + +### Naming (devices, verbs) + +See the SND_USE_CASE_VERB constains like #SND_USE_CASE_VERB_HIFI for the full list of known verbs. + +See the SND_USE_CASE_DEV constants like #SND_USE_CASE_DEV_SPEAKER for the full list of known devices. +If multiple devices with the same name exists, the number suffixes should +be added to these names like HDMI1,HDMI2,HDMI3 etc. No number gaps are +allowed. The names with numbers must be continuous. It is allowed to put +a whitespace between name and index (like 'Line 1') for the better +readability. The device names 'Line 1' and 'Line1' are equal for +this purpose. + +If EnableSequence/DisableSequence controls independent paths in the hardware +it is also recommended to split playback and capture UCM devices and use +the number suffixes. Example use case: Use the integrated microphone +in the laptop instead the microphone in headphones. + +The preference of the devices is determined by the priority value (higher value = higher priority). + +See the SND_USE_CASE_MOD constants like #SND_USE_CASE_MOD_ECHO_REF for the full list of known modifiers. + +### Boot (alsactl) + +The *FixedBootSequence* is executed at each boot. The *BootSequence* is executed only +if the card's configuration is missing. The purpose is to let the users modify the +configuration like volumes or switches. The alsactl ensures the persistency (store +the state of the controls to the /var tree and loads the previous state in the next +boot). + +### Device volume + +It is expected that the applications handle the volume settings. It is not recommended +to set the fixed values for the volume settings to the Enable / Disable sequences for +verbs or devices, if the device exports the hardware volume (MixerElem or Volume/Switch +values). The default volume settings should be set in the *BootSequence*. + +### Dynamic configuration tree + +The evaluation order may look a bit different from the user perspective. +At first, the standard alsa-lib configuration tree is parsed. All other +layers on top are working with this tree. It may shift / move the configuration +blocks from the configuration files as they are placed to the tree internally. + +~~~{.html} +Example configuration | Parsed static tree | Identical static tree +----------------------------+-------------------------+------------------------------- +If.1 { | If { | If.1.True.Define.VAR "A" + True.Define.VAR "A" | 1.True.Define.VAR "A" | If.2.True.Define.VAR "C" +} | 2.True.Define.VAR "C" | Define.VAR "B" +Define.VAR "B" | } | +If.2 { | Define.VAR "B" | + True.Define.VAR "C" | | +} | | +~~~ + +Even if one or both conditions are evaluated as true, the variable _VAR_ will +be evaluated always as **B** because the first _If_ block was before the non-nested +_Define_ . The second _If_ block was appended to the first _If_ block (before +_Define_ in the configuration tree) in the text configuration parser. + + +### Syntax + +Unless described, the syntax version 4 is used. + +~~~ +Syntax 4 +~~~ + + +### Include + +There are two ways to include other configuration files. + +#### Static include + +The static include is inherited from the standard alsa-lib configuration +syntax. It can be placed anywhere in the configuration files. The search +path is composed from the root alsa configuration path (usually +_/usr/share/alsa_) and _ucm2_ directory. + +~~~{.html} + # include file using the search path + # include file using the absolute path +~~~ + +#### Lazy include + +The lazy include is evaluated at runtime. The root path is the ucm2 +tree. The absolute include appends the ucm2 absolute path to the +specified path. The relative include is relative to the file which +contains the _Include_ configuration block. + +~~~{.html} +Include.id1.File "/some/path/file.conf" # absolute include (in the ucm2 tree) +Include.id2.File "subdir/file.conf" # relative include to the current configuration directory (UseCasePath) +~~~ + +### Configuration tree evaluation + +The evaluation of the static configuration tree is proceed in +the specific order (see table bellow). When the dynamic configuration +tree changes, the evaluation sequence is restarted to evaluate +all possible changes (new *Define* or *Include* or *If* blocks). + +Evaluation order | Configuration block | Evaluation restart +------------------:|---------------------|-------------------- +1 | Define | No +2 | Include | Yes +3 | If | Yes + + +### Substitutions + +The dynamic tree identifiers and assigned values in the configuration tree are +substituted. The substitutes strings are in the table bellow. + +Substituted string | Value +---------------------|--------------------- +${OpenName} | Original UCM card name (passed to snd_use_case_mgr_open()) +${ConfLibDir} | Library top-level configuration directory (e.g. /usr/share/alsa) +${ConfTopDir} | Top-level UCM configuration directory (e.g. /usr/share/alsa/ucm2) +${ConfDir} | Card's UCM configuration directory (e.g. /usr/share/alsa/ucm2/conf.d/USB-Audio) +${ConfName} | Configuration name (e.g. USB-Audio.conf) +${CardNumber} | Real ALSA card number (or empty string for the virtual UCM card) +${CardId} | ALSA card identifier (see snd_ctl_card_info_get_id()) +${CardDriver} | ALSA card driver (see snd_ctl_card_info_get_driver()) +${CardName} | ALSA card name (see snd_ctl_card_info_get_name()) +${CardLongName} | ALSA card long name (see snd_ctl_card_info_get_longname()) +${CardComponents} | ALSA card components (see snd_ctl_card_info_get_components()) +${env:} | Environment variable +${sys:} | Contents of sysfs file +${var:} | UCM parser variable (set using a _Define_ block) +${eval:} | Evaluate expression like *($var+2)/3* [**Syntax 5**] +${find-card:} | Find a card - see _Find card substitution_ section +${find-device:} | Find a device - see _Find device substitution_ section + +#### Find card substitution + +This substitutions finds the ALSA card and returns the appropriate identifier or +the card number (see return argument). + +Usage example: + +~~~{.html} +${find-card:field=name,regex='^acp$',return=number} +~~~ + +Arguments: + +Argument | Description +---------------------|----------------------- +return | return value type (id, number), id is the default +field | field for the lookup (id, driver, name, longname, mixername, components) +regex | regex string for the field match + +#### Find device substitution + +Usage example: + +~~~{.html} +${find-device:type=pcm,field=name,regex='DMIC'} +~~~ + +Arguments: + +Argument | Description +---------------------|----------------------- +type | device type (pcm) +stream | stream type (playback, capture), playback is default +field | field for the lookup (id, name, subname) +regex | regex string for the field match + + +### Variable defines + +The variables can be defined and altered with the *Define* or *DefineRegex* blocks. +The *Define* block looks like: + +~~~{.html} +Define { + variable1 "a" + variable2 "b" +} +~~~ + +The *DefineRegex* allows substring extraction like: + +~~~{.html} +DefineRegex.rval { + Regex "(hello)|(regex)" + String "hello, it's my regex" +} +~~~ + +The result will be stored to variables *rval1* as *hello* and *rval2* as *regex* (every matched +substrings are stored to a separate variable with the sequence number postfix. + +Variables can be substituted using the `${var:rval1}` reference for example. + +### Conditions + +The configuration tree evaluation supports the conditions - *If* blocks. Each *If* blocks +must define a *Condition* block and *True* or *False* blocks or both. The *True* or *False* +blocks will be merged to the parent tree (where the *If* block is defined) when +the *Condition* is evaluated. + +Example: + +~~~{.html} +If.uniqueid { + Condition { + Type String + Haystack "abcd" + Needle "a" + } + True { + Define.a a + define.b b + } +} +~~~ + +#### True (Type AlwaysTrue) + +Execute only *True* block. It may be used to change the evaluation order as +explained in the *Configuration Tree* paragraph. + +#### String is empty (Type String) + +Field | Description +---------------------|----------------------- +Empty | string + +#### Strings are equal (Type String) + +Field | Description +---------------------|----------------------- +String1 | string +String2 | substring in string + +#### Substring is present (Type String) + +Field | Description +---------------------|----------------------- +Haystack | string +Needle | substring in string + +#### Regex match (Type RegexMatch) + +Field | Description +---------------------|----------------------- +String | string +Regex | regex expression (extended posix, ignore case) + +#### ALSA control element exists (Type ControlExists) + +Field | Description +---------------------|----------------------- +Device | ALSA control device (see snd_ctl_open()) +Control | control in ASCII (parsed using snd_ctl_ascii_elem_id_parse()) +ControlEnum | value for the enum control (optional) + +Example: + +~~~{.html} +If.fmic { + Condition { + Type ControlExists + Control "name='Front Mic Playback Switch'" + } + True { + ... + } +} +~~~ + + +*/ + +/** + \} + */ diff -Nru alsa-lib-1.2.2/src/ucm/ucm_exec.c alsa-lib-1.2.6.1/src/ucm/ucm_exec.c --- alsa-lib-1.2.2/src/ucm/ucm_exec.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_exec.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,291 @@ +/* + * Exec an external program + * Copyright (C) 2021 Jaroslav Kysela + * + * 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 of the License, or (at your option) any later version. + * + * This library 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 + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2021 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" +#include +#include +#include +#include + +static pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER; + +/* + * Search PATH for executable + */ +static int find_exec(const char *name, char *out, size_t len) +{ + int ret = 0; + char bin[PATH_MAX]; + char *path, *tmp, *tmp2 = NULL; + DIR *dir; + struct dirent *de; + struct stat st; + if (name[0] == '/') { + if (lstat(name, &st)) + return 0; + if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) + return 0; + snd_strlcpy(out, name, len); + return 1; + } + if (!(tmp = getenv("PATH"))) + return 0; + path = alloca(strlen(tmp) + 1); + if (!path) + return 0; + strcpy(path, tmp); + tmp = strtok_r(path, ":", &tmp2); + while (tmp && !ret) { + if ((dir = opendir(tmp))) { + while ((de = readdir(dir))) { + if (strstr(de->d_name, name) != de->d_name) + continue; + snprintf(bin, sizeof(bin), "%s/%s", tmp, + de->d_name); + if (lstat(bin, &st)) + continue; + if (!S_ISREG(st.st_mode) + || !(st.st_mode & S_IEXEC)) + continue; + snd_strlcpy(out, bin, len); + closedir(dir); + return 1; + } + closedir(dir); + } + tmp = strtok_r(NULL, ":", &tmp2); + } + return ret; +} + +static void free_args(char **argv) +{ + char **a; + + for (a = argv; *a; a++) + free(*a); + free(argv); +} + +static int parse_args(char ***argv, int argc, const char *cmd) +{ + char *s, *f; + int i = 0, l, eow; + + if (!argv || !cmd) + return -1; + + s = alloca(strlen(cmd) + 1); + if (!s) + return -1; + strcpy(s, cmd); + *argv = calloc(argc, sizeof(char *)); + + while (*s && i < argc - 1) { + while (*s == ' ') + s++; + f = s; + eow = 0; + while (*s) { + if (*s == '\\') { + l = *(s + 1); + if (l == 'b') + l = '\b'; + else if (l == 'f') + l = '\f'; + else if (l == 'n') + l = '\n'; + else if (l == 'r') + l = '\r'; + else if (l == 't') + l = '\t'; + else + l = 0; + if (l) { + *s++ = l; + memmove(s, s + 1, strlen(s)); + } else { + memmove(s, s + 1, strlen(s)); + if (*s) + s++; + } + } else if (eow) { + if (*s == eow) { + memmove(s, s + 1, strlen(s)); + eow = 0; + } else { + s++; + } + } else if (*s == '\'' || *s == '"') { + eow = *s; + memmove(s, s + 1, strlen(s)); + } else if (*s == ' ') { + break; + } else { + s++; + } + } + if (f != s) { + if (*s) { + *(char *)s = '\0'; + s++; + } + (*argv)[i] = strdup(f); + if ((*argv)[i] == NULL) { + free_args(*argv); + return -ENOMEM; + } + i++; + } + } + (*argv)[i] = NULL; + return 0; +} + +/* + * execute a binary file + * + */ +int uc_mgr_exec(const char *prog) +{ + pid_t p, f, maxfd; + int err = 0, status; + char bin[PATH_MAX]; + struct sigaction sa; + struct sigaction intr, quit; + sigset_t omask; + char **argv; + + if (parse_args(&argv, 32, prog)) + return -EINVAL; + + prog = argv[0]; + if (prog == NULL) { + err = -EINVAL; + goto __error; + } + if (prog[0] != '/' && prog[0] != '.') { + if (!find_exec(argv[0], bin, sizeof(bin))) { + err = -ENOEXEC; + goto __error; + } + prog = bin; + } + + maxfd = sysconf(_SC_OPEN_MAX); + + /* + * block SIGCHLD signal + * ignore SIGINT and SIGQUIT in parent + */ + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGCHLD); + + pthread_mutex_lock(&fork_lock); + + sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask); + + sigaction(SIGINT, &sa, &intr); + sigaction(SIGQUIT, &sa, &quit); + + p = fork(); + + if (p == -1) { + err = -errno; + pthread_mutex_unlock(&fork_lock); + uc_error("Unable to fork() for \"%s\" -- %s", prog, + strerror(errno)); + goto __error; + } + + if (p == 0) { + f = open("/dev/null", O_RDWR); + if (f == -1) { + uc_error("pid %d cannot open /dev/null for redirect %s -- %s", + getpid(), prog, strerror(errno)); + exit(1); + } + + close(0); + close(1); + close(2); + + dup2(f, 0); + dup2(f, 1); + dup2(f, 2); + + close(f); + + for (f = 3; f < maxfd; f++) + close(f); + + /* install default handlers for the forked process */ + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + + execve(prog, argv, environ); + exit(1); + } + + sigaction(SIGINT, &intr, NULL); + sigaction(SIGQUIT, &quit, NULL); + sigprocmask(SIG_SETMASK, &omask, NULL); + + pthread_mutex_unlock(&fork_lock); + + /* make the spawned process a session leader so killing the + process group recursively kills any child process that + might have been spawned */ + setpgid(p, p); + + while (1) { + f = waitpid(p, &status, 0); + if (f == -1) { + if (errno == EAGAIN) + continue; + err = -errno; + goto __error; + } + if (WIFSIGNALED(status)) { + err = -EINTR; + break; + } + if (WIFEXITED(status)) { + err = WEXITSTATUS(status); + break; + } + } + + __error: + free_args(argv); + return err; +} diff -Nru alsa-lib-1.2.2/src/ucm/ucm_include.c alsa-lib-1.2.6.1/src/ucm/ucm_include.c --- alsa-lib-1.2.2/src/ucm/ucm_include.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_include.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,317 @@ +/* + * 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 of the License, or (at your option) any later version. + * + * This library 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 + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2020 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" + +static int get_string(snd_config_t *compound, const char *key, const char **str) +{ + snd_config_t *node; + int err; + + err = snd_config_search(compound, key, &node); + if (err < 0) + return err; + return snd_config_get_string(node, str); +} + +static int include_eval_one(snd_use_case_mgr_t *uc_mgr, + snd_config_t *inc, + snd_config_t **result, + snd_config_t **before, + snd_config_t **after) +{ + const char *file; + char *s; + int err; + + *result = NULL; + + if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for Include.1"); + return -EINVAL; + } + + err = get_string(inc, "File", &file); + if (err < 0) { + uc_error("file expected (Include)"); + return -EINVAL; + } + + err = snd_config_search(inc, "Before", before); + if (err < 0 && err != -ENOENT) { + uc_error("before block identifier error"); + return -EINVAL; + } + + err = snd_config_search(inc, "After", after); + if (err < 0 && err != -ENOENT) { + uc_error("before block identifier error"); + return -EINVAL; + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, file); + if (err < 0) + return err; + err = uc_mgr_config_load_file(uc_mgr, s, result); + free(s); + return err; +} + +#if 0 +static void config_dump(snd_config_t *cfg) +{ + snd_output_t *out; + snd_output_stdio_attach(&out, stderr, 0); + snd_output_printf(out, "-----\n"); + snd_config_save(cfg, out); + snd_output_close(out); +} +#endif + +static int find_position_node(snd_use_case_mgr_t *uc_mgr, + snd_config_t **res, snd_config_t *dst, + const char *id, snd_config_t *pos) +{ + const char *s; + char *s1; + int err; + + err = get_string(pos, id, &s); + if (err < 0 && err != -ENOENT) + return err; + if (err == 0) { + err = uc_mgr_get_substituted_value(uc_mgr, &s1, s); + if (err < 0) + return err; + err = snd_config_search(dst, s1, res); + free(s1); + if (err < 0 && err != -ENOENT) + return err; + } + return 0; +} + +static int merge_it(snd_config_t *dst, snd_config_t *n, snd_config_t **_dn) +{ + snd_config_t *dn; + const char *id; + int err; + + err = snd_config_get_id(n, &id); + if (err < 0) + return err; + err = snd_config_search(dst, id, &dn); + if (err < 0) + return err; + err = snd_config_merge(dn, n, 0); /* merge / append mode */ + if (err < 0) + snd_config_delete(n); + else + *_dn = dn; + return err; +} + +static int compound_merge(snd_use_case_mgr_t *uc_mgr, const char *id, + snd_config_t *dst, snd_config_t *src, + snd_config_t *before, snd_config_t *after) +{ + snd_config_iterator_t i, next; + snd_config_t *n, *_before = NULL, *_after = NULL; + char tmpid[32]; + int err, array, idx; + + if (snd_config_get_type(src) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for the merged block"); + return -EINVAL; + } + + if (before) { + err = find_position_node(uc_mgr, &_before, dst, id, before); + if (err < 0) + return err; + } + if (after) { + err = find_position_node(uc_mgr, &_after, dst, id, after); + if (err < 0) + return err; + } + + /* direct merge? */ + if (!_before && !_after) + return snd_config_merge(dst, src, 0); /* merge / append mode */ + + if (_before && _after) { + uc_error("defined both before and after identifiers in the If or Include block"); + return -EINVAL; + } + + array = snd_config_is_array(dst); + if (array < 0) { + uc_error("destination configuration node is not a compound"); + return array; + } + if (array && snd_config_is_array(src) <= 0) { + uc_error("source configuration node is not an array"); + return -EINVAL; + } + + idx = 0; + + /* for array, use a temporary non-clashing identifier */ + if (array > 0) { + snd_config_for_each(i, next, dst) { + n = snd_config_iterator_entry(i); + snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++); + err = snd_config_set_id(n, tmpid); + if (err < 0) + return err; + } + } + + snd_config_for_each(i, next, src) { + n = snd_config_iterator_entry(i); + err = snd_config_remove(n); + if (err < 0) + return err; + /* for array, use a temporary non-clashing identifier */ + if (array > 0) { + snprintf(tmpid, sizeof(tmpid), "_tmp_%d", idx++); + err = snd_config_set_id(n, tmpid); + if (err < 0) + return err; + } + if (_before) { + err = snd_config_add_before(_before, n); + if (err == -EEXIST) + err = merge_it(dst, n, &n); + if (err < 0) + return err; + _before = NULL; + _after = n; + } else if (_after) { + err = snd_config_add_after(_after, n); + if (err == -EEXIST) + err = merge_it(dst, n, &n); + if (err < 0) + return err; + _after = n; + } + } + + /* set new indexes for the final array */ + if (array > 0) { + idx = 0; + snd_config_for_each(i, next, dst) { + n = snd_config_iterator_entry(i); + snprintf(tmpid, sizeof(tmpid), "%d", idx++); + err = snd_config_set_id(n, tmpid); + if (err < 0) + return err; + } + } + + snd_config_delete(src); + return 0; +} + +int uc_mgr_config_tree_merge(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, snd_config_t *new_ctx, + snd_config_t *before, snd_config_t *after) +{ + snd_config_iterator_t i, next; + snd_config_t *n, *parent2; + const char *id; + int err; + + err = uc_mgr_substitute_tree(uc_mgr, new_ctx); + if (err < 0) + return err; + + snd_config_for_each(i, next, new_ctx) { + n = snd_config_iterator_entry(i); + err = snd_config_remove(n); + if (err < 0) + return err; + err = snd_config_get_id(n, &id); + if (err < 0) { +__add: + err = snd_config_add(parent, n); + if (err < 0) + return err; + } else { + err = snd_config_search(parent, id, &parent2); + if (err == -ENOENT) + goto __add; + err = compound_merge(uc_mgr, id, parent2, n, before, after); + if (err < 0) { + snd_config_delete(n); + return err; + } + } + } + return 0; +} + +/* + * put back the included configuration to the parent + */ +int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, + snd_config_t *inc) +{ + snd_config_iterator_t i, next; + snd_config_t *a, *n, *before, *after; + int err; + + if (uc_mgr->conf_format < 3) { + uc_error("in-place include is supported in v3+ syntax"); + return -EINVAL; + } + + if (snd_config_get_type(inc) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for Include"); + return -EINVAL; + } + + snd_config_for_each(i, next, inc) { + n = snd_config_iterator_entry(i); + before = after = NULL; + err = include_eval_one(uc_mgr, n, &a, &before, &after); + if (err < 0) + return err; + if (a == NULL) + continue; + err = uc_mgr_evaluate_inplace(uc_mgr, a); + if (err < 0) + return err; + err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after); + if (err < 0) + return err; + snd_config_delete(a); + + } + return 0; +} diff -Nru alsa-lib-1.2.2/src/ucm/ucm_local.h alsa-lib-1.2.6.1/src/ucm/ucm_local.h --- alsa-lib-1.2.2/src/ucm/ucm_local.h 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_local.h 2021-12-09 13:17:59.000000000 +0000 @@ -40,18 +40,23 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 2 +#define SYNTAX_VERSION_MAX 5 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 -#define SEQUENCE_ELEMENT_TYPE_CDEV 1 -#define SEQUENCE_ELEMENT_TYPE_CSET 2 -#define SEQUENCE_ELEMENT_TYPE_SLEEP 3 -#define SEQUENCE_ELEMENT_TYPE_EXEC 4 -#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5 -#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6 -#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 7 +#define SEQUENCE_ELEMENT_TYPE_CDEV 1 +#define SEQUENCE_ELEMENT_TYPE_CSET 2 +#define SEQUENCE_ELEMENT_TYPE_SLEEP 3 +#define SEQUENCE_ELEMENT_TYPE_EXEC 4 +#define SEQUENCE_ELEMENT_TYPE_SHELL 5 +#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 6 +#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 7 +#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 8 +#define SEQUENCE_ELEMENT_TYPE_CTL_REMOVE 9 +#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 10 +#define SEQUENCE_ELEMENT_TYPE_SYSSET 11 +#define SEQUENCE_ELEMENT_TYPE_CFGSAVE 12 struct ucm_value { struct list_head list; @@ -73,6 +78,8 @@ char *cdev; char *cset; char *exec; + char *sysw; + char *cfgsave; struct component_sequence cmpt_seq; /* component sequence */ } data; }; @@ -115,6 +122,8 @@ struct list_head dev_list; snd_ctl_t *ctl; snd_ctl_card_info_t *ctl_info; + int slave; + int ucm_group; }; struct ucm_dev_name { @@ -217,12 +226,24 @@ char *conf_dir_name; char *comment; int conf_format; + unsigned int ucm_card_number; + int suppress_nodev_errors; + + /* UCM cards list */ + struct list_head cards_list; /* use case verb, devices and modifier configs parsed from files */ struct list_head verb_list; + /* force boot settings - sequence */ + struct list_head fixedboot_list; + + /* boot settings - sequence */ + struct list_head boot_list; + /* default settings - sequence */ struct list_head default_list; + int default_list_executed; /* default settings - value list */ struct list_head value_list; @@ -235,9 +256,15 @@ /* locking */ pthread_mutex_t mutex; + /* UCM internal variables defined in configuration files */ + struct list_head variable_list; + /* list of opened control devices */ struct list_head ctl_list; + /* local library configuration */ + snd_config_t *local_config; + /* Components don't define cdev, the card device. When executing * a sequence of a component device, ucm manager enters component * domain and needs to provide cdev to the component. This cdev @@ -258,7 +285,11 @@ void uc_mgr_error(const char *fmt, ...); void uc_mgr_stdout(const char *fmt, ...); +const char *uc_mgr_sysfs_root(void); +const char *uc_mgr_config_dir(int format); +int uc_mgr_config_load_into(int format, const char *file, snd_config_t *cfg); int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg); +int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, const char *file, snd_config_t **cfg); int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr); int uc_mgr_scan_master_configs(const char **_list[]); @@ -273,24 +304,64 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr); void uc_mgr_free(snd_use_case_mgr_t *uc_mgr); -int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, - snd_ctl_t **ctl, - const char *device); +static inline int uc_mgr_has_local_config(snd_use_case_mgr_t *uc_mgr) +{ + return uc_mgr && snd_config_iterator_first(uc_mgr->local_config) != + snd_config_iterator_end(uc_mgr->local_config); +} + +int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr); +void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr); -struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr); +int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, + struct ctl_list **ctl_list, + const char *device, + int slave); + +struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr); +struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card); +struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, + const char *name, int idx); snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr); void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr); int uc_mgr_add_value(struct list_head *base, const char *key, char *val); +const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, + const char *name); + +int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, + const char *name, + const char *val); + int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, char **_rvalue, const char *value); +int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, + snd_config_t *node); + +int uc_mgr_config_tree_merge(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, snd_config_t *new_ctx, + snd_config_t *before, snd_config_t *after); + +int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg); + +int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr, + snd_config_t *parent, + snd_config_t *inc); + int uc_mgr_evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *parent, snd_config_t *cond); +int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, + const char *name, + snd_config_t *eval); + +int uc_mgr_exec(const char *prog); + /** The name of the environment variable containing the UCM directory */ #define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM" diff -Nru alsa-lib-1.2.2/src/ucm/ucm_regex.c alsa-lib-1.2.6.1/src/ucm/ucm_regex.c --- alsa-lib-1.2.2/src/ucm/ucm_regex.c 1970-01-01 00:00:00.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_regex.c 2021-12-09 13:17:59.000000000 +0000 @@ -0,0 +1,176 @@ +/* + * 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 of the License, or (at your option) any later version. + * + * This library 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 + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2019 Red Hat Inc. + * Authors: Jaroslav Kysela + */ + +#include "ucm_local.h" +#include +#include + +static int get_string(snd_config_t *compound, const char *key, const char **str) +{ + snd_config_t *node; + int err; + + err = snd_config_search(compound, key, &node); + if (err < 0) + return err; + return snd_config_get_string(node, str); +} + +static char *extract_substring(const char *data, regmatch_t *match) +{ + char *s; + size_t len; + + len = match->rm_eo - match->rm_so; + s = malloc(len + 1); + if (s == NULL) + return NULL; + memcpy(s, data + match->rm_so, len); + s[len] = '\0'; + return s; +} + +static int set_variables(snd_use_case_mgr_t *uc_mgr, const char *data, + unsigned int match_size, regmatch_t *match, + const char *name) +{ + size_t name2_len = strlen(name) + 16; + char *name2 = alloca(name2_len); + char *s; + unsigned int i; + int err; + + if (match[0].rm_so < 0 || match[0].rm_eo < 0) + return 0; + s = extract_substring(data, &match[0]); + if (s == NULL) + return -ENOMEM; + err = uc_mgr_set_variable(uc_mgr, name, s); + free(s); + if (err < 0) + return err; + for (i = 1; i < match_size; i++) { + if (match[0].rm_so < 0 || match[0].rm_eo < 0) + return 0; + s = extract_substring(data, &match[i]); + if (s == NULL) + return -ENOMEM; + snprintf(name2, name2_len, "%s%u", name, i); + err = uc_mgr_set_variable(uc_mgr, name2, s); + free(s); + if (err < 0) + return err; + } + return 0; +} + +int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr, const char *name, + snd_config_t *eval) +{ + const char *string, *regex_string, *flags_string; + char *s; + regex_t re; + int options = 0; + regmatch_t match[20]; + int err; + + if (uc_mgr->conf_format < 3) { + uc_error("define regex is supported in v3+ syntax"); + return -EINVAL; + } + + if (snd_config_get_type(eval) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineRegex"); + return -EINVAL; + } + + err = get_string(eval, "String", &string); + if (err < 0) { + uc_error("DefineRegex error (String)"); + return -EINVAL; + } + + err = get_string(eval, "Regex", ®ex_string); + if (err < 0) { + uc_error("DefineRegex error (Regex string)"); + return -EINVAL; + } + + err = get_string(eval, "Flags", &flags_string); + if (err == -ENOENT) { + options = REG_EXTENDED; + } else if (err < 0) { + uc_error("DefineRegex error (Flags string)"); + return -EINVAL; + } else { + while (*flags_string) { + switch (tolower(*flags_string)) { + case 'e': + options |= REG_EXTENDED; + break; + case 'i': + options |= REG_ICASE; + break; + case 's': + options |= REG_NOSUB; + break; + case 'n': + options |= REG_NEWLINE; + break; + default: + uc_error("DefineRegex error (unknown flag '%c')", *flags_string); + return -EINVAL; + } + flags_string++; + } + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, regex_string); + if (err < 0) + return err; + err = regcomp(&re, s, options); + free(s); + if (err) { + uc_error("Regex '%s' compilation failed (code %d)", err); + return -EINVAL; + } + + err = uc_mgr_get_substituted_value(uc_mgr, &s, string); + if (err < 0) { + regfree(&re); + return err; + } + err = regexec(&re, s, ARRAY_SIZE(match), match, 0); + if (err < 0) + err = -errno; + else if (err == REG_NOMATCH) + err = 0; + else + err = set_variables(uc_mgr, s, ARRAY_SIZE(match), match, name); + free(s); + regfree(&re); + return err; +} diff -Nru alsa-lib-1.2.2/src/ucm/ucm_subs.c alsa-lib-1.2.6.1/src/ucm/ucm_subs.c --- alsa-lib-1.2.2/src/ucm/ucm_subs.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/ucm_subs.c 2021-12-09 13:17:59.000000000 +0000 @@ -28,6 +28,49 @@ #include #include #include +#include + +static char *rval_open_name(snd_use_case_mgr_t *uc_mgr) +{ + const char *name; + if (uc_mgr->conf_format < 3) + return NULL; + name = uc_mgr->card_name; + if (name) { + if (strncmp(name, "strict:", 7) == 0) + name += 7; + return strdup(name); + } + return NULL; +} + +static char *rval_conf_libdir(snd_use_case_mgr_t *uc_mgr) +{ + if (uc_mgr->conf_format < 4) + return NULL; + return strdup(snd_config_topdir()); +} + +static char *rval_conf_topdir(snd_use_case_mgr_t *uc_mgr) +{ + const char *dir; + + if (uc_mgr->conf_format < 3) + return NULL; + dir = uc_mgr_config_dir(uc_mgr->conf_format); + if (dir && dir[0]) + return strdup(dir); + return NULL; +} + +static char *rval_conf_dir(snd_use_case_mgr_t *uc_mgr) +{ + if (uc_mgr->conf_format < 3) + return NULL; + if (uc_mgr->conf_dir_name && uc_mgr->conf_dir_name[0]) + return strdup(uc_mgr->conf_dir_name); + return NULL; +} static char *rval_conf_name(snd_use_case_mgr_t *uc_mgr) { @@ -36,11 +79,28 @@ return NULL; } +static char *get_card_number(struct ctl_list *ctl_list) +{ + char num[16]; + + if (ctl_list == NULL) + return strdup(""); + snprintf(num, sizeof(num), "%i", snd_ctl_card_info_get_card(ctl_list->ctl_info)); + return strdup(num); +} + +static char *rval_card_number(snd_use_case_mgr_t *uc_mgr) +{ + if (uc_mgr->conf_format < 3) + return NULL; + return get_card_number(uc_mgr_get_master_ctl(uc_mgr)); +} + static char *rval_card_id(snd_use_case_mgr_t *uc_mgr) { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); @@ -50,7 +110,7 @@ { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_driver(ctl_list->ctl_info)); @@ -60,7 +120,7 @@ { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_name(ctl_list->ctl_info)); @@ -70,7 +130,7 @@ { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_longname(ctl_list->ctl_info)); @@ -80,12 +140,352 @@ { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list == NULL) return NULL; return strdup(snd_ctl_card_info_get_components(ctl_list->ctl_info)); } +static struct ctl_list *get_ctl_list_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + char *name, *index; + long idx = 0; + + name = alloca(strlen(id) + 1); + strcpy(name, id); + index = strchr(name, '#'); + if (index) { + *index = '\0'; + if (safe_strtol(index + 1, &idx)) + return NULL; + } + return uc_mgr_get_ctl_by_name(uc_mgr, name, idx); +} + +static char *rval_card_number_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + if (uc_mgr->conf_format < 3) { + uc_error("CardNumberByName substitution is supported in v3+ syntax"); + return NULL; + } + + uc_error("${CardNumberByName} substitution is obsolete - use ${find-card}!"); + + return get_card_number(get_ctl_list_by_name(uc_mgr, id)); +} + +static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + struct ctl_list *ctl_list; + + if (uc_mgr->conf_format < 3) { + uc_error("CardIdByName substitution is supported in v3+ syntax"); + return NULL; + } + + uc_error("${CardIdByName} substitution is obsolete - use ${find-card}!"); + + ctl_list = get_ctl_list_by_name(uc_mgr, id); + if (ctl_list == NULL) + return NULL; + return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); +} + +typedef struct lookup_iterate *(*lookup_iter_fcn_t) + (snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter); +typedef const char *(*lookup_fcn_t)(void *); + +struct lookup_fcn { + char *name; + const char *(*fcn)(void *opaque); +}; + +struct lookup_iterate { + int (*init)(snd_use_case_mgr_t *uc_mgr, struct lookup_iterate *iter, + snd_config_t *config); + void (*done)(struct lookup_iterate *iter); + lookup_iter_fcn_t first; + lookup_iter_fcn_t next; + char *(*retfcn)(struct lookup_iterate *iter, snd_config_t *config); + struct lookup_fcn *fcns; + lookup_fcn_t fcn; + struct ctl_list *ctl_list; + void *info; +}; + +static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr, + const char *query, + struct lookup_iterate *iter) +{ + snd_config_t *config, *d; + struct lookup_fcn *fcn; + struct lookup_iterate *curr; + const char *s; + char *result; + regmatch_t match[1]; + regex_t re; + int err; + + if (uc_mgr->conf_format < 4) { + uc_error("Lookups are supported in v4+ syntax"); + return NULL; + } + + err = snd_config_load_string(&config, query, 0); + if (err < 0) { + uc_error("The lookup arguments '%s' are invalid", query); + return NULL; + } + if (iter->init && iter->init(uc_mgr, iter, config)) + goto null; + if (snd_config_search(config, "field", &d)) { + uc_error("Lookups require field!"); + goto null; + } + if (snd_config_get_string(d, &s)) + goto null; + for (fcn = iter->fcns ; fcn; fcn++) { + if (strcasecmp(fcn->name, s) == 0) { + iter->fcn = fcn->fcn; + break; + } + } + if (iter->fcn == NULL) { + uc_error("Unknown field value '%s'", s); + goto null; + } + if (snd_config_search(config, "regex", &d)) { + uc_error("Lookups require regex!"); + goto null; + } + if (snd_config_get_string(d, &s)) + goto null; + err = regcomp(&re, s, REG_EXTENDED | REG_ICASE); + if (err) { + uc_error("Regex '%s' compilation failed (code %d)", s, err); + goto null; + } + + result = NULL; + for (curr = iter->first(uc_mgr, iter); curr; curr = iter->next(uc_mgr, iter)) { + s = curr->fcn(iter->info); + if (s == NULL) + continue; + if (regexec(&re, s, ARRAY_SIZE(match), match, 0) == 0) { + result = curr->retfcn(iter, config); + break; + } + } + regfree(&re); +fin: + snd_config_delete(config); + if (iter->done) + iter->done(iter); + return result; +null: + result = NULL; + goto fin; +} + +static struct lookup_iterate *rval_card_lookup1(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter, + int card) +{ + if (snd_card_next(&card) < 0 || card < 0) + return NULL; + iter->ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card); + if (iter->ctl_list == NULL) + return NULL; + iter->info = iter->ctl_list->ctl_info; + return iter; +} + +static struct lookup_iterate *rval_card_lookup_first(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter) +{ + return rval_card_lookup1(uc_mgr, iter, -1); +} + +static struct lookup_iterate *rval_card_lookup_next(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter) +{ + return rval_card_lookup1(uc_mgr, iter, snd_ctl_card_info_get_card(iter->info)); +} + +static char *rval_card_lookup_return(struct lookup_iterate *iter, snd_config_t *config) +{ + snd_config_t *d; + const char *s; + + if (snd_config_search(config, "return", &d)) + return strdup(snd_ctl_card_info_get_id(iter->info)); + else if (snd_config_get_string(d, &s)) + return NULL; + else if (strcasecmp(s, "id") == 0) + return strdup(snd_ctl_card_info_get_id(iter->info)); + else if (strcasecmp(s, "number") == 0) { + char num[16]; + snprintf(num, sizeof(num), "%d", snd_ctl_card_info_get_card(iter->info)); + return strdup(num); + } else { + uc_error("Unknown return type '%s'", s); + return NULL; + } +} + +static char *rval_card_lookup(snd_use_case_mgr_t *uc_mgr, const char *query) +{ + static struct lookup_fcn fcns[] = { + { .name = "id", (lookup_fcn_t)snd_ctl_card_info_get_id }, + { .name = "driver", (lookup_fcn_t)snd_ctl_card_info_get_driver }, + { .name = "name", (lookup_fcn_t)snd_ctl_card_info_get_name }, + { .name = "longname", (lookup_fcn_t)snd_ctl_card_info_get_longname }, + { .name = "mixername", (lookup_fcn_t)snd_ctl_card_info_get_mixername }, + { .name = "components", (lookup_fcn_t)snd_ctl_card_info_get_components }, + { 0 }, + }; + struct lookup_iterate iter = { + .first = rval_card_lookup_first, + .next = rval_card_lookup_next, + .retfcn = rval_card_lookup_return, + .fcns = fcns, + }; + return rval_lookup_main(uc_mgr, query, &iter); +} + +static struct lookup_iterate *rval_pcm_lookup1(struct lookup_iterate *iter, + int device) +{ + snd_pcm_info_t *pcminfo; + snd_ctl_t *ctl = iter->ctl_list->ctl; + int err; + +next: + if (snd_ctl_pcm_next_device(ctl, &device) < 0 || device < 0) + return NULL; + pcminfo = iter->info; + snd_pcm_info_set_device(pcminfo, device); + err = snd_ctl_pcm_info(ctl, pcminfo); + if (err < 0) { + if (err == -ENOENT) + goto next; + uc_error("Unable to obtain PCM info (device %d)", device); + return NULL; + } + return iter; +} + +static struct lookup_iterate *rval_pcm_lookup_first(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct lookup_iterate *iter) +{ + return rval_pcm_lookup1(iter, -1); +} + +static struct lookup_iterate *rval_pcm_lookup_next(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct lookup_iterate *iter) +{ + return rval_pcm_lookup1(iter, snd_pcm_info_get_device(iter->info)); +} + +static char *rval_pcm_lookup_return(struct lookup_iterate *iter, + snd_config_t *config ATTRIBUTE_UNUSED) +{ + char num[16]; + snprintf(num, sizeof(num), "%d", snd_pcm_info_get_device(iter->info)); + return strdup(num); +} + +static int rval_pcm_lookup_init(struct lookup_iterate *iter, + snd_config_t *config) +{ + static struct lookup_fcn pcm_fcns[] = { + { .name = "id", (lookup_fcn_t)snd_pcm_info_get_id }, + { .name = "name", (lookup_fcn_t)snd_pcm_info_get_name }, + { .name = "subname", (lookup_fcn_t)snd_pcm_info_get_subdevice_name }, + { 0 }, + }; + snd_config_t *d; + const char *s; + snd_pcm_info_t *pcminfo; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + if (snd_config_search(config, "stream", &d) == 0 && + snd_config_get_string(d, &s) == 0) { + if (strcasecmp(s, "playback") == 0) + stream = SND_PCM_STREAM_PLAYBACK; + else if (strcasecmp(s, "capture") == 0) + stream = SND_PCM_STREAM_CAPTURE; + else { + uc_error("Unknown stream type '%s'", s); + return -EINVAL; + } + } + if (snd_pcm_info_malloc(&pcminfo)) + return -ENOMEM; + snd_pcm_info_set_device(pcminfo, 0); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + iter->first = rval_pcm_lookup_first; + iter->next = rval_pcm_lookup_next; + iter->retfcn = rval_pcm_lookup_return; + iter->fcns = pcm_fcns; + iter->info = pcminfo; + return 0; +} + +static int rval_device_lookup_init(snd_use_case_mgr_t *uc_mgr, + struct lookup_iterate *iter, + snd_config_t *config) +{ + static struct { + const char *name; + int (*init)(struct lookup_iterate *iter, snd_config_t *config); + } *t, types[] = { + { .name = "pcm", .init = rval_pcm_lookup_init }, + { 0 } + }; + snd_config_t *d; + const char *s; + int err; + + if (snd_config_search(config, "ctl", &d) || snd_config_get_string(d, &s)) { + iter->ctl_list = uc_mgr_get_master_ctl(uc_mgr); + if (iter->ctl_list == NULL) { + uc_error("Control device is not defined!"); + return -EINVAL; + } + } else { + err = uc_mgr_open_ctl(uc_mgr, &iter->ctl_list, s, 1); + if (err < 0) { + uc_error("Control device '%s' not found", s); + return -EINVAL; + } + } + if (snd_config_search(config, "type", &d) || snd_config_get_string(d, &s)) { + uc_error("Missing device type!"); + return -EINVAL; + } + for (t = types; t->name; t++) + if (strcasecmp(t->name, s) == 0) + return t->init(iter, config); + uc_error("Device type '%s' is invalid", s); + return -EINVAL; +} + +static void rval_device_lookup_done(struct lookup_iterate *iter) +{ + free(iter->info); +} + +static char *rval_device_lookup(snd_use_case_mgr_t *uc_mgr, const char *query) +{ + struct lookup_iterate iter = { + .init = rval_device_lookup_init, + .done = rval_device_lookup_done, + }; + return rval_lookup_main(uc_mgr, query, &iter); +} + static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) { char *e; @@ -101,12 +501,12 @@ char path[PATH_MAX], link[PATH_MAX + 1]; struct stat sb; ssize_t len; - char *e; + const char *e; int fd; - e = getenv("SYSFS_PATH"); + e = uc_mgr_sysfs_root(); if (e == NULL) - e = "/sys"; + return NULL; if (id[0] == '/') id++; snprintf(path, sizeof(path), "%s/%s", e, id); @@ -146,6 +546,57 @@ return strdup(path); } +static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id) +{ + const char *v; + + if (uc_mgr->conf_format < 3) { + uc_error("variable substitution is supported in v3+ syntax"); + return NULL; + } + + v = uc_mgr_get_variable(uc_mgr, id); + if (v) + return strdup(v); + return NULL; +} + +int _snd_eval_string(snd_config_t **dst, const char *s, + snd_config_expand_fcn_t fcn, void *private_data); + +static int rval_eval_var_cb(snd_config_t **dst, const char *s, void *private_data) +{ + snd_use_case_mgr_t *uc_mgr = private_data; + const char *v; + + v = uc_mgr_get_variable(uc_mgr, s); + if (v == NULL) + return -ENOENT; + return snd_config_imake_string(dst, NULL, v); +} + +static char *rval_eval(snd_use_case_mgr_t *uc_mgr, const char *e) +{ + snd_config_t *dst; + char *r; + int err; + + if (uc_mgr->conf_format < 5) { + uc_error("variable substitution is supported in v5+ syntax"); + return NULL; + } + err = _snd_eval_string(&dst, e, rval_eval_var_cb, uc_mgr); + if (err < 0) { + uc_error("unable to evaluate '%s'", e); + return NULL; + } + err = snd_config_get_ascii(dst, &r); + snd_config_delete(dst); + if (err < 0) + return NULL; + return r; +} + #define MATCH_VARIABLE(name, id, fcn, empty_ok) \ if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ rval = fcn(uc_mgr); \ @@ -154,31 +605,62 @@ goto __rval; \ } -#define MATCH_VARIABLE2(name, id, fcn) \ +#define MATCH_VARIABLE2(name, id, fcn, empty_ok) \ if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ idsize = sizeof(id) - 1; \ - tmp = strchr(value + idsize, '}'); \ - if (tmp) { \ - rvalsize = tmp - (value + idsize); \ - if (rvalsize > sizeof(v2)) { \ - err = -ENOMEM; \ - goto __error; \ - } \ - strncpy(v2, value + idsize, rvalsize); \ - v2[rvalsize] = '\0'; \ - idsize += rvalsize + 1; \ - rval = fcn(uc_mgr, v2); \ - goto __rval; \ - } \ + allow_empty = (empty_ok); \ + fcn2 = (fcn); \ + goto __match2; \ } +/* + * skip escaped } character (simple version) + */ +static inline const char *strchr_with_escape(const char *str, char c) +{ + char *s; + + while (1) { + s = strchr(str, c); + if (s && s != str) { + if (*(s - 1) == '\\') { + str = s + 1; + continue; + } + } + return s; + } +} + +/* + * remove escaped } character (simple version) + */ +static inline void strncpy_with_escape(char *dst, const char *src, size_t len) +{ + char c; + + c = *src++; + while (c != '\0' && len > 0) { + if (c == '\\' && *src == '}') { + c = *src++; + len--; + } + *dst++ = c; + len--; + c = *src++; + } + *dst = '\0'; +} + int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, char **_rvalue, const char *value) { size_t size, nsize, idsize, rvalsize, dpos = 0; const char *tmp; - char *r, *nr, *rval, v2[32]; + char *r, *nr, *rval, v2[128]; + bool ignore_error, allow_empty; + char *(*fcn2)(snd_use_case_mgr_t *, const char *id); int err; if (value == NULL) @@ -190,56 +672,104 @@ return -ENOMEM; while (*value) { - if (*value == '$' && *(value+1) == '{') { - bool allow_empty = false; - - MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false); - MATCH_VARIABLE(value, "${CardId}", rval_card_id, false); - MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false); - MATCH_VARIABLE(value, "${CardName}", rval_card_name, false); - MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false); - MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true); - MATCH_VARIABLE2(value, "${env:", rval_env); - MATCH_VARIABLE2(value, "${sys:", rval_sysfs); - err = -EINVAL; - tmp = strchr(value, '}'); - if (tmp) { - strncpy(r, value, tmp + 1 - value); - r[tmp + 1 - value] = '\0'; - uc_error("variable '%s' is not known!", r); + if (*value != '$') { +__std: + r[dpos++] = *value; + value++; + continue; + } + ignore_error = false; + if (value[1] == '$' && value[2] == '{' && uc_mgr->conf_format >= 3) { + value++; + ignore_error = true; + } else if (value[1] != '{') { + goto __std; + } + fcn2 = NULL; + MATCH_VARIABLE(value, "${OpenName}", rval_open_name, false); + MATCH_VARIABLE(value, "${ConfLibDir}", rval_conf_libdir, false); + MATCH_VARIABLE(value, "${ConfTopDir}", rval_conf_topdir, false); + MATCH_VARIABLE(value, "${ConfDir}", rval_conf_dir, false); + MATCH_VARIABLE(value, "${ConfName}", rval_conf_name, false); + MATCH_VARIABLE(value, "${CardNumber}", rval_card_number, true); + MATCH_VARIABLE(value, "${CardId}", rval_card_id, false); + MATCH_VARIABLE(value, "${CardDriver}", rval_card_driver, false); + MATCH_VARIABLE(value, "${CardName}", rval_card_name, false); + MATCH_VARIABLE(value, "${CardLongName}", rval_card_longname, false); + MATCH_VARIABLE(value, "${CardComponents}", rval_card_components, true); + MATCH_VARIABLE2(value, "${env:", rval_env, false); + MATCH_VARIABLE2(value, "${sys:", rval_sysfs, false); + MATCH_VARIABLE2(value, "${var:", rval_var, true); + MATCH_VARIABLE2(value, "${eval:", rval_eval, false); + MATCH_VARIABLE2(value, "${find-card:", rval_card_lookup, false); + MATCH_VARIABLE2(value, "${find-device:", rval_device_lookup, false); + MATCH_VARIABLE2(value, "${CardNumberByName:", rval_card_number_by_name, false); + MATCH_VARIABLE2(value, "${CardIdByName:", rval_card_id_by_name, false); +__merr: + err = -EINVAL; + tmp = strchr(value, '}'); + if (tmp) { + strncpy(r, value, tmp + 1 - value); + r[tmp + 1 - value] = '\0'; + uc_error("variable '%s' is not known!", r); + } else { + uc_error("variable reference '%s' is not complete", value); + } + goto __error; +__match2: + tmp = strchr_with_escape(value + idsize, '}'); + if (tmp) { + rvalsize = tmp - (value + idsize); + if (rvalsize >= sizeof(v2)) { + err = -ENOMEM; + goto __error; + } + strncpy_with_escape(v2, value + idsize, rvalsize); + idsize += rvalsize + 1; + if (*v2 == '$' && uc_mgr->conf_format >= 3) { + tmp = uc_mgr_get_variable(uc_mgr, v2 + 1); + if (tmp == NULL) { + uc_error("define '%s' is not reachable in this context!", v2 + 1); + rval = NULL; + } else { + rval = fcn2(uc_mgr, tmp); + } } else { - uc_error("variable reference '%s' is not complete", value); + rval = fcn2(uc_mgr, v2); } - goto __error; + goto __rval; + } + goto __merr; __rval: - if (rval == NULL || (!allow_empty && rval[0] == '\0')) { + if (rval == NULL || (!allow_empty && rval[0] == '\0')) { + free(rval); + if (ignore_error) { + value += idsize; + continue; + } + strncpy(r, value, idsize); + r[idsize] = '\0'; + uc_error("variable '%s' is %s in this context!", r, + rval ? "empty" : "not defined"); + err = -EINVAL; + goto __error; + } + value += idsize; + rvalsize = strlen(rval); + nsize = size + rvalsize - idsize; + if (nsize > size) { + nr = realloc(r, nsize); + if (nr == NULL) { free(rval); - strncpy(r, value, idsize); - r[idsize] = '\0'; - uc_error("variable '%s' is not defined in this context!", r); - err = -EINVAL; + err = -ENOMEM; goto __error; } - value += idsize; - rvalsize = strlen(rval); - nsize = size + rvalsize - idsize; - if (nsize > size) { - nr = realloc(r, nsize); - if (nr == NULL) { - free(rval); - err = -ENOMEM; - goto __error; - } - size = nsize; - r = nr; - } - strcpy(r + dpos, rval); - dpos += rvalsize; - free(rval); - } else { - r[dpos++] = *value; - value++; + size = nsize; + r = nr; } + strcpy(r + dpos, rval); + dpos += rvalsize; + free(rval); } r[dpos] = '\0'; @@ -250,3 +780,57 @@ free(r); return err; } + +static inline int uc_mgr_substitute_check(const char *s) +{ + return s && strstr(s, "${") != NULL; +} + +int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id, *s2; + char *s; + int err; + + err = snd_config_get_id(node, &id); + if (err < 0) + return err; + if (uc_mgr_substitute_check(id)) { + err = uc_mgr_get_substituted_value(uc_mgr, &s, id); + if (err < 0) + return err; + err = snd_config_set_id(node, s); + if (err < 0) { + uc_error("unable to set substituted id '%s' (old id '%s')", s, id); + free(s); + return err; + } + free(s); + } + if (snd_config_get_type(node) != SND_CONFIG_TYPE_COMPOUND) { + if (snd_config_get_type(node) == SND_CONFIG_TYPE_STRING) { + err = snd_config_get_string(node, &s2); + if (err < 0) + return err; + if (!uc_mgr_substitute_check(s2)) + return 0; + err = uc_mgr_get_substituted_value(uc_mgr, &s, s2); + if (err < 0) + return err; + err = snd_config_set_string(node, s); + free(s); + if (err < 0) + return err; + } + return 0; + } + snd_config_for_each(i, next, node) { + n = snd_config_iterator_entry(i); + err = uc_mgr_substitute_tree(uc_mgr, n); + if (err < 0) + return err; + } + return 0; +} diff -Nru alsa-lib-1.2.2/src/ucm/utils.c alsa-lib-1.2.6.1/src/ucm/utils.c --- alsa-lib-1.2.2/src/ucm/utils.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/src/ucm/utils.c 2021-12-09 13:17:59.000000000 +0000 @@ -49,26 +49,94 @@ va_end(va); } -struct ctl_list *uc_mgr_get_one_ctl(snd_use_case_mgr_t *uc_mgr) +const char *uc_mgr_sysfs_root(void) +{ + const char *e = getenv("SYSFS_PATH"); + if (e == NULL) + return "/sys"; + if (*e == '\0') + uc_error("no sysfs root!"); + return e; +} + +struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr) { struct list_head *pos; - struct ctl_list *ctl_list = NULL; + struct ctl_list *ctl_list = NULL, *ctl_list2; list_for_each(pos, &uc_mgr->ctl_list) { + ctl_list2 = list_entry(pos, struct ctl_list, list); + if (ctl_list2->slave) + continue; if (ctl_list) { uc_error("multiple control device names were found!"); return NULL; } - ctl_list = list_entry(pos, struct ctl_list, list); + ctl_list = ctl_list2; } return ctl_list; } +struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card) +{ + struct ctl_list *ctl_list; + char cname[32]; + int err; + + sprintf(cname, "hw:%d", card); + err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1); + if (err < 0) + return NULL; + return ctl_list; +} + +struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx) +{ + struct list_head *pos; + struct ctl_list *ctl_list; + const char *s; + int idx2, card; + + idx2 = idx; + list_for_each(pos, &uc_mgr->ctl_list) { + ctl_list = list_entry(pos, struct ctl_list, list); + s = snd_ctl_card_info_get_name(ctl_list->ctl_info); + if (s == NULL) + continue; + if (strcmp(s, name) == 0) { + if (idx2 == 0) + return ctl_list; + idx2--; + } + } + + idx2 = idx; + card = -1; + if (snd_card_next(&card) < 0 || card < 0) + return NULL; + + while (card >= 0) { + ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card); + if (ctl_list == NULL) + continue; /* really? */ + s = snd_ctl_card_info_get_name(ctl_list->ctl_info); + if (s && strcmp(s, name) == 0) { + if (idx2 == 0) + return ctl_list; + idx2--; + } + if (snd_card_next(&card) < 0) + break; + } + + return NULL; +} + snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr) { struct ctl_list *ctl_list; - ctl_list = uc_mgr_get_one_ctl(uc_mgr); + ctl_list = uc_mgr_get_master_ctl(uc_mgr); if (ctl_list) return ctl_list->ctl; return NULL; @@ -127,10 +195,11 @@ } static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr, - struct ctl_list *ctl_list, + struct ctl_list **ctl_list, snd_ctl_t *ctl, int card, snd_ctl_card_info_t *info, - const char *device) + const char *device, + int slave) { struct ctl_list *cl = NULL; const char *id = snd_ctl_card_info_get_id(info); @@ -139,7 +208,7 @@ if (id == NULL || id[0] == '\0') return -ENOENT; - if (!ctl_list) { + if (!(*ctl_list)) { cl = malloc(sizeof(*cl)); if (cl == NULL) return -ENOMEM; @@ -150,155 +219,185 @@ return -ENOMEM; } snd_ctl_card_info_copy(cl->ctl_info, info); - ctl_list = cl; + cl->slave = slave; + *ctl_list = cl; + } else { + if (!slave) + (*ctl_list)->slave = slave; } if (card >= 0) { snprintf(dev, sizeof(dev), "hw:%d", card); hit |= !!(device && (strcmp(dev, device) == 0)); - err = uc_mgr_ctl_add_dev(ctl_list, dev); + err = uc_mgr_ctl_add_dev(*ctl_list, dev); if (err < 0) goto __nomem; } snprintf(dev, sizeof(dev), "hw:%s", id); hit |= !!(device && (strcmp(dev, device) == 0)); - err = uc_mgr_ctl_add_dev(ctl_list, dev); + err = uc_mgr_ctl_add_dev(*ctl_list, dev); if (err < 0) goto __nomem; /* the UCM name not based on the card name / id */ if (!hit && device) { - err = uc_mgr_ctl_add_dev(ctl_list, device); + err = uc_mgr_ctl_add_dev(*ctl_list, device); if (err < 0) goto __nomem; } - list_add_tail(&ctl_list->list, &uc_mgr->ctl_list); + list_add_tail(&(*ctl_list)->list, &uc_mgr->ctl_list); return 0; __nomem: - if (ctl_list == cl) + if (*ctl_list == cl) { uc_mgr_free_ctl(cl); + *ctl_list = NULL; + } return -ENOMEM; } int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, - snd_ctl_t **ctl, - const char *device) + struct ctl_list **ctll, + const char *device, + int slave) { struct list_head *pos1, *pos2; + snd_ctl_t *ctl; struct ctl_list *ctl_list; struct ctl_dev *ctl_dev; snd_ctl_card_info_t *info; const char *id; - int err, card; + int err, card, ucm_group, ucm_offset; snd_ctl_card_info_alloca(&info); + ucm_group = _snd_is_ucm_device(device); + ucm_offset = ucm_group ? 8 : 0; + /* cache lookup */ list_for_each(pos1, &uc_mgr->ctl_list) { ctl_list = list_entry(pos1, struct ctl_list, list); + if (ctl_list->ucm_group != ucm_group) + continue; list_for_each(pos2, &ctl_list->dev_list) { ctl_dev = list_entry(pos2, struct ctl_dev, list); - if (strcmp(ctl_dev->device, device) == 0) { - *ctl = ctl_list->ctl; + if (strcmp(ctl_dev->device, device + ucm_offset) == 0) { + *ctll = ctl_list; + if (!slave) + ctl_list->slave = 0; return 0; } } } - err = snd_ctl_open(ctl, device, 0); + err = snd_ctl_open(&ctl, device, 0); if (err < 0) return err; id = NULL; - err = snd_ctl_card_info(*ctl, info); + err = snd_ctl_card_info(ctl, info); if (err == 0) id = snd_ctl_card_info_get_id(info); if (err < 0 || id == NULL || id[0] == '\0') { uc_error("control hardware info (%s): %s", device, snd_strerror(err)); - snd_ctl_close(*ctl); - *ctl = NULL; - return err; + snd_ctl_close(ctl); + return err >= 0 ? -EINVAL : err; } /* insert to cache, if just name differs */ list_for_each(pos1, &uc_mgr->ctl_list) { ctl_list = list_entry(pos1, struct ctl_list, list); + if (ctl_list->ucm_group != ucm_group) + continue; if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) { card = snd_card_get_index(id); - err = uc_mgr_ctl_add(uc_mgr, ctl_list, *ctl, card, info, device); + err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, card, info, device + ucm_offset, slave); if (err < 0) goto __nomem; - snd_ctl_close(*ctl); - *ctl = ctl_list->ctl; + snd_ctl_close(ctl); + ctl_list->ucm_group = ucm_group; + *ctll = ctl_list; return 0; } } - err = uc_mgr_ctl_add(uc_mgr, NULL, *ctl, -1, info, device); + ctl_list = NULL; + err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, -1, info, device + ucm_offset, slave); if (err < 0) goto __nomem; + ctl_list->ucm_group = ucm_group; + *ctll = ctl_list; return 0; __nomem: - snd_ctl_close(*ctl); - *ctl = NULL; + snd_ctl_close(ctl); return -ENOMEM; } -int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) +const char *uc_mgr_config_dir(int format) +{ + const char *path; + + if (format >= 2) { + path = getenv(ALSA_CONFIG_UCM2_VAR); + if (!path || path[0] == '\0') + path = ALSA_CONFIG_DIR "/ucm2"; + } else { + path = getenv(ALSA_CONFIG_UCM_VAR); + if (!path || path[0] == '\0') + path = ALSA_CONFIG_DIR "/ucm"; + } + return path; +} + +int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top) { FILE *fp; snd_input_t *in; - snd_config_t *top; - const char *path, *default_paths[2]; + const char *default_paths[2]; int err; fp = fopen(file, "r"); if (!fp) { err = -errno; - __err0: + __err_open: uc_error("could not open configuration file %s", file); return err; } err = snd_input_stdio_attach(&in, fp, 1); if (err < 0) - goto __err0; - err = snd_config_top(&top); - if (err < 0) - goto __err1; + goto __err_open; - if (format >= 2) { - path = getenv(ALSA_CONFIG_UCM2_VAR); - if (!path || path[0] == '\0') - path = ALSA_CONFIG_DIR "/ucm2"; - } else { - path = getenv(ALSA_CONFIG_UCM_VAR); - if (!path || path[0] == '\0') - path = ALSA_CONFIG_DIR "/ucm"; - } - - default_paths[0] = path; + default_paths[0] = uc_mgr_config_dir(format); default_paths[1] = NULL; err = _snd_config_load_with_include(top, in, 0, default_paths); if (err < 0) { uc_error("could not load configuration file %s", file); - goto __err2; + if (in) + snd_input_close(in); + return err; } err = snd_input_close(in); + if (err < 0) + return err; + return 0; +} + +int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) +{ + snd_config_t *top; + int err; + + err = snd_config_top(&top); + if (err < 0) + return err; + err = uc_mgr_config_load_into(format, file, top); if (err < 0) { - in = NULL; - goto __err2; + snd_config_delete(top); + return err; } *cfg = top; return 0; - - __err2: - snd_config_delete(top); - __err1: - if (in) - snd_input_close(in); - return err; } void uc_mgr_free_value(struct list_head *base) @@ -371,7 +470,7 @@ return 0; } } - return -ENOENT; + return -ENODEV; } int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name) @@ -400,13 +499,22 @@ free(seq->data.cdev); break; case SEQUENCE_ELEMENT_TYPE_CSET: + case SEQUENCE_ELEMENT_TYPE_CSET_NEW: case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE: case SEQUENCE_ELEMENT_TYPE_CSET_TLV: + case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE: free(seq->data.cset); break; + case SEQUENCE_ELEMENT_TYPE_SYSSET: + free(seq->data.sysw); + break; case SEQUENCE_ELEMENT_TYPE_EXEC: + case SEQUENCE_ELEMENT_TYPE_SHELL: free(seq->data.exec); break; + case SEQUENCE_ELEMENT_TYPE_CFGSAVE: + free(seq->data.cfgsave); + break; default: break; } @@ -528,17 +636,72 @@ { struct use_case_device *device; struct list_head *pos, *npos; + int err, found = 0; list_for_each_safe(pos, npos, &verb->device_list) { device = list_entry(pos, struct use_case_device, list); if (strcmp(device->name, name) == 0) { uc_mgr_free_device(device); + found++; continue; } - uc_mgr_remove_from_dev_list(&device->dev_list, name); - return 0; + err = uc_mgr_remove_from_dev_list(&device->dev_list, name); + if (err < 0 && err != -ENODEV) + return err; + if (err == 0) + found++; + } + return found == 0 ? -ENODEV : 0; +} + +const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name) +{ + struct list_head *pos; + struct ucm_value *value; + + list_for_each(pos, &uc_mgr->variable_list) { + value = list_entry(pos, struct ucm_value, list); + if (strcmp(value->name, name) == 0) + return value->data; + } + return NULL; +} + +int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, + const char *val) +{ + struct list_head *pos; + struct ucm_value *curr; + char *val2; + + list_for_each(pos, &uc_mgr->variable_list) { + curr = list_entry(pos, struct ucm_value, list); + if (strcmp(curr->name, name) == 0) { + val2 = strdup(val); + if (val2 == NULL) + return -ENOMEM; + free(curr->data); + curr->data = val2; + return 0; + } + } + + curr = calloc(1, sizeof(struct ucm_value)); + if (curr == NULL) + return -ENOMEM; + curr->name = strdup(name); + if (curr->name == NULL) { + free(curr); + return -ENOMEM; + } + curr->data = strdup(val); + if (curr->data == NULL) { + free(curr->name); + free(curr); + return -ENOMEM; } - return -ENOENT; + list_add_tail(&curr->list, &uc_mgr->variable_list); + return 0; } void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) @@ -562,8 +725,11 @@ list_del(&verb->list); free(verb); } + uc_mgr_free_sequence(&uc_mgr->fixedboot_list); + uc_mgr_free_sequence(&uc_mgr->boot_list); uc_mgr_free_sequence(&uc_mgr->default_list); uc_mgr_free_value(&uc_mgr->value_list); + uc_mgr_free_value(&uc_mgr->variable_list); free(uc_mgr->comment); free(uc_mgr->conf_dir_name); free(uc_mgr->conf_file_name); @@ -577,8 +743,98 @@ void uc_mgr_free(snd_use_case_mgr_t *uc_mgr) { + snd_config_delete(uc_mgr->local_config); uc_mgr_free_verb(uc_mgr); uc_mgr_free_ctl_list(uc_mgr); free(uc_mgr->card_name); free(uc_mgr); } + +/* + * UCM card list stuff + */ + +static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER; +static LIST_HEAD(ucm_cards); +static unsigned int ucm_card_assign; + +static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number) +{ + struct list_head *pos; + snd_use_case_mgr_t *uc_mgr; + + list_for_each(pos, &ucm_cards) { + uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list); + if (uc_mgr->ucm_card_number == card_number) + return uc_mgr; + } + return NULL; +} + +int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr) +{ + unsigned int prev; + + pthread_mutex_lock(&ucm_cards_mutex); + prev = ucm_card_assign++; + while (uc_mgr_card_find(ucm_card_assign)) { + ucm_card_assign++; + ucm_card_assign &= 0xffff; + /* avoid zero card instance number */ + if (ucm_card_assign == 0) + ucm_card_assign++; + if (ucm_card_assign == prev) { + pthread_mutex_unlock(&ucm_cards_mutex); + return -ENOMEM; + } + } + uc_mgr->ucm_card_number = ucm_card_assign; + list_add(&uc_mgr->cards_list, &ucm_cards); + pthread_mutex_unlock(&ucm_cards_mutex); + return 0; +} + +void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr) +{ + pthread_mutex_lock(&ucm_cards_mutex); + list_del(&uc_mgr->cards_list); + pthread_mutex_unlock(&ucm_cards_mutex); +} + +/** + * \brief Get library configuration based on the private ALSA device name + * \param name[in] ALSA device name + * \retval config A configuration tree or NULL + * + * The returned configuration (non-NULL) should be unreferenced using + * snd_config_unref() call. + */ +const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name) +{ + char buf[5]; + long card_num; + snd_config_t *config; + snd_use_case_mgr_t *uc_mgr; + int err; + + if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.') + return NULL; + strncpy(buf, name + 4, 4); + buf[4] = '\0'; + err = safe_strtol_base(buf, &card_num, 16); + if (err < 0 || card_num < 0 || card_num > 0xffff) + return NULL; + config = NULL; + pthread_mutex_lock(&ucm_cards_mutex); + uc_mgr = uc_mgr_card_find(card_num); + /* non-empty configs are accepted only */ + if (uc_mgr_has_local_config(uc_mgr)) { + config = uc_mgr->local_config; + snd_config_ref(config); + } + pthread_mutex_unlock(&ucm_cards_mutex); + if (!config) + return NULL; + *top = config; + return name + 9; +} diff -Nru alsa-lib-1.2.2/test/Makefile.in alsa-lib-1.2.6.1/test/Makefile.in --- alsa-lib-1.2.2/test/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/test/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -419,6 +419,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/test/audio_time.c alsa-lib-1.2.6.1/test/audio_time.c --- alsa-lib-1.2.2/test/audio_time.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/test/audio_time.c 2021-12-09 13:17:59.000000000 +0000 @@ -19,7 +19,6 @@ #include #include "../include/asoundlib.h" -static char *command; static char *pcm_name = "hw:0"; snd_output_t *output = NULL; @@ -32,7 +31,7 @@ "-d, --delay add delay \n" "-D, --device=NAME select PCM by name \n" "-p, --playback playback tstamps \n" - "-t, --ts_type=TYPE Default(0),link(1),link_estimated(2),synchronized(3) \n" + "-t, --ts_type=TYPE Compat(0),default(1),link(2),link_absolute(3),link_estimated(4),link_synchronized(5) \n" "-r, --report show audio timestamp and accuracy validity\n" , command); } @@ -42,7 +41,7 @@ { long long nsec; - nsec = t.tv_sec * 1000000000; + nsec = t.tv_sec * 1000000000ULL; nsec += t.tv_nsec; return nsec; @@ -149,7 +148,7 @@ while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { switch (c) { case 'h': - usage(command); + usage(argv[0]); return 0; case 'p': do_playback = 1; @@ -201,17 +200,17 @@ goto _exit; } - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 0)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT)) printf("Playback supports audio compat timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 1)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) printf("Playback supports audio default timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 2)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK)) printf("Playback supports audio link timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 3)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE)) printf("Playback supports audio link absolute timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 4)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED)) printf("Playback supports audio link estimated timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, 5)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_p, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)) printf("Playback supports audio link synchronized timestamps\n"); snd_pcm_sw_params_alloca(&swparams_p); @@ -269,17 +268,17 @@ goto _exit; } - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 0)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_COMPAT)) printf("Capture supports audio compat timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 1)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) printf("Capture supports audio default timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 2)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK)) printf("Capture supports audio link timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 3)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE)) printf("Capture supports audio link absolute timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 4)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED)) printf("Capture supports audio link estimated timestamps\n"); - if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, 5)) + if (snd_pcm_hw_params_supports_audio_ts_type(hwparams_c, SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)) printf("Capture supports audio link synchronized timestamps\n"); snd_pcm_sw_params_alloca(&swparams_c); diff -Nru alsa-lib-1.2.2/test/lsb/Makefile.in alsa-lib-1.2.6.1/test/lsb/Makefile.in --- alsa-lib-1.2.2/test/lsb/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/test/lsb/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -509,6 +509,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/test/lsb/config.c alsa-lib-1.2.6.1/test/lsb/config.c --- alsa-lib-1.2.2/test/lsb/config.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/test/lsb/config.c 2021-12-09 13:17:59.000000000 +0000 @@ -548,6 +548,72 @@ ALSA_CHECK(snd_config_delete(c)); } +static int _expand_fcn(snd_config_t **dst, const char *s, void *private_data ATTRIBUTE_UNUSED) +{ + if (strcmp(s, "var10") == 0) + return snd_config_imake_integer(dst, NULL, 10); + if (strcmp(s, "var50") == 0) + return snd_config_imake_integer(dst, NULL, 50); + return snd_config_imake_string(dst, NULL, ""); +} + +static void test_evaluate_string(void) +{ + struct { + const char *expr; + long long result; + } *p, e[] = { + { .expr = "$var10", .result = 10 }, + { .expr = "$var50", .result = 50 }, + { .expr = "$[1+1]", .result = 2 }, + { .expr = "$[10-5]", .result = 5 }, + { .expr = "$[10*5]", .result = 50 }, + { .expr = "$[15/5]", .result = 3 }, + { .expr = "$[12%5]", .result = 2 }, + { .expr = "$[0xaa|0x55]", .result = 0xff }, + { .expr = "$[0xff&0xfc]", .result = 0xfc }, + { .expr = "$[4294967296+10]", .result = 4294967306LL }, + { .expr = "$[$var10+1]", .result = 11 }, + { .expr = "$[$var10 + $var50]", .result = 60 }, + { .expr = "$[ $var10 + $[ $var50 + 10 ] ]", .result = 70 }, + { .expr = "$[ ( $var10 + ( $var50 + 112 ) ) + 5 ]", .result = 177 }, + { .expr = NULL, .result = 0 }, + }; + snd_config_t *dst; + long l; + long long ll; + + for (p = e; p->expr; p++) { + ALSA_CHECK(snd_config_evaluate_string(&dst, p->expr, _expand_fcn, NULL)); + if (snd_config_get_type(dst) == SND_CONFIG_TYPE_INTEGER) { + ALSA_CHECK(snd_config_get_integer(dst, &l)); + TEST_CHECK(l == p->result); + } else if (snd_config_get_type(dst) == SND_CONFIG_TYPE_INTEGER64) { + ALSA_CHECK(snd_config_get_integer64(dst, &ll)); + TEST_CHECK(ll == p->result); + } else { + ALSA_CHECK(0); + } + ALSA_CHECK(snd_config_delete(dst)); + } +} + +static void test_load_string(void) +{ + const char **cfg, *configs[] = { + "a=1,b=2", + "j 3;z 15;", + "x 0 y -1", + NULL + }; + snd_config_t *dst; + + for (cfg = configs; *cfg; cfg++) { + ALSA_CHECK(snd_config_load_string(&dst, *cfg, 0)); + ALSA_CHECK(snd_config_delete(dst)); + } +} + int main(void) { test_top(); @@ -578,5 +644,7 @@ test_get_ascii(); test_iterators(); test_for_each(); + test_evaluate_string(); + test_load_string(); return TEST_EXIT_CODE(); } diff -Nru alsa-lib-1.2.2/test/pcm_min.c alsa-lib-1.2.6.1/test/pcm_min.c --- alsa-lib-1.2.2/test/pcm_min.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/test/pcm_min.c 2021-12-09 13:17:59.000000000 +0000 @@ -5,47 +5,49 @@ #include "../include/asoundlib.h" static char *device = "default"; /* playback device */ - -snd_output_t *output = NULL; unsigned char buffer[16*1024]; /* some random data */ int main(void) { - int err; - unsigned int i; - snd_pcm_t *handle; - snd_pcm_sframes_t frames; + int err; + unsigned int i; + snd_pcm_t *handle; + snd_pcm_sframes_t frames; - for (i = 0; i < sizeof(buffer); i++) - buffer[i] = random() & 0xff; + for (i = 0; i < sizeof(buffer); i++) + buffer[i] = random() & 0xff; if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = snd_pcm_set_params(handle, - SND_PCM_FORMAT_U8, - SND_PCM_ACCESS_RW_INTERLEAVED, - 1, - 48000, - 1, - 500000)) < 0) { /* 0.5sec */ + SND_PCM_FORMAT_U8, + SND_PCM_ACCESS_RW_INTERLEAVED, + 1, + 48000, + 1, + 500000)) < 0) { /* 0.5sec */ printf("Playback open error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } for (i = 0; i < 16; i++) { - frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); - if (frames < 0) - frames = snd_pcm_recover(handle, frames, 0); - if (frames < 0) { - printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); - break; - } - if (frames > 0 && frames < (long)sizeof(buffer)) - printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames); - } + frames = snd_pcm_writei(handle, buffer, sizeof(buffer)); + if (frames < 0) + frames = snd_pcm_recover(handle, frames, 0); + if (frames < 0) { + printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); + break; + } + if (frames > 0 && frames < (long)sizeof(buffer)) + printf("Short write (expected %li, wrote %li)\n", (long)sizeof(buffer), frames); + } + /* pass the remaining samples, otherwise they're dropped in close */ + err = snd_pcm_drain(handle); + if (err < 0) + printf("snd_pcm_drain failed: %s\n", snd_strerror(err)); snd_pcm_close(handle); return 0; } diff -Nru alsa-lib-1.2.2/test/rawmidi.c alsa-lib-1.2.6.1/test/rawmidi.c --- alsa-lib-1.2.2/test/rawmidi.c 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/test/rawmidi.c 2021-12-09 13:17:59.000000000 +0000 @@ -13,6 +13,7 @@ fprintf(stderr, " -o device-id : test ALSA output device\n"); fprintf(stderr, " -I node : test input node\n"); fprintf(stderr, " -O node : test output node\n"); + fprintf(stderr, " -c clock : kernel clock type (0=none, 1=realtime, 2=monotonic, 3=monotonic raw)\n"); fprintf(stderr, " -t: test midi thru\n"); fprintf(stderr, " example:\n"); fprintf(stderr, " rawmidi -i hw:0,0 -O /dev/midi1\n"); @@ -37,7 +38,8 @@ char *device_out = NULL; char *node_in = NULL; char *node_out = NULL; - + int clock_type = -1; + int fd_in = -1,fd_out = -1; snd_rawmidi_t *handle_in = 0,*handle_out = 0; @@ -58,6 +60,10 @@ case 't': thru = 1; break; + case 'c': + if (i + 1 < argc) + clock_type = atoi(argv[++i]); + break; case 'i': if (i + 1 < argc) device_in = argv[++i]; @@ -133,20 +139,67 @@ } } - - if (!thru) { if (handle_in || fd_in!=-1) { + if (clock_type != -1) { + snd_rawmidi_params_t *params; + snd_rawmidi_params_malloc(¶ms); + if (!handle_in) { + fprintf(stderr, "-c only usable with -i"); + clock_type = -1; + } + if (clock_type != -1) { + fprintf(stderr, "Enable kernel clock type %d\n", clock_type); + snd_rawmidi_params_current(handle_in, params); + err = snd_rawmidi_params_set_read_mode(handle_in, params, SND_RAWMIDI_READ_TSTAMP); + if (err) { + fprintf(stderr,"snd_rawmidi_params_set_read_mode failed: %d\n", err); + clock_type = -1; + } + } + if (clock_type != -1) { + err = snd_rawmidi_params_set_clock_type(handle_in, params, clock_type); + if (err) { + fprintf(stderr, "snd_rawmidi_params_set_clock_type failed: %d\n", err); + clock_type = -1; + } + } + if (clock_type != -1) { + err = snd_rawmidi_params(handle_in, params); + if (err) { + fprintf(stderr, "snd_rawmidi_params failed: %d\n", err); + clock_type = -1; + } + } + snd_rawmidi_params_free(params); + } + fprintf(stderr,"Read midi in\n"); fprintf(stderr,"Press ctrl-c to stop\n"); } if (handle_in) { - unsigned char ch; + unsigned char buf[1024]; + ssize_t ret; while (!stop) { - snd_rawmidi_read(handle_in,&ch,1); - if (verbose) { - fprintf(stderr,"read %02x\n",ch); + if (clock_type != -1) { + struct timespec tstamp; + ret = snd_rawmidi_tread(handle_in, &tstamp, buf, sizeof(buf)); + if (ret < 0) + fprintf(stderr, "read timestamp error: %d - %s\n", (int)ret, snd_strerror(ret)); + if (ret > 0 && verbose) { + fprintf(stderr, "read [%lld:%09lld]", (long long)tstamp.tv_sec, (long long)tstamp.tv_nsec); + for (i = 0; i < ret; i++) + fprintf(stderr, " %02x", buf[i]); + fprintf(stderr, "\n"); + } + } else { + ret = snd_rawmidi_read(handle_in, buf, sizeof(buf)); + if (ret < 0) + fprintf(stderr, "read error: %d - %s\n", (int)ret, snd_strerror(ret)); + if (ret > 0 && verbose) + for (i = 0; i < ret; i++) + fprintf(stderr,"read %02x\n",buf[i]); } } } diff -Nru alsa-lib-1.2.2/test-driver alsa-lib-1.2.6.1/test-driver --- alsa-lib-1.2.2/test-driver 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/test-driver 2021-12-09 14:53:52.000000000 +0000 @@ -3,7 +3,7 @@ scriptversion=2018-03-07.03; # UTC -# Copyright (C) 2011-2018 Free Software Foundation, Inc. +# Copyright (C) 2011-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff -Nru alsa-lib-1.2.2/utils/Makefile.in alsa-lib-1.2.6.1/utils/Makefile.in --- alsa-lib-1.2.2/utils/Makefile.in 2020-02-19 10:25:26.000000000 +0000 +++ alsa-lib-1.2.6.1/utils/Makefile.in 2021-12-09 14:53:52.000000000 +0000 @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -277,6 +277,7 @@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ diff -Nru alsa-lib-1.2.2/utils/alsa.m4 alsa-lib-1.2.6.1/utils/alsa.m4 --- alsa-lib-1.2.2/utils/alsa.m4 2020-02-19 09:35:39.000000000 +0000 +++ alsa-lib-1.2.6.1/utils/alsa.m4 2021-12-09 13:17:59.000000000 +0000 @@ -81,12 +81,11 @@ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` AC_MSG_RESULT($alsa_min_major_version.$alsa_min_minor_version.$alsa_min_micro_version) -AC_LANG_SAVE -AC_LANG_C +AC_LANG_PUSH([C]) AC_MSG_CHECKING([for libasound headers version >= $alsa_min_major_version.$alsa_min_minor_version.$alsa_min_micro_version ($min_alsa_version)]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include -], [ +]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) #define SND_LIB_MAJOR SOUNDLIB_VERSION_MAJOR @@ -118,21 +117,20 @@ # endif # endif exit(0); -], +]])], [AC_MSG_RESULT(found.)], [AC_MSG_RESULT(not present.) ifelse([$3], , [AC_MSG_ERROR(Sufficiently new version of libasound not found.)]) alsa_found=no] ) -AC_LANG_RESTORE +AC_LANG_POP([C]) -AC_LANG_SAVE -AC_LANG_C +AC_LANG_PUSH([C]) AC_MSG_CHECKING([for libatopology (sound headers version > 1.1.9)]) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include -], [ +]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_VERSION) #define SND_LIB_VERSION 0 @@ -143,13 +141,12 @@ # error not present #endif exit(0); -], +]])], [AC_MSG_RESULT(yes) enable_atopology="yes"], [AC_MSG_RESULT(no)] ) -AC_LANG_RESTORE - +AC_LANG_POP([C]) fi dnl Now that we know that we have the right version, let's see if we have the library and not just the headers. diff -Nru alsa-lib-1.2.2/version alsa-lib-1.2.6.1/version --- alsa-lib-1.2.2/version 2020-02-19 10:25:35.000000000 +0000 +++ alsa-lib-1.2.6.1/version 2021-12-09 14:54:00.000000000 +0000 @@ -1 +1 @@ -1.2.2 +1.2.6.1