diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e23bd502..b01ad81d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,10 +13,12 @@ jobs: dnf -y upgrade dnf -y install @development-tools libtool bzip2 - name: Checkout - uses: actions/checkout@v2 - - name: Checkout all tags + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Safe git directory run: | - git fetch --prune --unshallow + git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Modify version run: | mv configure.ac configure.ac.old @@ -73,7 +75,7 @@ jobs: image: ubuntu:latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Prepare environment run: | export DEBIAN_FRONTEND=noninteractive diff --git a/.gitignore b/.gitignore index 88d6b6ad..a74b5195 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ m4/ltsugar.m4 m4/ltversion.m4 m4/lt~obsolete.m4 src/Versions +src/Versions.in src/conf/topology/sklrt286/data/pvt_data src/control/ctl_symbols_list.c src/pcm/pcm_symbols_list.c diff --git a/README.md b/README.md index 56af78d0..2cf6a077 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 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) +[![Build alsa-lib](https://github.com/alsa-project/alsa-lib/workflows/Build%20alsa-lib/badge.svg?branch=master)](https://github.com/alsa-project/alsa-lib/actions/workflows/build.yml) The alsa-lib is a library to interface with ALSA in the Linux kernel and virtual devices using a plugin system. diff --git a/aserver/aserver.c b/aserver/aserver.c index 28387021..50ee3992 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -18,6 +18,8 @@ * */ +#include "aserver.h" + #include #include #include @@ -33,7 +35,6 @@ #include #include -#include "aserver.h" char *command; @@ -530,7 +531,7 @@ transport_ops_t pcm_shm_ops = { static int ctl_handler(waiter_t *waiter, unsigned short events) { client_t *client = waiter->private_data; - char buf[1]; + char buf[1] = ""; ssize_t n; if (events & POLLIN) { n = write(client->poll_fd, buf, 1); @@ -737,7 +738,7 @@ static int snd_client_open(client_t *client) ans.result = -EINVAL; goto _answer; } - name = alloca(req.namelen); + name = alloca(req.namelen + 1); err = read(client->ctrl_fd, name, req.namelen); if (err < 0) { SYSERROR("read failed"); @@ -774,6 +775,10 @@ static int snd_client_open(client_t *client) name[req.namelen] = '\0'; client->transport_type = req.transport_type; + if (sizeof(client->name) < (size_t)(req.namelen + 1)) { + ans.result = -ENOMEM; + goto _answer; + } strcpy(client->name, name); client->stream = req.stream; client->mode = req.mode; diff --git a/configure.ac b/configure.ac index 4fd16d10..3f238302 100644 --- a/configure.ac +++ b/configure.ac @@ -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.6) +AC_INIT(alsa-lib, 1.2.11) AC_CONFIG_SRCDIR([src/control/control.c]) AC_CONFIG_MACRO_DIR([m4]) @@ -46,12 +46,16 @@ dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_C_INLINE AC_HEADER_TIME +AC_CHECK_ATTRIBUTE_SYMVER dnl Checks for library functions. AC_PROG_GCC_TRADITIONAL AC_CHECK_FUNCS([uselocale]) AC_CHECK_FUNCS([eaccess]) +dnl Enable largefile support +AC_SYS_LARGEFILE + SAVE_LIBRARY_VERSION AC_SUBST(LIBTOOL_VERSION_INFO) @@ -235,6 +239,19 @@ if test "$softfloat" != "yes"; then ALSA_DEPLIBS="-lm" fi +dnl Check for scandir64 +AC_MSG_CHECKING(for LFS calls) +AC_TRY_LINK([#include ], + [struct dirent64 a; ], + [have_lfs=yes], + [have_lfs=no]) +if test "$have_lfs" = "yes"; then + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_LFS], 1, [Have LFS]) +else + AC_MSG_RESULT(no) +fi + dnl Check for libdl AC_MSG_CHECKING(for libdl) AC_ARG_WITH(libdl, @@ -242,9 +259,8 @@ AC_ARG_WITH(libdl, [ have_libdl="$withval" ], [ have_libdl="yes" ]) HAVE_LIBDL= if test "$have_libdl" = "yes"; then - AC_CHECK_LIB([dl], [dlsym], [HAVE_LIBDL="yes"]) + AC_SEARCH_LIBS([dlsym], [dl], [HAVE_LIBDL="yes"]) if test "$HAVE_LIBDL" = "yes" ; then - ALSA_DEPLIBS="$ALSA_DEPLIBS -ldl" AC_DEFINE([HAVE_LIBDL], 1, [Have libdl]) fi else @@ -322,7 +338,7 @@ else fi dnl Check for headers -AC_CHECK_HEADERS([endian.h sys/endian.h sys/shm.h]) +AC_CHECK_HEADERS([endian.h sys/endian.h sys/shm.h malloc.h]) dnl Check for resmgr support... AC_MSG_CHECKING(for resmgr support) @@ -626,6 +642,9 @@ fi if test "$build_pcm_alaw" = "yes"; then AC_DEFINE([BUILD_PCM_PLUGIN_ALAW], "1", [Build PCM alaw plugin]) fi +if test "$build_pcm_iec958" = "yes"; then + AC_DEFINE([BUILD_PCM_PLUGIN_IEC958], "1", [Build PCM iec958 plugin]) +fi if test "$build_pcm_mmap_emul" = "yes"; then AC_DEFINE([BUILD_PCM_PLUGIN_MMAP_EMUL], "1", [Build PCM mmap-emul plugin]) fi @@ -731,28 +750,31 @@ if test ! -L "$srcdir"/include/alsa ; then ln -sf . "$srcdir"/include/alsa fi -AC_OUTPUT(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) +AC_CONFIG_FILES(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ + include/Makefile include/sound/Makefile include/sound/uapi/Makefile \ + src/Versions.in 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) + +AC_OUTPUT() dnl Create asoundlib.h dynamically according to configure options echo "Creating asoundlib.h..." cp "$srcdir"/include/asoundlib-head.h include/asoundlib.h +if test "$ac_cv_header_sys_endian_h" != "yes"; then test "$ac_cv_header_endian_h" = "yes" && echo "#include " >> include/asoundlib.h -if test "$ac_cv_header_sys_endian_h" = "yes"; then +else cat >> include/asoundlib.h < #ifndef __BYTE_ORDER @@ -768,9 +790,11 @@ EOF fi cat >> include/asoundlib.h < #include @@ -782,6 +806,7 @@ cat >> include/asoundlib.h <" >> include/asoundlib.h test "$build_rawmidi" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_rawmidi" = "yes" && echo "#include " >> include/asoundlib.h test "$build_pcm" = "yes" && echo "#include " >> include/asoundlib.h test "$build_hwdep" = "yes" && echo "#include " >> include/asoundlib.h echo "#include " >> include/asoundlib.h diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index c5ebdfab..8bf29b21 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -36,18 +36,21 @@ INPUT = @top_srcdir@/doc/index.doxygen \ @top_srcdir@/src/input.c \ @top_srcdir@/src/output.c \ @top_srcdir@/src/conf.c \ + @top_srcdir@/src/confeval.c \ @top_srcdir@/src/confmisc.c \ @top_srcdir@/src/names.c \ @top_srcdir@/src/shmarea.c \ @top_srcdir@/src/userfile.c \ @top_srcdir@/src/control/cards.c \ @top_srcdir@/src/control/control.c \ + @top_srcdir@/src/control/control_ext.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/namehint.c \ @top_srcdir@/src/control/setup.c \ @top_srcdir@/src/control/tlv.c \ @top_srcdir@/src/mixer \ @@ -109,8 +112,9 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \ @top_srcdir@/src/topology/tplg_local.h RECURSIVE = YES FILE_PATTERNS = *.c *.h +INCLUDE_PATH = @top_builddir@/include EXAMPLE_PATH = @top_srcdir@/test -IMAGE_PATH = pictures +IMAGE_PATH = @top_srcdir@/doc/pictures QUIET = YES EXTRACT_ALL = NO @@ -126,7 +130,8 @@ PREDEFINED = DOXYGEN PIC "DOC_HIDDEN" \ ALSA_PCM_NEW_HW_PARAMS_API \ _POSIX_C_SOURCE \ "use_default_symbol_version(x,y,z)=" \ - "link_warning(x,y)=" + "link_warning(x,y)=" \ + __attribute__((x))= OPTIMIZE_OUTPUT_FOR_C = YES # doxygen 1.2.6 option TYPEDEF_HIDES_STRUCT = YES # needed in doxygen >= 1.5.4 diff --git a/doc/pictures/Makefile.am b/doc/pictures/Makefile.am index 17b6e12d..df3128bd 100644 --- a/doc/pictures/Makefile.am +++ b/doc/pictures/Makefile.am @@ -1 +1,9 @@ -EXTRA_DIST=wave1.gif wave2.gif +GIT_FILES=$(wildcard *.gif) +PUML_FILES=$(wildcard *.puml) +SVG_FILES=$(PUML_FILES:.puml=.svg) +EXTRA_DIST=$(GIT_FILES) $(PUML_FILES) $(SVG_FILES) + +all: $(SVG_FILES) + +.puml.svg: + plantuml -tsvg $< diff --git a/doc/pictures/ucm-seq-boot.puml b/doc/pictures/ucm-seq-boot.puml new file mode 100644 index 00000000..3b797280 --- /dev/null +++ b/doc/pictures/ucm-seq-boot.puml @@ -0,0 +1,17 @@ +@startuml +title "UCM sequence boot order (using udev and alsactl)" + +start +:Sound card detected (udev); +:UCM: FixedBootSequence; +if (Card state (/var/lib/alsa/asound.state)) then (not present) + :UCM: BootSequence; +else (present) + :alsactl: Restore state (from asound.state); +endif +:Standard sound card use (with or without UCM); +:Save sound card state (asound.state); +:Sound card detached; +stop + +@enduml diff --git a/doc/pictures/ucm-seq-boot.svg b/doc/pictures/ucm-seq-boot.svg new file mode 100644 index 00000000..867c817e --- /dev/null +++ b/doc/pictures/ucm-seq-boot.svg @@ -0,0 +1,27 @@ +UCM sequence boot order (using udev and alsactl)Sound card detected (udev)UCM: FixedBootSequenceCard state (/var/lib/alsa/asound.state)not presentpresentUCM: BootSequencealsactl: Restore state (from asound.state)Standard sound card use (with or without UCM)Save sound card state (asound.state)Sound card detached \ No newline at end of file diff --git a/doc/pictures/ucm-seq-device.puml b/doc/pictures/ucm-seq-device.puml new file mode 100644 index 00000000..a1c3f8cd --- /dev/null +++ b/doc/pictures/ucm-seq-device.puml @@ -0,0 +1,26 @@ +@startuml +title "UCM device sequence graph" + +start +split + :Enable device; + :EnableSequence; + :Use device; + :Disable device; + :DisableSequence; +split again + :Switch device; + :Disable old device; + if (TransitionSequence) then (present) + :TransitionSequence; + :Enable new device; + else (not present) + :Disable old device; + :DisableSequence; + :Enable new device; + :EnableSequence; + endif +end split +stop + +@enduml diff --git a/doc/pictures/ucm-seq-device.svg b/doc/pictures/ucm-seq-device.svg new file mode 100644 index 00000000..fff208dc --- /dev/null +++ b/doc/pictures/ucm-seq-device.svg @@ -0,0 +1,36 @@ +UCM device sequence graphEnable deviceEnableSequenceUse deviceDisable deviceDisableSequenceSwitch deviceDisable old deviceTransitionSequencepresentnot presentTransitionSequenceEnable new deviceDisable old deviceDisableSequenceEnable new deviceEnableSequence \ No newline at end of file diff --git a/doc/pictures/ucm-seq-verb.puml b/doc/pictures/ucm-seq-verb.puml new file mode 100644 index 00000000..e0e54805 --- /dev/null +++ b/doc/pictures/ucm-seq-verb.puml @@ -0,0 +1,30 @@ +@startuml +title "UCM verb sequence graph" + + +start +split + :Enable verb; + :SectionDefaults sequence; + :EnableSequence; + + :Enable, use and disable verb devices; + + :Disable verb; + :DisableSequence; +split again + :Switch verb; + :Disable old verb; + if (TransitionSequence) then (present) + :TransitionSequence; + :Enable new verb; + else (not present) + :Disable old verb; + :DisableSequence; + :Enable new verb; + :EnableSequence; + endif +end split +stop + +@enduml diff --git a/doc/pictures/ucm-seq-verb.svg b/doc/pictures/ucm-seq-verb.svg new file mode 100644 index 00000000..82eb9da6 --- /dev/null +++ b/doc/pictures/ucm-seq-verb.svg @@ -0,0 +1,40 @@ +UCM verb sequence graphEnable verbSectionDefaults sequenceEnableSequenceEnable, use and disable verb devicesDisable verbDisableSequenceSwitch verbDisable old verbTransitionSequencepresentnot presentTransitionSequenceEnable new verbDisable old verbDisableSequenceEnable new verbEnableSequence \ No newline at end of file diff --git a/doc/pictures/ucm-volume.puml b/doc/pictures/ucm-volume.puml new file mode 100644 index 00000000..a698b316 --- /dev/null +++ b/doc/pictures/ucm-volume.puml @@ -0,0 +1,19 @@ +@startuml +title "UCM volume" + +start +if (Card state (/var/lib/alsa/asound.state)) then (not present) + :UCM: BootSequence; +else (present) + :alsactl: Restore state (from asound.state); +endif +:UCM application:set device volume; +note right + The application uses (Playback|Capture)MixerElem or + (Playback/Capture)(Volume/Switch) values to get + the controls. +end note +:alsactl: Store state (to asound.state); +stop + +@enduml diff --git a/doc/pictures/ucm-volume.svg b/doc/pictures/ucm-volume.svg new file mode 100644 index 00000000..d07b2dc2 --- /dev/null +++ b/doc/pictures/ucm-volume.svg @@ -0,0 +1,29 @@ +UCM volumeCard state (/var/lib/alsa/asound.state)not presentpresentUCM: BootSequencealsactl: Restore state (from asound.state)The application uses (Playback|Capture)MixerElem or(Playback/Capture)(Volume/Switch) values to getthe controls.UCM application:set device volumealsactl: Store state (to asound.state) \ No newline at end of file diff --git a/gitcompile b/gitcompile index 41da25dc..c70448f7 100755 --- a/gitcompile +++ b/gitcompile @@ -2,7 +2,8 @@ set -e -bit32= +bits32= +cbits32= modules= alisp= lto= @@ -12,6 +13,7 @@ if [ $# -ne 0 ]; then case "$1" in 32) bits32=yes + cbits32="-m32" echo "Forced 32-bit library build..." shift ;; modules) @@ -30,6 +32,10 @@ if [ $# -ne 0 ]; then lto="-flto -flto-partition=none" echo "Forced lto build..." shift ;; + static) + static=yes + echo "Selected static build..." + shift ;; *) endloop=yes ;; @@ -40,7 +46,7 @@ if [ $# -ne 0 -a -z "$bit32" ]; then args="$@" elif [ -r /etc/asound/library_args ]; then args="`cat /etc/asound/library_args`" - if [ -z "$bit32" ]; then + if [ -z "$bits32" ]; then test -r /etc/asound/library64_args && \ args="`cat /etc/asound/library64_args`" fi @@ -48,7 +54,7 @@ else prefix="/usr" libdir="/usr/lib" libdir2="/usr/lib" - if [ -z "$bit32" ]; then + if [ -z "$bits32" ]; then test -d /usr/lib64 && libdir="/usr/lib64" test -f /lib64/libasound.so.2 && libdir="/lib64" test -d /usr/lib64 && libdir2="/usr/lib64" @@ -73,6 +79,12 @@ if [ "$python2" = "yes" ]; then args="$args --enable-python2" fi +if [ "$static" = "yes" ]; then + #args="$args --enable-shared=no --enable-static=yes" + args="$args --disable-shared" +fi + + touch ltconfig libtoolize --force --copy --automake aclocal $ACLOCAL_FLAGS @@ -80,7 +92,7 @@ autoheader automake --foreign --copy --add-missing touch depcomp # seems to be missing for old automake autoconf -export CFLAGS="-O2 -Wall -W -Wunused-const-variable=0 -pipe -g $lto" +export CFLAGS="$cbits32 -O2 -Wall -W -Wunused-const-variable=0 -pipe -g $lto" if [ -n "$lto" ]; then export AR="gcc-ar" export RANLIB="gcc-ranlib" diff --git a/include/Makefile.am b/include/Makefile.am index 9909fb73..16d68b51 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -34,7 +34,7 @@ endif endif if BUILD_RAWMIDI -alsainclude_HEADERS += rawmidi.h +alsainclude_HEADERS += rawmidi.h ump.h ump_msg.h endif if BUILD_HWDEP diff --git a/include/alsa-symbols.h b/include/alsa-symbols.h index 344f021a..2298cb50 100644 --- a/include/alsa-symbols.h +++ b/include/alsa-symbols.h @@ -29,10 +29,17 @@ #define INTERNAL_CONCAT2_2(Pre, Post) Pre##Post #define INTERNAL(Name) INTERNAL_CONCAT2_2(__, Name) +#if HAVE_ATTRIBUTE_SYMVER && __GNUC__ > 10 +#define symbol_version(real, name, version) \ + extern __typeof (real) real __attribute__((symver (#name "@" #version))) +#define default_symbol_version(real, name, version) \ + extern __typeof (real) real __attribute__((symver (#name "@@" #version))) +#else #define symbol_version(real, name, version) \ __asm__ (".symver " ASM_NAME(#real) "," ASM_NAME(#name) "@" #version) #define default_symbol_version(real, name, version) \ __asm__ (".symver " ASM_NAME(#real) "," ASM_NAME(#name) "@@" #version) +#endif #ifdef __clang__ #define EXPORT_SYMBOL __attribute__((visibility("default"))) diff --git a/include/aserver.h b/include/aserver.h index 6fb9480c..ffddf2b4 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -18,9 +18,9 @@ * */ -#include #include "../src/pcm/pcm_local.h" #include "../src/control/control_local.h" +#include int snd_receive_fd(int sock, void *data, size_t len, int *fd); diff --git a/include/asoundef.h b/include/asoundef.h index f4dfa920..261db40b 100644 --- a/include/asoundef.h +++ b/include/asoundef.h @@ -215,6 +215,8 @@ extern "C" { #define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED (1<<7) /**< stereo downmis prohibited */ #define CEA861_AUDIO_INFOFRAME_DB5_LSV (0xf<<3) /**< mask - level-shift values */ +/** \} */ + /** * \defgroup MIDI_Interface Constants for MIDI v1.0 * Constants for MIDI v1.0. @@ -224,6 +226,8 @@ extern "C" { #define MIDI_CHANNELS 16 /**< Number of channels per port/cable. */ #define MIDI_GM_DRUM_CHANNEL (10-1) /**< Channel number for GM drums. */ +/** \} */ + /** * \defgroup MIDI_Commands MIDI Commands * MIDI command codes. @@ -335,8 +339,6 @@ extern "C" { /** \} */ -/** \} */ - #ifdef __cplusplus } #endif diff --git a/include/asoundlib.h b/include/asoundlib.h index cb2c5838..d64e2766 100644 --- a/include/asoundlib.h +++ b/include/asoundlib.h @@ -1,7 +1,14 @@ -/* - * ALSA lib header file include/asoundlib.h - * Copyright (c) 2022 Huawei Device Co., Ltd. +/** + * \file include/asoundlib.h + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \date 1998-2001 * + * Application interface library for the ALSA driver + */ +/* * 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 @@ -33,9 +40,11 @@ #include #include +#ifndef DOC_HIDDEN #ifndef __GNUC__ #define __inline__ inline #endif +#endif /* DOC_HIDDEN */ #include #include @@ -46,6 +55,7 @@ #include #include #include +#include #include #include #include diff --git a/include/bswap.h b/include/bswap.h index 4e5b3e2a..e590124c 100644 --- a/include/bswap.h +++ b/include/bswap.h @@ -27,6 +27,11 @@ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 +#elif defined(__OpenBSD__) +#include +#define bswap_16 swap16 +#define bswap_32 swap32 +#define bswap_64 swap64 #elif defined (__sun) #include #define bswap_16 BSWAP_16 diff --git a/include/conf.h b/include/conf.h index 56ba6c23..09da0e9a 100644 --- a/include/conf.h +++ b/include/conf.h @@ -33,7 +33,7 @@ extern "C" { #endif /** - * \defgroup Config Configuration Interface + * \defgroup Configuration Configuration Interface * The configuration functions and types allow you to read, enumerate, * modify and write the contents of ALSA configuration files. * \{ @@ -109,6 +109,16 @@ int snd_config_search_definition(snd_config_t *config, const char *base, const char *key, snd_config_t **result); +/** + * \brief custom expansion callback + * \param[out] dst The function puts the handle to the new configuration + * node at the address specified by \a dst. + * \param[in] s string the string to be expanded + * \param[in] private_data Handle to the \c private_data node. + * \return A non-negative value if successful, otherwise a negative error code. + * + * Use a function of this type to define a custom expansion + */ 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, @@ -129,6 +139,7 @@ int snd_config_remove(snd_config_t *config); 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_substitute(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, diff --git a/include/config.h b/include/config.h index 25dc05c3..68013447 100644 --- a/include/config.h +++ b/include/config.h @@ -1,31 +1,11 @@ -/* - * ALSA lib header file include/config.h - * Copyright (c) 2022 Huawei Device Co., Ltd. - * - * 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_CONFIG_H -#define __ALSA_CONFIG_H +/* include/config.h. Generated from config.h.in by configure. */ +/* include/config.h.in. Generated from configure.ac by autoheader. */ /* Directory with aload* device files */ #define ALOAD_DEVICE_DIRECTORY "/dev/" /* directory containing ALSA configuration database */ -#define ALSA_CONFIG_DIR "/system/etc/audio/alsa/share" +#define ALSA_CONFIG_DIR "/usr/share/alsa" /* Enable assert at error message handler */ /* #undef ALSA_DEBUG_ASSERT */ @@ -33,8 +13,11 @@ /* Directory with ALSA device files */ #define ALSA_DEVICE_DIRECTORY "/dev/snd/" +/* directory containing pkgconfig files */ +#define ALSA_PKGCONF_DIR "/usr/lib/pkgconfig" + /* directory containing ALSA add-on modules */ -#define ALSA_PLUGIN_DIR "/system/lib" +#define ALSA_PLUGIN_DIR "/usr/lib/alsa-lib" /* Build hwdep component */ #define BUILD_HWDEP "1" @@ -51,6 +34,9 @@ /* Build PCM alaw plugin */ #define BUILD_PCM_PLUGIN_ALAW "1" +/* Build PCM iec958 plugin */ +#define BUILD_PCM_PLUGIN_IEC958 "1" + /* Build PCM lfloat plugin */ #define BUILD_PCM_PLUGIN_LFLOAT "1" @@ -78,6 +64,9 @@ /* Build UCM component */ #define BUILD_UCM "1" +/* Define to 0 if __attribute__((symver)) is not supported */ +#define HAVE_ATTRIBUTE_SYMVER 0 + /* Have clock gettime */ #define HAVE_CLOCK_GETTIME 1 @@ -93,6 +82,9 @@ /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 +/* Have LFS */ +#define HAVE_LFS 1 + /* Have libdl */ #define HAVE_LIBDL 1 @@ -105,11 +97,14 @@ /* Have librt */ #define HAVE_LIBRT 1 +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* MMX technology is enabled */ -/* #undef HAVE_MMX */ +#define HAVE_MMX "1" /* Define if your pthreads implementation have PTHREAD_MUTEX_RECURSIVE */ #define HAVE_PTHREAD_MUTEX_RECURSIVE /**/ @@ -172,7 +167,7 @@ #define PACKAGE_NAME "alsa-lib" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "alsa-lib 1.2.6" +#define PACKAGE_STRING "alsa-lib 1.2.11" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "alsa-lib" @@ -181,7 +176,7 @@ #define PACKAGE_URL "" /* Define to the version of this package. */ -#define PACKAGE_VERSION "1.2.6" +#define PACKAGE_VERSION "1.2.11" /* Max number of cards */ #define SND_MAX_CARDS 32 @@ -204,11 +199,44 @@ /* directory to put tmp socket files */ #define TMPDIR "/tmp" +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + /* sound library version string */ -#define VERSION "1.2.6" +#define VERSION "1.2.11" /* compiled with versioned symbols */ -#define VERSIONED_SYMBOLS +#define VERSIONED_SYMBOLS /**/ + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ /* Define to 1 if on MINIX. */ /* #undef _MINIX */ @@ -231,5 +259,3 @@ #ifndef __cplusplus /* #undef inline */ #endif - -#endif /* __ALSA_CONFIG_H */ diff --git a/include/control.h b/include/control.h index e386ecec..e7541d56 100644 --- a/include/control.h +++ b/include/control.h @@ -356,6 +356,9 @@ typedef enum _snd_ctl_type { /** Read only (flag for open mode) \hideinitializer */ #define SND_CTL_READONLY 0x0004 +/** Return EINTR instead blocking (flag for open mode) \hideinitializer */ +#define SND_CTL_EINTR 0x0080 + /** CTL handle */ typedef struct _snd_ctl snd_ctl_t; @@ -371,10 +374,6 @@ int snd_card_get_index(const char *name); int snd_card_get_name(int card, char **name); int snd_card_get_longname(int card, char **name); -int snd_device_name_hint(int card, const char *iface, void ***hints); -int snd_device_name_free_hint(void **hints); -char *snd_device_name_get_hint(const void *hint, const char *id); - int snd_ctl_open(snd_ctl_t **ctl, const char *name, int mode); int snd_ctl_open_lconf(snd_ctl_t **ctl, const char *name, int mode, snd_config_t *lconf); int snd_ctl_open_fallback(snd_ctl_t **ctl, snd_config_t *root, const char *name, const char *orig_name, int mode); @@ -415,6 +414,11 @@ int snd_ctl_rawmidi_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_rawmidi_info(snd_ctl_t *ctl, snd_rawmidi_info_t * info); int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev); #endif +#ifdef __ALSA_UMP_H +int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device); +int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info); +int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info); +#endif int snd_ctl_set_power_state(snd_ctl_t *ctl, unsigned int state); int snd_ctl_get_power_state(snd_ctl_t *ctl, unsigned int *state); @@ -791,6 +795,20 @@ int snd_sctl_remove(snd_sctl_t *handle); /** \} */ +/** + * \defgroup Hint Name Hint Interface + * \ingroup Configuration + * The name hint interface - get descriptive information about a device + * (name, description, input/output). + * \{ + */ + +int snd_device_name_hint(int card, const char *iface, void ***hints); +int snd_device_name_free_hint(void **hints); +char *snd_device_name_get_hint(const void *hint, const char *id); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/include/global.h b/include/global.h index 71a1b12f..3ecaeee8 100644 --- a/include/global.h +++ b/include/global.h @@ -51,6 +51,11 @@ const char *snd_asoundlib_version(void); #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) #endif +#ifndef __STRING +/** \brief Return 'x' argument as string */ +#define __STRING(x) #x +#endif + #ifdef PIC /* dynamic build */ /** \hideinitializer \brief Helper macro for #SND_DLSYM_BUILD_VERSION. */ @@ -82,18 +87,13 @@ extern struct snd_dlsym_link *snd_dlsym_start; void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) __attribute__ ((constructor)); \ void __SND_DLSYM_VERSION(snd_dlsym_constructor_, name, version) (void) { \ __SND_DLSYM_VERSION(snd_dlsym_, name, version).next = snd_dlsym_start; \ - __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_name = # name; \ + __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_name = __STRING(name); \ __SND_DLSYM_VERSION(snd_dlsym_, name, version).dlsym_ptr = (void *)&name; \ snd_dlsym_start = &__SND_DLSYM_VERSION(snd_dlsym_, name, version); \ } #endif -#ifndef __STRING -/** \brief Return 'x' argument as string */ -#define __STRING(x) #x -#endif - /** \brief Returns the version of a dynamic symbol as a string. */ #define SND_DLSYM_VERSION(version) __STRING(version) diff --git a/include/input.h b/include/input.h index e7ce791f..f84954a9 100644 --- a/include/input.h +++ b/include/input.h @@ -28,6 +28,8 @@ #ifndef __ALSA_INPUT_H #define __ALSA_INPUT_H +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/include/local.h b/include/local.h index ebc9350c..512e4455 100644 --- a/include/local.h +++ b/include/local.h @@ -34,6 +34,9 @@ #include #elif defined(HAVE_SYS_ENDIAN_H) #include +#else +#error Header defining endianness not defined +#endif #ifndef __BYTE_ORDER #define __BYTE_ORDER BYTE_ORDER #endif @@ -43,9 +46,6 @@ #ifndef __BIG_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif -#else -#error Header defining endianness not defined -#endif #include #include #include @@ -69,13 +69,28 @@ #if __BYTE_ORDER == __LITTLE_ENDIAN #define SND_LITTLE_ENDIAN #define SNDRV_LITTLE_ENDIAN +#define SNDRV_LITTLE_ENDIAN_BITFIELD #elif __BYTE_ORDER == __BIG_ENDIAN #define SND_BIG_ENDIAN #define SNDRV_BIG_ENDIAN +#define SNDRV_BIG_ENDIAN_BITFIELD #else #error "Unsupported endian..." #endif +#ifndef HAVE_LFS +#define stat64 stat +#define lstat64 lstat +#define dirent64 dirent +#define readdir64 readdir +#define scandir64 scandir +#define versionsort64 versionsort +#define alphasort64 alphasort +#define ino64_t ino_t +#define fstat64 fstat +#define stat64 stat +#endif + #define _snd_config_iterator list_head #define _snd_interval snd_interval #define _snd_pcm_info snd_pcm_info @@ -167,6 +182,7 @@ #include "pcm.h" #include "pcm_plugin.h" #include "rawmidi.h" +#include "ump.h" #include "timer.h" #include "hwdep.h" #include "control.h" @@ -180,7 +196,9 @@ #define snd_seq_real_time sndrv_seq_real_time #define snd_seq_timestamp sndrv_seq_timestamp #define snd_seq_event_type_t sndrv_seq_event_type_t +#define snd_seq_event_data sndrv_seq_event_data #define snd_seq_event sndrv_seq_event +#define snd_seq_ump_event sndrv_seq_ump_event #define snd_seq_connect sndrv_seq_connect #define snd_seq_ev_note sndrv_seq_ev_note #define snd_seq_ev_ctrl sndrv_seq_ev_ctrl @@ -232,10 +250,14 @@ size_t page_align(size_t size); 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_strtoll_base(const char *str, long long *val, int base); +#define safe_strtoll_base _snd_safe_strtoll_base +int _snd_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); +#define safe_strtol_base _snd_safe_strtol_base +int _snd_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); } +#define safe_strtod _snd_safe_strtod +int _snd_safe_strtod(const char *str, double *val); 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); diff --git a/include/mixer.h b/include/mixer.h index 51b6f04a..735dfdd8 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -47,7 +47,7 @@ typedef struct _snd_mixer_elem snd_mixer_elem_t; /** * \brief Mixer callback function - * \param mixer Mixer handle + * \param ctl Mixer handle * \param mask event mask * \param elem related mixer element (if any) * \return 0 on success otherwise a negative error code diff --git a/include/mixer_abst.h b/include/mixer_abst.h index ea8c096e..ec23e0dd 100644 --- a/include/mixer_abst.h +++ b/include/mixer_abst.h @@ -97,10 +97,10 @@ struct sm_elem_ops { int snd_mixer_selem_compare(const snd_mixer_elem_t *c1, const snd_mixer_elem_t *c2); -int snd_mixer_sbasic_info(const snd_mixer_class_t *class, sm_class_basic_t *info); -void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *class); -void snd_mixer_sbasic_set_private(const snd_mixer_class_t *class, void *private_data); -void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *class, void (*private_free)(snd_mixer_class_t *class)); +int snd_mixer_sbasic_info(const snd_mixer_class_t *mixer_class, sm_class_basic_t *info); +void *snd_mixer_sbasic_get_private(const snd_mixer_class_t *mixer_class); +void snd_mixer_sbasic_set_private(const snd_mixer_class_t *mixer_class, void *private_data); +void snd_mixer_sbasic_set_private_free(const snd_mixer_class_t *mixer_class, void (*private_free)(snd_mixer_class_t *mixer_class)); /** \} */ diff --git a/include/output.h b/include/output.h index 4a970dc4..5e16420b 100644 --- a/include/output.h +++ b/include/output.h @@ -28,6 +28,8 @@ #ifndef __ALSA_OUTPUT_H #define __ALSA_OUTPUT_H +#include + #ifdef __cplusplus extern "C" { #endif diff --git a/include/pcm.h b/include/pcm.h index b5a514fa..102ff812 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -282,9 +282,17 @@ typedef enum _snd_pcm_format { /** PCM sample subformat */ typedef enum _snd_pcm_subformat { + /** Unknown */ + SND_PCM_SUBFORMAT_UNKNOWN = -1, /** Standard */ SND_PCM_SUBFORMAT_STD = 0, - SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_STD + /** Maximum bits based on PCM format */ + SND_PCM_SUBFORMAT_MSBITS_MAX = 1, + /** 20 most significant bits */ + SND_PCM_SUBFORMAT_MSBITS_20 = 2, + /** 24 most significant bits */ + SND_PCM_SUBFORMAT_MSBITS_24 = 3, + SND_PCM_SUBFORMAT_LAST = SND_PCM_SUBFORMAT_MSBITS_24 } snd_pcm_subformat_t; /** PCM state */ @@ -343,6 +351,7 @@ typedef enum _snd_pcm_tstamp { SND_PCM_TSTAMP_LAST = SND_PCM_TSTAMP_ENABLE } snd_pcm_tstamp_t; +/** PCM timestamp type */ typedef enum _snd_pcm_tstamp_type { SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY = 0, /**< gettimeofday equivalent */ SND_PCM_TSTAMP_TYPE_MONOTONIC, /**< posix_clock_monotonic equivalent */ @@ -350,6 +359,7 @@ typedef enum _snd_pcm_tstamp_type { SND_PCM_TSTAMP_TYPE_LAST = SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW, } snd_pcm_tstamp_type_t; +/** PCM audio timestamp type */ typedef enum _snd_pcm_audio_tstamp_type { /** * first definition for backwards compatibility only, @@ -364,24 +374,22 @@ typedef enum _snd_pcm_audio_tstamp_type { SND_PCM_AUDIO_TSTAMP_TYPE_LAST = SND_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED } snd_pcm_audio_tstamp_type_t; +/** PCM audio timestamp config */ typedef struct _snd_pcm_audio_tstamp_config { /* 5 of max 16 bits used */ - unsigned int type_requested:4; - unsigned int report_delay:1; /* add total delay to A/D or D/A */ + unsigned int type_requested:4; /**< requested audio tstamp type */ + unsigned int report_delay:1; /**< add total delay to A/D or D/A */ } snd_pcm_audio_tstamp_config_t; +/** PCM audio timestamp report */ typedef struct _snd_pcm_audio_tstamp_report { /* 6 of max 16 bits used for bit-fields */ - /* for backwards compatibility */ - unsigned int valid:1; + unsigned int valid:1; /**< for backwards compatibility */ + unsigned int actual_type:4; /**< actual type if hardware could not support requested timestamp */ - /* actual type if hardware could not support requested timestamp */ - unsigned int actual_type:4; - - /* accuracy represented in ns units */ - unsigned int accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ - unsigned int accuracy; /* up to 4.29s, will be packed in separate field */ + unsigned int accuracy_report:1; /**< 0 if accuracy unknown, 1 if accuracy field is valid */ + unsigned int accuracy; /**< up to 4.29s in ns units, will be packed in separate field */ } snd_pcm_audio_tstamp_report_t; /** Unsigned frames quantity */ @@ -393,6 +401,8 @@ typedef long snd_pcm_sframes_t; #define SND_PCM_NONBLOCK 0x00000001 /** Async notification (flag for open mode) \hideinitializer */ #define SND_PCM_ASYNC 0x00000002 +/** Return EINTR instead blocking (wait operation) */ +#define SND_PCM_EINTR 0x00000080 /** In an abort state (internal, not allowed for open) */ #define SND_PCM_ABORT 0x00008000 /** Disable automatic (but not forced!) rate resamplinig */ @@ -498,6 +508,13 @@ typedef union _snd_pcm_sync_id { unsigned int id32[4]; } snd_pcm_sync_id_t; +/** Infinite wait for snd_pcm_wait() */ +#define SND_PCM_WAIT_INFINITE (-1) +/** Wait for next i/o in snd_pcm_wait() */ +#define SND_PCM_WAIT_IO (-10001) +/** Wait for drain in snd_pcm_wait() */ +#define SND_PCM_WAIT_DRAIN (-10002) + /** #SND_PCM_TYPE_METER scope handle */ typedef struct _snd_pcm_scope snd_pcm_scope_t; @@ -722,6 +739,7 @@ int snd_pcm_hw_params_is_half_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_is_joint_duplex(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_sync_start(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_is_perfect_drain(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_supports_audio_wallclock_ts(const snd_pcm_hw_params_t *params); /* deprecated, use audio_ts_type */ int snd_pcm_hw_params_supports_audio_ts_type(const snd_pcm_hw_params_t *params, int type); int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, @@ -821,6 +839,8 @@ int snd_pcm_hw_params_set_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *par int snd_pcm_hw_params_get_export_buffer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); int snd_pcm_hw_params_set_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); +int snd_pcm_hw_params_set_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); +int snd_pcm_hw_params_get_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); @@ -1080,6 +1100,7 @@ const char *snd_pcm_format_name(const snd_pcm_format_t format); const char *snd_pcm_format_description(const snd_pcm_format_t format); const char *snd_pcm_subformat_name(const snd_pcm_subformat_t subformat); const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat); +snd_pcm_subformat_t snd_pcm_subformat_value(const char* name); snd_pcm_format_t snd_pcm_format_value(const char* name); const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode); const char *snd_pcm_state_name(const snd_pcm_state_t state); diff --git a/include/pcm_old.h b/include/pcm_old.h index e6e050fc..a9f5308f 100644 --- a/include/pcm_old.h +++ b/include/pcm_old.h @@ -2,11 +2,14 @@ * Old ALSA 0.9.x API */ +#define ___symbol_version(name, version) \ + __asm__ (".symver " #name "," #name "@" version) + #ifdef ALSA_PCM_OLD_HW_PARAMS_API -asm(".symver snd_pcm_hw_params_get_access,snd_pcm_hw_params_get_access@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_first,snd_pcm_hw_params_set_access_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_access_last,snd_pcm_hw_params_set_access_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_access, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_access_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_access_last, "ALSA_0.9"); int snd_pcm_hw_params_get_access(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_test_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t val); @@ -16,9 +19,9 @@ snd_pcm_access_t snd_pcm_hw_params_set_access_last(snd_pcm_t *pcm, snd_pcm_hw_pa int snd_pcm_hw_params_set_access_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); void snd_pcm_hw_params_get_access_mask(snd_pcm_hw_params_t *params, snd_pcm_access_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_format,snd_pcm_hw_params_get_format@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_first,snd_pcm_hw_params_set_format_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_format_last,snd_pcm_hw_params_set_format_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_format, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_format_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_format_last, "ALSA_0.9"); int snd_pcm_hw_params_get_format(const snd_pcm_hw_params_t *params); int snd_pcm_hw_params_test_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); @@ -28,9 +31,9 @@ snd_pcm_format_t snd_pcm_hw_params_set_format_last(snd_pcm_t *pcm, snd_pcm_hw_pa int snd_pcm_hw_params_set_format_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); void snd_pcm_hw_params_get_format_mask(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_subformat,snd_pcm_hw_params_get_subformat@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_first,snd_pcm_hw_params_set_subformat_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_subformat_last,snd_pcm_hw_params_set_subformat_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_subformat, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_subformat_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_subformat_last, "ALSA_0.9"); int snd_pcm_hw_params_test_subformat(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t val); int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params); @@ -40,12 +43,12 @@ snd_pcm_subformat_t snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm int snd_pcm_hw_params_set_subformat_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); void snd_pcm_hw_params_get_subformat_mask(snd_pcm_hw_params_t *params, snd_pcm_subformat_mask_t *mask); -asm(".symver snd_pcm_hw_params_get_channels,snd_pcm_hw_params_get_channels@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_min,snd_pcm_hw_params_get_channels_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_channels_max,snd_pcm_hw_params_get_channels_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_near,snd_pcm_hw_params_set_channels_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_first,snd_pcm_hw_params_set_channels_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_channels_last,snd_pcm_hw_params_set_channels_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_channels_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_channels_last, "ALSA_0.9"); int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params); unsigned int snd_pcm_hw_params_get_channels_min(const snd_pcm_hw_params_t *params); @@ -59,12 +62,12 @@ unsigned int snd_pcm_hw_params_set_channels_near(snd_pcm_t *pcm, snd_pcm_hw_para unsigned int snd_pcm_hw_params_set_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); unsigned int snd_pcm_hw_params_set_channels_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_rate,snd_pcm_hw_params_get_rate@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_min,snd_pcm_hw_params_get_rate_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_rate_max,snd_pcm_hw_params_get_rate_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_near,snd_pcm_hw_params_set_rate_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_first,snd_pcm_hw_params_set_rate_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_rate_last,snd_pcm_hw_params_set_rate_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_rate_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_rate_last, "ALSA_0.9"); int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_rate_min(const snd_pcm_hw_params_t *params, int *dir); @@ -80,12 +83,12 @@ unsigned int snd_pcm_hw_params_set_rate_last(snd_pcm_t *pcm, snd_pcm_hw_params_t int snd_pcm_hw_params_set_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); int snd_pcm_hw_params_get_rate_resample(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val); -asm(".symver snd_pcm_hw_params_get_period_time,snd_pcm_hw_params_get_period_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_min,snd_pcm_hw_params_get_period_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_time_max,snd_pcm_hw_params_get_period_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_near,snd_pcm_hw_params_set_period_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_first,snd_pcm_hw_params_set_period_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_time_last,snd_pcm_hw_params_set_period_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_period_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -99,12 +102,12 @@ unsigned int snd_pcm_hw_params_set_period_time_near(snd_pcm_t *pcm, snd_pcm_hw_p unsigned int snd_pcm_hw_params_set_period_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_set_period_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -asm(".symver snd_pcm_hw_params_get_period_size,snd_pcm_hw_params_get_period_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_min,snd_pcm_hw_params_get_period_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_period_size_max,snd_pcm_hw_params_get_period_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_near,snd_pcm_hw_params_set_period_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_first,snd_pcm_hw_params_set_period_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_period_size_last,snd_pcm_hw_params_set_period_size_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_period_size_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_period_size_last, "ALSA_0.9"); snd_pcm_sframes_t snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, int *dir); snd_pcm_uframes_t snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, int *dir); @@ -119,12 +122,12 @@ snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_first(snd_pcm_t *pcm, snd_pc snd_pcm_uframes_t snd_pcm_hw_params_set_period_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); int snd_pcm_hw_params_set_period_size_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_periods,snd_pcm_hw_params_get_periods@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_min,snd_pcm_hw_params_get_periods_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_periods_max,snd_pcm_hw_params_get_periods_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_near,snd_pcm_hw_params_set_periods_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_first,snd_pcm_hw_params_set_periods_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_periods_last,snd_pcm_hw_params_set_periods_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_periods_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_periods_last, "ALSA_0.9"); int snd_pcm_hw_params_get_periods(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_periods_min(const snd_pcm_hw_params_t *params, int *dir); @@ -139,12 +142,12 @@ unsigned int snd_pcm_hw_params_set_periods_first(snd_pcm_t *pcm, snd_pcm_hw_para unsigned int snd_pcm_hw_params_set_periods_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); int snd_pcm_hw_params_set_periods_integer(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_buffer_time,snd_pcm_hw_params_get_buffer_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_min,snd_pcm_hw_params_get_buffer_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_time_max,snd_pcm_hw_params_get_buffer_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_near,snd_pcm_hw_params_set_buffer_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_first,snd_pcm_hw_params_set_buffer_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_time_last,snd_pcm_hw_params_set_buffer_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_buffer_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_buffer_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -158,12 +161,12 @@ unsigned int snd_pcm_hw_params_set_buffer_time_near(snd_pcm_t *pcm, snd_pcm_hw_p unsigned int snd_pcm_hw_params_set_buffer_time_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_set_buffer_time_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir); -asm(".symver snd_pcm_hw_params_get_buffer_size,snd_pcm_hw_params_get_buffer_size@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_min,snd_pcm_hw_params_get_buffer_size_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_buffer_size_max,snd_pcm_hw_params_get_buffer_size_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_near,snd_pcm_hw_params_set_buffer_size_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_first,snd_pcm_hw_params_set_buffer_size_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_buffer_size_last,snd_pcm_hw_params_set_buffer_size_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_buffer_size_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_buffer_size_last, "ALSA_0.9"); snd_pcm_sframes_t snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params); snd_pcm_uframes_t snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params); @@ -177,12 +180,12 @@ snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); snd_pcm_uframes_t snd_pcm_hw_params_set_buffer_size_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); -asm(".symver snd_pcm_hw_params_get_tick_time,snd_pcm_hw_params_get_tick_time@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_min,snd_pcm_hw_params_get_tick_time_min@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_get_tick_time_max,snd_pcm_hw_params_get_tick_time_max@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_near,snd_pcm_hw_params_set_tick_time_near@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_first,snd_pcm_hw_params_set_tick_time_first@ALSA_0.9"); -asm(".symver snd_pcm_hw_params_set_tick_time_last,snd_pcm_hw_params_set_tick_time_last@ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time_min, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_get_tick_time_max, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_near, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_first, "ALSA_0.9"); +___symbol_version(snd_pcm_hw_params_set_tick_time_last, "ALSA_0.9"); int snd_pcm_hw_params_get_tick_time(const snd_pcm_hw_params_t *params, int *dir); unsigned int snd_pcm_hw_params_get_tick_time_min(const snd_pcm_hw_params_t *params, int *dir); @@ -201,14 +204,14 @@ unsigned int snd_pcm_hw_params_set_tick_time_last(snd_pcm_t *pcm, snd_pcm_hw_par #ifdef ALSA_PCM_OLD_SW_PARAMS_API -asm(".symver snd_pcm_sw_params_get_tstamp_mode,snd_pcm_sw_params_get_tstamp_mode@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_sleep_min,snd_pcm_sw_params_get_sleep_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_avail_min,snd_pcm_sw_params_get_avail_min@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_xfer_align,snd_pcm_sw_params_get_xfer_align@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_start_threshold,snd_pcm_sw_params_get_start_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_stop_threshold,snd_pcm_sw_params_get_stop_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_threshold,snd_pcm_sw_params_get_silence_threshold@ALSA_0.9"); -asm(".symver snd_pcm_sw_params_get_silence_size,snd_pcm_sw_params_get_silence_size@ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_tstamp_mode, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_sleep_min, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_avail_min, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_xfer_align, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_start_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_stop_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_silence_threshold, "ALSA_0.9"); +___symbol_version(snd_pcm_sw_params_get_silence_size, "ALSA_0.9"); int snd_pcm_sw_params_set_tstamp_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_tstamp_t val); snd_pcm_tstamp_t snd_pcm_sw_params_get_tstamp_mode(const snd_pcm_sw_params_t *params); diff --git a/include/pcm_plugin.h b/include/pcm_plugin.h index 2d014394..f3e8f3b5 100644 --- a/include/pcm_plugin.h +++ b/include/pcm_plugin.h @@ -133,6 +133,19 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, const char *name, snd_config_t *root, snd_config_t *conf, snd_pcm_stream_t stream, int mode); +/* + * IEC958 subframe conversion plugin + */ +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, + int hdmi_mode); +int _snd_pcm_iec958_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root, snd_config_t *conf, + snd_pcm_stream_t stream, int mode); + /* * Route plugin for linear formats */ diff --git a/include/rawmidi.h b/include/rawmidi.h index 71681053..2630d1e6 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -93,6 +93,9 @@ typedef enum _snd_rawmidi_read_mode { SND_RAWMIDI_READ_TSTAMP = 1, } snd_rawmidi_read_mode_t; +/** rawmidi info bit flags */ +#define SND_RAWMIDI_INFO_UMP 0x00000008 /* rawmidi is UMP */ + 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, diff --git a/include/seq.h b/include/seq.h index 123a1057..e55f5c16 100644 --- a/include/seq.h +++ b/include/seq.h @@ -130,6 +130,13 @@ typedef enum snd_seq_client_type { SND_SEQ_KERNEL_CLIENT = 2 /**< kernel client */ } snd_seq_client_type_t; +/** client MIDI version */ +enum { + SND_SEQ_CLIENT_LEGACY_MIDI = 0, /**< Legacy client */ + SND_SEQ_CLIENT_UMP_MIDI_1_0 = 1, /**< UMP MIDI 1.0 */ + SND_SEQ_CLIENT_UMP_MIDI_2_0 = 2 /**< UMP MIDI 2.0 */ +}; + size_t snd_seq_client_info_sizeof(void); /** allocate a #snd_seq_client_info_t container on stack */ #define snd_seq_client_info_alloca(ptr) \ @@ -149,11 +156,22 @@ const unsigned char *snd_seq_client_info_get_event_filter(const snd_seq_client_i int snd_seq_client_info_get_num_ports(const snd_seq_client_info_t *info); int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info); +int snd_seq_client_info_get_midi_version(const snd_seq_client_info_t *info); +int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, + int group); +int snd_seq_client_info_get_ump_groupless_enabled(const snd_seq_client_info_t *info); +int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info); void snd_seq_client_info_set_client(snd_seq_client_info_t *info, int client); void snd_seq_client_info_set_name(snd_seq_client_info_t *info, const char *name); void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int val); void snd_seq_client_info_set_error_bounce(snd_seq_client_info_t *info, int val); void snd_seq_client_info_set_event_filter(snd_seq_client_info_t *info, unsigned char *filter); +void snd_seq_client_info_set_midi_version(snd_seq_client_info_t *info, int midi_version); +void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, + int group, int enable); +void snd_seq_client_info_set_ump_groupless_enabled(snd_seq_client_info_t *info, + int enable); +void snd_seq_client_info_set_ump_conversion(snd_seq_client_info_t *info, int enable); void snd_seq_client_info_event_filter_clear(snd_seq_client_info_t *info); void snd_seq_client_info_event_filter_add(snd_seq_client_info_t *info, int event_type); @@ -165,6 +183,11 @@ int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_in int snd_seq_set_client_info(snd_seq_t *handle, snd_seq_client_info_t *info); int snd_seq_query_next_client(snd_seq_t *handle, snd_seq_client_info_t *info); +int snd_seq_get_ump_endpoint_info(snd_seq_t *seq, int client, void *info); +int snd_seq_get_ump_block_info(snd_seq_t *seq, int client, int blk, void *info); +int snd_seq_set_ump_endpoint_info(snd_seq_t *seq, const void *info); +int snd_seq_set_ump_block_info(snd_seq_t *seq, int blk, const void *info); + /* */ @@ -222,6 +245,14 @@ typedef struct _snd_seq_port_info snd_seq_port_info_t; #define SND_SEQ_PORT_CAP_SUBS_READ (1<<5) /**< allow read subscription */ #define SND_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /**< allow write subscription */ #define SND_SEQ_PORT_CAP_NO_EXPORT (1<<7) /**< routing not allowed */ +#define SND_SEQ_PORT_CAP_INACTIVE (1<<8) /**< inactive port */ +#define SND_SEQ_PORT_CAP_UMP_ENDPOINT (1<<9) /**< UMP Endpoint port */ + +/** port direction */ +#define SND_SEQ_PORT_DIR_UNKNOWN 0 /**< Unknown */ +#define SND_SEQ_PORT_DIR_INPUT 1 /**< Input only */ +#define SND_SEQ_PORT_DIR_OUTPUT 2 /**< Output only */ +#define SND_SEQ_PORT_DIR_BIDIRECTION 3 /**< Input/output bidirectional */ /* port type */ /** Messages sent from/to this port have device-specific semantics. */ @@ -238,6 +269,8 @@ typedef struct _snd_seq_port_info snd_seq_port_info_t; #define SND_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /** This port is compatible with the General MIDI 2 specification. */ #define SND_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) +/** This port is a UMP port. */ +#define SND_SEQ_PORT_TYPE_MIDI_UMP (1<<7) /** This port understands SND_SEQ_EVENT_SAMPLE_xxx messages (these are not MIDI messages). */ #define SND_SEQ_PORT_TYPE_SYNTH (1<<10) @@ -283,6 +316,8 @@ int snd_seq_port_info_get_port_specified(const snd_seq_port_info_t *info); int snd_seq_port_info_get_timestamping(const snd_seq_port_info_t *info); int snd_seq_port_info_get_timestamp_real(const snd_seq_port_info_t *info); int snd_seq_port_info_get_timestamp_queue(const snd_seq_port_info_t *info); +int snd_seq_port_info_get_direction(const snd_seq_port_info_t *info); +int snd_seq_port_info_get_ump_group(const snd_seq_port_info_t *info); void snd_seq_port_info_set_client(snd_seq_port_info_t *info, int client); void snd_seq_port_info_set_port(snd_seq_port_info_t *info, int port); @@ -297,6 +332,8 @@ void snd_seq_port_info_set_port_specified(snd_seq_port_info_t *info, int val); void snd_seq_port_info_set_timestamping(snd_seq_port_info_t *info, int enable); void snd_seq_port_info_set_timestamp_real(snd_seq_port_info_t *info, int realtime); void snd_seq_port_info_set_timestamp_queue(snd_seq_port_info_t *info, int queue); +void snd_seq_port_info_set_direction(snd_seq_port_info_t *info, int direction); +void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group); int snd_seq_create_port(snd_seq_t *handle, snd_seq_port_info_t *info); int snd_seq_delete_port(snd_seq_t *handle, int port); @@ -572,6 +609,12 @@ void snd_seq_remove_events_set_tag(snd_seq_remove_events_t *info, int tag); int snd_seq_remove_events(snd_seq_t *handle, snd_seq_remove_events_t *info); +int snd_seq_ump_event_output(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_event_output_buffer(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_extract_output(snd_seq_t *seq, snd_seq_ump_event_t **ev_res); +int snd_seq_ump_event_output_direct(snd_seq_t *seq, snd_seq_ump_event_t *ev); +int snd_seq_ump_event_input(snd_seq_t *seq, snd_seq_ump_event_t **ev); + /** \} */ /** @@ -729,6 +772,10 @@ extern const unsigned int snd_seq_event_types[]; #define snd_seq_ev_is_direct(ev) \ ((ev)->queue == SND_SEQ_QUEUE_DIRECT) +/** UMP events */ +#define snd_seq_ev_is_ump(ev) \ + ((ev)->flags & SND_SEQ_EVENT_UMP) + /** \} */ #ifdef __cplusplus diff --git a/include/seq_event.h b/include/seq_event.h index 60727f52..9ca384ee 100644 --- a/include/seq_event.h +++ b/include/seq_event.h @@ -225,6 +225,7 @@ typedef union snd_seq_timestamp { #define SND_SEQ_PRIORITY_HIGH (1<<4) /**< event should be processed before others */ #define SND_SEQ_PRIORITY_MASK (1<<4) /**< mask for priority bits */ +#define SND_SEQ_EVENT_UMP (1<<5) /**< UMP packet event */ /** Note event */ typedef struct snd_seq_ev_note { @@ -291,6 +292,19 @@ typedef struct snd_seq_ev_queue_control { } param; /**< data value union */ } snd_seq_ev_queue_control_t; +/** Sequencer event data */ +typedef union snd_seq_event_data { + snd_seq_ev_note_t note; /**< note information */ + snd_seq_ev_ctrl_t control; /**< MIDI control information */ + snd_seq_ev_raw8_t raw8; /**< raw8 data */ + snd_seq_ev_raw32_t raw32; /**< raw32 data */ + snd_seq_ev_ext_t ext; /**< external data */ + snd_seq_ev_queue_control_t queue; /**< queue control */ + snd_seq_timestamp_t time; /**< timestamp */ + snd_seq_addr_t addr; /**< address */ + snd_seq_connect_t connect; /**< connect information */ + snd_seq_result_t result; /**< operation result code */ +} snd_seq_event_data_t; /** Sequencer event */ typedef struct snd_seq_event { @@ -304,20 +318,24 @@ typedef struct snd_seq_event { snd_seq_addr_t source; /**< source address */ snd_seq_addr_t dest; /**< destination address */ - union { - snd_seq_ev_note_t note; /**< note information */ - snd_seq_ev_ctrl_t control; /**< MIDI control information */ - snd_seq_ev_raw8_t raw8; /**< raw8 data */ - snd_seq_ev_raw32_t raw32; /**< raw32 data */ - snd_seq_ev_ext_t ext; /**< external data */ - snd_seq_ev_queue_control_t queue; /**< queue control */ - snd_seq_timestamp_t time; /**< timestamp */ - snd_seq_addr_t addr; /**< address */ - snd_seq_connect_t connect; /**< connect information */ - snd_seq_result_t result; /**< operation result code */ - } data; /**< event data... */ + snd_seq_event_data_t data; /**< event data... */ } snd_seq_event_t; +/** UMP sequencer event; compatible with legacy sequencer event */ +typedef struct snd_seq_ump_event { + snd_seq_event_type_t type; /**< event type */ + unsigned char flags; /**< event flags */ + unsigned char tag; /**< tag */ + unsigned char queue; /**< schedule queue */ + snd_seq_timestamp_t time; /**< schedule time */ + snd_seq_addr_t source; /**< source address */ + snd_seq_addr_t dest; /**< destination address */ + + union { + snd_seq_event_data_t data; /**< (shared) legacy data */ + unsigned int ump[4]; /**< UMP data bytes */ + }; +} snd_seq_ump_event_t; /** \} */ diff --git a/include/seqmid.h b/include/seqmid.h index 3986628a..4464c2d3 100644 --- a/include/seqmid.h +++ b/include/seqmid.h @@ -45,8 +45,21 @@ extern "C" { * * This macro clears the given event record pointer to the default status. */ -#define snd_seq_ev_clear(ev) \ - memset(ev, 0, sizeof(snd_seq_event_t)) +static inline void snd_seq_ev_clear(snd_seq_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); +} + +/** + * \brief initialize event record for UMP + * \param ev event record pointer + * + * This macro clears the given UMP event record pointer to the default status. + */ +static inline void snd_seq_ump_ev_clear(snd_seq_ump_event_t *ev) +{ + memset(ev, 0, sizeof(*ev)); +} /** * \brief set the tag for given event @@ -284,6 +297,31 @@ extern "C" { (ev)->data.queue.queue = (q),\ (ev)->data.queue.param.time.tick = (ttime)) +/** + * \brief set the event UMP flag + * \param ev event record + */ +static inline void snd_seq_ev_set_ump(snd_seq_ump_event_t *ev) +{ + ev->flags |= SND_SEQ_EVENT_UMP; + ev->type = 0; /* unused for UMP */ +} + +/** + * \brief set the event UMP flag and fill UMP raw bytes + * \param ev event record + * \param data UMP packet data + * \param bytes UMP packet size in bytes + */ +static inline int snd_seq_ev_set_ump_data(snd_seq_ump_event_t *ev, void *data, size_t bytes) +{ + if (bytes > 16) + return -EINVAL; + snd_seq_ev_set_ump(ev); + memcpy(ev->ump, data, bytes); + return 0; +} + /* set and send a queue control event */ int snd_seq_control_queue(snd_seq_t *seq, int q, int type, int value, snd_seq_event_t *ev); @@ -343,6 +381,8 @@ int snd_seq_disconnect_to(snd_seq_t *seq, int my_port, int dest_client, int dest */ int snd_seq_set_client_name(snd_seq_t *seq, const char *name); int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type); +int snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version); +int snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable); int snd_seq_set_client_pool_output(snd_seq_t *seq, size_t size); int snd_seq_set_client_pool_output_room(snd_seq_t *seq, size_t size); int snd_seq_set_client_pool_input(snd_seq_t *seq, size_t size); diff --git a/include/sound/type_compat.h b/include/sound/type_compat.h index 953ce566..c670245c 100644 --- a/include/sound/type_compat.h +++ b/include/sound/type_compat.h @@ -1,9 +1,9 @@ -#ifndef __TYPE_COMPAT_H -#define __TYPE_COMPAT_H +#ifndef __SOUND_TYPE_COMPAT_H +#define __SOUND_TYPE_COMPAT_H #ifndef DOC_HIDDEN #include -#ifdef __linux__ +#if defined(__linux__) #include #else typedef uint8_t __u8; @@ -15,8 +15,14 @@ typedef int16_t __s16; typedef int32_t __s32; typedef int64_t __s64; -#include -#include +#if defined(__sun) +#include +#define __cpu_to_le32 LE_32(x) +#define __cpu_to_be32 BE_32(x) +#define __cpu_to_le16 LE_16(x) +#define __cpu_to_be16 BE_16(x) +#else +#include #if __BYTE_ORDER == __LITTLE_ENDIAN #define __cpu_to_le32(x) (x) #define __cpu_to_be32(x) bswap_32(x) @@ -28,20 +34,12 @@ typedef int64_t __s64; #define __cpu_to_le16(x) bswap_16(x) #define __cpu_to_be16(x) (x) #endif +#endif #define __le32_to_cpu __cpu_to_le32 #define __be32_to_cpu __cpu_to_be32 #define __le16_to_cpu __cpu_to_le16 #define __be16_to_cpu __cpu_to_be16 - -#define __le64 __u64 -#define __le32 __u32 -#define __le16 __u16 -#define __le8 __u8 -#define __be64 __u64 -#define __be32 __u32 -#define __be16 __u16 -#define __be8 __u8 #endif #ifndef __kernel_long_t @@ -58,4 +56,4 @@ typedef int64_t __s64; #endif /* DOC_HIDDEN */ -#endif /* __TYPE_COMPAT_H */ +#endif /* __SOUND_TYPE_COMPAT_H */ diff --git a/include/sound/uapi/asequencer.h b/include/sound/uapi/asequencer.h index 2d600320..b913f31d 100644 --- a/include/sound/uapi/asequencer.h +++ b/include/sound/uapi/asequencer.h @@ -26,7 +26,7 @@ #include /** version of the sequencer */ -#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2) +#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 3) /** * definition of sequencer event types @@ -190,6 +190,7 @@ struct snd_seq_connect { #define SNDRV_SEQ_PRIORITY_HIGH (1<<4) /* event should be processed before others */ #define SNDRV_SEQ_PRIORITY_MASK (1<<4) +#define SNDRV_SEQ_EVENT_UMP (1<<5) /* event holds a UMP packet */ /* note event */ struct snd_seq_ev_note { @@ -268,6 +269,19 @@ struct snd_seq_ev_quote { struct snd_seq_event *event; /* quoted event */ } __attribute__((packed)); +union snd_seq_event_data { /* event data... */ + struct snd_seq_ev_note note; + struct snd_seq_ev_ctrl control; + struct snd_seq_ev_raw8 raw8; + struct snd_seq_ev_raw32 raw32; + struct snd_seq_ev_ext ext; + struct snd_seq_ev_queue_control queue; + union snd_seq_timestamp time; + struct snd_seq_addr addr; + struct snd_seq_connect connect; + struct snd_seq_result result; + struct snd_seq_ev_quote quote; +}; /* sequencer event */ struct snd_seq_event { @@ -278,25 +292,27 @@ struct snd_seq_event { unsigned char queue; /* schedule queue */ union snd_seq_timestamp time; /* schedule time */ - struct snd_seq_addr source; /* source address */ struct snd_seq_addr dest; /* destination address */ - union { /* event data... */ - struct snd_seq_ev_note note; - struct snd_seq_ev_ctrl control; - struct snd_seq_ev_raw8 raw8; - struct snd_seq_ev_raw32 raw32; - struct snd_seq_ev_ext ext; - struct snd_seq_ev_queue_control queue; - union snd_seq_timestamp time; - struct snd_seq_addr addr; - struct snd_seq_connect connect; - struct snd_seq_result result; - struct snd_seq_ev_quote quote; - } data; + union snd_seq_event_data data; }; + /* (compatible) event for UMP-capable clients */ +struct snd_seq_ump_event { + snd_seq_event_type_t type; /* event type */ + unsigned char flags; /* event flags */ + char tag; + unsigned char queue; /* schedule queue */ + union snd_seq_timestamp time; /* schedule time */ + struct snd_seq_addr source; /* source address */ + struct snd_seq_addr dest; /* destination address */ + + union { + union snd_seq_event_data data; + unsigned int ump[4]; + }; +}; /* * bounce event - stored as variable size data @@ -344,10 +360,11 @@ typedef int __bitwise snd_seq_client_type_t; #define KERNEL_CLIENT ((snd_seq_client_type_t) 2) /* event filter flags */ -#define SNDRV_SEQ_FILTER_BROADCAST (1<<0) /* accept broadcast messages */ -#define SNDRV_SEQ_FILTER_MULTICAST (1<<1) /* accept multicast messages */ -#define SNDRV_SEQ_FILTER_BOUNCE (1<<2) /* accept bounce event in error */ -#define SNDRV_SEQ_FILTER_USE_EVENT (1<<31) /* use event filter */ +#define SNDRV_SEQ_FILTER_BROADCAST (1U<<0) /* accept broadcast messages */ +#define SNDRV_SEQ_FILTER_MULTICAST (1U<<1) /* accept multicast messages */ +#define SNDRV_SEQ_FILTER_BOUNCE (1U<<2) /* accept bounce event in error */ +#define SNDRV_SEQ_FILTER_NO_CONVERT (1U<<30) /* don't convert UMP events */ +#define SNDRV_SEQ_FILTER_USE_EVENT (1U<<31) /* use event filter */ struct snd_seq_client_info { int client; /* client number to inquire */ @@ -360,9 +377,18 @@ struct snd_seq_client_info { int event_lost; /* number of lost events */ int card; /* RO: card number[kernel] */ int pid; /* RO: pid[user] */ - char reserved[56]; /* for future use */ + unsigned int midi_version; /* MIDI version */ + unsigned int group_filter; /* UMP group filter bitmap + * (bit 0 = groupless messages, + * bit 1-16 = messages for groups 1-16) + */ + char reserved[48]; /* for future use */ }; +/* MIDI version numbers in client info */ +#define SNDRV_SEQ_CLIENT_LEGACY_MIDI 0 /* Legacy client */ +#define SNDRV_SEQ_CLIENT_UMP_MIDI_1_0 1 /* UMP MIDI 1.0 */ +#define SNDRV_SEQ_CLIENT_UMP_MIDI_2_0 2 /* UMP MIDI 2.0 */ /* client pool size */ struct snd_seq_client_pool { @@ -422,6 +448,8 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_CAP_SUBS_READ (1<<5) /* allow read subscription */ #define SNDRV_SEQ_PORT_CAP_SUBS_WRITE (1<<6) /* allow write subscription */ #define SNDRV_SEQ_PORT_CAP_NO_EXPORT (1<<7) /* routing not allowed */ +#define SNDRV_SEQ_PORT_CAP_INACTIVE (1<<8) /* inactive port */ +#define SNDRV_SEQ_PORT_CAP_UMP_ENDPOINT (1<<9) /* MIDI 2.0 UMP Endpoint port */ /* port type */ #define SNDRV_SEQ_PORT_TYPE_SPECIFIC (1<<0) /* hardware specific */ @@ -431,6 +459,7 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_TYPE_MIDI_XG (1<<4) /* XG compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_MT32 (1<<5) /* MT-32 compatible device */ #define SNDRV_SEQ_PORT_TYPE_MIDI_GM2 (1<<6) /* General MIDI 2 compatible device */ +#define SNDRV_SEQ_PORT_TYPE_MIDI_UMP (1<<7) /* UMP */ /* other standards...*/ #define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10) /* Synth device (no MIDI compatible - direct wavetable) */ @@ -448,6 +477,12 @@ struct snd_seq_remove_events { #define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1) #define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<2) +/* port direction */ +#define SNDRV_SEQ_PORT_DIR_UNKNOWN 0 +#define SNDRV_SEQ_PORT_DIR_INPUT 1 +#define SNDRV_SEQ_PORT_DIR_OUTPUT 2 +#define SNDRV_SEQ_PORT_DIR_BIDIRECTION 3 + struct snd_seq_port_info { struct snd_seq_addr addr; /* client/port numbers */ char name[64]; /* port name */ @@ -464,7 +499,9 @@ struct snd_seq_port_info { void *kernel; /* reserved for kernel use (must be NULL) */ unsigned int flags; /* misc. conditioning */ unsigned char time_queue; /* queue # for timestamping */ - char reserved[59]; /* for future use */ + unsigned char direction; /* port usage direction (r/w/bidir) */ + unsigned char ump_group; /* 0 = UMP EP (no conversion), 1-16 = UMP group number */ + char reserved[57]; /* for future use */ }; @@ -568,6 +605,18 @@ struct snd_seq_query_subs { char reserved[64]; /* for future use */ }; +/* + * UMP-specific information + */ +/* type of UMP info query */ +#define SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT 0 +#define SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK 1 + +struct snd_seq_client_ump_info { + int client; /* client number to inquire/set */ + int type; /* type to inquire/set */ + unsigned char info[512]; /* info (either UMP ep or block info) */ +} __packed; /* * IOCTL commands @@ -577,9 +626,12 @@ struct snd_seq_query_subs { #define SNDRV_SEQ_IOCTL_CLIENT_ID _IOR ('S', 0x01, int) #define SNDRV_SEQ_IOCTL_SYSTEM_INFO _IOWR('S', 0x02, struct snd_seq_system_info) #define SNDRV_SEQ_IOCTL_RUNNING_MODE _IOWR('S', 0x03, struct snd_seq_running_info) +#define SNDRV_SEQ_IOCTL_USER_PVERSION _IOW('S', 0x04, int) #define SNDRV_SEQ_IOCTL_GET_CLIENT_INFO _IOWR('S', 0x10, struct snd_seq_client_info) #define SNDRV_SEQ_IOCTL_SET_CLIENT_INFO _IOW ('S', 0x11, struct snd_seq_client_info) +#define SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO _IOWR('S', 0x12, struct snd_seq_client_ump_info) +#define SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO _IOWR('S', 0x13, struct snd_seq_client_ump_info) #define SNDRV_SEQ_IOCTL_CREATE_PORT _IOWR('S', 0x20, struct snd_seq_port_info) #define SNDRV_SEQ_IOCTL_DELETE_PORT _IOW ('S', 0x21, struct snd_seq_port_info) diff --git a/include/sound/uapi/asoc.h b/include/sound/uapi/asoc.h index f32c5697..3903f2f4 100644 --- a/include/sound/uapi/asoc.h +++ b/include/sound/uapi/asoc.h @@ -16,7 +16,9 @@ #ifndef __LINUX_UAPI_SND_ASOC_H #define __LINUX_UAPI_SND_ASOC_H +#if defined(__linux__) #include +#endif /* * Maximum number of channels topology kcontrol can represent. diff --git a/include/sound/uapi/asound.h b/include/sound/uapi/asound.h index c3f65e13..f3b2b94d 100644 --- a/include/sound/uapi/asound.h +++ b/include/sound/uapi/asound.h @@ -28,7 +28,7 @@ #include #include #else -#include +#include #include #endif @@ -274,13 +274,17 @@ typedef int __bitwise snd_pcm_format_t; typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_SUBFORMAT_STD ((snd_pcm_subformat_t) 0) -#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_STD +#define SNDRV_PCM_SUBFORMAT_MSBITS_MAX ((snd_pcm_subformat_t) 1) +#define SNDRV_PCM_SUBFORMAT_MSBITS_20 ((snd_pcm_subformat_t) 2) +#define SNDRV_PCM_SUBFORMAT_MSBITS_24 ((snd_pcm_subformat_t) 3) +#define SNDRV_PCM_SUBFORMAT_LAST SNDRV_PCM_SUBFORMAT_MSBITS_24 #define SNDRV_PCM_INFO_MMAP 0x00000001 /* hardware supports mmap */ #define SNDRV_PCM_INFO_MMAP_VALID 0x00000002 /* period data are valid during transfer */ #define SNDRV_PCM_INFO_DOUBLE 0x00000004 /* Double buffering needed for PCM start/stop */ #define SNDRV_PCM_INFO_BATCH 0x00000010 /* double buffering */ #define SNDRV_PCM_INFO_SYNC_APPLPTR 0x00000020 /* need the explicit sync of appl_ptr update */ +#define SNDRV_PCM_INFO_PERFECT_DRAIN 0x00000040 /* silencing at the end of stream is not required */ #define SNDRV_PCM_INFO_INTERLEAVED 0x00000100 /* channels are interleaved */ #define SNDRV_PCM_INFO_NONINTERLEAVED 0x00000200 /* channels are not interleaved */ #define SNDRV_PCM_INFO_COMPLEX 0x00000400 /* complex frame organization (mmap only) */ @@ -389,6 +393,9 @@ typedef int snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ #define SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER (1<<1) /* export buffer */ #define SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP (1<<2) /* disable period wakeups */ +#define SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE (1<<3) /* suppress the silence fill + * for draining + */ struct snd_interval { unsigned int min, max; @@ -702,7 +709,7 @@ enum { * Raw MIDI section - /dev/snd/midi?? */ -#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 2) +#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4) enum { SNDRV_RAWMIDI_STREAM_OUTPUT = 0, @@ -713,6 +720,7 @@ enum { #define SNDRV_RAWMIDI_INFO_OUTPUT 0x00000001 #define SNDRV_RAWMIDI_INFO_INPUT 0x00000002 #define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004 +#define SNDRV_RAWMIDI_INFO_UMP 0x00000008 struct snd_rawmidi_info { unsigned int device; /* RO/WR (control): device number */ @@ -771,6 +779,72 @@ struct snd_rawmidi_status { unsigned char reserved[16]; /* reserved for future use */ }; +/* UMP EP info flags */ +#define SNDRV_UMP_EP_INFO_STATIC_BLOCKS 0x01 + +/* UMP EP Protocol / JRTS capability bits */ +#define SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 +#define SNDRV_UMP_EP_INFO_PROTO_MIDI1 0x0100 /* MIDI 1.0 */ +#define SNDRV_UMP_EP_INFO_PROTO_MIDI2 0x0200 /* MIDI 2.0 */ +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003 +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 /* JRTS Transmit */ +#define SNDRV_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 /* JRTS Receive */ + +/* UMP Endpoint information */ +struct snd_ump_endpoint_info { + int card; /* card number */ + int device; /* device number */ + unsigned int flags; /* additional info */ + unsigned int protocol_caps; /* protocol capabilities */ + unsigned int protocol; /* current protocol */ + unsigned int num_blocks; /* # of function blocks */ + unsigned short version; /* UMP major/minor version */ + unsigned short family_id; /* MIDI device family ID */ + unsigned short model_id; /* MIDI family model ID */ + unsigned int manufacturer_id; /* MIDI manufacturer ID */ + unsigned char sw_revision[4]; /* software revision */ + unsigned short padding; + unsigned char name[128]; /* endpoint name string */ + unsigned char product_id[128]; /* unique product id string */ + unsigned char reserved[32]; +} __packed; + +/* UMP direction */ +#define SNDRV_UMP_DIR_INPUT 0x01 +#define SNDRV_UMP_DIR_OUTPUT 0x02 +#define SNDRV_UMP_DIR_BIDIRECTION 0x03 + +/* UMP block info flags */ +#define SNDRV_UMP_BLOCK_IS_MIDI1 (1U << 0) /* MIDI 1.0 port w/o restrict */ +#define SNDRV_UMP_BLOCK_IS_LOWSPEED (1U << 1) /* 31.25Kbps B/W MIDI1 port */ + +/* UMP block user-interface hint */ +#define SNDRV_UMP_BLOCK_UI_HINT_UNKNOWN 0x00 +#define SNDRV_UMP_BLOCK_UI_HINT_RECEIVER 0x01 +#define SNDRV_UMP_BLOCK_UI_HINT_SENDER 0x02 +#define SNDRV_UMP_BLOCK_UI_HINT_BOTH 0x03 + +/* UMP groups and blocks */ +#define SNDRV_UMP_MAX_GROUPS 16 +#define SNDRV_UMP_MAX_BLOCKS 32 + +/* UMP Block information */ +struct snd_ump_block_info { + int card; /* card number */ + int device; /* device number */ + unsigned char block_id; /* block ID (R/W) */ + unsigned char direction; /* UMP direction */ + unsigned char active; /* Activeness */ + unsigned char first_group; /* first group ID */ + unsigned char num_groups; /* number of groups */ + unsigned char midi_ci_version; /* MIDI-CI support version */ + unsigned char sysex8_streams; /* max number of sysex8 streams */ + unsigned char ui_hint; /* user interface hint */ + unsigned int flags; /* various info flags */ + unsigned char name[128]; /* block name string */ + unsigned char reserved[32]; +} __packed; + #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) @@ -778,6 +852,9 @@ struct snd_rawmidi_status { #define SNDRV_RAWMIDI_IOCTL_STATUS _IOWR('W', 0x20, struct snd_rawmidi_status) #define SNDRV_RAWMIDI_IOCTL_DROP _IOW('W', 0x30, int) #define SNDRV_RAWMIDI_IOCTL_DRAIN _IOW('W', 0x31, int) +/* Additional ioctls for UMP rawmidi devices */ +#define SNDRV_UMP_IOCTL_ENDPOINT_INFO _IOR('W', 0x40, struct snd_ump_endpoint_info) +#define SNDRV_UMP_IOCTL_BLOCK_INFO _IOR('W', 0x41, struct snd_ump_block_info) /* * Timer section - /dev/snd/timer @@ -949,7 +1026,7 @@ struct snd_timer_tread { * * ****************************************************************************/ -#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) +#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 9) struct snd_ctl_card_info { int card; /* card number */ @@ -1110,6 +1187,9 @@ struct snd_ctl_tlv { #define SNDRV_CTL_IOCTL_RAWMIDI_NEXT_DEVICE _IOWR('U', 0x40, int) #define SNDRV_CTL_IOCTL_RAWMIDI_INFO _IOWR('U', 0x41, struct snd_rawmidi_info) #define SNDRV_CTL_IOCTL_RAWMIDI_PREFER_SUBDEVICE _IOW('U', 0x42, int) +#define SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE _IOWR('U', 0x43, int) +#define SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO _IOWR('U', 0x44, struct snd_ump_endpoint_info) +#define SNDRV_CTL_IOCTL_UMP_BLOCK_INFO _IOWR('U', 0x45, struct snd_ump_block_info) #define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int) #define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int) diff --git a/include/topology.h b/include/topology.h index d1feee4d..a590d1a8 100644 --- a/include/topology.h +++ b/include/topology.h @@ -219,7 +219,7 @@ extern "C" { * *
  * SectionData."data element name" {
- *	index "1"	#Index number
+ *	index "1"	# Index number
  *	tuples [
  *		"id of the 1st vendor tuples section"
  *		"id of the 2nd vendor tuples section"
@@ -254,8 +254,6 @@ extern "C" {
  * And data of these sections will be merged in the same order as they are
  * in the list, as the element's private data for kernel.
  *
- * 
- * *
Vendor Tokens
* A vendor token list is defined as a new section. Each token element is * a pair of string ID and integer value. And both the ID and value are @@ -419,7 +417,7 @@ extern "C" { * Values [ * "value1" * "value2" - "value3" + * "value3" * ] * } * @@ -504,7 +502,7 @@ extern "C" { * no_pm "true" # No PM control bit. * reg "20" # PM bit register offset * shift "0" # PM bit register shift - * invert "1 # PM bit is inverted + * invert "1" # PM bit is inverted * subseq "8" # subsequence number * * event_type "1" # DAPM widget event type @@ -639,7 +637,7 @@ extern "C" { * ... * ] * - * default_hw_conf_id "1" #default HW config ID for init + * default_hw_conf_id "1" # default HW config ID for init * * # Optional boolean flags * symmetric_rates "true" @@ -842,7 +840,7 @@ struct snd_tplg_tlv_dbscale_template { int mute; /*!< is min dB value mute ? */ }; -/** \struct snd_tplg_channel_template +/** \struct snd_tplg_channel_elem * \brief Template type for single channel mapping. */ struct snd_tplg_channel_elem { @@ -1023,26 +1021,26 @@ struct snd_tplg_pcm_template { * hardware config, i.e. hardware audio formats. */ struct snd_tplg_hw_config_template { - int id; /* unique ID - - used to match */ - unsigned int fmt; /* SND_SOC_DAI_FORMAT_ format value */ - 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_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 */ - unsigned int bclk_rate; /* BCLK freqency in Hz */ - unsigned int fsync_rate; /* frame clock in Hz */ - unsigned int tdm_slots; /* number of TDM slots in use */ - unsigned int tdm_slot_width; /* width in bits for each slot */ - unsigned int tx_slots; /* bit mask for active Tx slots */ - unsigned int rx_slots; /* bit mask for active Rx slots */ - unsigned int tx_channels; /* number of Tx channels */ - unsigned int *tx_chanmap; /* array of slot number */ - unsigned int rx_channels; /* number of Rx channels */ - unsigned int *rx_chanmap; /* array of slot number */ + int id; /*!< unique ID - - used to match */ + unsigned int fmt; /*!< SND_SOC_DAI_FORMAT_ format value */ + 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_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 */ + unsigned int bclk_rate; /*!< BCLK freqency in Hz */ + unsigned int fsync_rate; /*!< frame clock in Hz */ + unsigned int tdm_slots; /*!< number of TDM slots in use */ + unsigned int tdm_slot_width; /*!< width in bits for each slot */ + unsigned int tx_slots; /*!< bit mask for active Tx slots */ + unsigned int rx_slots; /*!< bit mask for active Rx slots */ + unsigned int tx_channels; /*!< number of Tx channels */ + unsigned int *tx_chanmap; /*!< array of slot number */ + unsigned int rx_channels; /*!< number of Rx channels */ + unsigned int *rx_chanmap; /*!< array of slot number */ }; /** \struct snd_tplg_dai_template @@ -1073,15 +1071,15 @@ struct snd_tplg_link_template { struct snd_tplg_stream_template *stream; /*!< supported configs */ struct snd_tplg_hw_config_template *hw_config; /*!< supported HW configs */ - int num_hw_configs; /* number of hw configs */ - int default_hw_config_id; /* default hw config ID for init */ + int num_hw_configs; /*!< number of hw configs */ + int default_hw_config_id; /*!< default hw config ID for init */ - unsigned int flag_mask; /* bitmask of flags to configure */ - unsigned int flags; /* SND_SOC_TPLG_LNK_FLGBIT_* flag value */ + unsigned int flag_mask; /*!< bitmask of flags to configure */ + unsigned int flags; /*!< SND_SOC_TPLG_LNK_FLGBIT_* flag value */ struct snd_soc_tplg_private *priv; /*!< private data */ }; -/** \struct snd_tplg_obj_template +/** \struct snd_tplg_obj_template_t * \brief Generic Template Object */ typedef struct snd_tplg_obj_template { @@ -1154,7 +1152,13 @@ int snd_tplg_set_version(snd_tplg_t *tplg, unsigned int version); * \brief Save the topology to the text configuration string. * \param tplg Topology instance. * \param dst A pointer to string with result (malloc). + * \param flags save mode * \return Zero on success, otherwise a negative error code + * + * Valid flags are + * - SND_TPLG_SAVE_SORT + * - SND_TPLG_SAVE_GROUPS + * - SND_TPLG_SAVE_NOCHECK */ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); @@ -1163,11 +1167,12 @@ int snd_tplg_save(snd_tplg_t *tplg, char **dst, int flags); * \param tplg Topology instance. * \param bin Binary topology input buffer. * \param size Binary topology input buffer size. + * \param dflags - not used, must be set to 0. * \return Zero on success, otherwise a negative error code */ int snd_tplg_decode(snd_tplg_t *tplg, void *bin, size_t size, int dflags); -/* \} */ +/** \} */ #ifdef __cplusplus } diff --git a/include/type_compat.h b/include/type_compat.h index 000057f1..b43379c0 100644 --- a/include/type_compat.h +++ b/include/type_compat.h @@ -26,7 +26,7 @@ #define EBADFD EBADF #endif #ifndef ESTRPIPE -#define ESTRPIPE EPIPE +#define ESTRPIPE ESPIPE #endif #ifndef __u16 diff --git a/include/ump.h b/include/ump.h new file mode 100644 index 00000000..1e5e0534 --- /dev/null +++ b/include/ump.h @@ -0,0 +1,141 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file include/ump.h + * \brief API library for ALSA rawmidi/UMP interface + * + * API library for ALSA rawmidi/UMP interface + */ + +#ifndef __ALSA_UMP_H +#define __ALSA_UMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** UMP (Endpoint) RawMIDI device */ +typedef struct _snd_ump snd_ump_t; +/** UMP Endpoint information container */ +typedef struct snd_ump_endpoint_info snd_ump_endpoint_info_t; +/** UMP Block information container */ +typedef struct snd_ump_block_info snd_ump_block_info_t; + +int snd_ump_open(snd_ump_t **inputp, snd_ump_t **outputp, const char *name, int mode); +int snd_ump_close(snd_ump_t *ump); +snd_rawmidi_t *snd_ump_rawmidi(snd_ump_t *ump); +const char *snd_ump_name(snd_ump_t *ump); +int snd_ump_poll_descriptors_count(snd_ump_t *ump); +int snd_ump_poll_descriptors(snd_ump_t *ump, struct pollfd *pfds, unsigned int space); +int snd_ump_poll_descriptors_revents(snd_ump_t *ump, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); +int snd_ump_nonblock(snd_ump_t *ump, int nonblock); +int snd_ump_rawmidi_info(snd_ump_t *ump, snd_rawmidi_info_t *info); +int snd_ump_rawmidi_params(snd_ump_t *ump, snd_rawmidi_params_t *params); +int snd_ump_rawmidi_params_current(snd_ump_t *ump, snd_rawmidi_params_t *params); +int snd_ump_rawmidi_status(snd_ump_t *ump, snd_rawmidi_status_t *status); +int snd_ump_drop(snd_ump_t *ump); +int snd_ump_drain(snd_ump_t *ump); +ssize_t snd_ump_write(snd_ump_t *ump, const void *buffer, size_t size); +ssize_t snd_ump_read(snd_ump_t *ump, void *buffer, size_t size); +ssize_t snd_ump_tread(snd_ump_t *ump, struct timespec *tstamp, void *buffer, size_t size); + +/** Max number of UMP Groups */ +#define SND_UMP_MAX_GROUPS 16 +/** Max number of UMP Blocks */ +#define SND_UMP_MAX_BLOCKS 32 + +/** UMP direction */ +enum _snd_ump_direction { + /** Input only */ + SND_UMP_DIR_INPUT = 0x01, + /** Output only */ + SND_UMP_DIR_OUTPUT = 0x02, + /** Bidirectional */ + SND_UMP_DIR_BIDIRECTION = 0x03, +}; + +/** UMP EP holds only static blocks */ +#define SND_UMP_EP_INFO_STATIC_BLOCKS 0x01 + +/** Bitmask for UMP EP MIDI protocols */ +#define SND_UMP_EP_INFO_PROTO_MIDI_MASK 0x0300 +/** Bit flag for MIDI 1.0 protocol */ +#define SND_UMP_EP_INFO_PROTO_MIDI1 0x0100 +/** Bit flag for MIDI 2.0 protocol */ +#define SND_UMP_EP_INFO_PROTO_MIDI2 0x0200 +/** Bitmask for UMP Jitter-reduction timestamp */ +#define SND_UMP_EP_INFO_PROTO_JRTS_MASK 0x0003 +/** Bit flag for JRTS in Transmit */ +#define SND_UMP_EP_INFO_PROTO_JRTS_TX 0x0001 +/** Bit flag for JRTS in Receive */ +#define SND_UMP_EP_INFO_PROTO_JRTS_RX 0x0002 + +size_t snd_ump_endpoint_info_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_ump_endpoint_info_t using standard alloca + * \param ptr returned pointer + */ +#define snd_ump_endpoint_info_alloca(ptr) __snd_alloca(ptr, snd_ump_endpoint_info) +int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info); +void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info); +void snd_ump_endpoint_info_copy(snd_ump_endpoint_info_t *dst, const snd_ump_endpoint_info_t *src); +int snd_ump_endpoint_info_get_card(const snd_ump_endpoint_info_t *info); +int snd_ump_endpoint_info_get_device(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_flags(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_protocol_caps(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_protocol(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_num_blocks(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_manufacturer_id(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_family_id(const snd_ump_endpoint_info_t *info); +unsigned int snd_ump_endpoint_info_get_model_id(const snd_ump_endpoint_info_t *info); +const unsigned char *snd_ump_endpoint_info_get_sw_revision(const snd_ump_endpoint_info_t *info); +const char *snd_ump_endpoint_info_get_name(const snd_ump_endpoint_info_t *info); +const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t *info); +int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info); + +/** Bit flag for MIDI 1.0 port w/o restrict in UMP Block info flags */ +#define SND_UMP_BLOCK_IS_MIDI1 (1U << 0) +/** Bit flag for 31.25Kbps B/W MIDI1 port in UMP Block info flags */ +#define SND_UMP_BLOCK_IS_LOWSPEED (1U << 1) + +/** UMP block user-interface hint */ +enum _snd_ump_block_ui_hint { + /** Unknown or undeclared */ + SND_UMP_BLOCK_UI_HINT_UNKNOWN = 0x00, + /** Primarily a receiver or a destination for MIDI messages */ + SND_UMP_BLOCK_UI_HINT_RECEIVER = 0x01, + /** Primarily a sender or a source of MIDI messages */ + SND_UMP_BLOCK_UI_HINT_SENDER = 0x02, + /** Both a sender and receiver of MIDI messages */ + SND_UMP_BLOCK_UI_HINT_BOTH = 0x03, +}; + +size_t snd_ump_block_info_sizeof(void); +/** \hideinitializer + * \brief allocate an invalid #snd_ump_block_info_t using standard alloca + * \param ptr returned pointer + */ +#define snd_ump_block_info_alloca(ptr) __snd_alloca(ptr, snd_ump_block_info) +int snd_ump_block_info_malloc(snd_ump_block_info_t **info); +void snd_ump_block_info_free(snd_ump_block_info_t *info); +void snd_ump_block_info_copy(snd_ump_block_info_t *dst, const snd_ump_block_info_t *src); +int snd_ump_block_info_get_card(const snd_ump_block_info_t *info); +int snd_ump_block_info_get_device(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info); +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, unsigned int id); +unsigned int snd_ump_block_info_get_active(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_flags(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_direction(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_first_group(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_num_groups(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_midi_ci_version(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_sysex8_streams(const snd_ump_block_info_t *info); +unsigned int snd_ump_block_info_get_ui_hint(const snd_ump_block_info_t *info); +const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info); +int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info); + +#ifdef __cplusplus +} +#endif + +#endif /* __ALSA_UMP_H */ diff --git a/include/ump_msg.h b/include/ump_msg.h new file mode 100644 index 00000000..e0264f6e --- /dev/null +++ b/include/ump_msg.h @@ -0,0 +1,665 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file include/ump_msg.h + * \brief API library for ALSA rawmidi/UMP interface + * + * API library for ALSA rawmidi/UMP interface + */ + +#ifndef __ALSA_UMP_MSG_H +#define __ALSA_UMP_MSG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** general UMP packet header in 32bit word */ +typedef struct _snd_ump_msg_hdr { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t byte1; /**< First data byte */ + uint8_t byte2; /**< Second data byte */ +#else + uint8_t byte2; /**< Second data byte */ + uint8_t byte1; /**< First data byte */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_hdr_t; + +/** MIDI 1.0 Note Off / Note On (32bit) */ +typedef struct _snd_ump_msg_midi1_note { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t velocity; /**< Velocity (7bit) */ +#else + uint8_t velocity; /**< Velocity (7bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_note_t; + +/** MIDI 1.0 Poly Pressure (32bit) */ +typedef struct _snd_ump_msg_midi1_paf { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /** Note (7bit) */ + uint8_t data; /** Pressure (7bit) */ +#else + uint8_t data; /** Pressure (7bit) */ + uint8_t note; /** Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_paf_t; + +/** MIDI 1.0 Control Change (32bit) */ +typedef struct _snd_ump_msg_midi1_cc { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t index; /** Control index (7bit) */ + uint8_t data; /** Control data (7bit) */ +#else + uint8_t data; /** Control data (7bit) */ + uint8_t index; /** Control index (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_cc_t; + +/** MIDI 1.0 Program Change (32bit) */ +typedef struct _snd_ump_msg_midi1_program { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t program; /**< Program number (7bit) */ + uint8_t reserved; /**< Unused */ +#else + uint8_t reserved; /**< Unused */ + uint8_t program; /**< Program number (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_program_t; + +/** MIDI 1.0 Channel Pressure (32bit) */ +typedef struct _snd_ump_msg_midi1_caf { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t data; /**< Pressure (7bit) */ + uint8_t reserved; /**< Unused */ +#else + uint8_t reserved; /**< Unused */ + uint8_t data; /**< Pressure (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_caf_t; + +/** MIDI 1.0 Pitch Bend (32bit) */ +typedef struct _snd_ump_msg_midi1_pitchbend { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t data_lsb; /**< LSB of pitchbend (7bit) */ + uint8_t data_msb; /**< MSB of pitchbend (7bit) */ +#else + uint8_t data_msb; /**< MSB of pitchbend (7bit) */ + uint8_t data_lsb; /**< LSB of pitchbend (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_midi1_pitchbend_t; + +/** System Common and Real Time messages (32bit); no channel field */ +typedef struct snd_ump_msg_system { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status; /**< Status */ + uint8_t parm1; /**< First parameter */ + uint8_t parm2; /**< Second parameter */ +#else + uint8_t parm1; /**< First parameter */ + uint8_t parm2; /**< Second parameter */ + uint8_t status; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ +#endif +} __attribute((packed)) snd_ump_msg_system_t; + +/** MIDI 1.0 UMP CVM (32bit) */ +typedef union _snd_ump_msg_midi1 { + snd_ump_msg_midi1_note_t note_on; + snd_ump_msg_midi1_note_t note_off; + snd_ump_msg_midi1_paf_t poly_pressure; + snd_ump_msg_midi1_cc_t control_change; + snd_ump_msg_midi1_program_t program_change; + snd_ump_msg_midi1_caf_t channel_pressure; + snd_ump_msg_midi1_pitchbend_t pitchbend; + snd_ump_msg_system_t system; + snd_ump_msg_hdr_t hdr; + uint32_t raw; +} snd_ump_msg_midi1_t; + +/** MIDI 2.0 Note-on/off attribute type */ +enum { + SND_UMP_MIDI2_NOTE_ATTR_NO_DATA = 0x00, /**< No attribute data */ + SND_UMP_MIDI2_NOTE_ATTR_MANUFACTURER = 0x01, /**< Manufacturer specific */ + SND_UMP_MIDI2_NOTE_ATTR_PROFILE = 0x02, /**< Profile specific */ + SND_UMP_MIDI2_NOTE_ATTR_PITCH79 = 0x03, /**< Pitch 7.9 */ +}; + +/* MIDI 2.0 Note Off / Note On (64bit) */ +typedef struct _snd_ump_msg_midi2_note { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t attr_type; /**< Attribute type */ + + uint16_t velocity; /**< Velocity (16bit) */ + uint16_t attr_data; /**< Attribute data (16bit) */ +#else + uint8_t attr_type; /**< Attribute type */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint16_t attr_data; /**< Attribute data (16bit) */ + uint16_t velocity; /**< Velocity (16bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_note_t; + +/** MIDI 2.0 Poly Pressure (64bit) */ +typedef struct _snd_ump_msg_midi2_paf { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Pressure (32bit) */ +#else + uint8_t reserved; /**< Unused */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Pressure (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_paf_t; + +/** MIDI 2.0 Per-Note Controller (64bit) */ +typedef struct _snd_ump_msg_midi2_per_note_cc { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t index; /**< Control index (8bit) */ + + uint32_t data; /**< Data (32bit) */ +#else + uint8_t index; /**< Control index (8bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_cc_t; + +/** MIDI 2.0 per-note management flag bits */ +enum { + SND_UMP_MIDI2_PNMGMT_RESET_CONTROLLERS = 0x01, /**< Reset (set) per-note controllers */ + SND_UMP_MIDI2_PNMGMT_DETACH_CONTROLLERS = 0x02, /**< Detach per-note controllers */ +}; + +/** MIDI 2.0 Per-Note Management (64bit) */ +typedef struct _snd_ump_msg_midi2_per_note_mgmt { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t flags; /**< Option flags (8bit) */ + + uint32_t reserved; /**< Unused */ +#else + uint8_t flags; /**< Option flags (8bit) */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t reserved; /**< Unused */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_mgmt_t; + +/** MIDI 2.0 Control Change (64bit) */ +typedef struct _snd_ump_msg_midi2_cc { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t index; /**< Control index (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Control data (32bit) */ +#else + uint8_t reserved; /**< Unused */ + uint8_t index; /**< Control index (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Control data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_cc_t; + +/** MIDI 2.0 Registered Controller (RPN) / Assignable Controller (NRPN) (64bit) */ +typedef struct _snd_ump_msg_midi2_rpn { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t bank; /**< Bank number (7bit) */ + uint8_t index; /**< Control index (7bit) */ + + uint32_t data; /**< Data (32bit) */ +#else + uint8_t index; /**< Control index (7bit) */ + uint8_t bank; /**< Bank number (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_rpn_t; + +/** MIDI 2.0 Program Change (64bit) */ +typedef struct _snd_ump_msg_midi2_program { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint16_t reserved:15; /**< Unused */ + uint16_t bank_valid:1; /**< Option flag: bank valid */ + + uint8_t program; /**< Program number (7bit) */ + uint8_t reserved2; /**< Unused */ + uint8_t bank_msb; /**< MSB of bank (8bit) */ + uint8_t bank_lsb; /**< LSB of bank (7bit) */ +#else + uint16_t bank_valid:1; /**< Option flag: bank valid */ + uint16_t reserved:15; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint8_t bank_lsb; /**< LSB of bank (7bit) */ + uint8_t bank_msb; /**< MSB of bank (8bit) */ + uint8_t reserved2; /**< Unused */ + uint8_t program; /**< Program number (7bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_program_t; + +/** MIDI 2.0 Channel Pressure (64bit) */ +typedef struct _snd_ump_msg_midi2_caf { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint16_t reserved; /**< Unused */ + + uint32_t data; /** Data (32bit) */ +#else + uint16_t reserved; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /** Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_caf_t; + +/* MIDI 2.0 Pitch Bend (64bit) */ +typedef struct _snd_ump_msg_midi2_pitchbend { +#ifdef SNDRV_BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint16_t reserved; /**< Unused */ + + uint32_t data; /** Data (32bit) */ +#else + uint16_t reserved; /**< Unused */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /** Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_pitchbend_t; + +/* MIDI 2.0 Per-Note Pitch Bend (64bit) */ +typedef struct _snd_ump_msg_midi2_per_note_pitchbend { +#ifdef __BIG_ENDIAN_BITFIELD + uint8_t type:4; /**< UMP packet type */ + uint8_t group:4; /**< UMP Group */ + uint8_t status:4; /**< Status */ + uint8_t channel:4; /**< Channel */ + uint8_t note; /**< Note (7bit) */ + uint8_t reserved; /**< Unused */ + + uint32_t data; /**< Data (32bit) */ +#else + /* 0 */ + uint8_t reserved; /**< Unused */ + uint8_t note; /**< Note (7bit) */ + uint8_t channel:4; /**< Channel */ + uint8_t status:4; /**< Status */ + uint8_t group:4; /**< UMP Group */ + uint8_t type:4; /**< UMP packet type */ + + uint32_t data; /**< Data (32bit) */ +#endif +} __attribute((packed)) snd_ump_msg_midi2_per_note_pitchbend_t; + +/** MIDI2 UMP packet (64bit little-endian) */ +typedef union _snd_ump_msg_midi2 { + snd_ump_msg_midi2_note_t note_on; + snd_ump_msg_midi2_note_t note_off; + snd_ump_msg_midi2_paf_t poly_pressure; + snd_ump_msg_midi2_per_note_cc_t per_note_acc; + snd_ump_msg_midi2_per_note_cc_t per_note_rcc; + snd_ump_msg_midi2_per_note_mgmt_t per_note_mgmt; + snd_ump_msg_midi2_cc_t control_change; + snd_ump_msg_midi2_rpn_t rpn; + snd_ump_msg_midi2_rpn_t nrpn; + snd_ump_msg_midi2_rpn_t relative_rpn; + snd_ump_msg_midi2_rpn_t relative_nrpn; + snd_ump_msg_midi2_program_t program_change; + snd_ump_msg_midi2_caf_t channel_pressure; + snd_ump_msg_midi2_pitchbend_t pitchbend; + snd_ump_msg_midi2_per_note_pitchbend_t per_note_pitchbend; + snd_ump_msg_hdr_t hdr; + uint32_t raw[2]; +} snd_ump_msg_midi2_t; + +/** + * UMP message type + */ +enum { + SND_UMP_MSG_TYPE_UTILITY = 0x00, /* Utility messages */ + SND_UMP_MSG_TYPE_SYSTEM = 0x01, /* System messages */ + SND_UMP_MSG_TYPE_MIDI1_CHANNEL_VOICE = 0x02, /* MIDI 1.0 messages */ + SND_UMP_MSG_TYPE_DATA = 0x03, /* 7bit SysEx messages */ + SND_UMP_MSG_TYPE_MIDI2_CHANNEL_VOICE = 0x04, /* MIDI 2.0 messages */ + SND_UMP_MSG_TYPE_EXTENDED_DATA = 0x05, /* 8bit data message */ + SND_UMP_MSG_TYPE_FLEX_DATA = 0x0d, /* Flexible data messages */ + SND_UMP_MSG_TYPE_STREAM = 0x0f, /* Stream messages */ +}; + +/** + * UMP MIDI 1.0 / 2.0 message status code (4bit) + */ +enum { + SND_UMP_MSG_PER_NOTE_RCC = 0x0, + SND_UMP_MSG_PER_NOTE_ACC = 0x1, + SND_UMP_MSG_RPN = 0x2, + SND_UMP_MSG_NRPN = 0x3, + SND_UMP_MSG_RELATIVE_RPN = 0x4, + SND_UMP_MSG_RELATIVE_NRPN = 0x5, + SND_UMP_MSG_PER_NOTE_PITCHBEND = 0x6, + SND_UMP_MSG_NOTE_OFF = 0x8, + SND_UMP_MSG_NOTE_ON = 0x9, + SND_UMP_MSG_POLY_PRESSURE = 0xa, + SND_UMP_MSG_CONTROL_CHANGE = 0xb, + SND_UMP_MSG_PROGRAM_CHANGE = 0xc, + SND_UMP_MSG_CHANNEL_PRESSURE = 0xd, + SND_UMP_MSG_PITCHBEND = 0xe, + SND_UMP_MSG_PER_NOTE_MGMT = 0xf, +}; + +/** + * MIDI System / Realtime message status code (8bit) + */ +enum { + SND_UMP_MSG_REALTIME = 0xf0, /* mask */ + SND_UMP_MSG_SYSEX_START = 0xf0, + SND_UMP_MSG_MIDI_TIME_CODE = 0xf1, + SND_UMP_MSG_SONG_POSITION = 0xf2, + SND_UMP_MSG_SONG_SELECT = 0xf3, + SND_UMP_MSG_TUNE_REQUEST = 0xf6, + SND_UMP_MSG_SYSEX_END = 0xf7, + SND_UMP_MSG_TIMING_CLOCK = 0xf8, + SND_UMP_MSG_START = 0xfa, + SND_UMP_MSG_CONTINUE = 0xfb, + SND_UMP_MSG_STOP = 0xfc, + SND_UMP_MSG_ACTIVE_SENSING = 0xfe, + SND_UMP_MSG_RESET = 0xff, +}; + +/** MIDI 2.0 SysEx / Data Status; same values for both 7-bit and 8-bit SysEx */ +enum { + SND_UMP_SYSEX_STATUS_SINGLE = 0, + SND_UMP_SYSEX_STATUS_START = 1, + SND_UMP_SYSEX_STATUS_CONTINUE = 2, + SND_UMP_SYSEX_STATUS_END = 3, +}; + +/** UMP Utility Type Status (type 0x0) **/ +enum { + SND_UMP_UTILITY_MSG_STATUS_NOOP = 0x00, + SND_UMP_UTILITY_MSG_STATUS_JR_CLOCK = 0x01, + SND_UMP_UTILITY_MSG_STATUS_JR_TSTAMP = 0x02, + SND_UMP_UTILITY_MSG_STATUS_DCTPQ = 0x03, + SND_UMP_UTILITY_MSG_STATUS_DC = 0x04, +}; + +/** UMP Stream Message Status (type 0xf) */ +enum { + SND_UMP_STREAM_MSG_STATUS_EP_DISCOVERY = 0x00, + SND_UMP_STREAM_MSG_STATUS_EP_INFO = 0x01, + SND_UMP_STREAM_MSG_STATUS_DEVICE_INFO = 0x02, + SND_UMP_STREAM_MSG_STATUS_EP_NAME = 0x03, + SND_UMP_STREAM_MSG_STATUS_PRODUCT_ID = 0x04, + SND_UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST = 0x05, + SND_UMP_STREAM_MSG_STATUS_STREAM_CFG = 0x06, + SND_UMP_STREAM_MSG_STATUS_FB_DISCOVERY = 0x10, + SND_UMP_STREAM_MSG_STATUS_FB_INFO = 0x11, + SND_UMP_STREAM_MSG_STATUS_FB_NAME = 0x12, + SND_UMP_STREAM_MSG_STATUS_START_CLIP = 0x20, + SND_UMP_STREAM_MSG_STATUS_END_CLIP = 0x21, +}; + +/** UMP Endpoint Discovery filter bitmap */ +enum { + SND_UMP_STREAM_MSG_REQUEST_EP_INFO = (1U << 0), + SND_UMP_STREAM_MSG_REQUEST_DEVICE_INFO = (1U << 1), + SND_UMP_STREAM_MSG_REQUEST_EP_NAME = (1U << 2), + SND_UMP_STREAM_MSG_REQUEST_PRODUCT_ID = (1U << 3), + SND_UMP_STREAM_MSG_REQUEST_STREAM_CFG = (1U << 4), +}; + +/** UMP Function Block Discovery filter bitmap */ +enum { + SND_UMP_STREAM_MSG_REQUEST_FB_INFO = (1U << 0), + SND_UMP_STREAM_MSG_REQUEST_FB_NAME = (1U << 1), +}; + +/** UMP Endpoint Info capability bits (used for protocol request/notify, too) */ +enum { + SND_UMP_STREAM_MSG_EP_INFO_CAP_TXJR = (1U << 0), /* Sending JRTS */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_RXJR = (1U << 1), /* Receiving JRTS */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 = (1U << 8), /* MIDI 1.0 */ + SND_UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 = (1U << 9), /* MIDI 2.0 */ +}; + +/** UMP Endpoint / Function Block name string format bits */ +enum { + SND_UMP_STREAM_MSG_FORMAT_SINGLE = 0, + SND_UMP_STREAM_MSG_FORMAT_START = 1, + SND_UMP_STREAM_MSG_FORMAT_CONTINUE = 2, + SND_UMP_STREAM_MSG_FORMAT_END = 3, +}; + +/** + * \brief get UMP status (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_status(uint32_t ump) +{ + return (ump >> 20) & 0x0f; +} + +/** + * \brief get UMP channel (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_channel(uint32_t ump) +{ + return (ump >> 16) & 0x0f; +} + +/** + * \brief get UMP message type (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_type(uint32_t ump) +{ + return (ump >> 28); +} + +/** + * \brief check if the given UMP type is a groupless message + */ +static inline int snd_ump_msg_type_is_groupless(uint8_t type) +{ + return type == SND_UMP_MSG_TYPE_UTILITY || type == SND_UMP_MSG_TYPE_STREAM; +} + +/** + * \brief get UMP group (4bit) from 32bit UMP message header + */ +static inline uint8_t snd_ump_msg_hdr_group(uint32_t ump) +{ + return (ump >> 24) & 0x0f; +} + +/** + * \brief get UMP status from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_status(const uint32_t *ump) +{ + return snd_ump_msg_hdr_status(*ump); +} + +/** + * \brief get UMP channel from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_channel(const uint32_t *ump) +{ + return snd_ump_msg_hdr_channel(*ump); +} + +/** + * \brief get UMP message type from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_type(const uint32_t *ump) +{ + return snd_ump_msg_hdr_type(*ump); +} + +/** + * \brief get UMP group from UMP packet pointer + */ +static inline uint8_t snd_ump_msg_group(const uint32_t *ump) +{ + return snd_ump_msg_hdr_group(*ump); +} + +/** + * \brief get UMP sysex message status + */ +static inline uint8_t snd_ump_sysex_msg_status(const uint32_t *ump) +{ + return (*ump >> 20) & 0xf; +} + +/** + * \brief get UMP sysex message length + */ +static inline uint8_t snd_ump_sysex_msg_length(const uint32_t *ump) +{ + return (*ump >> 16) & 0xf; +} + +int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled); + +#ifdef __cplusplus +} +#endif + +#endif /* __ALSA_UMP_MSG_H */ diff --git a/include/use-case.h b/include/use-case.h index 7890358b..297664ee 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -141,6 +141,7 @@ extern "C" { #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) */ +#define SND_USE_CASE_DEV_DIRECT "Direct" /**< Direct Device (no channel remapping), (e.g. ProAudio usage) */ /* add new devices to end of list */ @@ -314,38 +315,50 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - playback device sample rate * - PlaybackChannels * - playback device channel count + * - PlaybackChannel# + * - describe index of the logical channel in the PCM stream + * - e.g. "PlaybackChannel0 2" - logical channel 0 is third channel in the PCM stream + * - PlaybackChannelPos# + * - describe sound position of the logical channel (ALSA chmap names) + * - e.g. "PlaybackChannel0 FR" - logical channel 0 is at front left * - PlaybackCTL * - playback control device name * - PlaybackVolume * - playback control volume identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - PlaybackSwitch * - playback control switch identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - PlaybackPriority * - priority value (1-10000), higher value means higher priority * - CaptureRate * - capture device sample rate * - CaptureChannels * - capture device channel count + * - CaptureChannel# + * - describe index of the logical channel in the PCM stream + * - e.g. "CaptureChannel0 2" - logical channel 0 is third channel in the PCM stream + * - CaptureChannelPos# + * - describe sound position of the logical channel (ALSA chmap names) + * - e.g. "CaptureChannel0 FR" - logical channel 0 is at front left * - CaptureCTL * - capture control device name * - CaptureVolume * - capture control volume identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - CaptureSwitch * - capture control switch identifier string - * - can be parsed using snd_use_case_parse_ctl_elem_id() + * - can be parsed using #snd_use_case_parse_ctl_elem_id() * - CapturePriority * - priority value (1-10000), higher value means higher priority * - PlaybackMixer * - name of playback mixer * - PlaybackMixerElem * - mixer element playback identifier - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - PlaybackMasterElem * - mixer element playback identifier for the master control - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - PlaybackMasterType * - type of the master volume control * - Valid values: "soft" (software attenuation) @@ -353,13 +366,16 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * - name of capture mixer * - CaptureMixerElem * - mixer element capture identifier - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - CaptureMasterElem * - mixer element playback identifier for the master control - * - can be parsed using snd_use_case_parse_selem_id() + * - can be parsed using #snd_use_case_parse_selem_id() * - CaptureMasterType * - type of the master volume control * - Valid values: "soft" (software attenuation) + * - CaptureMicInfoFile + * - json file with the microphone array placement and type description + * (e.g. output from nhlt-dmic-info) * - EDIDFile * - Path to EDID file for HDMI devices * - JackCTL @@ -389,9 +405,9 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * but that's application policy configuration that doesn't belong * to UCM configuration files. * - MinBufferLevel - * - This is used on platform where reported buffer level is not accurate. - * E.g. "512", which holds 512 samples in device buffer. Note: this will - * increase latency. + * - This is used on platform where reported buffer level is not accurate. + * E.g. "512", which holds 512 samples in device buffer. Note: this will + * increase latency. */ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, @@ -420,24 +436,24 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, * \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 - * - _swdev/{old_device} - new_device = value - * - disable old_device and then enable new_device - * - if old_device is not enabled just return - * - check transmit sequence firstly - * - _enamod - enable given modifier = value - * - _dismod - disable given modifier = value + * - _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 + * - _swdev/{old_device} - new_device = value + * - disable old_device and then enable new_device + * - if old_device is not enabled just return + * - check transmit sequence firstly + * - _enamod - enable given modifier = value + * - _dismod - disable given modifier = value * - _swmod/{old_modifier} - new_modifier = value - * - disable old_modifier and then enable new_modifier - * - if old_modifier is not enabled just return - * - check transmit sequence firstly + * - disable old_modifier and then enable new_modifier + * - if old_modifier is not enabled just return + * - check transmit sequence firstly */ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, const char *identifier, @@ -518,7 +534,7 @@ static __inline__ int snd_use_case_verb_list(snd_use_case_mgr_t *uc_mgr, /** * \brief Parse control element identifier - * \param elem_id Element identifier + * \param dst Element identifier * \param ucm_id Use case identifier * \param value String value to be parsed * \return Zero if success, otherwise a negative error code diff --git a/include/version.h b/include/version.h index b0db9197..beba2b22 100644 --- a/include/version.h +++ b/include/version.h @@ -1,34 +1,14 @@ /* - * ALSA lib header file include/version.h - * Copyright (c) 2022 Huawei Device Co., Ltd. - * - * 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 - * + * version.h */ -#ifndef __ALSA_VERSION_H -#define __ALSA_VERSION_H - #define SND_LIB_MAJOR 1 /**< major number of library version */ #define SND_LIB_MINOR 2 /**< minor number of library version */ -#define SND_LIB_SUBMINOR 6 /**< subminor number of library version */ +#define SND_LIB_SUBMINOR 11 /**< subminor number of library version */ #define SND_LIB_EXTRAVER 1000000 /**< extra version number, used mainly for betas */ /** library version */ #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.6" +#define SND_LIB_VERSION_STR "1.2.11" -#endif /* __ALSA_VERSION_H */ diff --git a/m4/ac_check_attribute_symver.m4 b/m4/ac_check_attribute_symver.m4 new file mode 100644 index 00000000..ac62bf36 --- /dev/null +++ b/m4/ac_check_attribute_symver.m4 @@ -0,0 +1,24 @@ +dnl Check compiler support for symver function attribute +AC_DEFUN([AC_CHECK_ATTRIBUTE_SYMVER], [ + saved_CFLAGS=$CFLAGS + CFLAGS="-O0 -Werror" + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[ + void _test_attribute_symver(void); + __attribute__((__symver__("sym@VER_1.2.3"))) void _test_attribute_symver(void) {} + ]], + [[ + _test_attribute_symver() + ]] + )], + [ + AC_DEFINE([HAVE_ATTRIBUTE_SYMVER], 1, [Define to 1 if __attribute__((symver)) is supported]) + ], + [ + AC_DEFINE([HAVE_ATTRIBUTE_SYMVER], 0, [Define to 0 if __attribute__((symver)) is not supported]) + ] + ) + CFLAGS=$saved_CFLAGS +]) + diff --git a/modules/mixer/simple/python.c b/modules/mixer/simple/python.c index 8a7264d4..6b51e6bc 100644 --- a/modules/mixer/simple/python.c +++ b/modules/mixer/simple/python.c @@ -775,8 +775,8 @@ pymixer_melement_new(struct pymixer *pymixer, PyObject *args) obj = PyDict_GetItemString(pymixer->mdict, class); if (obj) { obj1 = PyTuple_New(4); - if (PyTuple_SET_ITEM(obj1, 0, (PyObject *)pymixer)) - Py_INCREF((PyObject *)pymixer); + PyTuple_SET_ITEM(obj1, 0, (PyObject *)pymixer); + Py_INCREF((PyObject *)pymixer); PyTuple_SET_ITEM(obj1, 1, PyUnicode_FromString(name)); PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(index)); PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(weight)); @@ -920,8 +920,8 @@ static PyObject *new_helem(struct python_priv *priv, snd_hctl_elem_t *helem) obj = PyDict_GetItemString(priv->py_mdict, "HElement"); if (obj) { obj1 = PyTuple_New(3); - if (PyTuple_SET_ITEM(obj1, 0, py_hctl)) - Py_INCREF(py_hctl); + PyTuple_SET_ITEM(obj1, 0, py_hctl); + Py_INCREF(py_hctl); PyTuple_SET_ITEM(obj1, 1, PyFloat_FromDouble(1)); PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong((long)helem)); obj2 = PyObject_CallObject(obj, obj1); @@ -995,11 +995,11 @@ int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask, } if (o == NULL) return 0; - if (PyTuple_SET_ITEM(t, 1, o)) - Py_INCREF(o); + PyTuple_SET_ITEM(t, 1, o); + Py_INCREF(o); o = melem ? find_melem(priv, melem) : Py_None; - if (PyTuple_SET_ITEM(t, 2, o)) - Py_INCREF(o); + PyTuple_SET_ITEM(t, 2, o); + Py_INCREF(o); r = PyObject_CallObject(priv->py_event_func, t); Py_DECREF(t); if (r) { @@ -1066,8 +1066,8 @@ static int alsa_mixer_simple_pyinit(struct python_priv *priv, obj1 = PyTuple_New(3); PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong((long)class)); PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong((long)mixer)); - if (PyTuple_SET_ITEM(obj1, 2, mdict)) - Py_INCREF(mdict); + PyTuple_SET_ITEM(obj1, 2, mdict); + Py_INCREF(mdict); obj2 = PyObject_CallObject(obj, obj1); Py_XDECREF(obj1); PyDict_SetItemString(mdict, "mixer", obj2); diff --git a/src/Makefile.am b/src/Makefile.am index df46dbc4..74a108dc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,10 +1,12 @@ -EXTRA_DIST=Versions +EXTRA_DIST = Versions.in.in COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS VSYMS = -Wl,--version-script=Versions +BUILT_SOURCES = $(top_builddir)/src/Versions else VSYMS = +BUILT_SOURCES = endif if SYMBOLIC_FUNCTIONS @@ -13,6 +15,8 @@ else SYMFUNCS = endif +VERSION_CPPFLAGS = + lib_LTLIBRARIES = libasound.la 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 @@ -23,6 +27,9 @@ SUBDIRS += mixer libasound_la_LIBADD += mixer/libmixer.la endif if BUILD_PCM +if VERSIONED_SYMBOLS +VERSION_CPPFLAGS += -DHAVE_PCM_SYMS -DHAVE_TIMER_SYMS +endif SUBDIRS += pcm timer libasound_la_LIBADD += pcm/libpcm.la timer/libtimer.la endif @@ -43,6 +50,9 @@ SUBDIRS += ucm libasound_la_LIBADD += ucm/libucm.la endif if BUILD_ALISP +if VERSIONED_SYMBOLS +VERSION_CPPFLAGS += -DHAVE_ALISP_SYMS +endif SUBDIRS += alisp libasound_la_LIBADD += alisp/libalisp.la endif @@ -51,6 +61,9 @@ libasound_la_LIBADD += @ALSA_DEPLIBS@ libasound_la_LDFLAGS = -version-info $(COMPATNUM) $(VSYMS) $(SYMFUNCS) $(LDFLAGS_NOUNDEFINED) +$(top_builddir)/src/Versions: $(top_builddir)/src/Versions.in + $(COMPILE) -E $(VERSION_CPPFLAGS) -x assembler-with-cpp -o $@ $< + control/libcontrol.la: $(MAKE) -C control libcontrol.la diff --git a/src/Versions.in b/src/Versions.in index 5daccbd4..d7e08810 100644 --- a/src/Versions.in +++ b/src/Versions.in @@ -1,136 +1,195 @@ ALSA_0.9 { global: - @SYMBOL_PREFIX@snd_*; + snd_*; - @SYMBOL_PREFIX@_snd_*_open; - @SYMBOL_PREFIX@_snd_*_dlsym_*; - @SYMBOL_PREFIX@_snd_*_poll_descriptor; - @SYMBOL_PREFIX@_snd_pcm_hook_*; + _snd_*_open; + _snd_*_dlsym_*; + _snd_*_poll_descriptor; + _snd_pcm_hook_*; - @SYMBOL_PREFIX@__snd_pcm_hw_params_*; - @SYMBOL_PREFIX@__snd_pcm_sw_params_*; - @SYMBOL_PREFIX@__snd_*_dlsym_*; + __snd_pcm_hw_params_*; + __snd_pcm_sw_params_*; + __snd_*_dlsym_*; local: *; }; ALSA_0.9.0rc4 { +#ifdef HAVE_PCM_SYMS global: - @SYMBOL_PREFIX@snd_pcm_hw_params_get_access; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_access_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_access_last; + snd_pcm_hw_params_get_access; + snd_pcm_hw_params_set_access_first; + snd_pcm_hw_params_set_access_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_format; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_format_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_format_last; + snd_pcm_hw_params_get_format; + snd_pcm_hw_params_set_format_first; + snd_pcm_hw_params_set_format_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_subformat; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_subformat_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_subformat_last; + snd_pcm_hw_params_get_subformat; + snd_pcm_hw_params_set_subformat_first; + snd_pcm_hw_params_set_subformat_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_last; + snd_pcm_hw_params_get_channels; + snd_pcm_hw_params_get_channels_min; + snd_pcm_hw_params_get_channels_max; + snd_pcm_hw_params_set_channels_near; + snd_pcm_hw_params_set_channels_first; + snd_pcm_hw_params_set_channels_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_last; + snd_pcm_hw_params_get_rate; + snd_pcm_hw_params_get_rate_min; + snd_pcm_hw_params_get_rate_max; + snd_pcm_hw_params_set_rate_near; + snd_pcm_hw_params_set_rate_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_last; + snd_pcm_hw_params_set_rate_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_last; + snd_pcm_hw_params_get_period_time; + snd_pcm_hw_params_get_period_time_min; + snd_pcm_hw_params_get_period_time_max; + snd_pcm_hw_params_set_period_time_near; + snd_pcm_hw_params_set_period_time_first; + snd_pcm_hw_params_set_period_time_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_last; + snd_pcm_hw_params_get_period_size; + snd_pcm_hw_params_get_period_size_min; + snd_pcm_hw_params_get_period_size_max; + snd_pcm_hw_params_set_period_size_near; + snd_pcm_hw_params_set_period_size_first; + snd_pcm_hw_params_set_period_size_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_last; + snd_pcm_hw_params_get_periods; + snd_pcm_hw_params_get_periods_min; + snd_pcm_hw_params_get_periods_max; + snd_pcm_hw_params_set_periods_near; + snd_pcm_hw_params_set_periods_first; + snd_pcm_hw_params_set_periods_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_last; + snd_pcm_hw_params_get_buffer_time; + snd_pcm_hw_params_get_buffer_time_min; + snd_pcm_hw_params_get_buffer_time_max; + snd_pcm_hw_params_set_buffer_time_near; + snd_pcm_hw_params_set_buffer_time_first; + snd_pcm_hw_params_set_buffer_time_last; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time_min; - @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time_max; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_near; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_first; - @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_last; + snd_pcm_hw_params_get_buffer_size; + snd_pcm_hw_params_get_buffer_size_min; + snd_pcm_hw_params_get_buffer_size_max; + snd_pcm_hw_params_set_buffer_size_near; + snd_pcm_hw_params_set_buffer_size_first; + snd_pcm_hw_params_set_buffer_size_last; + + snd_pcm_hw_params_get_tick_time; + snd_pcm_hw_params_get_tick_time_min; + snd_pcm_hw_params_get_tick_time_max; + snd_pcm_hw_params_set_tick_time_near; + snd_pcm_hw_params_set_tick_time_first; + snd_pcm_hw_params_set_tick_time_last; +#endif } ALSA_0.9; ALSA_0.9.0rc8 { +#ifdef HAVE_PCM_SYMS global: - @SYMBOL_PREFIX@snd_pcm_forward; - @SYMBOL_PREFIX@snd_pcm_status_get_trigger_htstamp; - @SYMBOL_PREFIX@snd_pcm_status_get_htstamp; + snd_pcm_forward; + snd_pcm_status_get_trigger_htstamp; + snd_pcm_status_get_htstamp; +#endif } ALSA_0.9.0rc4; ALSA_0.9.0 { +#if defined HAVE_PCM_SYMS || defined HAVE_TIMER_SYMS global: - @SYMBOL_PREFIX@snd_pcm_type_name; - @SYMBOL_PREFIX@snd_timer_query_info; - @SYMBOL_PREFIX@snd_timer_query_params; - @SYMBOL_PREFIX@snd_timer_query_status; - @SYMBOL_PREFIX@snd_timer_params_set_exclusive; - @SYMBOL_PREFIX@snd_timer_params_get_exclusive; - @SYMBOL_PREFIX@snd_timer_params_set_filter; - @SYMBOL_PREFIX@snd_timer_params_get_filter; +#if defined HAVE_PCM_SYMS + snd_pcm_type_name; +#endif +#ifdef HAVE_TIMER_SYMS + snd_timer_query_info; + snd_timer_query_params; + snd_timer_query_status; + snd_timer_params_set_exclusive; + snd_timer_params_get_exclusive; + snd_timer_params_set_filter; + snd_timer_params_get_filter; +#endif +#endif } ALSA_0.9.0rc8; ALSA_0.9.3 { global: - @SYMBOL_PREFIX@snd_ctl_elem_info_get_dimensions; - @SYMBOL_PREFIX@snd_ctl_elem_info_get_dimension; + snd_ctl_elem_info_get_dimensions; + snd_ctl_elem_info_get_dimension; } ALSA_0.9.0; ALSA_0.9.5 { +#ifdef HAVE_ALISP_SYMS global: - @SYMBOL_PREFIX@alsa_lisp; + alsa_lisp; +#endif } ALSA_0.9.3; ALSA_0.9.7 { +#ifdef HAVE_ALISP_SYMS global: - @SYMBOL_PREFIX@alsa_lisp_*; + alsa_lisp_*; +#endif } ALSA_0.9.5; ALSA_1.1.6 { global: - @SYMBOL_PREFIX@snd_dlopen; + snd_dlopen; } ALSA_0.9.7; + +ALSA_1.2.6 { + global: + + _snd_safe_strto*; +} ALSA_1.1.6; + +ALSA_1.2.9 { +#ifdef HAVE_PCM_SYMS + global: + + snd_pcm_hw_params_is_perfect_drain; + snd_pcm_hw_params_set_drain_silence; + snd_pcm_hw_params_get_drain_silence; +#endif +} ALSA_1.2.6; + +ALSA_1.2.10 { + global: + + snd_ump_*; + snd_ctl_ump_next_device; + snd_ctl_ump_endpoint_info; + snd_ctl_ump_block_info; + snd_seq_ump_*; + snd_seq_client_info_get_midi_version; + snd_seq_client_info_get_ump_group_enabled; + snd_seq_client_info_get_ump_groupless_enabled; + snd_seq_client_info_get_ump_conversion; + snd_seq_client_info_set_midi_version; + snd_seq_client_info_set_ump_group_enabled; + snd_seq_client_info_set_ump_groupless_enabled; + snd_seq_client_info_set_ump_conversion; + snd_seq_get_ump_endpoint_info; + snd_seq_get_ump_block_info; + snd_seq_set_ump_endpoint_info; + snd_seq_set_ump_block_info; + snd_seq_port_info_get_direction; + snd_seq_port_info_get_ump_group; + snd_seq_port_info_set_direction; + snd_seq_port_info_set_ump_group; + snd_seq_set_client_midi_version; + snd_seq_set_client_ump_conversion; +} ALSA_1.2.9; diff --git a/src/Versions.in.in b/src/Versions.in.in new file mode 100644 index 00000000..98f36ded --- /dev/null +++ b/src/Versions.in.in @@ -0,0 +1,195 @@ +ALSA_0.9 { + global: + @SYMBOL_PREFIX@snd_*; + + @SYMBOL_PREFIX@_snd_*_open; + @SYMBOL_PREFIX@_snd_*_dlsym_*; + @SYMBOL_PREFIX@_snd_*_poll_descriptor; + @SYMBOL_PREFIX@_snd_pcm_hook_*; + + @SYMBOL_PREFIX@__snd_pcm_hw_params_*; + @SYMBOL_PREFIX@__snd_pcm_sw_params_*; + @SYMBOL_PREFIX@__snd_*_dlsym_*; + + local: + *; +}; + +ALSA_0.9.0rc4 { +#ifdef HAVE_PCM_SYMS + global: + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_access; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_access_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_access_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_format; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_format_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_format_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_subformat; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_subformat_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_subformat_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_channels_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_channels_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_rate_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_first; + + @SYMBOL_PREFIX@snd_pcm_hw_params_set_rate_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_time_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_time_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_period_size_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_period_size_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_periods_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_periods_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_time_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_time_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_buffer_size_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_buffer_size_last; + + @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time_min; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_tick_time_max; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_near; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_first; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_tick_time_last; +#endif + +} ALSA_0.9; + +ALSA_0.9.0rc8 { +#ifdef HAVE_PCM_SYMS + global: + + @SYMBOL_PREFIX@snd_pcm_forward; + @SYMBOL_PREFIX@snd_pcm_status_get_trigger_htstamp; + @SYMBOL_PREFIX@snd_pcm_status_get_htstamp; +#endif + +} ALSA_0.9.0rc4; + +ALSA_0.9.0 { +#if defined HAVE_PCM_SYMS || defined HAVE_TIMER_SYMS + global: + +#if defined HAVE_PCM_SYMS + @SYMBOL_PREFIX@snd_pcm_type_name; +#endif +#ifdef HAVE_TIMER_SYMS + @SYMBOL_PREFIX@snd_timer_query_info; + @SYMBOL_PREFIX@snd_timer_query_params; + @SYMBOL_PREFIX@snd_timer_query_status; + @SYMBOL_PREFIX@snd_timer_params_set_exclusive; + @SYMBOL_PREFIX@snd_timer_params_get_exclusive; + @SYMBOL_PREFIX@snd_timer_params_set_filter; + @SYMBOL_PREFIX@snd_timer_params_get_filter; +#endif +#endif +} ALSA_0.9.0rc8; + +ALSA_0.9.3 { + global: + + @SYMBOL_PREFIX@snd_ctl_elem_info_get_dimensions; + @SYMBOL_PREFIX@snd_ctl_elem_info_get_dimension; +} ALSA_0.9.0; + +ALSA_0.9.5 { +#ifdef HAVE_ALISP_SYMS + global: + + @SYMBOL_PREFIX@alsa_lisp; +#endif +} ALSA_0.9.3; + +ALSA_0.9.7 { +#ifdef HAVE_ALISP_SYMS + global: + + @SYMBOL_PREFIX@alsa_lisp_*; +#endif +} ALSA_0.9.5; + +ALSA_1.1.6 { + global: + + @SYMBOL_PREFIX@snd_dlopen; +} ALSA_0.9.7; + +ALSA_1.2.6 { + global: + + @SYMBOL_PREFIX@_snd_safe_strto*; +} ALSA_1.1.6; + +ALSA_1.2.9 { +#ifdef HAVE_PCM_SYMS + global: + + @SYMBOL_PREFIX@snd_pcm_hw_params_is_perfect_drain; + @SYMBOL_PREFIX@snd_pcm_hw_params_set_drain_silence; + @SYMBOL_PREFIX@snd_pcm_hw_params_get_drain_silence; +#endif +} ALSA_1.2.6; + +ALSA_1.2.10 { + global: + + @SYMBOL_PREFIX@snd_ump_*; + @SYMBOL_PREFIX@snd_ctl_ump_next_device; + @SYMBOL_PREFIX@snd_ctl_ump_endpoint_info; + @SYMBOL_PREFIX@snd_ctl_ump_block_info; + @SYMBOL_PREFIX@snd_seq_ump_*; + @SYMBOL_PREFIX@snd_seq_client_info_get_midi_version; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_groupless_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_get_ump_conversion; + @SYMBOL_PREFIX@snd_seq_client_info_set_midi_version; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_group_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_groupless_enabled; + @SYMBOL_PREFIX@snd_seq_client_info_set_ump_conversion; + @SYMBOL_PREFIX@snd_seq_get_ump_endpoint_info; + @SYMBOL_PREFIX@snd_seq_get_ump_block_info; + @SYMBOL_PREFIX@snd_seq_set_ump_endpoint_info; + @SYMBOL_PREFIX@snd_seq_set_ump_block_info; + @SYMBOL_PREFIX@snd_seq_port_info_get_direction; + @SYMBOL_PREFIX@snd_seq_port_info_get_ump_group; + @SYMBOL_PREFIX@snd_seq_port_info_set_direction; + @SYMBOL_PREFIX@snd_seq_port_info_set_ump_group; + @SYMBOL_PREFIX@snd_seq_set_client_midi_version; + @SYMBOL_PREFIX@snd_seq_set_client_ump_conversion; +} ALSA_1.2.9; diff --git a/src/alisp/alisp.c b/src/alisp/alisp.c index 476d0d52..bb841119 100644 --- a/src/alisp/alisp.c +++ b/src/alisp/alisp.c @@ -21,6 +21,12 @@ * */ +#define alisp_seq_iterator alisp_object + +#include "local.h" +#include "alisp.h" +#include "alisp_local.h" + #include #include @@ -31,11 +37,6 @@ #include #include -#define alisp_seq_iterator alisp_object - -#include "local.h" -#include "alisp.h" -#include "alisp_local.h" struct alisp_object alsa_lisp_nil; struct alisp_object alsa_lisp_t; diff --git a/src/async.c b/src/async.c index e6a8b5f2..76c6fb9e 100644 --- a/src/async.c +++ b/src/async.c @@ -29,7 +29,9 @@ #include static struct sigaction previous_action; +#ifndef DOC_HIDDEN #define MAX_SIG_FUNCTION_CODE 10 /* i.e. SIG_DFL SIG_IGN SIG_HOLD et al */ +#endif /* DOC_HIDDEN */ #ifdef SND_ASYNC_RT_SIGNAL /** async signal number */ @@ -54,6 +56,15 @@ static LIST_HEAD(snd_async_handlers); static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, void *context ATTRIBUTE_UNUSED) { +#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) + /* siginfo_t does not have si_fd */ + struct list_head *i; + list_for_each(i, &snd_async_handlers) { + snd_async_handler_t *h = list_entry(i, snd_async_handler_t, glist); + if (h->callback) + h->callback(h); + } +#else int fd; struct list_head *i; //assert(siginfo->si_code == SI_SIGIO); @@ -66,6 +77,7 @@ static void snd_async_handler(int signo ATTRIBUTE_UNUSED, siginfo_t *siginfo, vo if (h->fd == fd && h->callback) h->callback(h); } +#endif } /** diff --git a/src/conf.c b/src/conf.c index 0f6d2ba8..eca44c03 100644 --- a/src/conf.c +++ b/src/conf.c @@ -527,7 +527,7 @@ static inline void snd_config_unlock(void) { } #endif /* - * Add a diretory to the paths to search included files. + * Add a directory to the paths to search included files. * param fd - File object that owns these paths to search files included by it. * param dir - Path of the directory to add. Allocated externally and need to * be freed manually later. @@ -584,6 +584,8 @@ static void free_include_paths(struct filedesc *fd) } } +#endif /* DOC_HIDDEN */ + /** * \brief Returns the default top-level config directory * \return The top-level config directory path string @@ -605,6 +607,8 @@ const char *snd_config_topdir(void) return topdir; } +#ifndef DOC_HIDDEN + static char *_snd_config_path(const char *name) { const char *root = snd_config_topdir(); @@ -663,7 +667,7 @@ static int input_stdio_open(snd_input_t **inputp, const char *file, return err; } -int safe_strtoll_base(const char *str, long long *val, int base) +int _snd_safe_strtoll_base(const char *str, long long *val, int base) { char *end; long v; @@ -679,7 +683,7 @@ int safe_strtoll_base(const char *str, long long *val, int base) return 0; } -int safe_strtol_base(const char *str, long *val, int base) +int _snd_safe_strtol_base(const char *str, long *val, int base) { char *end; long v; @@ -695,7 +699,7 @@ int safe_strtol_base(const char *str, long *val, int base) return 0; } -static int safe_strtod(const char *str, double *val) +int _snd_safe_strtod(const char *str, double *val) { char *end; double v; @@ -814,11 +818,12 @@ static int get_char_skip_comments(input_t *input) closedir(dirp); err = add_include_path(input->current, str); - free(str); if (err < 0) { SNDERR("Cannot add search dir %s", str); + free(str); return err; } + free(str); continue; } @@ -1699,7 +1704,7 @@ static int _snd_config_save_children(snd_config_t *config, snd_output_t *out, } return 0; } -#endif +#endif /* DOC_HIDDEN */ /** @@ -1741,6 +1746,8 @@ int snd_config_substitute(snd_config_t *dst, snd_config_t *src) src->u.compound.fields.prev->next = &dst->u.compound.fields; } free(dst->id); + if (dst->type == SND_CONFIG_TYPE_STRING) + free(dst->u.string); dst->id = src->id; dst->type = src->type; dst->u = src->u; @@ -2052,7 +2059,7 @@ int snd_config_load(snd_config_t *config, snd_input_t *in) /** * \brief Loads a configuration tree from a string. - * \param[out] The function puts the handle to the configuration + * \param[out] config 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 @@ -2257,9 +2264,9 @@ static int _snd_config_array_merge(snd_config_t *dst, snd_config_t *src, int ind /** * \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 + * \param[out] dst Config handle for the merged contents + * \param[in] src Config handle to merge into dst (may be NULL) + * \param[in] override Override flag * \return Zero if successful, otherwise a negative error code. * * This function merges all fields from the source compound to the destination compound. @@ -2276,7 +2283,7 @@ static int _snd_config_array_merge(snd_config_t *dst, snd_config_t *src, int ind * * \par Errors: *
- *
-EEXIST
identifier already exists (!overwrite) + *
-EEXIST
identifier already exists (!override) *
-ENOMEM
not enough memory *
*/ @@ -2870,6 +2877,26 @@ int snd_config_imake_string(snd_config_t **config, const char *id, const char *v return 0; } +/** + * \brief Creates a string 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. + * \param[in] id The id of the new node. + * \param[in] value The initial value of the new node. May be \c NULL. + * \return Zero if successful, otherwise a negative error code. + * + * This function creates a new node of type #SND_CONFIG_TYPE_STRING. The node + * contains with a copy of the string \c value, replacing any character other + * than alphanumeric, space, or '-' with the character '_'. + * + * \par Errors: + *
+ *
-ENOMEM
Out of memory. + *
+ * + * \par Conforming to: + * LSB 3.2 + */ int snd_config_imake_safe_string(snd_config_t **config, const char *id, const char *value) { int err; @@ -3891,7 +3918,6 @@ int snd_config_search_alias_hooks(snd_config_t *config, #define ALSA_CONFIG_PATH_VAR "ALSA_CONFIG_PATH" /** - * \ingroup Config * \brief Configuration top-level node (the global configuration). * * This variable contains a handle to the top-level configuration node, @@ -3919,7 +3945,7 @@ snd_config_t *snd_config = NULL; struct finfo { char *name; dev_t dev; - ino_t ino; + ino64_t ino; time_t mtime; }; @@ -4062,7 +4088,7 @@ static int snd_config_hooks(snd_config_t *config, snd_config_t *private_data) return err; } -static int config_filename_filter(const struct dirent *dirent) +static int config_filename_filter(const struct dirent64 *dirent) { size_t flen; @@ -4100,26 +4126,26 @@ static int config_file_open(snd_config_t *root, const char *filename) static int config_file_load(snd_config_t *root, const char *fn, int errors) { - struct stat st; - struct dirent **namelist; + struct stat64 st; + struct dirent64 **namelist; int err, n; if (!errors && access(fn, R_OK) < 0) return 1; - if (stat(fn, &st) < 0) { + if (stat64(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 +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(__ANDROID__) +#define SORTFUNC versionsort64 #else -#define SORTFUNC alphasort +#define SORTFUNC alphasort64 #endif #endif - n = scandir(fn, &namelist, config_filename_filter, SORTFUNC); + n = scandir64(fn, &namelist, config_filename_filter, SORTFUNC); if (n > 0) { int j; err = 0; @@ -4292,7 +4318,7 @@ SND_DLSYM_BUILD_VERSION(snd_config_hook_load, SND_CONFIG_DLSYM_VERSION_HOOK); int snd_determine_driver(int card, char **driver); #endif -snd_config_t *_snd_config_hook_private_data(int card, const char *driver) +static snd_config_t *_snd_config_hook_private_data(int card, const char *driver) { snd_config_t *private_data, *v; int err; @@ -4543,9 +4569,9 @@ int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, cons c++; } for (k = 0; k < local->count; ++k) { - struct stat st; + struct stat64 st; struct finfo *lf = &local->finfo[k]; - if (stat(lf->name, &st) >= 0) { + if (stat64(lf->name, &st) >= 0) { lf->dev = st.st_dev; lf->ino = st.st_ino; lf->mtime = st.st_mtime; @@ -4994,8 +5020,10 @@ int snd_config_copy(snd_config_t **dst, 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) - return snd_config_make_string(dst, ""); + if (snd_config_search(vars, s, &val) < 0) { + *dst = NULL; + return 0; + } return snd_config_copy(dst, val); } @@ -5060,6 +5088,8 @@ static int _snd_config_expand(snd_config_t *src, 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); @@ -5803,6 +5833,7 @@ static void _snd_config_end(void) } #endif +#ifndef DOC_HIDDEN size_t page_size(void) { long s = sysconf(_SC_PAGE_SIZE); @@ -5838,3 +5869,4 @@ size_t page_ptr(size_t object_offset, size_t object_size, size_t *offset, size_t *offset = object_offset; return r; } +#endif /* DOC_HIDDEN */ diff --git a/src/conf/cards/Audigy.conf b/src/conf/cards/Audigy.conf index 1c924966..42692cfd 100644 --- a/src/conf/cards/Audigy.conf +++ b/src/conf/cards/Audigy.conf @@ -26,14 +26,6 @@ Audigy.pcm.front.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -42,15 +34,6 @@ Audigy.pcm.front.0 { optional true value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] - } - ] } } @@ -79,14 +62,6 @@ Audigy.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] - } ] } } @@ -115,14 +90,6 @@ Audigy.pcm.center_lfe.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -131,14 +98,6 @@ Audigy.pcm.center_lfe.0 { optional true value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] - } ] } } @@ -265,14 +224,6 @@ Audigy.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - lock true - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true @@ -287,14 +238,6 @@ Audigy.pcm.iec958.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -303,14 +246,6 @@ Audigy.pcm.iec958.0 { optional true value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] - } { name "Audigy Analog/Digital Output Jack" lock true diff --git a/src/conf/cards/Audigy2.conf b/src/conf/cards/Audigy2.conf index cbec7835..35126d23 100644 --- a/src/conf/cards/Audigy2.conf +++ b/src/conf/cards/Audigy2.conf @@ -26,14 +26,6 @@ Audigy2.pcm.front.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -42,14 +34,6 @@ Audigy2.pcm.front.0 { optional true value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 8 9 0 0 0 0 0 0 ] - } ] } @@ -79,14 +63,6 @@ Audigy2.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 0 0 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 ] - } ] } } @@ -115,14 +91,6 @@ Audigy2.pcm.center_lfe.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -131,14 +99,6 @@ Audigy2.pcm.center_lfe.0 { optional true value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 6 7 0 0 0 0 0 0 ] - } ] } } @@ -167,14 +127,6 @@ Audigy2.pcm.side.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -183,14 +135,6 @@ Audigy2.pcm.side.0 { optional true value [ 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 14 15 0 0 0 0 0 0 ] - } ] } } @@ -374,13 +318,6 @@ Audigy2.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true @@ -395,14 +332,6 @@ Audigy2.pcm.iec958.0 { optional true value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 255 255 0 0 0 0 0 0 255 0 0 0 0 0 0 0 0 255 0 0 0 0 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -411,14 +340,6 @@ Audigy2.pcm.iec958.0 { optional true value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - lock true - optional true - value [ 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 20 21 0 0 0 0 0 0 ] - } { name "Audigy Analog/Digital Output Jack" lock true diff --git a/src/conf/cards/EMU10K1.conf b/src/conf/cards/EMU10K1.conf index ef193fe0..430926c7 100644 --- a/src/conf/cards/EMU10K1.conf +++ b/src/conf/cards/EMU10K1.conf @@ -28,14 +28,6 @@ EMU10K1.pcm.front.0 { optional true value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -44,14 +36,6 @@ EMU10K1.pcm.front.0 { optional true value [ 8 9 0 0 8 9 0 0 8 9 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 8 9 0 0 8 9 0 0 8 9 0 0 ] - } ] } } @@ -87,14 +71,6 @@ EMU10K1.pcm.rear.0 { optional true value [ 0 0 255 255 0 0 255 0 0 0 0 255 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 0 0 255 255 0 0 255 0 0 0 0 255 ] - } ] } } @@ -152,14 +128,6 @@ EMU10K1.pcm.center_lfe.0 { optional true value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Volume" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 255 255 0 0 255 0 0 0 0 255 0 0 ] - } { interface PCM name "EMU10K1 PCM Send Routing" @@ -168,14 +136,6 @@ EMU10K1.pcm.center_lfe.0 { optional true value [ 6 7 0 0 6 7 0 0 6 7 0 0 ] } - { - # for compatibility with older drivers - name "EMU10K1 PCM Send Routing" - index { @func private_pcm_subdevice } - # lock true - optional true - value [ 6 7 0 0 6 7 0 0 6 7 0 0 ] - } ] } } @@ -304,14 +264,6 @@ EMU10K1.pcm.iec958.0 { optional true value [ $AES0 $AES1 $AES2 $AES3 ] } - { - # for compatibility with older drivers - name "IEC958 Playback Default" - lock true - preserve true - optional true - value [ $AES0 $AES1 $AES2 $AES3 ] - } { name "IEC958 Optical Raw Playback Switch" lock true diff --git a/src/conf/cards/USB-Audio.conf b/src/conf/cards/USB-Audio.conf index c7d9e5fd..2f6d2ee0 100644 --- a/src/conf/cards/USB-Audio.conf +++ b/src/conf/cards/USB-Audio.conf @@ -27,6 +27,7 @@ USB-Audio.pcm.use_dmix { USB-Audio.pcm.surround40_type { "AudioPhile" two_stereo_devices "Audiophile USB (tm)" two_stereo_devices + "ICUSBAUDIO7D" six_channels "OmniStudio" two_stereo_devices "Quattro" two_stereo_devices "SB Audigy 2 NX" six_channels @@ -52,6 +53,7 @@ USB-Audio.pcm.iec958_device { "Blue Snowball" 999 "C-Media USB Headphone Set" 999 "Cmedia Audio" 999 + "Corsair HS60 PRO Surround USB S" 999 "DELL PROFESSIONAL SOUND BAR AE5" 999 "HP Digital Stereo Headset" 999 "GN 9330" 999 @@ -65,8 +67,11 @@ USB-Audio.pcm.iec958_device { "Plantronics USB Headset" 999 "Plantronics Wireless Audio" 999 "SB WoW Headset" 999 + "Scarlett 2i2 4th Gen" 999 "Scarlett 2i2 USB" 999 "Scarlett 2i4 USB" 999 + "Scarlett Solo 4th Gen" 999 + "Scarlett Solo USB" 999 "Sennheiser USB headset" 999 "SWTOR Gaming Headset by Razer" 999 "ThinkStation P620 Main" 999 @@ -77,6 +82,7 @@ USB-Audio.pcm.iec958_device { "USB Device 0x46d_0x992" 999 "WD15 Dock" 999 "WD19 Dock" 999 + "ThinkPad USB-C Dock Gen2 USB Au" 999 } # Second iec958 device number, if any. diff --git a/src/conf/cards/vc4-hdmi.conf b/src/conf/cards/vc4-hdmi.conf index 027804a1..af87b3a8 100644 --- a/src/conf/cards/vc4-hdmi.conf +++ b/src/conf/cards/vc4-hdmi.conf @@ -3,36 +3,9 @@ # subframe conversion # - + -vc4-hdmi.pcm.front.0 { - @args [ CARD ] - @args.CARD { - type string - } - type hw - card $CARD -} - -# default with dmix -vc4-hdmi.pcm.default { - @args [ CARD ] - @args.CARD { - type string - } - type asym - playback.pcm { - type plug - slave.pcm { - @func concat - strings [ "dmix:" $CARD ] - } - } -} - - - -vc4-hdmi.pcm.iec958.0 { +vc4-hdmi.pcm.hdmi.0 { @args [ CARD AES0 AES1 AES2 AES3 ] @args.CARD { type string @@ -53,12 +26,57 @@ vc4-hdmi.pcm.iec958.0 { slave { format IEC958_SUBFRAME_LE pcm { - type plug + type hooks slave.pcm { type hw card $CARD + device 0 + } + hooks.0 { + type ctl_elems + hook_args [ + { + name "IEC958 Playback Default" + interface PCM + optional true + lock true + preserve true + value [ $AES0 $AES1 $AES2 $AES3 ] + } + ] } } } status [ $AES0 $AES1 $AES2 $AES3 ] + hdmi_mode true +} + +# default with plug and softvol +vc4-hdmi.pcm.default { + @args [ CARD ] + @args.CARD { + type string + } + type asym + playback.pcm { + type plug + slave.pcm { + type softvol + slave.pcm { + @func concat + strings [ + "cards.vc4-hdmi.pcm.hdmi.0:" + "CARD=" $CARD "," + "AES0=0x04," # IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE + "AES1=0x82," # IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER + "AES2=0x00," # IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC + "AES3=0x01" # IEC958_AES3_CON_FS_NOTID (iec958 plugin will fill in actual rate) + ] + } + control { + name "PCM Playback Volume" + card $CARD + } + } + } } diff --git a/src/confeval.c b/src/confeval.c index a971bf38..56b6ebec 100644 --- a/src/confeval.c +++ b/src/confeval.c @@ -31,14 +31,16 @@ * */ +#include "local.h" #include #include #include #include #include -#include "local.h" +#ifndef DOC_HIDDEN typedef long long value_type_t; +#endif /* DOC_HIDDEN */ static const char *_find_end_of_expression(const char *s, char begin, char end) { @@ -119,6 +121,7 @@ static int _to_integer(value_type_t *val, snd_config_t *c) return err; } +#ifndef DOC_HIDDEN int _snd_eval_string(snd_config_t **dst, const char *s, snd_config_expand_fcn_t fcn, void *private_data) { @@ -190,7 +193,7 @@ int _snd_eval_string(snd_config_t **dst, const char *s, } else { e = s + 1; while (*e) { - if (!isalnum(*e)) + if (!isalnum(*e) && *e != '_') break; e++; } @@ -203,6 +206,11 @@ int _snd_eval_string(snd_config_t **dst, const char *s, 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); @@ -239,6 +247,7 @@ int _snd_eval_string(snd_config_t **dst, const char *s, else return snd_config_imake_integer(dst, NULL, left); } +#endif /* DOC_HIDDEN */ /** * \brief Evaluate an math expression in the string @@ -246,7 +255,7 @@ int _snd_eval_string(snd_config_t **dst, const char *s, * 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 + * \param[in] private_data 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, diff --git a/src/confmisc.c b/src/confmisc.c index 64af96fa..9b30d6c1 100644 --- a/src/confmisc.c +++ b/src/confmisc.c @@ -74,12 +74,12 @@ */ +#include "local.h" #include #include #include #include #include -#include "local.h" /** * \brief Gets the boolean value from the given ASCII string. @@ -645,7 +645,7 @@ static int string_from_integer(char **dst, long v) } #endif -int _snd_func_private_data(snd_config_t **dst, snd_config_t *src, +static int _snd_func_private_data(snd_config_t **dst, snd_config_t *src, snd_config_t **private_data, const char *id) { int err; @@ -1031,6 +1031,14 @@ int snd_func_card_name(snd_config_t **dst, snd_config_t *root, SND_DLSYM_BUILD_VERSION(snd_func_card_name, SND_CONFIG_DLSYM_VERSION_EVALUATE); #endif +#ifdef DOXYGEN +/* For consistency with the PCM Interface module, include documentation even + * when PCM module is not included in the build. */ +#ifndef BUILD_PCM +#define BUILD_PCM +#endif +#endif /* DOXYGEN */ + #ifdef BUILD_PCM /** diff --git a/src/control/Makefile.am b/src/control/Makefile.am index eb66fa50..d1ff1fd8 100644 --- a/src/control/Makefile.am +++ b/src/control/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES = libcontrol.la -libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \ +libcontrol_la_SOURCES = cards.c tlv.c eld.c namehint.c hcontrol.c \ control.c control_hw.c control_empty.c \ setup.c ctlparse.c \ control_plugin.c control_symbols.c diff --git a/src/control/cards.c b/src/control/cards.c index 6145ebcd..a93f10a4 100644 --- a/src/control/cards.c +++ b/src/control/cards.c @@ -25,6 +25,7 @@ * */ +#include "control_local.h" #include #include #include @@ -32,7 +33,6 @@ #include #include #include -#include "control_local.h" #ifndef DOC_HIDDEN #define SND_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i" diff --git a/src/control/control.c b/src/control/control.c index 91415b51..d77ab24c 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -185,6 +185,7 @@ in-kernel implementations utilize this feature for I/O operations. This is against the original design. */ +#include "control_local.h" #include #include #include @@ -196,7 +197,6 @@ against the original design. #include #include #include -#include "control_local.h" /** * \brief get identifier of CTL handle @@ -265,13 +265,14 @@ int snd_ctl_nonblock(snd_ctl_t *ctl, int nonblock) } #ifndef DOC_HIDDEN -int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name) +int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name, int mode) { snd_ctl_t *ctl; ctl = calloc(1, sizeof(*ctl)); if (!ctl) return -ENOMEM; ctl->type = type; + ctl->mode = mode; if (name) ctl->name = strdup(name); INIT_LIST_HEAD(&ctl->async_handlers); @@ -427,6 +428,7 @@ int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info) return ctl->ops->element_info(ctl, info); } +#ifndef DOC_HIDDEN #if 0 /* deprecated */ static bool validate_element_member_dimension(snd_ctl_elem_info_t *info) { @@ -502,6 +504,8 @@ int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, return ctl->ops->element_add(ctl, info); } +#endif /* DOC_HIDDEN */ + /** * \brief Create and add some user-defined control elements of integer type. * \param ctl A handle of backend module for control interface. @@ -1266,6 +1270,44 @@ int snd_ctl_rawmidi_prefer_subdevice(snd_ctl_t *ctl, int subdev) return ctl->ops->rawmidi_prefer_subdevice(ctl, subdev); } +/** + * \brief Get next UMP device number + * \param ctl CTL handle + * \param device current device on entry and next device on return + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_ump_next_device(snd_ctl_t *ctl, int *device) +{ + assert(ctl && device); + if (ctl->ops->ump_next_device) + return ctl->ops->ump_next_device(ctl, device); + return -ENXIO; +} + +/** + * \brief Get UMP Endpoint info about a UMP RawMidi device + * \param ctl CTL handle + * \param info UMP Endpoint info pointer + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_ump_endpoint_info(snd_ctl_t *ctl, snd_ump_endpoint_info_t *info) +{ + assert(ctl && info); + return ctl->ops->ump_endpoint_info(ctl, info); +} + +/** + * \brief Get UMP Block info about a UMP RawMidi device + * \param ctl CTL handle + * \param info UMP Block info pointer + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_ump_block_info(snd_ctl_t *ctl, snd_ump_block_info_t *info) +{ + assert(ctl && info); + return ctl->ops->ump_block_info(ctl, info); +} + /** * \brief Set Power State to given SND_CTL_POWER_* value and do the power management * \param ctl CTL handle @@ -1320,7 +1362,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout) npfds = snd_ctl_poll_descriptors_count(ctl); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -1328,7 +1370,7 @@ int snd_ctl_wait(snd_ctl_t *ctl, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } for (;;) { diff --git a/src/control/control_ext.c b/src/control/control_ext.c index 515f7882..cf3da3d8 100644 --- a/src/control/control_ext.c +++ b/src/control/control_ext.c @@ -27,12 +27,12 @@ * */ +#include "control_local.h" +#include "control_external.h" #include #include #include #include -#include "control_local.h" -#include "control_external.h" #ifndef PIC /* entry for static linking */ @@ -622,7 +622,7 @@ The rest fields are filled by #snd_ctl_ext_create(). The handle field is the resultant PCM handle. The others are the current status of the PCM. -\section ctl_ext_impl Callback Functions of External Control Plugins +\section ctl_ext_impl_cb Callback Functions of External Control Plugins The callback functions in #snd_ctl_ext_callback_t define the real behavior of the driver. There are many callbacks but many of them are optional. @@ -712,11 +712,11 @@ int snd_ctl_ext_create(snd_ctl_ext_t *ext, const char *name, int mode) if (ext->version < SNDRV_PROTOCOL_VERSION(1, 0, 0) || ext->version > SND_CTL_EXT_VERSION) { - SNDERR("ctl_ext: Plugin version mismatch\n"); + SNDERR("ctl_ext: Plugin version mismatch"); return -ENXIO; } - err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_EXT, name, mode); if (err < 0) return err; diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 680f0fec..a353767d 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -26,6 +26,7 @@ * */ +#include "control_local.h" #include #include #include @@ -33,18 +34,18 @@ #include #include #include -#include "control_local.h" #ifndef PIC /* entry for static linking */ const char *_snd_module_control_hw = ""; #endif +#ifndef DOC_HIDDEN + #ifndef F_SETSIG #define F_SETSIG 10 #endif -#ifndef DOC_HIDDEN #define SNDRV_FILE_CONTROL ALSA_DEVICE_DIRECTORY "controlC%i" #define SNDRV_CTL_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 4) @@ -287,6 +288,9 @@ static int snd_ctl_hw_pcm_info(snd_ctl_t *handle, snd_pcm_info_t * info) snd_ctl_hw_t *hw = handle->private_data; if (ioctl(hw->fd, SNDRV_CTL_IOCTL_PCM_INFO, info) < 0) return -errno; + /* may be configurable (optional) */ + if (__snd_pcm_info_eld_fixup_check(info)) + return __snd_pcm_info_eld_fixup(info); return 0; } @@ -322,6 +326,32 @@ static int snd_ctl_hw_rawmidi_prefer_subdevice(snd_ctl_t *handle, int subdev) return 0; } +static int snd_ctl_hw_ump_next_device(snd_ctl_t *handle, int *device) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_NEXT_DEVICE, device) < 0) + return -errno; + return 0; +} + +static int snd_ctl_hw_ump_endpoint_info(snd_ctl_t *handle, + snd_ump_endpoint_info_t *info) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_ENDPOINT_INFO, info) < 0) + return -errno; + return 0; +} + +static int snd_ctl_hw_ump_block_info(snd_ctl_t *handle, + snd_ump_block_info_t *info) +{ + snd_ctl_hw_t *hw = handle->private_data; + if (ioctl(hw->fd, SNDRV_CTL_IOCTL_UMP_BLOCK_INFO, info) < 0) + return -errno; + return 0; +} + static int snd_ctl_hw_set_power_state(snd_ctl_t *handle, unsigned int state) { snd_ctl_hw_t *hw = handle->private_data; @@ -345,7 +375,7 @@ static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) if (res <= 0) return -errno; if (CHECK_SANITY(res != sizeof(*event))) { - SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)\n", + SNDMSG("snd_ctl_hw_read: read size error (req:%d, got:%d)", sizeof(*event), res); return -EINVAL; } @@ -376,6 +406,9 @@ static const snd_ctl_ops_t snd_ctl_hw_ops = { .rawmidi_next_device = snd_ctl_hw_rawmidi_next_device, .rawmidi_info = snd_ctl_hw_rawmidi_info, .rawmidi_prefer_subdevice = snd_ctl_hw_rawmidi_prefer_subdevice, + .ump_next_device = snd_ctl_hw_ump_next_device, + .ump_endpoint_info = snd_ctl_hw_ump_endpoint_info, + .ump_block_info = snd_ctl_hw_ump_block_info, .set_power_state = snd_ctl_hw_set_power_state, .get_power_state = snd_ctl_hw_get_power_state, .read = snd_ctl_hw_read, @@ -441,7 +474,7 @@ int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode) hw->fd = fd; hw->protocol = ver; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_HW, name, mode); if (err < 0) { close(fd); free(hw); diff --git a/src/control/control_local.h b/src/control/control_local.h index b3f6ee79..2afa62cc 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -47,6 +47,9 @@ typedef struct _snd_ctl_ops { int (*rawmidi_next_device)(snd_ctl_t *handle, int *device); int (*rawmidi_info)(snd_ctl_t *handle, snd_rawmidi_info_t * info); int (*rawmidi_prefer_subdevice)(snd_ctl_t *handle, int subdev); + int (*ump_next_device)(snd_ctl_t *handle, int *device); + int (*ump_endpoint_info)(snd_ctl_t *handle, snd_ump_endpoint_info_t *info); + int (*ump_block_info)(snd_ctl_t *handle, snd_ump_block_info_t *info); int (*set_power_state)(snd_ctl_t *handle, unsigned int state); int (*get_power_state)(snd_ctl_t *handle, unsigned int *state); int (*read)(snd_ctl_t *handle, snd_ctl_event_t *event); @@ -62,6 +65,7 @@ struct _snd_ctl { snd_ctl_type_t type; const snd_ctl_ops_t *ops; void *private_data; + int mode; int nonblock; int poll_fd; struct list_head async_handlers; @@ -93,7 +97,7 @@ struct _snd_hctl { /* make local functions really local */ #define snd_ctl_new snd1_ctl_new -int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name); +int snd_ctl_new(snd_ctl_t **ctlp, snd_ctl_type_t type, const char *name, int mode); int _snd_ctl_poll_descriptor(snd_ctl_t *ctl); #define _snd_ctl_async_descriptor _snd_ctl_poll_descriptor int snd_ctl_hw_open(snd_ctl_t **handle, const char *name, int card, int mode); @@ -124,3 +128,12 @@ int __snd_ctl_add_elem_set(snd_ctl_t *ctl, snd_ctl_elem_info_t *info, int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str, const char **ret_ptr); + +static inline int +__snd_pcm_info_eld_fixup_check(snd_pcm_info_t *info) +{ + return info->stream == SND_PCM_STREAM_PLAYBACK && + strncmp((char *)info->name, "HDMI ", 5) == 0; +} + +int __snd_pcm_info_eld_fixup(snd_pcm_info_t *info); diff --git a/src/control/control_remap.c b/src/control/control_remap.c index 4914f960..81cf38e3 100644 --- a/src/control/control_remap.c +++ b/src/control/control_remap.c @@ -25,14 +25,15 @@ * */ +#include "control_local.h" #include #include #include #include #include #include -#include "control_local.h" +#ifndef DOC_HIDDEN #if 0 #define REMAP_DEBUG 1 #define debug(format, args...) fprintf(stderr, format, ##args) @@ -48,6 +49,7 @@ #endif #define EREMAPNOTFOUND (888899) +#endif /* DOC_HIDDEN */ #ifndef PIC /* entry for static linking */ @@ -146,7 +148,7 @@ static snd_ctl_numid_t *remap_numid_child_new(snd_ctl_remap_t *priv, unsigned in if (numid_child == 0) return NULL; - if (remap_find_numid_app(priv, numid_child)) { + if (priv->numid_remap_active && 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; @@ -220,6 +222,7 @@ static snd_ctl_map_t *remap_find_map_numid(snd_ctl_remap_t *priv, unsigned int n } 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; @@ -379,10 +382,12 @@ static int snd_ctl_remap_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list) return 0; } +#ifndef DOC_HIDDEN #define ACCESS_BITS(bits) \ (bits & (SNDRV_CTL_ELEM_ACCESS_READWRITE|\ SNDRV_CTL_ELEM_ACCESS_VOLATILE|\ SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)) +#endif /* DOC_HIDDEN */ static int remap_map_elem_info(snd_ctl_remap_t *priv, snd_ctl_elem_info_t *info) { @@ -1141,6 +1146,7 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf) * \param name Name of control device * \param remap Remap configuration * \param map Map configuration + * \param child child configuration root * \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 @@ -1148,7 +1154,7 @@ static int parse_map(snd_ctl_remap_t *priv, snd_config_t *conf) * 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_config_t *map, snd_ctl_t *child, int mode) { snd_ctl_remap_t *priv; snd_ctl_t *ctl; @@ -1195,7 +1201,7 @@ int snd_ctl_remap_open(snd_ctl_t **handlep, const char *name, snd_config_t *rema priv->numid_remap_active = priv->map_items > 0; priv->child = child; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_REMAP, name, mode); if (err < 0) { result = err; goto _err; @@ -1326,4 +1332,6 @@ int _snd_ctl_remap_open(snd_ctl_t **handlep, char *name, snd_config_t *root, snd snd_ctl_close(cctl); return err; } +#ifndef DOC_HIDDEN SND_DLSYM_BUILD_VERSION(_snd_ctl_remap_open, SND_CONTROL_DLSYM_VERSION); +#endif diff --git a/src/control/control_shm.c b/src/control/control_shm.c index c5723549..3d1555ee 100644 --- a/src/control/control_shm.c +++ b/src/control/control_shm.c @@ -51,7 +51,7 @@ static int snd_ctl_shm_action(snd_ctl_t *ctl) { snd_ctl_shm_t *shm = ctl->private_data; int err; - char buf[1]; + char buf[1] = {0}; volatile snd_ctl_shm_ctrl_t *ctrl = shm->ctrl; err = write(shm->socket, buf, 1); if (err != 1) @@ -502,7 +502,7 @@ int snd_ctl_shm_open(snd_ctl_t **handlep, const char *name, const char *sockname shm->socket = sock; shm->ctrl = ctrl; - err = snd_ctl_new(&ctl, SND_CTL_TYPE_SHM, name); + err = snd_ctl_new(&ctl, SND_CTL_TYPE_SHM, name, mode); if (err < 0) { result = err; goto _err; diff --git a/src/control/ctl_symbols_list.c b/src/control/ctl_symbols_list.c index ea17749d..40b986f2 100644 --- a/src/control/ctl_symbols_list.c +++ b/src/control/ctl_symbols_list.c @@ -1,23 +1,3 @@ -/* - * ALSA lib C file ctl_symbols_list.c - * Copyright (c) 2022 Huawei Device Co., Ltd. - * - * 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 - * - */ - &_snd_module_control_remap, &_snd_module_control_shm, &_snd_module_control_ext, diff --git a/src/control/ctlparse.c b/src/control/ctlparse.c index 3ed1c647..ecb5cf91 100644 --- a/src/control/ctlparse.c +++ b/src/control/ctlparse.c @@ -25,11 +25,11 @@ * */ +#include "control_local.h" #include #include #include #include -#include "control_local.h" /* Function to convert from percentage to volume. val = percentage */ diff --git a/src/control/eld.c b/src/control/eld.c new file mode 100644 index 00000000..78dd4382 --- /dev/null +++ b/src/control/eld.c @@ -0,0 +1,109 @@ +/** + * \file control/eld.c + * \brief ELD decoder + * \author Jaroslav Kysela + * \date 2022 + */ +/* + * Control Interface - Decode ELD + * + * Copyright (c) 2022 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" +#include +#include +#include +#include +#include + +static void __fill_eld_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev) +{ + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM); + snd_ctl_elem_id_set_name(id, "ELD"); + snd_ctl_elem_id_set_device(id, dev); + snd_ctl_elem_id_set_index(id, subdev); +} + +int __snd_pcm_info_eld_fixup(snd_pcm_info_t * info) +{ + snd_ctl_t *ctl; + snd_ctl_elem_info_t cinfo = {0}; + snd_ctl_elem_value_t value = {0}; + unsigned char *eld; + unsigned int l, spc; + char *s, c; + int ret, valid; + + ret = snd_ctl_hw_open(&ctl, NULL, info->card, 0); + if (ret < 0) { + SYSMSG("Cannot open the associated CTL"); + return ret; + } + + __fill_eld_ctl_id(&cinfo.id, info->device, info->subdevice); + value.id = cinfo.id; + ret = snd_ctl_elem_info(ctl, &cinfo); + if (ret >= 0 && cinfo.type == SND_CTL_ELEM_TYPE_BYTES) + ret = snd_ctl_elem_read(ctl, &value); + snd_ctl_close(ctl); + if (ret == -ENOENT || cinfo.type != SND_CTL_ELEM_TYPE_BYTES || cinfo.count == 0) + return 0; + if (ret < 0) { + SYSMSG("Cannot read ELD"); + return ret; + } + /* decode connected HDMI device name */ + eld = value.value.bytes.data; + if (cinfo.count < 20 || cinfo.count > 256) + return -EIO; + l = eld[4] & 0x1f; + if (l == 0) + /* no monitor name detected */ + goto __present; + if (l > 16 || 20 + l > cinfo.count) { + SNDERR("ELD decode failed, using old HDMI output names"); + return 0; + } + s = alloca(l + 1); + /* sanitize */ + valid = 0; + spc = 0; + while (l > 0) { + l--; + c = eld[20 + l]; + if (c <= ' ' || c >= 0x7f) { + s[l] = ' '; + } else { + valid += !!isalnum(c); + s[l] = c; + if (spc == 0) + spc = l + 1; + } + } + if (valid > 3) { + s[spc] = '\0'; + snd_strlcpy((char *)info->name, s, sizeof(info->name)); + } else { +__present: + strncat((char *)info->name, " *", sizeof(info->name) - 1); + ((char *)info->name)[sizeof(info->name)-1] = '\0'; + } + return 0; +} diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index 0cac8956..204d2e50 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -42,13 +42,13 @@ to reduce overhead accessing the real controls in kernel drivers. */ +#include "control_local.h" #include #include #include #include #include #include -#include "control_local.h" #ifdef HAVE_LIBPTHREAD #include #endif @@ -680,7 +680,7 @@ int snd_hctl_wait(snd_hctl_t *hctl, int timeout) npfds = snd_hctl_poll_descriptors_count(hctl); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -689,14 +689,14 @@ int snd_hctl_wait(snd_hctl_t *hctl, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } do { pollio = 0; err_poll = poll(pfd, npfds, timeout); if (err_poll < 0) { - if (errno == EINTR && !CTLINABORT(hctl->ctl)) + if (errno == EINTR && !CTLINABORT(hctl->ctl) && !(hctl->ctl->mode & SND_CTL_EINTR)) continue; return -errno; } diff --git a/src/control/namehint.c b/src/control/namehint.c index e4f696ad..11783c0c 100644 --- a/src/control/namehint.c +++ b/src/control/namehint.c @@ -1,5 +1,6 @@ /** * \file control/namehint.c + * \ingroup Configuration * \brief Give device name hints * \author Jaroslav Kysela * \date 2006 @@ -602,9 +603,7 @@ int snd_device_name_hint(int card, const char *iface, void ***hints) list.siface = iface; list.show_all = 0; list.cardname = NULL; - if (strcmp(iface, "card") == 0) - list.iface = SND_CTL_ELEM_IFACE_CARD; - else if (strcmp(iface, "pcm") == 0) + if (strcmp(iface, "pcm") == 0) list.iface = SND_CTL_ELEM_IFACE_PCM; else if (strcmp(iface, "rawmidi") == 0) list.iface = SND_CTL_ELEM_IFACE_RAWMIDI; diff --git a/src/control/setup.c b/src/control/setup.c index ab84bbee..fb096117 100644 --- a/src/control/setup.c +++ b/src/control/setup.c @@ -29,13 +29,13 @@ * */ +#include "local.h" #include #include #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN typedef struct { @@ -311,7 +311,7 @@ static int snd_config_get_ctl_elem_value(snd_config_t *conf, unsigned int idx = 0; if (len % 2 != 0 || len > count * 2) { _bad_content: - SNDERR("bad value content\n"); + SNDERR("bad value content"); return -EINVAL; } while (*buf) { diff --git a/src/control/tlv.c b/src/control/tlv.c index d5044eb5..3a2b731d 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -26,6 +26,7 @@ * */ +#include "control_local.h" #include #include #include @@ -33,7 +34,6 @@ #ifndef HAVE_SOFT_FLOAT #include #endif -#include "control_local.h" #ifndef DOC_HIDDEN /* convert to index of integer array */ diff --git a/src/dlmisc.c b/src/dlmisc.c index f64c716a..d7aff456 100644 --- a/src/dlmisc.c +++ b/src/dlmisc.c @@ -27,8 +27,8 @@ * */ -#include "list.h" #include "local.h" +#include "list.h" #ifdef HAVE_LIBPTHREAD #include #endif @@ -170,8 +170,10 @@ EXPORT_SYMBOL void *INTERNAL(snd_dlopen_old)(const char *name, int mode) } #endif +#ifndef DOC_HIDDEN use_symbol_version(__snd_dlopen_old, snd_dlopen, ALSA_0.9); use_default_symbol_version(__snd_dlopen, snd_dlopen, ALSA_1.1.6); +#endif /* DOC_HIDDEN */ /** * \brief Closes a dynamic library - ALSA wrapper for \c dlclose. diff --git a/src/error.c b/src/error.c index 2e617f87..c06af7c7 100644 --- a/src/error.c +++ b/src/error.c @@ -28,11 +28,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" /** * Array of error codes in US ASCII. diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index c5b3513d..72f6f0d0 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -28,13 +28,13 @@ * */ +#include "hwdep_local.h" #include #include #include #include #include #include -#include "hwdep_local.h" static int snd_hwdep_open_conf(snd_hwdep_t **hwdep, const char *name, snd_config_t *hwdep_root, diff --git a/src/hwdep/hwdep_hw.c b/src/hwdep/hwdep_hw.c index 1d3cf8e1..0f28f23b 100644 --- a/src/hwdep/hwdep_hw.c +++ b/src/hwdep/hwdep_hw.c @@ -19,13 +19,13 @@ * */ +#include "hwdep_local.h" #include #include #include #include #include #include -#include "hwdep_local.h" #ifndef PIC /* entry for static linking */ diff --git a/src/hwdep/hwdep_local.h b/src/hwdep/hwdep_local.h index 6cc95b04..9424272c 100644 --- a/src/hwdep/hwdep_local.h +++ b/src/hwdep/hwdep_local.h @@ -19,10 +19,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" typedef struct { int (*close)(snd_hwdep_t *hwdep); diff --git a/src/input.c b/src/input.c index 35324f1f..aa1cef5f 100644 --- a/src/input.c +++ b/src/input.c @@ -27,11 +27,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index b1af9945..7dd5c47a 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -39,13 +39,13 @@ This is an abstraction layer over the hcontrol layer. */ +#include "mixer_local.h" #include #include #include #include #include #include -#include "mixer_local.h" #ifndef DOC_HIDDEN typedef struct _snd_mixer_slave { @@ -87,6 +87,11 @@ int snd_mixer_open(snd_mixer_t **mixerp, int mode ATTRIBUTE_UNUSED) * \return 0 on success otherwise a negative error code * * For use by mixer element class specific code. + * + * The implementation of mixer class typically calls it at #SND_CTL_EVENT_MASK_ADD event. Once + * attaching, the implementation should make sure to detach it by call of #snd_mixer_elem_detach() + * at #SND_CTL_EVENT_MASK_REMOVE event. Unless detaching, mixer API internal hits assertion due + * to unsatisfied postcondition after the event. */ int snd_mixer_elem_attach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem) @@ -106,6 +111,10 @@ int snd_mixer_elem_attach(snd_mixer_elem_t *melem, * \return 0 on success otherwise a negative error code * * For use by mixer element class specific code. + * + * The implementation of mixer class typically calls it at #SND_CTL_EVENT_MASK_REMOVE event for + * attached mixer element at #SND_CTL_EVENT_MASK_ADD. Unless detaching, mixer API internal hits + * assertion due to unsatisfied postcondition after the event. */ int snd_mixer_elem_detach(snd_mixer_elem_t *melem, snd_hctl_elem_t *helem) @@ -146,6 +155,9 @@ static int hctl_elem_event_handler(snd_hctl_elem_t *helem, if (err < 0) res = err; } + // NOTE: Unsatisfied postcondition. Typically, some of registerd implementation of + // mixer class forget to detach mixer element from hcontrol element which has been + // attached at ADD event. assert(bag_empty(bag)); bag_free(bag); return res; @@ -470,7 +482,6 @@ int snd_mixer_elem_remove(snd_mixer_elem_t *elem) /** * \brief Free a mixer element * \param elem Mixer element - * \return 0 on success otherwise a negative error code * * For use by mixer element class specific code. */ diff --git a/src/mixer/simple.c b/src/mixer/simple.c index 571fa664..1ef2f975 100644 --- a/src/mixer/simple.c +++ b/src/mixer/simple.c @@ -29,6 +29,8 @@ * */ +#include "mixer_local.h" +#include "mixer_simple.h" #include #include #include @@ -36,9 +38,6 @@ #include #include #include -#include "config.h" -#include "mixer_local.h" -#include "mixer_simple.h" /** * \brief Register mixer simple element class diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 4dcc4cc7..ffc92e86 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -27,6 +27,8 @@ * */ +#include "mixer_local.h" +#include "mixer_simple.h" #include #include #include @@ -35,8 +37,6 @@ #include #include #include -#include "mixer_local.h" -#include "mixer_simple.h" #ifndef DOC_HIDDEN diff --git a/src/mixer/simple_none.c b/src/mixer/simple_none.c index 9b9f0000..dd03fcf1 100644 --- a/src/mixer/simple_none.c +++ b/src/mixer/simple_none.c @@ -29,6 +29,8 @@ * */ +#include "local.h" +#include "mixer_simple.h" #include #include #include @@ -38,9 +40,6 @@ #include #include #include -#include "local.h" -#include "config.h" -#include "mixer_simple.h" #ifndef DOC_HIDDEN @@ -1156,11 +1155,12 @@ static selem_ctl_t *get_selem_ctl(selem_none_t *s, int dir) c = &s->ctls[CTL_CAPTURE_VOLUME]; else return NULL; - if (! c->elem) { + if (! c->elem) c = &s->ctls[CTL_GLOBAL_VOLUME]; - if (! c->elem) - return NULL; - } + if (! c->elem) + c = &s->ctls[CTL_SINGLE]; + if (! c->elem) + return NULL; if (c->type != SND_CTL_ELEM_TYPE_INTEGER) return NULL; return c; diff --git a/src/names.c b/src/names.c index d909a11d..922ef781 100644 --- a/src/names.c +++ b/src/names.c @@ -30,10 +30,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" /** * \brief This function is unimplemented. diff --git a/src/output.c b/src/output.c index 70e6d65d..0e402b51 100644 --- a/src/output.c +++ b/src/output.c @@ -27,11 +27,11 @@ * */ +#include "local.h" #include #include #include #include -#include "local.h" #ifndef DOC_HIDDEN typedef struct _snd_output_ops { diff --git a/src/pcm/interval.c b/src/pcm/interval.c index ef4c2ed9..719e5086 100644 --- a/src/pcm/interval.c +++ b/src/pcm/interval.c @@ -22,9 +22,9 @@ #define SND_INTERVAL_C #define SND_INTERVAL_INLINE +#include "pcm_local.h" #include #include -#include "pcm_local.h" static inline void div64_32(uint64_t *n, uint32_t d, uint32_t *rem) { diff --git a/src/pcm/mask.c b/src/pcm/mask.c index f85357ca..6bd218da 100644 --- a/src/pcm/mask.c +++ b/src/pcm/mask.c @@ -22,6 +22,7 @@ #define SND_MASK_C #define SND_MASK_INLINE +#include "config.h" #include #include #include "pcm_local.h" diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 9aec52d1..62e76e7e 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -96,7 +96,7 @@ standard C open function - see 'man 2 open'). In non-blocked behaviour, these I/O functions never stops, they return -EAGAIN error code, when no data can be transferred (the ring buffer is full in our case). In blocked behaviour, these I/O functions stop and wait until there is a room in the -ring buffer (playback) or until there are a new samples (capture). The ALSA +ring buffer (playback) or until there are new samples (capture). The ALSA implementation can be found in the \ref alsa_pcm_rw section. \subsection pcm_transfer_event Event waiting routines @@ -215,7 +215,8 @@ range, thus you may get the significant bits for linear samples via #snd_pcm_hw_params_get_sbits() function. The example: ICE1712 chips support 32-bit sample processing, but low byte is ignored (playback) or zero (capture). The function snd_pcm_hw_params_get_sbits() -returns 24 in this case. +returns 24 in this case. The significant bits are related to the usable +sample bits (width) not the physical sample space. \section alsa_transfers ALSA transfers @@ -223,7 +224,7 @@ There are two methods to transfer samples in application. The first method is the standard read / write one. The second method, uses the direct audio buffer to communicate with the device while ALSA library manages this space itself. You can find examples of all communication schemes for playback -in \ref example_test_pcm "Sine-wave generator example". To complete the +in \link example_test_pcm Sine-wave generator example \endlink. To complete the list, we should note that #snd_pcm_wait() function contains embedded poll waiting implementation. @@ -351,9 +352,9 @@ enumeration. These parameters - #snd_pcm_sw_params_t can be modified at any time including the running state. -\par Minimum available count of samples +\par Minimum available count of frames -This parameter controls the wakeup point. If the count of available samples +This parameter controls the wakeup point. If the count of available frames is equal or greater than this value, then application will be activated. \par Timestamp mode @@ -372,29 +373,29 @@ is ignored by device. Usually, this value is set to one (no align). \par Start threshold The start threshold parameter is used to determine the start point in -stream. For playback, if samples in ring buffer is equal or greater than -the start threshold parameters and the stream is not running, the stream will -be started automatically from the device. For capture, if the application wants -to read count of samples equal or greater then the stream will be started. -If you want to use explicit start (#snd_pcm_start), you can -set this value greater than ring buffer size (in samples), but use the -constant MAXINT is not a bad idea. +stream. For playback, if the frame count in the ring buffer is equal or greater +than the start threshold parameter and the stream is not running, the stream +will be started automatically from the device. For capture, if the application +wants to read count of frames equal or greater then the stream will be started. +If you want to use explicit start (#snd_pcm_start), you can set this value +greater than the ring buffer size (in frames). For that simply using a large +constant such as LONG_MAX or the boundary value is not a bad idea. \par Stop threshold Similarly, the stop threshold parameter is used to automatically stop -the running stream, when the available samples crosses this boundary. +the running stream, when the available frames crosses this boundary. It means, for playback, the empty samples in ring buffer and for capture, the filled (used) samples in ring buffer. \par Silence threshold -The silence threshold specifies count of samples filled with silence -ahead of the current application pointer for playback. It is usable -for applications when an overrun is possible (like tasks depending on -network I/O etc.). If application wants to manage the ahead samples itself, -the #snd_pcm_rewind() function allows to forget the last -samples in the stream. +The silence threshold specifies the count of frames before an underrun when the +buffer gets filled with frames of silence according to the silence size parameter +ahead of the current application pointer for playback. It is usable for applications +when an underrun is possible (like tasks depending on network I/O etc.). If +application wants to manage the ahead samples itself, the #snd_pcm_rewind() function +allows to forget the last samples in the stream. \section pcm_status Obtaining stream status @@ -402,11 +403,11 @@ The stream status is stored in #snd_pcm_status_t structure. These parameters can be obtained: the current stream state - #snd_pcm_status_get_state(), timestamp of trigger - #snd_pcm_status_get_trigger_tstamp(), timestamp of last -pointer update #snd_pcm_status_get_tstamp(), delay in samples - -#snd_pcm_status_get_delay(), available count in samples - -#snd_pcm_status_get_avail(), maximum available samples - +pointer update #snd_pcm_status_get_tstamp(), delay in frames - +#snd_pcm_status_get_delay(), available count in frames - +#snd_pcm_status_get_avail(), maximum available frames - #snd_pcm_status_get_avail_max(), ADC over-range count in -samples - #snd_pcm_status_get_overrange(). The last two +frames - #snd_pcm_status_get_overrange(). The last two parameters - avail_max and overrange are reset to zero after the status call. @@ -414,7 +415,7 @@ call.

The function #snd_pcm_avail_update() updates the current -available count of samples for writing (playback) or filled samples for +available count of frames for writing (playback) or filled frames for reading (capture). This call is mandatory for updating actual r/w pointer. Using standalone, it is a light method to obtain current stream position, because it does not require the user <-> kernel context switch, but the value @@ -427,10 +428,10 @@ The function #snd_pcm_avail() reads the current hardware pointer in the ring buffer from hardware and calls #snd_pcm_avail_update() then.

-The function #snd_pcm_delay() returns the delay in samples. -For playback, it means count of samples in the ring buffer before -the next sample will be sent to DAC. For capture, it means count of samples -in the ring buffer before the next sample will be captured from ADC. It works +The function #snd_pcm_delay() returns the delay in frames. +For playback, it means count of frames in the ring buffer before +the next frames will be sent to DAC. For capture, it means count of frames +in the ring buffer before the next frames will be captured from ADC. It works only when the stream is in the running or draining (playback only) state. Note that this function does not update the current r/w pointer for applications, so the function #snd_pcm_avail_update() must be called afterwards @@ -632,42 +633,53 @@ The null device is null plugin. This device has not any arguments. The full featured examples with cross-links can be found in Examples section (see top of page): -\anchor example_test_pcm \par Sine-wave generator \par -alsa-lib/test/pcm.c example shows various transfer methods for the playback direction. +\link example_test_pcm alsa-lib/test/pcm.c \endlink +example shows various transfer methods for the playback direction. \par Minimalistic PCM playback code \par -alsa-lib/test/pcm_min.c example shows the minimal code to produce a sound. +\link example_test_minimal alsa-lib/test/pcm_min.c \endlink +example shows the minimal code to produce a sound. \par Latency measuring tool \par -alsa-lib/test/latency.c example shows the measuring of minimal latency between capture and +\link example_test_latency alsa-lib/test/latency.c \endlink +example shows the measuring of minimal latency between capture and playback devices. */ /** \example ../../test/pcm.c +\anchor example_test_pcm +Shows various transfer methods for the playback direction. */ /** \example ../../test/pcm_min.c +\anchor example_test_minimal +Shows the minimal code to produce a sound. */ /** \example ../../test/latency.c +\anchor example_test_latency +Shows the measuring of minimal latency between capture and +playback devices. */ +#include "pcm_local.h" #include #include +#if HAVE_MALLOC_H #include +#endif #include #include #include #include #include #include -#include "pcm_local.h" #ifndef DOC_HIDDEN /* return specific error codes for known bad PCM states */ @@ -883,6 +895,7 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) * \param pcm PCM handle * \param params Configuration space definition container * \return 0 on success otherwise a negative error code + * \retval -EBADFD no hardware configuration is set */ int snd_pcm_hw_params_current(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { @@ -949,6 +962,8 @@ int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) /** \brief Remove PCM hardware configuration and free associated resources * \param pcm PCM handle * \return 0 on success otherwise a negative error code + * + * The function will also report success if no configuration is set. */ int snd_pcm_hw_free(snd_pcm_t *pcm) { @@ -1703,7 +1718,7 @@ int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) assert(pcm1); assert(pcm2); if (pcm1->fast_ops->link) - err = pcm1->fast_ops->link(pcm1, pcm2); + err = pcm1->fast_ops->link(pcm1->fast_op_arg, pcm2); else err = -ENOSYS; return err; @@ -1720,7 +1735,7 @@ int snd_pcm_unlink(snd_pcm_t *pcm) assert(pcm); if (pcm->fast_ops->unlink) - err = pcm->fast_ops->unlink(pcm); + err = pcm->fast_ops->unlink(pcm->fast_op_arg); else err = -ENOSYS; return err; @@ -1797,6 +1812,12 @@ static int __snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, * corresponding FD_SET arrays and demangle events using * \link ::snd_pcm_poll_descriptors_revents() \endlink . * + * It is guaranteed that for the given PCM handle, the output poll + * descriptor structs (and their count) will not change after + * hardware and software parameters setup. Thus it is valid to call + * the function once when all parameters are set and reuse its output + * for the lifetime of the stream parameters. + * * The function is thread-safe when built with the proper option. */ int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space) @@ -1832,6 +1853,13 @@ static int __snd_pcm_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, * Note: Even if multiple poll descriptors are used (i.e. pfds > 1), * this function returns only a single event. * + * The passed in count of poll descriptors must be equal to + * \link ::snd_pcm_poll_descriptors_count() \endlink and the passed in array + * must match the array returned by \link ::snd_pcm_poll_descriptors() \endlink + * (in its full length and original order) with the revent fields updated + * according to the poll() result. This function will not modify the file + * descriptor or event field of any element of the given poll descriptor array. + * * The function is thread-safe when built with the proper option. */ int snd_pcm_poll_descriptors_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents) @@ -2059,10 +2087,16 @@ static const char *const snd_pcm_type_names[] = { static const char *const snd_pcm_subformat_names[] = { SUBFORMAT(STD), + SUBFORMAT(MSBITS_MAX), + SUBFORMAT(MSBITS_20), + SUBFORMAT(MSBITS_24), }; static const char *const snd_pcm_subformat_descriptions[] = { SUBFORMATD(STD, "Standard"), + SUBFORMATD(MSBITS_MAX, "Maximum based on PCM format"), + SUBFORMATD(MSBITS_20, "20 most significant bits"), + SUBFORMATD(MSBITS_24, "24 most significant bits"), }; static const char *const snd_pcm_start_mode_names[] = { @@ -2186,6 +2220,30 @@ const char *snd_pcm_subformat_description(const snd_pcm_subformat_t subformat) return snd_pcm_subformat_descriptions[subformat]; } +/** + * \brief get PCM sample subformat from name + * \param name PCM sample subformat name (case insensitive) + * \return PCM sample subformat + */ +snd_pcm_subformat_t snd_pcm_subformat_value(const char* name) +{ + snd_pcm_subformat_t subformat; + + for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) { + if (snd_pcm_subformat_names[subformat] && + !strcasecmp(name, snd_pcm_subformat_names[subformat])) + return subformat; + } + + for (subformat = 0; subformat <= SND_PCM_SUBFORMAT_LAST; subformat++) { + if (snd_pcm_subformat_descriptions[subformat] && + !strcasecmp(name, snd_pcm_subformat_descriptions[subformat])) + return subformat; + } + + return SND_PCM_SUBFORMAT_UNKNOWN; +} + /** * \brief (DEPRECATED) get name of PCM start mode setting * \param mode PCM start mode @@ -2232,7 +2290,7 @@ const char *snd_pcm_tstamp_mode_name(const snd_pcm_tstamp_t mode) /** * \brief get name of PCM tstamp type setting - * \param mode PCM tstamp type + * \param type PCM tstamp type * \return ascii name of PCM tstamp type setting */ const char *snd_pcm_tstamp_type_name(snd_pcm_tstamp_type_t type) @@ -2832,7 +2890,8 @@ int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name, * \brief Wait for a PCM to become ready * \param pcm PCM handle * \param timeout maximum time in milliseconds to wait, - * a negative value means infinity + * a -1 value means infinity (SND_PCM_WAIT_INFINITE), + * see also SND_PCM_WAIT_IO and SND_PCM_WAIT_DRAIN * \return a positive value on success otherwise a negative error code * (-EPIPE for the xrun and -ESTRPIPE for the suspended status, * others for general errors) @@ -2867,6 +2926,37 @@ int __snd_pcm_wait_in_lock(snd_pcm_t *pcm, int timeout) return snd_pcm_wait_nocheck(pcm, timeout); } +static int __snd_pcm_wait_io_timeout(snd_pcm_t *pcm) +{ + int timeout; + + /* period size is the time boundary */ + timeout = (pcm->period_size * 1000ULL) / pcm->rate; + /* should not happen */ + if (timeout < 0) + timeout = 0; + /* add extra time of 200 milliseconds */ + timeout += 200; + return timeout; +} + +static int __snd_pcm_wait_drain_timeout(snd_pcm_t *pcm) +{ + int timeout; + + /* for capture, there's no reason to wait, just one iteration */ + if (snd_pcm_stream(pcm) == SND_PCM_STREAM_CAPTURE) + return 0; + /* result is in milliseconds */ + timeout = (snd_pcm_mmap_playback_delay(pcm) * 1000LL) / pcm->rate; + /* should not happen */ + if (timeout < 0) + timeout = 0; + /* add extra time of 200 milliseconds */ + timeout += 200; + return timeout; +} + /* * like snd_pcm_wait() but doesn't check mmap_avail before calling poll() * @@ -2882,7 +2972,7 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) npfds = __snd_pcm_poll_descriptors_count(pcm); if (npfds <= 0 || npfds >= 16) { - SNDERR("Invalid poll_fds %d\n", npfds); + SNDERR("Invalid poll_fds %d", npfds); return -EIO; } pfd = alloca(sizeof(*pfd) * npfds); @@ -2890,15 +2980,21 @@ int snd_pcm_wait_nocheck(snd_pcm_t *pcm, int timeout) if (err < 0) return err; if (err != npfds) { - SNDMSG("invalid poll descriptors %d\n", err); + SNDMSG("invalid poll descriptors %d", err); return -EIO; } + if (timeout == SND_PCM_WAIT_IO) + timeout = __snd_pcm_wait_io_timeout(pcm); + else if (timeout == SND_PCM_WAIT_DRAIN) + timeout = __snd_pcm_wait_drain_timeout(pcm); + else if (timeout < -1) + SNDMSG("invalid snd_pcm_wait timeout argument %d", timeout); do { __snd_pcm_unlock(pcm->fast_op_arg); err_poll = poll(pfd, npfds, timeout); __snd_pcm_lock(pcm->fast_op_arg); if (err_poll < 0) { - if (errno == EINTR && !PCMINABORT(pcm)) + if (errno == EINTR && !PCMINABORT(pcm) && !(pcm->mode & SND_PCM_EINTR)) continue; return -errno; } @@ -3399,12 +3495,12 @@ int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_ /** * \brief Copy one or more areas - * \param dst_areas destination areas specification (one for each channel) + * \param dst_channels destination areas specification (one for each channel) * \param dst_offset offset in frames inside destination area * \param dst_size size in frames of the destination buffer - * \param src_areas source areas specification (one for each channel) + * \param src_channels source areas specification (one for each channel) * \param src_offset offset in frames inside source area - * \param dst_size size in frames of the source buffer + * \param src_size size in frames of the source buffer * \param channels channels count * \param frames frames to copy * \param format PCM sample format @@ -3705,6 +3801,29 @@ int snd_pcm_hw_params_can_disable_period_wakeup(const snd_pcm_hw_params_t *param return !!(params->info & SNDRV_PCM_INFO_NO_PERIOD_WAKEUP); } +/** + * \brief Check if hardware is capable of perfect drain + * \param params Configuration space + * \retval 0 Hardware doesn't do perfect drain + * \retval 1 Hardware does perfect drain + * + * This function should only be called when the configuration space + * contains a single configuration. Call #snd_pcm_hw_params to choose + * a single configuration from the configuration space. + * + * Perfect drain means that the hardware does not use samples + * beyond the stream application pointer. + */ +int snd_pcm_hw_params_is_perfect_drain(const snd_pcm_hw_params_t *params) +{ + assert(params); + if (CHECK_SANITY(params->info == ~0U)) { + SNDMSG("invalid PCM info field"); + return 0; /* FIXME: should be a negative error? */ + } + return !!(params->info & SNDRV_PCM_INFO_PERFECT_DRAIN); +} + /** * \brief Check if hardware supports audio wallclock timestamps * \param params Configuration space @@ -3785,7 +3904,16 @@ int snd_pcm_hw_params_get_rate_numden(const snd_pcm_hw_params_t *params, /** * \brief Get sample resolution info from a configuration space * \param params Configuration space - * \return signification bits in sample otherwise a negative error code if the info is not available + * \return sample resolution (in bits) otherwise a negative error code if the info is not available + * + * For linear formats, this function returns sample resolution - + * used bits starting from the first usable significant bit defined by + * the format (e.g. bit 31 for S32_LE format or bit 23 for S24_LE format - + * starting from bit zero). Application may use full sample bit range defined + * by the format, but additional bits (outside this sample resolution) are + * stripped (not processed). + * + * For non-linear formats, this value may have a special meaning which may be defined in future. * * This function should only be called when the configuration space * contains a single configuration. Call #snd_pcm_hw_params to choose @@ -3827,6 +3955,11 @@ int snd_pcm_hw_params_get_fifo_size(const snd_pcm_hw_params_t *params) * * The configuration space will be filled with all possible ranges * for the PCM device. + * + * Note that the configuration space may be constrained by the + * currently installed configuration on the PCM device. To remove + * any constrains, free the configuration with #snd_pcm_hw_free + * first. */ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { @@ -4393,7 +4526,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_get_subformat)(const snd_pcm_hw_par int snd_pcm_hw_params_get_subformat(const snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** @@ -4433,7 +4566,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_first)(snd_pcm_t *pcm int snd_pcm_hw_params_set_subformat_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** @@ -4449,7 +4582,7 @@ EXPORT_SYMBOL int INTERNAL(snd_pcm_hw_params_set_subformat_last)(snd_pcm_t *pcm, int snd_pcm_hw_params_set_subformat_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat) #endif { - return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, subformat, NULL); + return snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, (unsigned int *)subformat, NULL); } /** @@ -4933,6 +5066,43 @@ int snd_pcm_hw_params_get_period_wakeup(snd_pcm_t *pcm, snd_pcm_hw_params_t *par return 0; } +/** + * \brief Restrict a configuration space to fill the end of playback stream with silence when drain() is invoked + * \param pcm PCM handle + * \param params Configuration space + * \param val 0 = disabled, 1 = enabled (default) fill the end of the playback stream with silence when drain() is invoked + * \return Zero on success, otherwise a negative error code. + * + * When disabled, the application should handle the end of stream gracefully + * (fill the silent samples to align to the period size plus some extra + * samples for hardware / driver without perfect drain). Note that the rewind + * may be used for this purpose or the sw_params silencing mechanism. + */ +int snd_pcm_hw_params_set_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val) +{ + assert(pcm && params); + if (val) + params->flags &= ~SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE; + else + params->flags |= SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE; + params->rmask = ~0; + return snd_pcm_hw_refine(pcm, params); +} + +/** + * \brief Extract drain with the filling of silence samples from a configuration space + * \param pcm PCM handle + * \param params Configuration space + * \param val 0 = disabled, 1 = enabled + * \return 0 otherwise a negative error code + */ +int snd_pcm_hw_params_get_drain_silence(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val) +{ + assert(pcm && params && val); + *val = params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE ? 0 : 1; + return 0; +} + /** * \brief Extract period time from a configuration space * \param params Configuration space @@ -6165,6 +6335,25 @@ int snd_pcm_hw_params_get_min_align(const snd_pcm_hw_params_t *params, snd_pcm_u return 0; } +#ifndef DOXYGEN +void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) +{ + params->proto = SNDRV_PCM_VERSION; + params->tstamp_mode = pcm->tstamp_mode; + params->tstamp_type = pcm->tstamp_type; + params->period_step = pcm->period_step; + params->sleep_min = 0; + params->avail_min = pcm->avail_min; + sw_set_period_event(params, pcm->period_event); + params->xfer_align = 1; + params->start_threshold = pcm->start_threshold; + params->stop_threshold = pcm->stop_threshold; + params->silence_threshold = pcm->silence_threshold; + params->silence_size = pcm->silence_size; + params->boundary = pcm->boundary; +} +#endif + /** * \brief Return current software configuration for a PCM * \param pcm PCM handle @@ -6181,19 +6370,7 @@ int snd_pcm_sw_params_current(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) return -EIO; } __snd_pcm_lock(pcm); /* forced lock due to pcm field changes */ - params->proto = SNDRV_PCM_VERSION; - params->tstamp_mode = pcm->tstamp_mode; - params->tstamp_type = pcm->tstamp_type; - params->period_step = pcm->period_step; - params->sleep_min = 0; - params->avail_min = pcm->avail_min; - sw_set_period_event(params, pcm->period_event); - params->xfer_align = 1; - params->start_threshold = pcm->start_threshold; - params->stop_threshold = pcm->stop_threshold; - params->silence_threshold = pcm->silence_threshold; - params->silence_size = pcm->silence_size; - params->boundary = pcm->boundary; + snd_pcm_sw_params_current_no_lock(pcm, params); __snd_pcm_unlock(pcm); return 0; } @@ -6292,7 +6469,7 @@ int snd_pcm_sw_params_set_start_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params params->start_threshold = pcm->boundary; break; default: - SNDMSG("invalid start mode value %d\n", val); + SNDMSG("invalid start mode value %d", val); return -EINVAL; } return 0; @@ -6340,7 +6517,7 @@ int snd_pcm_sw_params_set_xrun_mode(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, params->stop_threshold = pcm->boundary; break; default: - SNDMSG("invalid xrun mode value %d\n", val); + SNDMSG("invalid xrun mode value %d", val); return -EINVAL; } return 0; @@ -6728,6 +6905,10 @@ int snd_pcm_sw_params_get_silence_threshold(const snd_pcm_sw_params_t *params, s * underrun is nearer than silence threshold (see * #snd_pcm_sw_params_set_silence_threshold) * + * When drain silence (see #snd_pcm_hw_params_get_drain_silence) is disabled, + * this will also apply for draining, i.e. silence is written also when the + * drain end is nearer than the silence threshold. + * * The special case is when silence size value is equal or greater than * boundary. The unused portion of the ring buffer (initial written samples * are untouched) is filled with silence at start. Later, only just processed @@ -6915,7 +7096,7 @@ void snd_pcm_status_get_driver_htstamp(const snd_pcm_status_t *obj, snd_htimesta /** * \brief Get audio_tstamp_report from a PCM status container * \param obj pointer to #snd_pcm_status_t - * \param ptr Pointer to returned report (valid fields are accuracy and type) + * \param audio_tstamp_report Pointer to returned report */ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, snd_pcm_audio_tstamp_report_t *audio_tstamp_report) @@ -6929,7 +7110,7 @@ void snd_pcm_status_get_audio_htstamp_report(const snd_pcm_status_t *obj, /** * \brief set audio_tstamp_config from a PCM status container * \param obj pointer to #snd_pcm_status_t - * \param ptr Pointer to config (valid fields are type and report_analog_delay) + * \param audio_tstamp_config Pointer to config (valid fields are type_requested and report_delay) */ void snd_pcm_status_set_audio_htstamp_config(snd_pcm_status_t *obj, snd_pcm_audio_tstamp_config_t *audio_tstamp_config) @@ -7324,7 +7505,7 @@ int __snd_pcm_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, _skip: \endcode * - * Look to the \ref example_test_pcm "Sine-wave generator" example + * Look to the \link example_test_pcm Sine-wave generator \endlink example * for more details about the generate_sine function. * * The function is thread-safe when built with the proper option. @@ -7456,7 +7637,7 @@ snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_ goto _end; } - err = __snd_pcm_wait_in_lock(pcm, -1); + err = __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_IO); if (err < 0) break; goto _again; @@ -7525,7 +7706,7 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area goto _end; } - err = snd_pcm_wait_nocheck(pcm, -1); + err = snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_IO); if (err < 0) break; goto _again; @@ -7554,7 +7735,8 @@ snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area /* some plugins might automatically start the stream */ state = __snd_pcm_state(pcm); if (state == SND_PCM_STATE_PREPARED && - hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) { + hw_avail >= 0 && + (snd_pcm_uframes_t) hw_avail >= pcm->start_threshold) { err = __snd_pcm_start(pcm); if (err < 0) goto _end; diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index ed065318..efd41451 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -56,11 +56,10 @@ IMA compatibility project proceedings, Vol 2, Issue 2, May 1992. come across a good description of XA yet. */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index 540ba25f..715b04c7 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -26,8 +26,8 @@ * */ -#include "bswap.h" #include "pcm_local.h" +#include "bswap.h" #include "pcm_plugin.h" #include "plugin_ops.h" diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c index 4c099acd..1bf745d2 100644 --- a/src/pcm/pcm_copy.c +++ b/src/pcm/pcm_copy.c @@ -26,9 +26,9 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_direct.c b/src/pcm/pcm_direct.c index 90417b2f..e53e5923 100644 --- a/src/pcm/pcm_direct.c +++ b/src/pcm/pcm_direct.c @@ -19,6 +19,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -44,12 +45,16 @@ * */ +#if !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__ANDROID__) union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ +#if defined(__linux__) struct seminfo *__buf; /* Buffer for IPC_INFO (Linux specific) */ +#endif }; +#endif /* * FIXME: @@ -560,8 +565,11 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix) return 0; } +#define RECOVERIES_FLAG_SUSPENDED (1U << 31) +#define RECOVERIES_MASK ((1U << 31) - 1) + /* - * Recover slave on XRUN. + * Recover slave on XRUN or SUSPENDED. * Even if direct plugins disable xrun detection, there might be an xrun * raised directly by some drivers. * The first client recovers slave pcm. @@ -569,6 +577,8 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix) */ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) { + unsigned int recoveries; + int state; int ret; int semerr; @@ -579,7 +589,8 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) return semerr; } - if (snd_pcm_state(direct->spcm) != SND_PCM_STATE_XRUN) { + state = snd_pcm_state(direct->spcm); + if (state != SND_PCM_STATE_XRUN && state != SND_PCM_STATE_SUSPENDED) { /* ignore... someone else already did recovery */ semerr = snd_pcm_direct_semaphore_up(direct, DIRECT_IPC_SEM_CLIENT); @@ -590,6 +601,24 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) return 0; } + recoveries = direct->shmptr->s.recoveries; + recoveries = (recoveries + 1) & RECOVERIES_MASK; + if (state == SND_PCM_STATE_SUSPENDED) + recoveries |= RECOVERIES_FLAG_SUSPENDED; + direct->shmptr->s.recoveries = recoveries; + + /* some buggy drivers require the device resumed before prepared; + * when a device has RESUME flag and is in SUSPENDED state, resume + * here but immediately drop to bring it to a sane active state. + */ + if (state == SND_PCM_STATE_SUSPENDED && + (direct->spcm->info & SND_PCM_INFO_RESUME)) { + snd_pcm_resume(direct->spcm); + snd_pcm_drop(direct->spcm); + snd_pcm_direct_timer_stop(direct); + snd_pcm_direct_clear_timer_queue(direct); + } + ret = snd_pcm_prepare(direct->spcm); if (ret < 0) { SNDERR("recover: unable to prepare slave"); @@ -621,7 +650,6 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) } return ret; } - direct->shmptr->s.recoveries++; semerr = snd_pcm_direct_semaphore_up(direct, DIRECT_IPC_SEM_CLIENT); if (semerr < 0) { @@ -632,25 +660,49 @@ int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct) } /* - * enter xrun state, if slave xrun occurred - * @return: 0 - no xrun >0: xrun happened + * enter xrun or suspended state, if slave xrun occurred or suspended + * @return: 0 for no xrun/suspend or a negative error code for xrun/suspend */ -int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm) +int snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm) { + int err; + + switch (snd_pcm_state(direct->spcm)) { + case SND_PCM_STATE_DISCONNECTED: + direct->state = SNDRV_PCM_STATE_DISCONNECTED; + return -ENODEV; + case SND_PCM_STATE_XRUN: + case SND_PCM_STATE_SUSPENDED: + if ((err = snd_pcm_direct_slave_recover(direct)) < 0) + return err; + break; + default: + break; + } + + if (direct->state == SND_PCM_STATE_XRUN) + return -EPIPE; + else if (direct->state == SND_PCM_STATE_SUSPENDED) + return -ESTRPIPE; if (direct->shmptr->s.recoveries != direct->recoveries) { /* no matter how many xruns we missed - * so don't increment but just update to actual counter */ direct->recoveries = direct->shmptr->s.recoveries; - pcm->fast_ops->drop(pcm); + pcm->fast_ops->drop(pcm->fast_op_arg); /* trigger_tstamp update is missing in drop callbacks */ gettimestamp(&direct->trigger_tstamp, pcm->tstamp_type); /* no timer clear: * if slave already entered xrun again the event is lost. * snd_pcm_direct_clear_timer_queue(direct); */ - direct->state = SND_PCM_STATE_XRUN; - return 1; + if (direct->recoveries & RECOVERIES_FLAG_SUSPENDED) { + direct->state = SND_PCM_STATE_SUSPENDED; + return -ESTRPIPE; + } else { + direct->state = SND_PCM_STATE_XRUN; + return -EPIPE; + } } return 0; } @@ -718,19 +770,11 @@ timer_changed: } empty = avail < pcm->avail_min; } - switch (snd_pcm_state(dmix->spcm)) { - case SND_PCM_STATE_XRUN: - /* recover slave and update client state to xrun - * before returning POLLERR - */ - snd_pcm_direct_slave_recover(dmix); - snd_pcm_direct_client_chk_xrun(dmix, pcm); - /* fallthrough */ - case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_SETUP: + + if (snd_pcm_direct_check_xrun(dmix, pcm) < 0 || + snd_pcm_state(dmix->spcm) == SND_PCM_STATE_SETUP) { events |= POLLERR; - break; - default: + } else { if (empty) { /* here we have a race condition: * if period event arrived after the avail_update call @@ -754,7 +798,6 @@ timer_changed: break; } } - break; } *revents = events; return 0; @@ -1028,7 +1071,34 @@ int snd_pcm_direct_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED) snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; - return snd_pcm_query_chmaps(dmix->spcm); + snd_pcm_chmap_query_t **smaps, **maps; + unsigned int i, j; + + if (dmix->bindings == NULL) + return snd_pcm_query_chmaps(dmix->spcm); + + maps = calloc(2, sizeof(*maps)); + if (!maps) + return NULL; + maps[0] = calloc(dmix->channels + 2, sizeof(int *)); + if (!maps[0]) { + free(maps); + return NULL; + } + smaps = snd_pcm_query_chmaps(dmix->spcm); + if (smaps == NULL) { + snd_pcm_free_chmaps(maps); + return NULL; + } + maps[0]->type = SND_CHMAP_TYPE_FIXED; + maps[0]->map.channels = dmix->channels; + for (i = 0; i < dmix->channels; i++) { + j = dmix->bindings[i]; + if (j == UINT_MAX || smaps[0]->map.channels < j) + continue; + maps[0]->map.pos[i] = smaps[0]->map.pos[j]; + } + return maps; } snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm) @@ -1073,27 +1143,10 @@ int snd_pcm_direct_prepare(snd_pcm_t *pcm) int snd_pcm_direct_resume(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; - snd_pcm_t *spcm = dmix->spcm; + int err; - snd_pcm_direct_semaphore_down(dmix, DIRECT_IPC_SEM_CLIENT); - /* some buggy drivers require the device resumed before prepared; - * when a device has RESUME flag and is in SUSPENDED state, resume - * here but immediately drop to bring it to a sane active state. - */ - if ((spcm->info & SND_PCM_INFO_RESUME) && - snd_pcm_state(spcm) == SND_PCM_STATE_SUSPENDED) { - snd_pcm_resume(spcm); - snd_pcm_drop(spcm); - snd_pcm_direct_timer_stop(dmix); - snd_pcm_direct_clear_timer_queue(dmix); - snd_pcm_areas_silence(snd_pcm_mmap_areas(spcm), 0, - spcm->channels, spcm->buffer_size, - spcm->format); - snd_pcm_prepare(spcm); - snd_pcm_start(spcm); - } - snd_pcm_direct_semaphore_up(dmix, DIRECT_IPC_SEM_CLIENT); - return -ENOSYS; + err = snd_pcm_direct_slave_recover(dmix); + return err < 0 ? err : -ENOSYS; } #define COPY_SLAVE(field) (dmix->shmptr->s.field = spcm->field) @@ -1689,7 +1742,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix, continue; err = safe_strtol(id, &cchannel); if (err < 0 || cchannel < 0) { - SNDERR("invalid client channel in binding: %s\n", id); + SNDERR("invalid client channel in binding: %s", id); return -EINVAL; } if ((unsigned)cchannel >= count) @@ -1714,7 +1767,7 @@ int snd_pcm_direct_parse_bindings(snd_pcm_direct_t *dmix, continue; safe_strtol(id, &cchannel); if (snd_config_get_integer(n, &schannel) < 0) { - SNDERR("unable to get slave channel (should be integer type) in binding: %s\n", id); + SNDERR("unable to get slave channel (should be integer type) in binding: %s", id); free(bindings); return -EINVAL; } @@ -1818,11 +1871,11 @@ static int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root, if (strcmp(id, "type") == 0) { err = snd_config_get_string(n, &str); if (err < 0) { - SNDERR("Invalid value for PCM type definition\n"); + SNDERR("Invalid value for PCM type definition"); return -EINVAL; } if (strcmp(str, "hw")) { - SNDERR("Invalid type '%s' for slave PCM\n", str); + SNDERR("Invalid type '%s' for slave PCM", str); return -EINVAL; } continue; @@ -1936,7 +1989,7 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf, SNDERR("Invalid type for %s", id); return -EINVAL; } - if (strcmp(str, "no") == 0) + if (strcmp(str, "no") == 0 || strcmp(str, "off") == 0) rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_NO; else if (strcmp(str, "roundup") == 0) rec->hw_ptr_alignment = SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP; @@ -2072,22 +2125,22 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf, return 0; } -void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix) +void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix, + snd_pcm_uframes_t hw_ptr) { - + dmix->slave_appl_ptr = dmix->slave_hw_ptr = hw_ptr; if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDUP || - (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && - pcm->buffer_size <= pcm->period_size * 2)) + (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && + pcm->buffer_size <= pcm->period_size * 2)) dmix->slave_appl_ptr = ((dmix->slave_appl_ptr + dmix->slave_period_size - 1) / - dmix->slave_period_size) * dmix->slave_period_size; + dmix->slave_period_size) * dmix->slave_period_size; else if (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_ROUNDDOWN || - (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && - (dmix->slave_period_size * SEC_TO_MS) / - pcm->rate < LOW_LATENCY_PERIOD_TIME)) + (dmix->hw_ptr_alignment == SND_PCM_HW_PTR_ALIGNMENT_AUTO && + ((dmix->slave_period_size * SEC_TO_MS) / pcm->rate) < LOW_LATENCY_PERIOD_TIME)) dmix->slave_appl_ptr = dmix->slave_hw_ptr = ((dmix->slave_hw_ptr / dmix->slave_period_size) * - dmix->slave_period_size); + dmix->slave_period_size); } int _snd_pcm_direct_new(snd_pcm_t **pcmp, snd_pcm_direct_t **_dmix, int type, diff --git a/src/pcm/pcm_direct.h b/src/pcm/pcm_direct.h index fb013a66..e7d89e5f 100644 --- a/src/pcm/pcm_direct.h +++ b/src/pcm/pcm_direct.h @@ -224,6 +224,8 @@ struct snd_pcm_direct { snd1_pcm_direct_nonblock #define snd_pcm_direct_async \ snd1_pcm_direct_async +#define snd_pcm_direct_poll_descriptors \ + snd1_pcm_direct_poll_descriptors #define snd_pcm_direct_poll_revents \ snd1_pcm_direct_poll_revents #define snd_pcm_direct_info \ @@ -264,6 +266,10 @@ struct snd_pcm_direct { snd1_pcm_direct_set_chmap #define snd_pcm_direct_reset_slave_ptr \ snd1_pcm_direct_reset_slave_ptr +#define snd_pcm_direct_check_xrun \ + snd1_pcm_direct_check_xrun +#define snd_pcm_direct_slave_recover \ + snd1_pcm_direct_slave_recover int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix); @@ -345,10 +351,10 @@ snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm); snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm); int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); int snd_pcm_direct_slave_recover(snd_pcm_direct_t *direct); -int snd_pcm_direct_client_chk_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm); +int snd_pcm_direct_check_xrun(snd_pcm_direct_t *direct, snd_pcm_t *pcm); int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid); struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm); -void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix); +void snd_pcm_direct_reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix, snd_pcm_uframes_t hw_ptr); struct snd_pcm_direct_open_conf { key_t ipc_key; diff --git a/src/pcm/pcm_dmix.c b/src/pcm/pcm_dmix.c index 94dbb1e0..55cae3e7 100644 --- a/src/pcm/pcm_dmix.c +++ b/src/pcm/pcm_dmix.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -424,25 +425,17 @@ static int snd_pcm_dmix_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; + snd_pcm_uframes_t slave_hw_ptr; int err; - switch (snd_pcm_state(dmix->spcm)) { - case SND_PCM_STATE_DISCONNECTED: - dmix->state = SND_PCM_STATE_DISCONNECTED; - return -ENODEV; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dmix)) < 0) - return err; - break; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dmix, pcm)) - return -EPIPE; if (dmix->slowptr) snd_pcm_hwsync(dmix->spcm); + slave_hw_ptr = *dmix->spcm->hw.ptr; + err = snd_pcm_direct_check_xrun(dmix, pcm); + if (err < 0) + return err; - return snd_pcm_dmix_sync_ptr0(pcm, *dmix->spcm->hw.ptr); + return snd_pcm_dmix_sync_ptr0(pcm, slave_hw_ptr); } /* @@ -452,22 +445,8 @@ static int snd_pcm_dmix_sync_ptr(snd_pcm_t *pcm) static snd_pcm_state_t snd_pcm_dmix_state(snd_pcm_t *pcm) { snd_pcm_direct_t *dmix = pcm->private_data; - int err; - snd_pcm_state_t state; - state = snd_pcm_state(dmix->spcm); - switch (state) { - case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_DISCONNECTED: - dmix->state = state; - return state; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dmix)) < 0) - return err; - break; - default: - break; - } - snd_pcm_direct_client_chk_xrun(dmix, pcm); + + snd_pcm_direct_check_xrun(dmix, pcm); if (dmix->state == STATE_RUN_PENDING) return SNDRV_PCM_STATE_RUNNING; return dmix->state; @@ -553,8 +532,7 @@ static int snd_pcm_dmix_reset(snd_pcm_t *pcm) snd_pcm_direct_t *dmix = pcm->private_data; dmix->hw_ptr %= pcm->period_size; dmix->appl_ptr = dmix->last_appl_ptr = dmix->hw_ptr; - dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dmix); + snd_pcm_direct_reset_slave_ptr(pcm, dmix, *dmix->spcm->hw.ptr); return 0; } @@ -563,8 +541,7 @@ static int snd_pcm_dmix_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dmix) int err; snd_pcm_hwsync(dmix->spcm); - dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dmix); + snd_pcm_direct_reset_slave_ptr(pcm, dmix, *dmix->spcm->hw.ptr); err = snd_timer_start(dmix->timer); if (err < 0) return err; @@ -647,7 +624,7 @@ static int __snd_pcm_dmix_drain(snd_pcm_t *pcm) if (dmix->state == SND_PCM_STATE_DRAINING) { snd_pcm_dmix_sync_area(pcm); if ((pcm->mode & SND_PCM_NONBLOCK) == 0) { - snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN); snd_pcm_direct_clear_timer_queue(dmix); /* force poll to wait */ } @@ -830,18 +807,9 @@ static snd_pcm_sframes_t snd_pcm_dmix_mmap_commit(snd_pcm_t *pcm, snd_pcm_direct_t *dmix = pcm->private_data; int err; - switch (snd_pcm_state(dmix->spcm)) { - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dmix)) < 0) - return err; - break; - case SND_PCM_STATE_SUSPENDED: - return -ESTRPIPE; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dmix, pcm)) - return -EPIPE; + err = snd_pcm_direct_check_xrun(dmix, pcm); + if (err < 0) + return err; if (! size) return 0; snd_pcm_mmap_appl_forward(pcm, size); @@ -1173,7 +1141,7 @@ pcm.name { ipc_perm INT # IPC permissions (octal, default 0600) hw_ptr_alignment STR # Slave application and hw pointer alignment type # STR can be one of the below strings : - # no + # no (or off) # roundup # rounddown # auto (default) diff --git a/src/pcm/pcm_dmix_generic.c b/src/pcm/pcm_dmix_generic.c index 8a5b6f14..701c66c9 100644 --- a/src/pcm/pcm_dmix_generic.c +++ b/src/pcm/pcm_dmix_generic.c @@ -330,7 +330,7 @@ static void generic_mix_areas_32_swap(unsigned int size, register signed int sample; for (;;) { - sample = bswap_32(*src) >> 8; + sample = (signed int) bswap_32(*src) >> 8; if (! *dst) { *sum = sample; *dst = *src; @@ -364,7 +364,7 @@ static void generic_remix_areas_32_swap(unsigned int size, register signed int sample; for (;;) { - sample = bswap_32(*src) >> 8; + sample = (signed int) bswap_32(*src) >> 8; if (! *dst) { *sum = -sample; *dst = bswap_32(-sample); diff --git a/src/pcm/pcm_dmix_i386.c b/src/pcm/pcm_dmix_i386.c index 82a91c5c..ea55d8ec 100644 --- a/src/pcm/pcm_dmix_i386.c +++ b/src/pcm/pcm_dmix_i386.c @@ -104,8 +104,7 @@ static void mix_select_callbacks(snd_pcm_direct_t *dmix) /* try to determine the capabilities of the CPU */ in = fopen("/proc/cpuinfo", "r"); if (in) { - while (!feof(in)) { - fgets(line, sizeof(line), in); + while (!feof(in) && (fgets(line, sizeof(line), in) != NULL)) { if (!strncmp(line, "processor", 9)) smp++; else if (!strncmp(line, "flags", 5)) { diff --git a/src/pcm/pcm_dmix_x86_64.c b/src/pcm/pcm_dmix_x86_64.c index 4d882bfd..1c80e181 100644 --- a/src/pcm/pcm_dmix_x86_64.c +++ b/src/pcm/pcm_dmix_x86_64.c @@ -87,8 +87,7 @@ static void mix_select_callbacks(snd_pcm_direct_t *dmix) /* try to determine, if we have SMP */ in = fopen("/proc/cpuinfo", "r"); if (in) { - while (!feof(in)) { - fgets(line, sizeof(line), in); + while (!feof(in) && (fgets(line, sizeof(line), in) != NULL)) { if (!strncmp(line, "processor", 9)) smp++; } diff --git a/src/pcm/pcm_dshare.c b/src/pcm/pcm_dshare.c index 01814dc8..c0329098 100644 --- a/src/pcm/pcm_dshare.c +++ b/src/pcm/pcm_dshare.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -199,25 +200,17 @@ static int snd_pcm_dshare_sync_ptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_p static int snd_pcm_dshare_sync_ptr(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; + snd_pcm_uframes_t slave_hw_ptr; int err; - switch (snd_pcm_state(dshare->spcm)) { - case SND_PCM_STATE_DISCONNECTED: - dshare->state = SNDRV_PCM_STATE_DISCONNECTED; - return -ENODEV; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dshare)) < 0) - return err; - break; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dshare, pcm)) - return -EPIPE; if (dshare->slowptr) snd_pcm_hwsync(dshare->spcm); + slave_hw_ptr = *dshare->spcm->hw.ptr; + err = snd_pcm_direct_check_xrun(dshare, pcm); + if (err < 0) + return err; - return snd_pcm_dshare_sync_ptr0(pcm, *dshare->spcm->hw.ptr); + return snd_pcm_dshare_sync_ptr0(pcm, slave_hw_ptr); } /* @@ -237,7 +230,7 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) 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->delay = snd_pcm_mmap_playback_delay(pcm); break; default: break; @@ -255,22 +248,8 @@ static int snd_pcm_dshare_status(snd_pcm_t *pcm, snd_pcm_status_t * status) static snd_pcm_state_t snd_pcm_dshare_state(snd_pcm_t *pcm) { snd_pcm_direct_t *dshare = pcm->private_data; - int err; - snd_pcm_state_t state; - state = snd_pcm_state(dshare->spcm); - switch (state) { - case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_DISCONNECTED: - dshare->state = state; - return state; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dshare)) < 0) - return err; - break; - default: - break; - } - snd_pcm_direct_client_chk_xrun(dshare, pcm); + + snd_pcm_direct_check_xrun(dshare, pcm); if (dshare->state == STATE_RUN_PENDING) return SNDRV_PCM_STATE_RUNNING; return dshare->state; @@ -327,8 +306,7 @@ static int snd_pcm_dshare_reset(snd_pcm_t *pcm) snd_pcm_direct_t *dshare = pcm->private_data; dshare->hw_ptr %= pcm->period_size; dshare->appl_ptr = dshare->last_appl_ptr = dshare->hw_ptr; - dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dshare); + snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr); return 0; } @@ -337,8 +315,7 @@ static int snd_pcm_dshare_start_timer(snd_pcm_t *pcm, snd_pcm_direct_t *dshare) int err; snd_pcm_hwsync(dshare->spcm); - dshare->slave_appl_ptr = dshare->slave_hw_ptr = *dshare->spcm->hw.ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dshare); + snd_pcm_direct_reset_slave_ptr(pcm, dshare, *dshare->spcm->hw.ptr); err = snd_timer_start(dshare->timer); if (err < 0) return err; @@ -424,7 +401,7 @@ static int __snd_pcm_dshare_drain(snd_pcm_t *pcm) } if (dshare->state == SND_PCM_STATE_DRAINING) { snd_pcm_dshare_sync_area(pcm); - snd_pcm_wait_nocheck(pcm, -1); + snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN); snd_pcm_direct_clear_timer_queue(dshare); /* force poll to wait */ switch (snd_pcm_state(dshare->spcm)) { @@ -529,18 +506,9 @@ static snd_pcm_sframes_t snd_pcm_dshare_mmap_commit(snd_pcm_t *pcm, snd_pcm_direct_t *dshare = pcm->private_data; int err; - switch (snd_pcm_state(dshare->spcm)) { - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dshare)) < 0) - return err; - break; - case SND_PCM_STATE_SUSPENDED: - return -ESTRPIPE; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dshare, pcm)) - return -EPIPE; + err = snd_pcm_direct_check_xrun(dshare, pcm); + if (err < 0) + return err; if (! size) return 0; snd_pcm_mmap_appl_forward(pcm, size); @@ -877,7 +845,7 @@ pcm.name { ipc_perm INT # IPC permissions (octal, default 0600) hw_ptr_alignment STR # Slave application and hw pointer alignment type # STR can be one of the below strings : - # no + # no (or off) # roundup # rounddown # auto (default) diff --git a/src/pcm/pcm_dsnoop.c b/src/pcm/pcm_dsnoop.c index 3f28df99..bf67c68a 100644 --- a/src/pcm/pcm_dsnoop.c +++ b/src/pcm/pcm_dsnoop.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -134,24 +135,14 @@ static int snd_pcm_dsnoop_sync_ptr(snd_pcm_t *pcm) snd_pcm_sframes_t diff; int err; - switch (snd_pcm_state(dsnoop->spcm)) { - case SND_PCM_STATE_DISCONNECTED: - dsnoop->state = SNDRV_PCM_STATE_DISCONNECTED; - return -ENODEV; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0) - return err; - break; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm)) - return -EPIPE; if (dsnoop->slowptr) snd_pcm_hwsync(dsnoop->spcm); old_slave_hw_ptr = dsnoop->slave_hw_ptr; snoop_timestamp(pcm); slave_hw_ptr = dsnoop->slave_hw_ptr; + err = snd_pcm_direct_check_xrun(dsnoop, pcm); + if (err < 0) + return err; diff = pcm_frame_diff(slave_hw_ptr, old_slave_hw_ptr, dsnoop->slave_boundary); if (diff == 0) /* fast path */ return 0; @@ -206,22 +197,8 @@ static int snd_pcm_dsnoop_status(snd_pcm_t *pcm, snd_pcm_status_t * status) static snd_pcm_state_t snd_pcm_dsnoop_state(snd_pcm_t *pcm) { snd_pcm_direct_t *dsnoop = pcm->private_data; - int err; - snd_pcm_state_t state; - state = snd_pcm_state(dsnoop->spcm); - switch (state) { - case SND_PCM_STATE_SUSPENDED: - case SND_PCM_STATE_DISCONNECTED: - dsnoop->state = state; - return state; - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0) - return err; - break; - default: - break; - } - snd_pcm_direct_client_chk_xrun(dsnoop, pcm); + + snd_pcm_direct_check_xrun(dsnoop, pcm); return dsnoop->state; } @@ -239,7 +216,7 @@ static int snd_pcm_dsnoop_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp) /* Fall through */ case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_SUSPENDED: - *delayp = snd_pcm_mmap_capture_avail(pcm); + *delayp = snd_pcm_mmap_capture_delay(pcm); return 0; case SNDRV_PCM_STATE_XRUN: return -EPIPE; @@ -275,8 +252,7 @@ static int snd_pcm_dsnoop_reset(snd_pcm_t *pcm) snd_pcm_direct_t *dsnoop = pcm->private_data; dsnoop->hw_ptr %= pcm->period_size; dsnoop->appl_ptr = dsnoop->hw_ptr; - dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dsnoop); + snd_pcm_direct_reset_slave_ptr(pcm, dsnoop, dsnoop->slave_hw_ptr); return 0; } @@ -289,8 +265,7 @@ static int snd_pcm_dsnoop_start(snd_pcm_t *pcm) return -EBADFD; snd_pcm_hwsync(dsnoop->spcm); snoop_timestamp(pcm); - dsnoop->slave_appl_ptr = dsnoop->slave_hw_ptr; - snd_pcm_direct_reset_slave_ptr(pcm, dsnoop); + snd_pcm_direct_reset_slave_ptr(pcm, dsnoop, dsnoop->slave_hw_ptr); err = snd_timer_start(dsnoop->timer); if (err < 0) return err; @@ -327,7 +302,7 @@ static int __snd_pcm_dsnoop_drain(snd_pcm_t *pcm) break; if (pcm->mode & SND_PCM_NONBLOCK) return -EAGAIN; - __snd_pcm_wait_in_lock(pcm, -1); + __snd_pcm_wait_in_lock(pcm, SND_PCM_WAIT_DRAIN); } pcm->stop_threshold = stop_threshold; return snd_pcm_dsnoop_drop(pcm); @@ -420,18 +395,9 @@ static snd_pcm_sframes_t snd_pcm_dsnoop_mmap_commit(snd_pcm_t *pcm, snd_pcm_direct_t *dsnoop = pcm->private_data; int err; - switch (snd_pcm_state(dsnoop->spcm)) { - case SND_PCM_STATE_XRUN: - if ((err = snd_pcm_direct_slave_recover(dsnoop)) < 0) - return err; - break; - case SND_PCM_STATE_SUSPENDED: - return -ESTRPIPE; - default: - break; - } - if (snd_pcm_direct_client_chk_xrun(dsnoop, pcm)) - return -EPIPE; + err = snd_pcm_direct_check_xrun(dsnoop, pcm); + if (err < 0) + return err; if (dsnoop->state == SND_PCM_STATE_RUNNING) { err = snd_pcm_dsnoop_sync_ptr(pcm); if (err < 0) @@ -733,7 +699,7 @@ pcm.name { ipc_perm INT # IPC permissions (octal, default 0600) hw_ptr_alignment STR # Slave application and hw pointer alignment type # STR can be one of the below strings : - # no + # no (or off) # roundup # rounddown # auto (default) diff --git a/src/pcm/pcm_extplug.c b/src/pcm/pcm_extplug.c index 99455d9c..feb32b99 100644 --- a/src/pcm/pcm_extplug.c +++ b/src/pcm/pcm_extplug.c @@ -650,8 +650,8 @@ parameter linked #snd_pcm_extplug_set_param_link() can be used for the corresponding parameter. For example if the extplug does not support channel nor format conversion the supported client parameters can be limited with snd_pcm_extplug_set_param_*() and afterwards -#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and -#snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be +snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_FORMAT, 1) and +snd_pcm_extplug_set_param_link(ext, SND_PCM_EXTPLUG_HW_CHANNELS, 1) should be called to keep the client and slave parameters the same. */ @@ -690,7 +690,7 @@ int snd_pcm_extplug_create(snd_pcm_extplug_t *extplug, const char *name, /* We support 1.0.0 to current */ if (extplug->version < 0x010000 || extplug->version > SND_PCM_EXTPLUG_VERSION) { - SNDERR("extplug: Plugin version mismatch: 0x%x\n", + SNDERR("extplug: Plugin version mismatch: 0x%x", extplug->version); return -ENXIO; } diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c index 7709a554..90b3f3f5 100644 --- a/src/pcm/pcm_file.c +++ b/src/pcm/pcm_file.c @@ -26,11 +26,11 @@ * */ +#include "pcm_local.h" +#include "pcm_plugin.h" #include "bswap.h" #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_generic.c b/src/pcm/pcm_generic.c index 4a10c6b4..8177adc7 100644 --- a/src/pcm/pcm_generic.c +++ b/src/pcm/pcm_generic.c @@ -26,10 +26,10 @@ * */ -#include -#include #include "pcm_local.h" #include "pcm_generic.h" +#include +#include #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index b3f9d157..f3fbcb6e 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -27,6 +27,9 @@ * */ +#include "pcm_local.h" +#include "../control/control_local.h" +#include "../timer/timer_local.h" #include #include #include @@ -37,9 +40,6 @@ #include #include #include -#include "pcm_local.h" -#include "../control/control_local.h" -#include "../timer/timer_local.h" //#define DEBUG_RW /* use to debug readi/writei/readn/writen */ //#define DEBUG_MMAP /* debug mmap_commit */ @@ -98,14 +98,21 @@ typedef struct { bool mmap_control_fallbacked; struct snd_pcm_sync_ptr *sync_ptr; + bool prepare_reset_sw_params; + bool perfect_drain; + int period_event; snd_timer_t *period_timer; struct pollfd period_timer_pfd; int period_timer_need_poll; /* restricted parameters */ snd_pcm_format_t format; - int rate; + struct { + int min; + int max; + } rates; int channels; + int drain_silence; /* for chmap */ unsigned int chmap_caps; snd_pcm_chmap_query_t **chmap_override; @@ -320,6 +327,9 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) SYSMSG("SNDRV_PCM_IOCTL_INFO failed (%i)", err); return err; } + /* may be configurable (optional) */ + if (__snd_pcm_info_eld_fixup_check(info)) + return __snd_pcm_info_eld_fixup(info); return 0; } @@ -347,9 +357,9 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) if (err < 0) return err; } - if (hw->rate > 0) { + if (hw->rates.min > 0) { err = _snd_pcm_hw_param_set_minmax(params, SND_PCM_HW_PARAM_RATE, - hw->rate, 0, hw->rate + 1, -1); + hw->rates.min, 0, hw->rates.max + 1, -1); if (err < 0) return err; } @@ -369,12 +379,28 @@ static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) return 0; } -static inline int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) +#define hw_param_mask(params,var) \ + &((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK]) + +static int hw_params_call(snd_pcm_hw_t *pcm_hw, snd_pcm_hw_params_t *params) { + int err; + /* check for new hw_params structure; it's available from 2.0.2 version of PCM API */ if (SNDRV_PROTOCOL_VERSION(2, 0, 2) <= pcm_hw->version) - return ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); - return use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + err = ioctl(pcm_hw->fd, SNDRV_PCM_IOCTL_HW_PARAMS, params); + else + err = use_old_hw_params_ioctl(pcm_hw->fd, SND_PCM_IOCTL_HW_PARAMS_OLD, params); + if (err >= 0 && pcm_hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 17) && params->msbits > 0) { + snd_mask_t *m = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + int width = snd_pcm_format_width(format); + if (width > 0 && params->msbits > (unsigned int)width) + params->msbits = width; + } + } + return err; } static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) @@ -389,6 +415,8 @@ static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) params->info &= ~0xf0000000; if (pcm->tstamp_type != SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY) params->info |= SND_PCM_INFO_MONOTONIC; + hw->perfect_drain = !!(params->info & SND_PCM_INFO_PERFECT_DRAIN) || + !!(params->flags & SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE); return query_status_data(hw); } @@ -528,13 +556,14 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); goto out; } + hw->prepare_reset_sw_params = false; if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) { if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) { int on = (snd_pcm_tstamp_type_t) params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC; if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { err = -errno; - SNDMSG("TSTAMP failed\n"); + SNDMSG("TSTAMP failed"); goto out; } } @@ -654,7 +683,18 @@ static int snd_pcm_hw_hwsync(snd_pcm_t *pcm) static int snd_pcm_hw_prepare(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_sw_params_t sw_params; int fd = hw->fd, err; + + if (hw->prepare_reset_sw_params) { + snd_pcm_sw_params_current_no_lock(pcm, &sw_params); + if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); + return err; + } + hw->prepare_reset_sw_params = false; + } if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) { err = -errno; SYSMSG("SNDRV_PCM_IOCTL_PREPARE failed (%i)", err); @@ -712,7 +752,52 @@ static int snd_pcm_hw_drop(snd_pcm_t *pcm) static int snd_pcm_hw_drain(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_sw_params_t sw_params; + snd_pcm_uframes_t silence_size; int err; + + if (pcm->stream != SND_PCM_STREAM_PLAYBACK) + goto __skip_silence; + /* stream probably in SETUP, prevent divide by zero */ + if (pcm->period_size == 0) + goto __skip_silence; + if (hw->drain_silence == 0 || hw->perfect_drain) + goto __skip_silence; + snd_pcm_sw_params_current_no_lock(pcm, &sw_params); + if (hw->drain_silence > 0) { + silence_size = (pcm->rate * hw->drain_silence) / 1000; + goto __manual_silence; + } + /* compute end silence size, align to period size + extra time */ + if ((pcm->boundary % pcm->period_size) == 0) { + silence_size = pcm->period_size - (*pcm->appl.ptr % pcm->period_size); + if (silence_size == pcm->period_size) + silence_size = 0; + } else { + /* it not not easy to compute the period crossing point + * in this case because the period is not aligned to the boundary + * - use the full range (one period) in this case + */ + silence_size = pcm->period_size; + } + silence_size += pcm->rate / 10; /* 1/10th of second */ +__manual_silence: + if (sw_params.silence_size < silence_size) { + /* fill the silence soon as possible (in the bellow ioctl + * or the next period wake up) + */ + sw_params.silence_threshold = pcm->buffer_size; + if (silence_size > pcm->buffer_size) + silence_size = pcm->buffer_size; + sw_params.silence_size = silence_size; + if (ioctl(hw->fd, SNDRV_PCM_IOCTL_SW_PARAMS, &sw_params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err); + return err; + } + hw->prepare_reset_sw_params = true; + } +__skip_silence: if (ioctl(hw->fd, SNDRV_PCM_IOCTL_DRAIN) < 0) { err = -errno; SYSMSG("SNDRV_PCM_IOCTL_DRAIN failed (%i)", err); @@ -832,7 +917,7 @@ static int snd_pcm_hw_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) { if (pcm2->type != SND_PCM_TYPE_HW) { if (pcm2->fast_ops->link_slaves) - return pcm2->fast_ops->link_slaves(pcm2, pcm1); + return pcm2->fast_ops->link_slaves(pcm2->fast_op_arg, pcm1); return -ENOSYS; } return hw_link(pcm1, pcm2); @@ -1087,7 +1172,7 @@ static int snd_pcm_hw_close(snd_pcm_t *pcm) int err = 0; if (close(hw->fd)) { err = -errno; - SYSMSG("close failed (%i)\n", err); + SYSMSG("close failed (%i)", err); } unmap_status_and_control_data(hw); @@ -1210,7 +1295,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, ret = snd_ctl_hw_open(&ctl, NULL, card, 0); if (ret < 0) { - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); return NULL; } @@ -1218,7 +1303,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, ret = snd_ctl_elem_tlv_read(ctl, &id, tlv, sizeof(tlv)); snd_ctl_close(ctl); if (ret < 0) { - SYSMSG("Cannot read Channel Map TLV\n"); + SYSMSG("Cannot read Channel Map TLV"); return NULL; } @@ -1232,7 +1317,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, type = tlv[SNDRV_CTL_TLVO_TYPE]; if (type != SND_CTL_TLVT_CONTAINER) { if (!is_chmap_type(type)) { - SYSMSG("Invalid TLV type %d\n", type); + SYSMSG("Invalid TLV type %d", type); return NULL; } start = tlv; @@ -1245,7 +1330,7 @@ snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev, nums = 0; for (p = start; size > 0; ) { if (!is_chmap_type(p[0])) { - SYSMSG("Invalid TLV type %d\n", p[0]); + SYSMSG("Invalid TLV type %d", p[0]); return NULL; } nums++; @@ -1336,7 +1421,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) case SNDRV_PCM_STATE_SUSPENDED: break; default: - SYSMSG("Invalid PCM state for chmap_get: %s\n", + SYSMSG("Invalid PCM state for chmap_get: %s", snd_pcm_state_name(FAST_PCM_STATE(hw))); return NULL; } @@ -1347,7 +1432,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); if (ret < 0) { free(map); - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); chmap_caps_set_error(hw, CHMAP_CTL_GET); return NULL; } @@ -1357,7 +1442,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) snd_ctl_close(ctl); if (ret < 0) { free(map); - SYSMSG("Cannot read Channel Map ctl\n"); + SYSMSG("Cannot read Channel Map ctl"); chmap_caps_set_error(hw, CHMAP_CTL_GET); return NULL; } @@ -1383,17 +1468,17 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) return -ENXIO; if (map->channels > 128) { - SYSMSG("Invalid number of channels %d\n", map->channels); + SYSMSG("Invalid number of channels %d", map->channels); return -EINVAL; } if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) { - SYSMSG("Invalid PCM state for chmap_set: %s\n", + SYSMSG("Invalid PCM state for chmap_set: %s", snd_pcm_state_name(FAST_PCM_STATE(hw))); return -EBADFD; } ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); if (ret < 0) { - SYSMSG("Cannot open the associated CTL\n"); + SYSMSG("Cannot open the associated CTL"); chmap_caps_set_error(hw, CHMAP_CTL_SET); return ret; } @@ -1411,7 +1496,7 @@ static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) ret = -ENXIO; } if (ret < 0) - SYSMSG("Cannot write Channel Map ctl\n"); + SYSMSG("Cannot write Channel Map ctl"); return ret; } @@ -1575,7 +1660,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, unsigned int user_ver = SNDRV_PCM_VERSION; if (ioctl(fd, SNDRV_PCM_IOCTL_USER_PVERSION, &user_ver) < 0) { ret = -errno; - SNDMSG("USER_PVERSION failed\n"); + SNDMSG("USER_PVERSION failed"); return ret; } } @@ -1587,7 +1672,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, int on = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC; if (ioctl(fd, SNDRV_PCM_IOCTL_TTSTAMP, &on) < 0) { ret = -errno; - SNDMSG("TTSTAMP failed\n"); + SNDMSG("TTSTAMP failed"); return ret; } tstamp_type = SND_PCM_TSTAMP_TYPE_MONOTONIC; @@ -1598,7 +1683,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, int on = 1; if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) { ret = -errno; - SNDMSG("TSTAMP failed\n"); + SNDMSG("TSTAMP failed"); return ret; } } @@ -1616,7 +1701,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd, hw->fd = fd; /* no restriction */ hw->format = SND_PCM_FORMAT_UNKNOWN; - hw->rate = 0; + hw->rates.min = hw->rates.max = 0; hw->channels = 0; ret = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, info.stream, mode); @@ -1763,7 +1848,9 @@ pcm.name { [format STR] # Restrict only to the given format [channels INT] # Restrict only to the given channels [rate INT] # Restrict only to the given rate + or [rate [INT INT]] # Restrict only to the given rate range (min max) [chmap MAP] # Override channel maps; MAP is a string array + [drain_silence INT] # Add silence in drain (-1 = auto /default/, 0 = off, > 0 milliseconds) } \endcode @@ -1796,7 +1883,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, long card = -1, device = 0, subdevice = -1; const char *str; int err, sync_ptr_ioctl = 0; - int rate = 0, channels = 0; + int min_rate = 0, max_rate = 0, channels = 0, drain_silence = -1; snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; snd_config_t *n; int nonblock = 1; /* non-block per default */ @@ -1854,13 +1941,58 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, continue; } if (strcmp(id, "rate") == 0) { + long val; + if (snd_config_get_type(n) == SND_CONFIG_TYPE_COMPOUND && + snd_config_is_array(n)) { + snd_config_t *m; + err = snd_config_search(n, "0", &m); + if (err < 0) { + SNDERR("array expected for rate compound"); + goto fail; + } + err = snd_config_get_integer(m, &val); + if (err < 0) { + SNDERR("Invalid type for rate.0"); + goto fail; + } + min_rate = max_rate = val; + err = snd_config_search(n, "1", &m); + if (err >= 0) { + err = snd_config_get_integer(m, &val); + if (err < 0) { + SNDERR("Invalid type for rate.0"); + goto fail; + } + max_rate = val; + } + } else { + err = snd_config_get_integer(n, &val); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto fail; + } + min_rate = max_rate = val; + } + continue; + } + if (strcmp(id, "min_rate") == 0) { long val; err = snd_config_get_integer(n, &val); if (err < 0) { SNDERR("Invalid type for %s", id); goto fail; } - rate = val; + min_rate = val; + continue; + } + if (strcmp(id, "max_rate") == 0) { + long val; + err = snd_config_get_integer(n, &val); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto fail; + } + max_rate = val; continue; } if (strcmp(id, "format") == 0) { @@ -1892,6 +2024,16 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, } continue; } + if (strcmp(id, "drain_silence") == 0) { + long val; + err = snd_config_get_integer(n, &val); + if (err < 0) { + SNDERR("Invalid type for %s", id); + goto fail; + } + drain_silence = val; + continue; + } SNDERR("Unknown field %s", id); err = -EINVAL; goto fail; @@ -1901,6 +2043,11 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, err = -EINVAL; goto fail; } + if ((min_rate < 0) || (max_rate < min_rate)) { + SNDERR("min_rate - max_rate configuration invalid"); + err = -EINVAL; + goto fail; + } err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode | (nonblock ? SND_PCM_NONBLOCK : 0), 0, sync_ptr_ioctl); @@ -1923,10 +2070,13 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, hw->format = format; if (channels > 0) hw->channels = channels; - if (rate > 0) - hw->rate = rate; + if (min_rate > 0) { + hw->rates.min = min_rate; + hw->rates.max = max_rate; + } if (chmap) hw->chmap_override = chmap; + hw->drain_silence = drain_silence; return 0; diff --git a/src/pcm/pcm_iec958.c b/src/pcm/pcm_iec958.c index a11a0439..7b8459fb 100644 --- a/src/pcm/pcm_iec958.c +++ b/src/pcm/pcm_iec958.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c index 98184398..df2c7f81 100644 --- a/src/pcm/pcm_ioplug.c +++ b/src/pcm/pcm_ioplug.c @@ -522,7 +522,7 @@ static int ioplug_drain_via_poll(snd_pcm_t *pcm) /* in non-blocking mode, let application to poll() by itself */ if (io->data->nonblock) return -EAGAIN; - if (snd_pcm_wait_nocheck(pcm, -1) < 0) + if (snd_pcm_wait_nocheck(pcm, SND_PCM_WAIT_DRAIN) < 0) break; } @@ -1086,7 +1086,7 @@ int snd_pcm_ioplug_create(snd_pcm_ioplug_t *ioplug, const char *name, /* We support 1.0.0 to current */ if (ioplug->version < 0x010000 || ioplug->version > SND_PCM_IOPLUG_VERSION) { - SNDERR("ioplug: Plugin version mismatch: 0x%x\n", + SNDERR("ioplug: Plugin version mismatch: 0x%x", ioplug->version); return -ENXIO; } diff --git a/src/pcm/pcm_ladspa.c b/src/pcm/pcm_ladspa.c index ad73347d..25eac76f 100644 --- a/src/pcm/pcm_ladspa.c +++ b/src/pcm/pcm_ladspa.c @@ -32,11 +32,11 @@ * http://www.medianet.ag */ +#include "pcm_local.h" +#include "pcm_plugin.h" #include #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" #include "ladspa.h" @@ -1147,7 +1147,7 @@ static int snd_pcm_ladspa_check_dir(snd_pcm_ladspa_plugin_t * const plugin, const unsigned long ladspa_id) { DIR *dir; - struct dirent * dirent; + struct dirent64 * dirent; int len = strlen(path), err; int need_slash; char *filename; @@ -1161,7 +1161,7 @@ static int snd_pcm_ladspa_check_dir(snd_pcm_ladspa_plugin_t * const plugin, return -ENOENT; while (1) { - dirent = readdir(dir); + dirent = readdir64(dir); if (!dirent) { closedir(dir); return 0; @@ -1210,7 +1210,7 @@ static int snd_pcm_ladspa_look_for_plugin(snd_pcm_ladspa_plugin_t * const plugin return err; err = snd_pcm_ladspa_check_dir(plugin, fullpath, label, ladspa_id); free(fullpath); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; if (err > 0) return 0; diff --git a/src/pcm/pcm_lfloat.c b/src/pcm/pcm_lfloat.c index f32a82a7..d9aa136d 100644 --- a/src/pcm/pcm_lfloat.c +++ b/src/pcm/pcm_lfloat.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef DOC_HIDDEN diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index 33e7fc45..81edccaa 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 6f03365c..152c92c3 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -20,6 +20,11 @@ * */ +#ifndef __PCM_LOCAL_H +#define __PCM_LOCAL_H + +#include "config.h" + #include #include #include @@ -46,12 +51,9 @@ #include "mask.h" #define SND_PCM_HW_PARAM_ACCESS SNDRV_PCM_HW_PARAM_ACCESS -#define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK #define SND_PCM_HW_PARAM_FORMAT SNDRV_PCM_HW_PARAM_FORMAT #define SND_PCM_HW_PARAM_SUBFORMAT SNDRV_PCM_HW_PARAM_SUBFORMAT -#define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK #define SND_PCM_HW_PARAM_SAMPLE_BITS SNDRV_PCM_HW_PARAM_SAMPLE_BITS -#define SND_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_FIRST_INTERVAL #define SND_PCM_HW_PARAM_FRAME_BITS SNDRV_PCM_HW_PARAM_FRAME_BITS #define SND_PCM_HW_PARAM_CHANNELS SNDRV_PCM_HW_PARAM_CHANNELS #define SND_PCM_HW_PARAM_RATE SNDRV_PCM_HW_PARAM_RATE @@ -63,7 +65,6 @@ #define SND_PCM_HW_PARAM_BUFFER_SIZE SNDRV_PCM_HW_PARAM_BUFFER_SIZE #define SND_PCM_HW_PARAM_BUFFER_BYTES SNDRV_PCM_HW_PARAM_BUFFER_BYTES #define SND_PCM_HW_PARAM_TICK_TIME SNDRV_PCM_HW_PARAM_TICK_TIME -#define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL #define SND_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_LAST_MASK #define SND_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_FIRST_MASK #define SND_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_LAST_INTERVAL @@ -77,6 +78,8 @@ #define SND_PCM_INFO_DOUBLE SNDRV_PCM_INFO_DOUBLE /** device transfers samples in batch */ #define SND_PCM_INFO_BATCH SNDRV_PCM_INFO_BATCH +/** device does perfect drain (silencing not required) */ +#define SND_PCM_INFO_PERFECT_DRAIN SNDRV_PCM_INFO_PERFECT_DRAIN /** device accepts interleaved samples */ #define SND_PCM_INFO_INTERLEAVED SNDRV_PCM_INFO_INTERLEAVED /** device accepts non-interleaved samples */ @@ -103,6 +106,7 @@ #define SND_PCM_HW_PARAMS_NORESAMPLE SNDRV_PCM_HW_PARAMS_NORESAMPLE #define SND_PCM_HW_PARAMS_EXPORT_BUFFER SNDRV_PCM_HW_PARAMS_EXPORT_BUFFER #define SND_PCM_HW_PARAMS_NO_PERIOD_WAKEUP SNDRV_PCM_HW_PARAMS_NO_PERIOD_WAKEUP +#define SND_PCM_HW_PARAMS_NO_DRAIN_SILENCE SNDRV_PCM_HW_PARAMS_NO_DRAIN_SILENCE #define SND_PCM_INFO_MONOTONIC 0x80000000 @@ -364,6 +368,8 @@ struct _snd_pcm { snd1_pcm_hw_param_get_max #define snd_pcm_hw_param_name \ snd1_pcm_hw_param_name +#define snd_pcm_sw_params_current_no_lock \ + snd1_pcm_sw_params_current_no_lock int snd_pcm_new(snd_pcm_t **pcmp, snd_pcm_type_t type, const char *name, snd_pcm_stream_t stream, int mode); @@ -388,6 +394,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); +void snd_pcm_sw_params_current_no_lock(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); + snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); snd_pcm_sframes_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size); snd_pcm_sframes_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size); @@ -1142,7 +1150,7 @@ static inline int snd_pcm_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes if (avail >= pcm->avail_min) return 0; if (pcm->fast_ops->may_wait_for_avail_min) - return pcm->fast_ops->may_wait_for_avail_min(pcm, avail); + return pcm->fast_ops->may_wait_for_avail_min(pcm->fast_op_arg, avail); return 1; } @@ -1218,3 +1226,5 @@ static inline void snd_pcm_unlock(snd_pcm_t *pcm) #define snd_pcm_lock(pcm) do {} while (0) #define snd_pcm_unlock(pcm) do {} while (0) #endif /* THREAD_SAFE_API */ + +#endif /* __PCM_LOCAL_H */ diff --git a/src/pcm/pcm_meter.c b/src/pcm/pcm_meter.c index 2dcf8e45..68c369de 100644 --- a/src/pcm/pcm_meter.c +++ b/src/pcm/pcm_meter.c @@ -27,16 +27,18 @@ */ +#include "pcm_local.h" +#include "pcm_plugin.h" #include "bswap.h" #include #include #include -#include "pcm_local.h" -#include "pcm_plugin.h" +#ifndef DOC_HIDDEN #define atomic_read(ptr) __atomic_load_n(ptr, __ATOMIC_SEQ_CST ) #define atomic_add(ptr, n) __atomic_add_fetch(ptr, n, __ATOMIC_SEQ_CST) #define atomic_dec(ptr) __atomic_sub_fetch(ptr, 1, __ATOMIC_SEQ_CST) +#endif #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index ce8d8189..3cff4326 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -19,12 +19,12 @@ * */ +#include "pcm_local.h" +#include "bswap.h" #include #include #include #include -#include "bswap.h" -#include "pcm_local.h" /** @@ -203,9 +203,9 @@ int snd_pcm_format_cpu_endian(snd_pcm_format_t format) } /** - * \brief Return nominal bits per a PCM sample + * \brief Return the bit-width of the format * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_width(snd_pcm_format_t format) { @@ -270,9 +270,9 @@ int snd_pcm_format_width(snd_pcm_format_t format) } /** - * \brief Return bits needed to store a PCM sample + * \brief Return the physical bit-width of the format (bits needed to store a PCM sample) * \param format Sample format - * \return bits per sample, a negative error code if not applicable + * \return the physical bit-width of the format, or a negative error code if not applicable */ int snd_pcm_format_physical_width(snd_pcm_format_t format) { diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index 9cbaae05..0b62978e 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -18,16 +18,17 @@ * */ -#include "config.h" +#include "pcm_local.h" #include +#if HAVE_MALLOC_H #include +#endif #include #include #include #ifdef HAVE_SYS_SHM_H #include #endif -#include "pcm_local.h" void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) { diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index 73b0c3bc..177a61bb 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 7fb21276..74e1e3f1 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -26,13 +26,13 @@ * */ +#include "pcm_local.h" +#include "pcm_generic.h" #include #include #include #include #include -#include "pcm_local.h" -#include "pcm_generic.h" #ifndef PIC /* entry for static linking */ @@ -388,11 +388,21 @@ static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) return 0; } +static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm); static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status) { snd_pcm_multi_t *multi = pcm->private_data; snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm; - return snd_pcm_status(slave, status); + + int err = snd_pcm_status(slave, status); + if (err < 0) + return err; + snd_pcm_sframes_t avail = snd_pcm_multi_avail_update(pcm); + if (avail < 0) + return avail; + status->hw_ptr = *pcm->hw.ptr; + status->avail = avail; + return 0; } static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm) @@ -749,8 +759,9 @@ static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master) static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) { snd_pcm_multi_t *multi = pcm1->private_data; - if (multi->slaves[0].pcm->fast_ops->link) - return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2); + snd_pcm_t *main_pcm = multi->slaves[0].pcm; + if (main_pcm->fast_ops->link) + return main_pcm->fast_ops->link(main_pcm->fast_op_arg, pcm2); return -ENOSYS; } @@ -1292,7 +1303,7 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name, ++slaves_count; } if (master_slave < 0 || master_slave >= (long)slaves_count) { - SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1); + SNDERR("Master slave is out of range (0-%u)", slaves_count-1); return -EINVAL; } snd_config_for_each(i, inext, bindings) { diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c index c8ea9b38..f7b096bc 100644 --- a/src/pcm/pcm_null.c +++ b/src/pcm/pcm_null.c @@ -26,10 +26,10 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" +#include "bswap.h" +#include #ifndef PIC /* entry for static linking */ diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c index ceb3b1aa..05bfe3b2 100644 --- a/src/pcm/pcm_params.c +++ b/src/pcm/pcm_params.c @@ -2008,7 +2008,10 @@ static const snd_mask_t refine_masks[SND_PCM_HW_PARAM_LAST_MASK - SND_PCM_HW_PAR }, [SND_PCM_HW_PARAM_SUBFORMAT - SND_PCM_HW_PARAM_FIRST_MASK] = { .bits = { - PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) + PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_MAX) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_20) | + PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_24), }, }, }; @@ -2072,6 +2075,7 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t { unsigned int k; snd_interval_t *i; + snd_mask_t *m; unsigned int rstamps[RULES]; unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1]; unsigned int stamp = 2; @@ -2156,6 +2160,11 @@ int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS); if (snd_interval_single(i)) params->msbits = snd_interval_value(i); + m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); + if (snd_mask_single(m)) { + snd_pcm_format_t format = snd_mask_min(m); + params->msbits = snd_pcm_format_width(format); + } } if (!params->rate_den) { @@ -2335,6 +2344,9 @@ static int snd_pcm_sw_params_default(snd_pcm_t *pcm, snd_pcm_sw_params_t *params params->silence_threshold = 0; params->silence_size = 0; params->boundary = pcm->buffer_size; + /* this should not happen (bad child?) */ + if (params->boundary == 0) + return -EINVAL; while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size) params->boundary *= 2; return 0; @@ -2407,6 +2419,8 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) INTERNAL(snd_pcm_hw_params_get_subformat)(params, &pcm->subformat); INTERNAL(snd_pcm_hw_params_get_channels)(params, &pcm->channels); INTERNAL(snd_pcm_hw_params_get_rate)(params, &pcm->rate, 0); + snd_interval_copy(&pcm->periods, ¶ms->intervals[SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL]); + snd_interval_copy(&pcm->buffer_time, ¶ms->intervals[SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL]); INTERNAL(snd_pcm_hw_params_get_period_time)(params, &pcm->period_time, 0); INTERNAL(snd_pcm_hw_params_get_period_size)(params, &pcm->period_size, 0); INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &pcm->buffer_size); @@ -2429,7 +2443,9 @@ int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) /* Default sw params */ memset(&sw, 0, sizeof(sw)); - snd_pcm_sw_params_default(pcm, &sw); + err = snd_pcm_sw_params_default(pcm, &sw); + if (err < 0) + return err; err = snd_pcm_sw_params(pcm, &sw); if (err < 0) return err; diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index abf6f1ab..bd681a9f 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -186,7 +186,8 @@ static const snd_pcm_format_t linear_preferred_formats[] = { #if defined(BUILD_PCM_PLUGIN_MULAW) || \ defined(BUILD_PCM_PLUGIN_ALAW) || \ - defined(BUILD_PCM_PLUGIN_ADPCM) + defined(BUILD_PCM_PLUGIN_ADPCM) || \ + defined(BUILD_PCM_PLUGIN_IEC958) #define BUILD_PCM_NONLINEAR #endif @@ -201,6 +202,10 @@ static const snd_pcm_format_t nonlinear_preferred_formats[] = { #ifdef BUILD_PCM_PLUGIN_ADPCM SND_PCM_FORMAT_IMA_ADPCM, #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + SND_PCM_FORMAT_IEC958_SUBFRAME_LE, + SND_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif }; #endif @@ -490,6 +495,18 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm } #endif +#ifdef BUILD_PCM_PLUGIN_IEC958 +static int iec958_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, snd_pcm_t *slave, + int close_slave) +{ + unsigned char preamble_vals[3] = { + 0x08, 0x02, 0x04 /* Z, X, Y */ + }; + return snd_pcm_iec958_open(pcmp, name, sformat, slave, close_slave, NULL, preamble_vals, 0); +} +#endif + static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private_data; @@ -526,6 +543,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: #ifdef BUILD_PCM_PLUGIN_LFLOAT @@ -566,6 +589,12 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_p case SND_PCM_FORMAT_IMA_ADPCM: f = snd_pcm_adpcm_open; break; +#endif +#ifdef BUILD_PCM_PLUGIN_IEC958 + case SND_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SND_PCM_FORMAT_IEC958_SUBFRAME_BE: + f = iec958_open; + break; #endif default: return -EINVAL; @@ -1065,6 +1094,18 @@ static int snd_pcm_plug_hw_free(snd_pcm_t *pcm) return err; } +static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) +{ + snd_pcm_plug_t *plug = pcm->private_data; + snd_pcm_t *slave = plug->gen.slave; + int err = snd_pcm_sw_params(slave, params); + if (err >= 0) { + pcm->fast_ops = slave->fast_ops; + pcm->fast_op_arg = slave->fast_op_arg; + } + return err; +} + static void snd_pcm_plug_dump(snd_pcm_t *pcm, snd_output_t *out) { snd_pcm_plug_t *plug = pcm->private_data; @@ -1078,7 +1119,7 @@ static const snd_pcm_ops_t snd_pcm_plug_ops = { .hw_refine = snd_pcm_plug_hw_refine, .hw_params = snd_pcm_plug_hw_params, .hw_free = snd_pcm_plug_hw_free, - .sw_params = snd_pcm_generic_sw_params, + .sw_params = snd_pcm_plug_sw_params, .channel_info = snd_pcm_generic_channel_info, .dump = snd_pcm_plug_dump, .nonblock = snd_pcm_generic_nonblock, diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c index 24c9941d..9d7e233e 100644 --- a/src/pcm/pcm_plugin.c +++ b/src/pcm/pcm_plugin.c @@ -82,9 +82,9 @@ pcm.rate44100Hz { */ -#include #include "pcm_local.h" #include "pcm_plugin.h" +#include #ifndef DOC_HIDDEN @@ -574,8 +574,10 @@ static int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) return 0; } -int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, - snd_pcm_uframes_t avail) +int snd_pcm_plugin_may_wait_for_avail_min_conv( + snd_pcm_t *pcm, + snd_pcm_uframes_t avail, + snd_pcm_uframes_t (*conv)(snd_pcm_t *, snd_pcm_uframes_t)) { if (pcm->stream == SND_PCM_STREAM_CAPTURE && pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED && @@ -595,8 +597,12 @@ int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, * a) the slave can provide contineous hw_ptr between periods * b) avail_min does not match one slave_period */ - snd_pcm_plugin_t *plugin = pcm->private_data; - snd_pcm_t *slave = plugin->gen.slave; + snd_pcm_generic_t *generic = pcm->private_data; + /* + * do not use snd_pcm_plugin_t pointer here + * this code is used from the generic plugins, too + */ + snd_pcm_t *slave = generic->slave; snd_pcm_uframes_t needed_slave_avail_min; snd_pcm_sframes_t available; @@ -614,6 +620,14 @@ int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, return 0; needed_slave_avail_min = pcm->avail_min - available; + + /* proportional adaption if rate converter is in place.. + * Can happen only on built-in rate plugin. + * This code is also used by extplug, but extplug does not allow to alter the sampling rate. + */ + if (conv) + needed_slave_avail_min = conv(pcm, needed_slave_avail_min); + if (slave->avail_min != needed_slave_avail_min) { snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); @@ -636,6 +650,12 @@ int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, return snd_pcm_generic_may_wait_for_avail_min(pcm, avail); } +int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, + snd_pcm_uframes_t avail) +{ + return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail, NULL); +} + const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops = { .status = snd_pcm_plugin_status, .state = snd_pcm_generic_state, diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index d7788b2a..9896ee8e 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -50,6 +50,8 @@ typedef struct { /* make local functions really local */ #define snd_pcm_plugin_init \ snd1_pcm_plugin_init +#define snd_pcm_plugin_may_wait_for_avail_min_conv \ + snd1_pcm_plugin_may_wait_for_avail_min_conv #define snd_pcm_plugin_may_wait_for_avail_min \ snd1_pcm_plugin_may_wait_for_avail_min #define snd_pcm_plugin_fast_ops \ @@ -66,6 +68,8 @@ typedef struct { void snd_pcm_plugin_init(snd_pcm_plugin_t *plugin); snd_pcm_sframes_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames); snd_pcm_sframes_t snd_pcm_plugin_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames); +int snd_pcm_plugin_may_wait_for_avail_min_conv(snd_pcm_t *pcm, snd_pcm_uframes_t avail, + snd_pcm_uframes_t (*conv)(snd_pcm_t *, snd_pcm_uframes_t)); int snd_pcm_plugin_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail); extern const snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops; diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index c45895a9..ef6b8006 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -27,13 +27,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ -#include -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" #include "pcm_rate.h" - #include "plugin_ops.h" +#include "bswap.h" +#include #if 0 #define DEBUG_REFINE @@ -770,7 +769,7 @@ static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED, static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, snd_pcm_uframes_t appl_offset, - snd_pcm_uframes_t size, + snd_pcm_uframes_t size ATTRIBUTE_UNUSED, snd_pcm_uframes_t slave_size) { snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset; @@ -781,16 +780,25 @@ static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, snd_pcm_sframes_t result; areas = snd_pcm_mmap_areas(pcm); - if (cont >= size) { + /* + * Because snd_pcm_rate_write_areas1() below will convert a full source period + * then there had better be a full period available in the current buffer. + */ + if (cont >= pcm->period_size) { result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames); if (result < 0) return result; - if (slave_frames < slave_size) { + /* + * Because snd_pcm_rate_write_areas1() below will convert to a full slave period + * then there had better be a full slave period available in the slave buffer. + */ + if (slave_frames < rate->gen.slave->period_size) { snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0); goto __partial; } snd_pcm_rate_write_areas1(pcm, areas, appl_offset, slave_areas, slave_offset); + /* Only commit the requested slave_size, even if more was actually converted */ result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size); if (result < (snd_pcm_sframes_t)slave_size) { if (result < 0) @@ -807,7 +815,7 @@ static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate, pcm->format); snd_pcm_areas_copy(rate->pareas, cont, areas, 0, - pcm->channels, size - cont, + pcm->channels, pcm->period_size - cont, pcm->format); snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0); @@ -1009,7 +1017,7 @@ static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t app slave_size -= rate->gen.slave->period_size; rate->last_commit_ptr += pcm->period_size; if (rate->last_commit_ptr >= pcm->boundary) - rate->last_commit_ptr = 0; + rate->last_commit_ptr -= pcm->boundary; } return 0; } @@ -1137,7 +1145,7 @@ static int snd_pcm_rate_drain(snd_pcm_t *pcm) snd_pcm_uframes_t psize, spsize; int err; - err = __snd_pcm_wait_in_lock(rate->gen.slave, -1); + err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN); if (err < 0) break; if (size > pcm->period_size) { @@ -1154,7 +1162,7 @@ static int snd_pcm_rate_drain(snd_pcm_t *pcm) if (commit_err == 1) { rate->last_commit_ptr += psize; if (rate->last_commit_ptr >= pcm->boundary) - rate->last_commit_ptr = 0; + rate->last_commit_ptr -= pcm->boundary; } else if (commit_err == 0) { if (pcm->mode & SND_PCM_NONBLOCK) { commit_err = -EAGAIN; @@ -1273,6 +1281,32 @@ static int snd_pcm_rate_close(snd_pcm_t *pcm) return snd_pcm_generic_close(pcm); } +/** + * \brief Convert rate pcm frames to corresponding rate slave pcm frames + * \param pcm PCM handle + * \param frames Frames to be converted to slave frames + * \retval Corresponding slave frames +*/ +static snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + snd_pcm_uframes_t sframes; + snd_pcm_rate_t *rate = pcm->private_data; + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + sframes = rate->ops.output_frames(rate->obj, frames); + else + sframes = rate->ops.input_frames(rate->obj, frames); + + return sframes; +} + +static int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm, + snd_pcm_uframes_t avail) +{ + return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail, + snd_pcm_rate_slave_frames); +} + static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = { .status = snd_pcm_rate_status, .state = snd_pcm_rate_state, @@ -1299,7 +1333,7 @@ static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = { .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, .poll_descriptors = snd_pcm_generic_poll_descriptors, .poll_revents = snd_pcm_rate_poll_revents, - .may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min, + .may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min, }; static const snd_pcm_ops_t snd_pcm_rate_ops = { diff --git a/src/pcm/pcm_rate_linear.c b/src/pcm/pcm_rate_linear.c index 53ce902d..35a4d8ea 100644 --- a/src/pcm/pcm_rate_linear.c +++ b/src/pcm/pcm_rate_linear.c @@ -20,13 +20,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include "bswap.h" #include "pcm_local.h" #include "pcm_plugin.h" #include "pcm_rate.h" - #include "plugin_ops.h" +#include "bswap.h" +#include /* LINEAR_DIV needs to be large enough to handle resampling from 768000 -> 8000 */ diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index d3e5f3ff..affb929f 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -26,12 +26,11 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" - #include "plugin_ops.h" +#include "bswap.h" +#include #ifndef PIC /* entry for static linking */ @@ -766,7 +765,9 @@ static int strtochannel(const char *id, snd_pcm_chmap_t *chmap, } } +#ifndef DOC_HIDDEN #define MAX_CHMAP_CHANNELS 256 +#endif static int determine_chmap(snd_config_t *tt, snd_pcm_chmap_t **tt_chmap) { @@ -1150,6 +1151,11 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent snd_config_iterator_t i, inext; unsigned int k; int err; + + long *scha = alloca(tt_ssize * sizeof(long)); + if (scha == NULL) + return -ENOMEM; + for (k = 0; k < tt_csize * tt_ssize; ++k) ttable[k] = 0.0; snd_config_for_each(i, inext, tt) { @@ -1171,7 +1177,6 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent snd_config_t *jnode = snd_config_iterator_entry(j); double value; int ss; - long *scha = alloca(tt_ssize * sizeof(long)); const char *id; if (snd_config_get_id(jnode, &id) < 0) continue; @@ -1182,15 +1187,10 @@ static int _snd_pcm_route_load_ttable(snd_config_t *tt, snd_pcm_route_ttable_ent return -EINVAL; } - err = snd_config_get_real(jnode, &value); + err = snd_config_get_ireal(jnode, &value); if (err < 0) { - long v; - err = snd_config_get_integer(jnode, &v); - if (err < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - value = v; + SNDERR("Invalid type for %s", id); + return -EINVAL; } for (k = 0; (int) k < ss; k++) { diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 72509491..0699fc87 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -36,7 +37,6 @@ #include #include #include -#include "pcm_local.h" #ifndef PIC /* entry for static linking */ @@ -205,6 +205,7 @@ static snd_pcm_uframes_t _snd_pcm_share_missing(snd_pcm_t *pcm) snd_pcm_sframes_t hw_avail; snd_pcm_uframes_t missing = INT_MAX; snd_pcm_sframes_t ready_missing; + ssize_t s; // printf("state=%s hw_ptr=%ld appl_ptr=%ld slave appl_ptr=%ld safety=%ld silence=%ld\n", snd_pcm_state_name(share->state), slave->hw_ptr, share->appl_ptr, *slave->pcm->appl_ptr, slave->safety_threshold, slave->silence_frames); switch (share->state) { case SND_PCM_STATE_RUNNING: @@ -288,16 +289,24 @@ static snd_pcm_uframes_t _snd_pcm_share_missing(snd_pcm_t *pcm) update_poll: if (ready != share->ready) { char buf[1]; - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - if (ready) - read(share->slave_socket, buf, 1); - else - write(share->client_socket, buf, 1); - } else { - if (ready) - write(share->slave_socket, buf, 1); - else - read(share->client_socket, buf, 1); + while (1) { + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + if (ready) + s = read(share->slave_socket, buf, 1); + else + s = write(share->client_socket, buf, 1); + } else { + if (ready) + s = write(share->slave_socket, buf, 1); + else + s = read(share->client_socket, buf, 1); + } + if (s < 0) { + if (errno == EINTR) + continue; + return INT_MAX; + } + break; } share->ready = ready; } @@ -1185,7 +1194,7 @@ static int snd_pcm_share_drain(snd_pcm_t *pcm) _snd_pcm_share_update(pcm); Pthread_mutex_unlock(&slave->mutex); if (!(pcm->mode & SND_PCM_NONBLOCK)) - snd_pcm_wait(pcm, -1); + snd_pcm_wait(pcm, SND_PCM_WAIT_DRAIN); return 0; default: assert(0); diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index 26a27a57..d9596547 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -26,6 +26,7 @@ * */ +#include "pcm_local.h" #include #include #include @@ -61,7 +62,7 @@ static long snd_pcm_shm_action_fd0(snd_pcm_t *pcm, int *fd) { snd_pcm_shm_t *shm = pcm->private_data; int err; - char buf[1]; + char buf[1] = ""; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; err = write(shm->socket, buf, 1); @@ -113,7 +114,7 @@ static long snd_pcm_shm_action(snd_pcm_t *pcm) { snd_pcm_shm_t *shm = pcm->private_data; int err, result; - char buf[1]; + char buf[1] = ""; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; if (ctrl->hw.changed || ctrl->appl.changed) @@ -148,7 +149,7 @@ static long snd_pcm_shm_action_fd(snd_pcm_t *pcm, int *fd) { snd_pcm_shm_t *shm = pcm->private_data; int err; - char buf[1]; + char buf[1] = ""; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; if (ctrl->hw.changed || ctrl->appl.changed) @@ -495,7 +496,7 @@ static int snd_pcm_shm_drain(snd_pcm_t *pcm) if (err < 0) return err; if (!(pcm->mode & SND_PCM_NONBLOCK)) - snd_pcm_wait(pcm, -1); + snd_pcm_wait(pcm, SND_PCM_WAIT_DRAIN); return err; } diff --git a/src/pcm/pcm_softvol.c b/src/pcm/pcm_softvol.c index 99d0d32e..38c63679 100644 --- a/src/pcm/pcm_softvol.c +++ b/src/pcm/pcm_softvol.c @@ -26,11 +26,10 @@ * */ -#include "bswap.h" -#include #include "pcm_local.h" #include "pcm_plugin.h" - +#include "bswap.h" +#include #include #ifndef PIC @@ -114,7 +113,7 @@ static inline int MULTI_DIV_32x16(int a, unsigned short b) y.i = 0; #if __BYTE_ORDER == __LITTLE_ENDIAN x.i = (unsigned short)v.s[0]; - x.i *= b; + x.i *= (unsigned int)b; y.s[0] = x.s[1]; y.i += (int)v.s[1] * b; #else @@ -1190,7 +1189,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, continue; } if (strcmp(id, "min_dB") == 0) { - err = snd_config_get_real(n, &min_dB); + err = snd_config_get_ireal(n, &min_dB); if (err < 0) { SNDERR("Invalid min_dB value"); return err; @@ -1198,7 +1197,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name, continue; } if (strcmp(id, "max_dB") == 0) { - err = snd_config_get_real(n, &max_dB); + err = snd_config_get_ireal(n, &max_dB); if (err < 0) { SNDERR("Invalid max_dB value"); return err; diff --git a/src/pcm/pcm_symbols_list.c b/src/pcm/pcm_symbols_list.c index 389a4e6a..90efa3e1 100644 --- a/src/pcm/pcm_symbols_list.c +++ b/src/pcm/pcm_symbols_list.c @@ -1,23 +1,3 @@ -/* - * ALSA lib C file pcm_symbols_list.c - * Copyright (c) 2022 Huawei Device Co., Ltd. - * - * 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 - * - */ - &_snd_module_pcm_copy, &_snd_module_pcm_linear, &_snd_module_pcm_route, diff --git a/src/rawmidi/Makefile.am b/src/rawmidi/Makefile.am index 41858a1f..26918659 100644 --- a/src/rawmidi/Makefile.am +++ b/src/rawmidi/Makefile.am @@ -1,10 +1,11 @@ EXTRA_LTLIBRARIES=librawmidi.la -librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c +librawmidi_la_SOURCES = rawmidi.c rawmidi_hw.c rawmidi_symbols.c \ + ump.c if BUILD_SEQ librawmidi_la_SOURCES += rawmidi_virt.c endif -noinst_HEADERS = rawmidi_local.h +noinst_HEADERS = rawmidi_local.h ump_local.h all: librawmidi.la diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 2ff3f6cf..c4b45fa2 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -132,7 +132,7 @@ The timestamping is available only on input streams. The full featured examples with cross-links: \par Simple input/output test program -\ref example_test_rawmidi "example code" +\link example_test_rawmidi example code \endlink \par This example shows open and read/write rawmidi operations. @@ -141,14 +141,15 @@ This example shows open and read/write rawmidi operations. /** * \example ../test/rawmidi.c * \anchor example_test_rawmidi + * Shows open and read/write rawmidi operations. */ +#include "rawmidi_local.h" #include #include #include #include #include -#include "rawmidi_local.h" /** * \brief setup the default parameters @@ -1114,9 +1115,28 @@ ssize_t snd_rawmidi_tread(snd_rawmidi_t *rawmidi, struct timespec *tstamp, void 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) + 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); } + +#ifndef DOXYGEN +/* + * internal API functions for obtaining UMP info from rawmidi instance + */ +int _snd_rawmidi_ump_endpoint_info(snd_rawmidi_t *rmidi, void *info) +{ + if (!rmidi->ops->ump_endpoint_info) + return -ENXIO; + return rmidi->ops->ump_endpoint_info(rmidi, info); +} + +int _snd_rawmidi_ump_block_info(snd_rawmidi_t *rmidi, void *info) +{ + if (!rmidi->ops->ump_block_info) + return -ENXIO; + return rmidi->ops->ump_block_info(rmidi, info); +} +#endif /* DOXYGEN */ diff --git a/src/rawmidi/rawmidi_hw.c b/src/rawmidi/rawmidi_hw.c index e5bb3ee3..3b1d941e 100644 --- a/src/rawmidi/rawmidi_hw.c +++ b/src/rawmidi/rawmidi_hw.c @@ -20,6 +20,7 @@ * */ +#include "config.h" #include #include #include @@ -35,6 +36,7 @@ const char *_snd_module_rawmidi_hw = ""; #endif #define SNDRV_FILE_RAWMIDI ALSA_DEVICE_DIRECTORY "midiC%iD%i" +#define SNDRV_FILE_UMP_RAWMIDI ALSA_DEVICE_DIRECTORY "umpC%iD%i" #define SNDRV_RAWMIDI_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 0) #ifndef DOC_HIDDEN @@ -67,7 +69,7 @@ static int snd_rawmidi_hw_close(snd_rawmidi_t *rmidi) return 0; if (close(hw->fd)) { err = -errno; - SYSERR("close failed\n"); + SYSMSG("close failed"); } free(hw->buf); free(hw); @@ -80,7 +82,7 @@ static int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) long flags; if ((flags = fcntl(hw->fd, F_GETFL)) < 0) { - SYSERR("F_GETFL failed"); + SYSMSG("F_GETFL failed"); return -errno; } if (nonblock) @@ -88,7 +90,7 @@ static int snd_rawmidi_hw_nonblock(snd_rawmidi_t *rmidi, int nonblock) else flags &= ~O_NONBLOCK; if (fcntl(hw->fd, F_SETFL, flags) < 0) { - SYSERR("F_SETFL for O_NONBLOCK failed"); + SYSMSG("F_SETFL for O_NONBLOCK failed"); return -errno; } return 0; @@ -99,7 +101,7 @@ static int snd_rawmidi_hw_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info) snd_rawmidi_hw_t *hw = rmidi->private_data; info->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_INFO, info) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_INFO failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); return -errno; } return 0; @@ -111,7 +113,7 @@ static int snd_rawmidi_hw_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * pa int tstamp; params->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_PARAMS, params) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_PARAMS failed"); return -errno; } buf_reset(hw); @@ -143,7 +145,7 @@ static int snd_rawmidi_hw_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * st snd_rawmidi_hw_t *hw = rmidi->private_data; status->stream = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_STATUS, status) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_STATUS failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_STATUS failed"); return -errno; } return 0; @@ -154,7 +156,7 @@ static int snd_rawmidi_hw_drop(snd_rawmidi_t *rmidi) snd_rawmidi_hw_t *hw = rmidi->private_data; int str = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DROP, &str) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_DROP failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_DROP failed"); return -errno; } buf_reset(hw); @@ -166,7 +168,7 @@ static int snd_rawmidi_hw_drain(snd_rawmidi_t *rmidi) snd_rawmidi_hw_t *hw = rmidi->private_data; int str = rmidi->stream; if (ioctl(hw->fd, SNDRV_RAWMIDI_IOCTL_DRAIN, &str) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_DRAIN failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_DRAIN failed"); return -errno; } return 0; @@ -272,6 +274,28 @@ static ssize_t snd_rawmidi_hw_tread(snd_rawmidi_t *rmidi, struct timespec *tstam return ret + result; } +static int snd_rawmidi_hw_ump_endpoint_info(snd_rawmidi_t *rmidi, void *buf) +{ + snd_rawmidi_hw_t *hw = rmidi->private_data; + + if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) + return -ENXIO; + if (ioctl(hw->fd, SNDRV_UMP_IOCTL_ENDPOINT_INFO, buf) < 0) + return -errno; + return 0; +} + +static int snd_rawmidi_hw_ump_block_info(snd_rawmidi_t *rmidi, void *buf) +{ + snd_rawmidi_hw_t *hw = rmidi->private_data; + + if (rmidi->version < SNDRV_PROTOCOL_VERSION(2, 0, 3)) + return -ENXIO; + if (ioctl(hw->fd, SNDRV_UMP_IOCTL_BLOCK_INFO, buf) < 0) + return -errno; + return 0; +} + static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { .close = snd_rawmidi_hw_close, .nonblock = snd_rawmidi_hw_nonblock, @@ -282,7 +306,9 @@ static const snd_rawmidi_ops_t snd_rawmidi_hw_ops = { .drain = snd_rawmidi_hw_drain, .write = snd_rawmidi_hw_write, .read = snd_rawmidi_hw_read, - .tread = snd_rawmidi_hw_tread + .tread = snd_rawmidi_hw_tread, + .ump_endpoint_info = snd_rawmidi_hw_ump_endpoint_info, + .ump_block_info = snd_rawmidi_hw_ump_block_info, }; @@ -297,8 +323,12 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, snd_rawmidi_t *rmidi; snd_rawmidi_hw_t *hw = NULL; snd_rawmidi_info_t info; + int is_ump; int fmode; + is_ump = !!(mode & _SND_RAWMIDI_OPEN_UMP); + mode &= ~_SND_RAWMIDI_OPEN_UMP; + if (inputp) *inputp = NULL; if (outputp) @@ -308,7 +338,10 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0) return ret; - sprintf(filename, SNDRV_FILE_RAWMIDI, card, device); + if (is_ump) + sprintf(filename, SNDRV_FILE_UMP_RAWMIDI, card, device); + else + sprintf(filename, SNDRV_FILE_RAWMIDI, card, device); __again: if (attempt++ > 3) { @@ -349,13 +382,13 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, fd = snd_open_device(filename, fmode); if (fd < 0) { snd_ctl_close(ctl); - SYSERR("open %s failed", filename); + SYSMSG("open %s failed", filename); return -errno; } } if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_PVERSION, &ver) < 0) { ret = -errno; - SYSERR("SNDRV_RAWMIDI_IOCTL_PVERSION failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_PVERSION failed"); close(fd); snd_ctl_close(ctl); return ret; @@ -374,7 +407,7 @@ int snd_rawmidi_hw_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, memset(&info, 0, sizeof(info)); info.stream = outputp ? SNDRV_RAWMIDI_STREAM_OUTPUT : SNDRV_RAWMIDI_STREAM_INPUT; if (ioctl(fd, SNDRV_RAWMIDI_IOCTL_INFO, &info) < 0) { - SYSERR("SNDRV_RAWMIDI_IOCTL_INFO failed"); + SYSMSG("SNDRV_RAWMIDI_IOCTL_INFO failed"); ret = -errno; close(fd); snd_ctl_close(ctl); diff --git a/src/rawmidi/rawmidi_local.h b/src/rawmidi/rawmidi_local.h index 4a88e7e4..f0bb06a7 100644 --- a/src/rawmidi/rawmidi_local.h +++ b/src/rawmidi/rawmidi_local.h @@ -19,10 +19,10 @@ * */ +#include "local.h" #include #include #include -#include "local.h" typedef struct { int (*close)(snd_rawmidi_t *rawmidi); @@ -35,6 +35,8 @@ typedef struct { 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); + int (*ump_endpoint_info)(snd_rawmidi_t *rmidi, void *buf); + int (*ump_block_info)(snd_rawmidi_t *rmidi, void *buf); } snd_rawmidi_ops_t; struct _snd_rawmidi { @@ -62,3 +64,8 @@ int snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp, int merge, int mode); #define snd_rawmidi_conf_generic_id(id) _snd_conf_generic_id(id) + +int _snd_rawmidi_ump_endpoint_info(snd_rawmidi_t *rmidi, void *info); +int _snd_rawmidi_ump_block_info(snd_rawmidi_t *rmidi, void *info); + +#define _SND_RAWMIDI_OPEN_UMP (1U << 16) /* internal open mode bit */ diff --git a/src/rawmidi/rawmidi_virt.c b/src/rawmidi/rawmidi_virt.c index 884b8ff8..04c485d3 100644 --- a/src/rawmidi/rawmidi_virt.c +++ b/src/rawmidi/rawmidi_virt.c @@ -19,13 +19,11 @@ * */ -#include -#include +#include "rawmidi_local.h" #include #include #include #include -#include "rawmidi_local.h" #include "seq.h" #include "seq_midi_event.h" diff --git a/src/rawmidi/ump.c b/src/rawmidi/ump.c new file mode 100644 index 00000000..39c1c4a9 --- /dev/null +++ b/src/rawmidi/ump.c @@ -0,0 +1,769 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/** + * \file rawmidi/ump.c + * \brief Universal MIDI Protocol (UMP) Interface + */ + +#include "rawmidi_local.h" +#include "ump_local.h" + +static int get_rawmidi_flags(snd_ump_t *ump) +{ + snd_rawmidi_info_t info; + int err; + + err = snd_rawmidi_info(ump->rawmidi, &info); + if (err < 0) + return err; + if (!(info.flags & SNDRV_RAWMIDI_INFO_UMP)) + return -EINVAL; + ump->flags = info.flags; + return 0; +} + +/** + * \brief Opens a new connection to the UMP interface. + * \param inputp Returned input handle (NULL if not wanted) + * \param outputp Returned output handle (NULL if not wanted) + * \param name ASCII identifier of the UMP handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the UMP interface specified with + * an ASCII identifier and mode. + */ +int snd_ump_open(snd_ump_t **inputp, snd_ump_t **outputp, const char *name, + int mode) +{ + snd_ump_t *input = NULL, *output = NULL; + int err; + + if (inputp) + *inputp = NULL; + if (outputp) + *outputp = NULL; + if (!inputp && !outputp) + return -EINVAL; + + err = -ENOMEM; + if (inputp) { + input = calloc(1, sizeof(*input)); + if (!input) + goto error; + input->is_input = 1; + } + if (outputp) { + output = calloc(1, sizeof(*output)); + if (!output) + goto error; + } + + err = snd_rawmidi_open(input ? &input->rawmidi : NULL, + output ? &output->rawmidi : NULL, + name, mode | _SND_RAWMIDI_OPEN_UMP); + if (err < 0) + goto error; + + if (input) { + err = get_rawmidi_flags(input); + if (err < 0) + goto error; + } + if (output) { + err = get_rawmidi_flags(output); + if (err < 0) + goto error; + } + + if (inputp) + *inputp = input; + if (outputp) + *outputp = output; + + return 0; + + error: + if (input) { + if (input->rawmidi) + snd_rawmidi_close(input->rawmidi); + free(input); + } + if (output) { + if (output->rawmidi) + snd_rawmidi_close(output->rawmidi); + free(output); + } + return err; +} + +/** + * \brief close UMP handle + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified UMP handle and frees all associated + * resources. + */ +int snd_ump_close(snd_ump_t *ump) +{ + int err; + + err = snd_rawmidi_close(ump->rawmidi); + free(ump); + return err; +} + +/** + * \brief get RawMidi instance associated with the UMP handle + * \param ump UMP handle + * \return the associated RawMidi handle + * + * Returns the associated RawMidi instance with the given UMP handle + */ +snd_rawmidi_t *snd_ump_rawmidi(snd_ump_t *ump) +{ + return ump->rawmidi; +} + +/** + * \brief get identifier of UMP handle + * \param ump UMP handle + * \return ascii identifier of UMP handle + * + * Returns the ASCII identifier of given UMP handle. It's the same + * identifier specified in snd_ump_open(). + */ +const char *snd_ump_name(snd_ump_t *ump) +{ + return snd_rawmidi_name(ump->rawmidi); +} + +/** + * \brief get count of poll descriptors for UMP handle + * \param ump UMP handle + * \return count of poll descriptors + */ +int snd_ump_poll_descriptors_count(snd_ump_t *ump) +{ + return snd_rawmidi_poll_descriptors_count(ump->rawmidi); +} + +/** + * \brief get poll descriptors + * \param ump UMP handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + */ +int snd_ump_poll_descriptors(snd_ump_t *ump, struct pollfd *pfds, + unsigned int space) +{ + return snd_rawmidi_poll_descriptors(ump->rawmidi, pfds, space); +} + +/** + * \brief get returned events from poll descriptors + * \param ump UMP handle + * \param pfds array of poll descriptors + * \param nfds count of poll descriptors + * \param revents returned events + * \return zero if success, otherwise a negative error code + */ +int snd_ump_poll_descriptors_revents(snd_ump_t *ump, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + return snd_rawmidi_poll_descriptors_revents(ump->rawmidi, pfds, nfds, + revents); +} + +/** + * \brief set nonblock mode + * \param ump UMP handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + * + * The nonblock mode cannot be used when the stream is in + * #SND_RAWMIDI_APPEND state. + */ +int snd_ump_nonblock(snd_ump_t *ump, int nonblock) +{ + return snd_rawmidi_nonblock(ump->rawmidi, nonblock); +} + +/** + * \brief get information about associated RawMidi handle + * \param ump UMP handle + * \param info pointer to a snd_rawmidi_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_info(snd_ump_t *ump, snd_rawmidi_info_t *info) +{ + return snd_rawmidi_info(ump->rawmidi, info); +} + +/** + * \brief set parameters about associated RawMidi stream + * \param ump UMP handle + * \param params pointer to a snd_rawmidi_params_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_params(snd_ump_t *ump, snd_rawmidi_params_t *params) +{ + return snd_rawmidi_params(ump->rawmidi, params); +} + +/** + * \brief get current parameters about associated RawMidi stream + * \param ump UMP handle + * \param params pointer to a snd_rawmidi_params_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_params_current(snd_ump_t *ump, snd_rawmidi_params_t *params) +{ + return snd_rawmidi_params_current(ump->rawmidi, params); +} + +/** + * \brief get status of associated RawMidi stream + * \param ump UMP handle + * \param status pointer to a snd_rawmidi_status_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_rawmidi_status(snd_ump_t *ump, snd_rawmidi_status_t *status) +{ + return snd_rawmidi_status(ump->rawmidi, status); +} + +/** + * \brief drop all packets in the rawmidi I/O ring buffer immediately + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + */ +int snd_ump_drop(snd_ump_t *ump) +{ + return snd_rawmidi_drop(ump->rawmidi); +} + +/** + * \brief drain all packets in the UMP I/O ring buffer + * \param ump UMP handle + * \return 0 on success otherwise a negative error code + * + * Waits until all MIDI packets are not drained (sent) to the + * hardware device. + */ +int snd_ump_drain(snd_ump_t *ump) +{ + return snd_rawmidi_drain(ump->rawmidi); +} + +/** + * \brief write UMP packets to UMP stream + * \param ump UMP handle + * \param buffer buffer containing UMP packets + * \param size output buffer size in bytes + */ +ssize_t snd_ump_write(snd_ump_t *ump, const void *buffer, size_t size) +{ + if (ump->is_input) + return -EINVAL; + return snd_rawmidi_write(ump->rawmidi, buffer, size); +} + +/** + * \brief read UMP packets from UMP stream + * \param ump UMP handle + * \param buffer buffer to store the input MIDI bytes + * \param size input buffer size in bytes + * \retval count of UMP packet in bytes otherwise a negative error code + */ +ssize_t snd_ump_read(snd_ump_t *ump, void *buffer, size_t size) +{ + if (!ump->is_input) + return -EINVAL; + return snd_rawmidi_read(ump->rawmidi, buffer, size); +} + +/** + * \brief read UMP packets from UMP stream with timestamp + * \param ump UMP handle + * \param[out] tstamp timestamp for the returned UMP packets + * \param buffer buffer to store the input UMP packets + * \param size input buffer size in bytes + * \retval count of UMP packet in bytes otherwise a negative error code + */ +ssize_t snd_ump_tread(snd_ump_t *ump, struct timespec *tstamp, void *buffer, + size_t size) +{ + if (!ump->is_input) + return -EINVAL; + return snd_rawmidi_tread(ump->rawmidi, tstamp, buffer, size); +} + +/** + * \brief get size of the snd_ump_endpoint_info_t structure in bytes + * \return size of the snd_ump_endpoint_info_t structure in bytes + */ +size_t snd_ump_endpoint_info_sizeof(void) +{ + return sizeof(snd_ump_endpoint_info_t); +} + +/** + * \brief allocate the snd_ump_endpoint_info_t structure + * \param info returned pointer + * \return 0 on success otherwise a negative error code if fails + * + * Allocates a new snd_rawmidi_status_t structure using the standard + * malloc C library function. + */ +int snd_ump_endpoint_info_malloc(snd_ump_endpoint_info_t **info) +{ + *info = calloc(1, sizeof(snd_ump_endpoint_info_t)); + if (!*info) + return -ENOMEM; + return 0; +} + +/** + * \brief frees the snd_ump_endpoint_info_t structure + * \param info pointer to the snd_ump_endpoint_info_t structure to free + * + * Frees the given snd_ump_endpoint_info_t structure using the standard + * free C library function. + */ +void snd_ump_endpoint_info_free(snd_ump_endpoint_info_t *info) +{ + free(info); +} + +/** + * \brief copy one snd_ump_endpoint_info_t structure to another + * \param dst destination snd_ump_endpoint_info_t structure + * \param src source snd_ump_endpoint_info_t structure + */ +void snd_ump_endpoint_info_copy(snd_ump_endpoint_info_t *dst, + const snd_ump_endpoint_info_t *src) +{ + *dst = *src; +} + +/** + * \brief get card number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return the card number of the given UMP endpoint + */ +int snd_ump_endpoint_info_get_card(const snd_ump_endpoint_info_t *info) +{ + return info->card; +} + +/** + * \brief get device number of UMP endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return the device number of the given UMP endpoint + */ +int snd_ump_endpoint_info_get_device(const snd_ump_endpoint_info_t *info) +{ + return info->device; +} + +/** + * \brief get UMP endpoint info flags + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint flag bits + */ +unsigned int snd_ump_endpoint_info_get_flags(const snd_ump_endpoint_info_t *info) +{ + return info->flags; +} + +/** + * \brief get UMP endpoint protocol capability bits + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint protocol capability bits + */ +unsigned int snd_ump_endpoint_info_get_protocol_caps(const snd_ump_endpoint_info_t *info) +{ + return info->protocol_caps; +} + +/** + * \brief get the current UMP endpoint protocol + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint protocol bits + */ +unsigned int snd_ump_endpoint_info_get_protocol(const snd_ump_endpoint_info_t *info) +{ + return info->protocol; +} + +/** + * \brief get the number of UMP blocks belonging to the endpoint + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return number of UMP blocks + */ +unsigned int snd_ump_endpoint_info_get_num_blocks(const snd_ump_endpoint_info_t *info) +{ + return info->num_blocks; +} + +/** + * \brief get UMP version number + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP version number + */ +unsigned int snd_ump_endpoint_info_get_version(const snd_ump_endpoint_info_t *info) +{ + return info->version; +} + +/** + * \brief get UMP manufacturer ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP manufacturer ID + */ +unsigned int snd_ump_endpoint_info_get_manufacturer_id(const snd_ump_endpoint_info_t *info) +{ + return info->manufacturer_id; +} + +/** + * \brief get UMP family ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP family ID + */ +unsigned int snd_ump_endpoint_info_get_family_id(const snd_ump_endpoint_info_t *info) +{ + return info->family_id; +} + +/** + * \brief get UMP model ID + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP model ID + */ +unsigned int snd_ump_endpoint_info_get_model_id(const snd_ump_endpoint_info_t *info) +{ + return info->model_id; +} + +/** + * \brief get UMP software revision + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP software revision + */ +const unsigned char *snd_ump_endpoint_info_get_sw_revision(const snd_ump_endpoint_info_t *info) +{ + return info->sw_revision; +} + +/** + * \brief get UMP endpoint name string + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint name string + */ +const char *snd_ump_endpoint_info_get_name(const snd_ump_endpoint_info_t *info) +{ + return (const char *)info->name; +} + +/** + * \brief get UMP endpoint product ID string + * \param info pointer to a snd_ump_endpoint_info_t structure + * \return UMP endpoint product ID string + */ +const char *snd_ump_endpoint_info_get_product_id(const snd_ump_endpoint_info_t *info) +{ + return (const char *)info->product_id; +} + +/** + * \brief get endpoint information about UMP handle + * \param ump UMP handle + * \param info pointer to a snd_ump_endpoint_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int snd_ump_endpoint_info(snd_ump_t *ump, snd_ump_endpoint_info_t *info) +{ + return _snd_rawmidi_ump_endpoint_info(ump->rawmidi, info); +} + +/** + * \brief get size of the snd_ump_block_info_t structure in bytes + * \return size of the snd_ump_block_info_t structure in bytes + */ +size_t snd_ump_block_info_sizeof(void) +{ + return sizeof(snd_ump_block_info_t); +} + +/** + * \brief allocate the snd_ump_block_info_t structure + * \param info returned pointer + * \return 0 on success otherwise a negative error code if fails + * + * Allocates a new snd_ump_block_info_t structure using the standard + * malloc C library function. + */ +int snd_ump_block_info_malloc(snd_ump_block_info_t **info) +{ + *info = calloc(1, sizeof(snd_ump_block_info_t)); + if (!*info) + return -ENOMEM; + return 0; +} + +/** + * \brief frees the snd_ump_block_info_t structure + * \param info pointer to the snd_ump_block_info_t structure to free + * + * Frees the given snd_ump_block_info_t structure using the standard + * free C library function. + */ +void snd_ump_block_info_free(snd_ump_block_info_t *info) +{ + free(info); +} + +/** + * \brief copy one snd_ump_block_info_t structure to another + * \param dst destination snd_ump_block_info_t structure + * \param src source snd_ump_block_info_t structure + */ +void snd_ump_block_info_copy(snd_ump_block_info_t *dst, + const snd_ump_block_info_t *src) +{ + *dst = *src; +} + +/** + * \brief get card number of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the card number of the given UMP block + */ +int snd_ump_block_info_get_card(const snd_ump_block_info_t *info) +{ + return info->card; +} + +/** + * \brief get device number of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the device number of the given UMP block + */ +int snd_ump_block_info_get_device(const snd_ump_block_info_t *info) +{ + return info->device; +} + +/** + * \brief get UMP block ID + * \param info pointer to a snd_ump_block_info_t structure + * \return ID number of the given UMP block + */ +unsigned int snd_ump_block_info_get_block_id(const snd_ump_block_info_t *info) +{ + return info->block_id; +} + +/** + * \brief set UMP block ID for query + * \param info pointer to a snd_ump_block_info_t structure + * \param id the ID number for query + */ +void snd_ump_block_info_set_block_id(snd_ump_block_info_t *info, + unsigned int id) +{ + info->block_id = id; +} + +/** + * \brief get UMP block activeness + * \param info pointer to a snd_ump_block_info_t structure + * \return 1 if the block is active or 0 if inactive + */ +unsigned int snd_ump_block_info_get_active(const snd_ump_block_info_t *info) +{ + return info->active; +} + +/** + * \brief get UMP block information flags + * \param info pointer to a snd_ump_block_info_t structure + * \return info flag bits for the given UMP block + */ +unsigned int snd_ump_block_info_get_flags(const snd_ump_block_info_t *info) +{ + return info->flags; +} + +/** + * \brief get UMP block direction + * \param info pointer to a snd_ump_block_info_t structure + * \return direction of UMP block (input,output,bidirectional) + */ +unsigned int snd_ump_block_info_get_direction(const snd_ump_block_info_t *info) +{ + return info->direction; +} + +/** + * \brief get first UMP group ID belonging to the block + * \param info pointer to a snd_ump_block_info_t structure + * \return the first UMP group ID belonging to the block + */ +unsigned int snd_ump_block_info_get_first_group(const snd_ump_block_info_t *info) +{ + return info->first_group; +} + +/** + * \brief get number of UMP groups belonging to the block + * \param info pointer to a snd_ump_block_info_t structure + * \return the number of UMP groups belonging to the block + */ +unsigned int snd_ump_block_info_get_num_groups(const snd_ump_block_info_t *info) +{ + return info->num_groups; +} + +/** + * \brief get MIDI-CI version number + * \param info pointer to a snd_ump_block_info_t structure + * \return MIDI-CI version number + */ +unsigned int snd_ump_block_info_get_midi_ci_version(const snd_ump_block_info_t *info) +{ + return info->midi_ci_version; +} + +/** + * \brief get number of supported SysEx8 streams + * \param info pointer to a snd_ump_block_info_t structure + * \return number of supported SysEx8 streams + */ +unsigned int snd_ump_block_info_get_sysex8_streams(const snd_ump_block_info_t *info) +{ + return info->sysex8_streams; +} + +/** + * \brief get UI hint of the given UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the hint bits + */ +unsigned int snd_ump_block_info_get_ui_hint(const snd_ump_block_info_t *info) +{ + return info->ui_hint; +} + +/** + * \brief get the name string of UMP block + * \param info pointer to a snd_ump_block_info_t structure + * \return the name string of UMP block + */ +const char *snd_ump_block_info_get_name(const snd_ump_block_info_t *info) +{ + return (const char *)info->name; +} + +/** + * \brief get UMP block information + * \param ump UMP handle + * \param info pointer to a snd_ump_block_info_t structure + * \return 0 on success otherwise a negative error code + * + * The caller should fill the block ID to query at first via + * snd_ump_block_info_set_block_id(). + */ +int snd_ump_block_info(snd_ump_t *ump, snd_ump_block_info_t *info) +{ + return _snd_rawmidi_ump_block_info(ump->rawmidi, info); +} + +/* + * UMP sysex helpers + */ +static int expand_sysex_data(const uint32_t *data, uint8_t *buf, + size_t maxlen, unsigned char bytes, int offset) +{ + int size = 0; + + for (; bytes; bytes--, size++) { + if (!maxlen) + break; + buf[size] = (*data >> offset) & 0x7f; + if (!offset) { + offset = 24; + data++; + } else { + offset -= 8; + } + } + + return size; +} + +static int expand_sysex7(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + unsigned char status; + unsigned char bytes; + + *filled = 0; + if (!maxlen) + return 0; + + status = snd_ump_sysex_msg_status(ump); + bytes = snd_ump_sysex_msg_length(ump); + if (bytes > 6) + return 0; // invalid - skip + + *filled = expand_sysex_data(ump, buf, maxlen, bytes, 8); + return (status == SND_UMP_SYSEX_STATUS_SINGLE || + status == SND_UMP_SYSEX_STATUS_END); +} + +static int expand_sysex8(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + unsigned char status; + unsigned char bytes; + + *filled = 0; + if (!maxlen) + return 0; + + status = snd_ump_sysex_msg_status(ump); + if (status > SND_UMP_SYSEX_STATUS_END) + return 0; // unsupported, skip + bytes = snd_ump_sysex_msg_length(ump); + if (!bytes || bytes > 14) + return 0; // skip + + *filled = expand_sysex_data(ump, buf, maxlen, bytes - 1, 0); + return (status == SND_UMP_SYSEX_STATUS_SINGLE || + status == SND_UMP_SYSEX_STATUS_END); +} + +/** + * \brief fill sysex byte from a UMP packet + * \param ump UMP packet pointer + * \param buf buffer point to fill sysex bytes + * \param maxlen max buffer size in bytes + * \param filled the size of filled sysex bytes on the buffer + * \return 1 if the sysex finished, otherwise 0 + */ +int snd_ump_msg_sysex_expand(const uint32_t *ump, uint8_t *buf, size_t maxlen, + size_t *filled) +{ + switch (snd_ump_msg_type(ump)) { + case SND_UMP_MSG_TYPE_DATA: + return expand_sysex7(ump, buf, maxlen, filled); + case SND_UMP_MSG_TYPE_EXTENDED_DATA: + return expand_sysex8(ump, buf, maxlen, filled); + default: + return -EINVAL; + } +} diff --git a/src/rawmidi/ump_local.h b/src/rawmidi/ump_local.h new file mode 100644 index 00000000..53ea9701 --- /dev/null +++ b/src/rawmidi/ump_local.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include "rawmidi.h" +#include "ump.h" +#include "ump_msg.h" + +#ifndef DOC_HIDDEN +struct _snd_ump { + snd_rawmidi_t *rawmidi; + unsigned int flags; + int is_input; +}; +#endif /* DOC_HIDDEN */ diff --git a/src/seq/seq.c b/src/seq/seq.c index f051426f..5eac4848 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -777,8 +777,8 @@ void event_filter(snd_seq_t *seq, snd_seq_event_t *ev) */ -#include #include "seq_local.h" +#include /**************************************************************************** * * @@ -1204,6 +1204,11 @@ size_t snd_seq_get_output_buffer_size(snd_seq_t *seq) return seq->obufsize; } +static inline size_t get_packet_size(snd_seq_t *seq) +{ + return seq->packet_size ? seq->packet_size : sizeof(snd_seq_event_t); +} + /** * \brief Return the size of input buffer * \param seq sequencer handle @@ -1219,7 +1224,7 @@ size_t snd_seq_get_input_buffer_size(snd_seq_t *seq) assert(seq); if (!seq->ibuf) return 0; - return seq->ibufsize * sizeof(snd_seq_event_t); + return seq->ibufsize * get_packet_size(seq); } /** @@ -1261,13 +1266,17 @@ int snd_seq_set_output_buffer_size(snd_seq_t *seq, size_t size) */ int snd_seq_set_input_buffer_size(snd_seq_t *seq, size_t size) { + size_t packet_size; + assert(seq && seq->ibuf); - assert(size >= sizeof(snd_seq_event_t)); + packet_size = get_packet_size(seq); + assert(size >= packet_size); snd_seq_drop_input(seq); - size = (size + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + size = (size + packet_size - 1) / packet_size; if (size != seq->ibufsize) { - snd_seq_event_t *newbuf; - newbuf = calloc(sizeof(snd_seq_event_t), size); + char *newbuf; + /* use ump event size for avoiding reallocation at switching */ + newbuf = calloc(sizeof(snd_seq_ump_event_t), size); if (newbuf == NULL) return -ENOMEM; free(seq->ibuf); @@ -1726,6 +1735,64 @@ int snd_seq_client_info_get_event_lost(const snd_seq_client_info_t *info) return info->event_lost; } +/** + * \brief Get the MIDI protocol version number of a client_info container + * \param info client_info container + * \return MIDI protocol version + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_midi_version(const snd_seq_client_info_t *info) +{ + assert(info); + return info->midi_version; +} + +/** + * \brief Get the UMP group filter status + * \param info client_info container + * \param group 0-based group index + * \return 0 if the group is filtered / disabled, 1 if it's processed + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_group_enabled(const snd_seq_client_info_t *info, + int group) +{ + assert(info); + return !(info->group_filter & (1U << group)); +} + +#ifndef DOC_HIDDEN +#define UMP_GROUPLESS_FILTER (1U << 0) +#endif /* DOC_HIDDEN */ + +/** + * \brief Get the UMP groupless message handling status + * \param info client_info container + * \return 1 if UMP groupless messages is processed, 0 if filtered/disabled + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_groupless_enabled(const snd_seq_client_info_t *info) +{ + assert(info); + return !(info->group_filter & UMP_GROUPLESS_FILTER); +} + +/** + * \brief Get the automatic conversion mode for UMP + * \param info client_info container + * \return 1 if the conversion is enabled, 0 if not + * + * \sa snd_seq_get_client_info() + */ +int snd_seq_client_info_get_ump_conversion(const snd_seq_client_info_t *info) +{ + assert(info); + return info->midi_version; +} + /** * \brief Set the client id of a client_info container * \param info client_info container @@ -1769,6 +1836,71 @@ void snd_seq_client_info_set_broadcast_filter(snd_seq_client_info_t *info, int v info->filter &= ~SNDRV_SEQ_FILTER_BROADCAST; } +/** + * \brief Set the MIDI protocol version of a client_info container + * \param info client_info container + * \param midi_version MIDI protocol version to set + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_get_midi_version() + */ +void snd_seq_client_info_set_midi_version(snd_seq_client_info_t *info, int midi_version) +{ + assert(info); + info->midi_version = midi_version; +} + +/** + * \brief Set the UMP group filter status + * \param info client_info container + * \param group 0-based group index + * \param enable 0 to filter/disable the group, non-zero to enable + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_group_enabled() + */ +void snd_seq_client_info_set_ump_group_enabled(snd_seq_client_info_t *info, + int group, int enable) +{ + assert(info); + if (enable) + info->group_filter &= ~(1U << group); + else + info->group_filter |= (1U << group); +} + +/** + * \brief Enable/disable the UMP groupless message handling + * \param info client_info container + * \param enable enable the UMP groupless messages + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_groupless_enabled() + */ +void snd_seq_client_info_set_ump_groupless_enabled(snd_seq_client_info_t *info, + int enable) +{ + assert(info); + if (enable) + info->group_filter &= ~UMP_GROUPLESS_FILTER; + else + info->group_filter |= UMP_GROUPLESS_FILTER; +} + +/** + * \brief Set the automatic conversion mode for UMP + * \param info client_info container + * \param enable 0 or 1 for disabling/enabling the conversion + * + * \sa snd_seq_set_client_info(), snd_seq_client_info_get_ump_conversion() + */ +void snd_seq_client_info_set_ump_conversion(snd_seq_client_info_t *info, + int enable) +{ + assert(info); + if (enable) + info->filter &= ~SNDRV_SEQ_FILTER_NO_CONVERT; + else + info->filter |= SNDRV_SEQ_FILTER_NO_CONVERT; +} + /** * \brief Set the error-bounce usage of a client_info container * \param info client_info container @@ -1887,6 +2019,65 @@ int snd_seq_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info) return seq->ops->query_next_client(seq, info); } +/** + * \brief Get UMP Endpoint information + * \param seq sequencer handle + * \param client client number to query + * \param info the pointer to store snd_ump_endpoint_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_get_ump_endpoint_info(snd_seq_t *seq, int client, void *info) +{ + assert(seq && info); + return seq->ops->get_ump_info(seq, client, + SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT, + info); +} + +/** + * \brief Get UMP Block information + * \param seq sequencer handle + * \param client sequencer client number to query + * \param blk UMP block number (0-based) to query + * \param info the pointer to store snd_ump_block_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_get_ump_block_info(snd_seq_t *seq, int client, int blk, void *info) +{ + assert(seq && info); + return seq->ops->get_ump_info(seq, client, + SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + blk, + info); +} + +/** + * \brief Set UMP Endpoint information to the current client + * \param seq sequencer handle + * \param info the pointer to send snd_ump_endpoint_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_set_ump_endpoint_info(snd_seq_t *seq, const void *info) +{ + assert(seq && info); + return seq->ops->set_ump_info(seq, + SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT, + info); +} + +/** + * \brief Set UMP Block information to the current client + * \param seq sequencer handle + * \param blk UMP block number (0-based) to send + * \param info the pointer to send snd_ump_block_info_t data + * \return 0 on success otherwise a negative error code + */ +int snd_seq_set_ump_block_info(snd_seq_t *seq, int blk, const void *info) +{ + assert(seq && info); + return seq->ops->set_ump_info(seq, + SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + blk, + info); +} /*----------------------------------------------------------------*/ @@ -2134,6 +2325,32 @@ int snd_seq_port_info_get_timestamp_queue(const snd_seq_port_info_t *info) return info->time_queue; } +/** + * \brief Get the direction of the port + * \param info port_info container + * \return the direction of the port + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_direction() + */ +int snd_seq_port_info_get_direction(const snd_seq_port_info_t *info) +{ + assert(info); + return info->direction; +} + +/** + * \brief Get the UMP Group assigned to the port + * \param info port_info container + * \return 0 for no conversion, or the (1-based) UMP Group number assigned to the port + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_ump_group() + */ +int snd_seq_port_info_get_ump_group(const snd_seq_port_info_t *info) +{ + assert(info); + return info->ump_group; +} + /** * \brief Set the client id of a port_info container * \param info port_info container @@ -2312,6 +2529,31 @@ void snd_seq_port_info_set_timestamp_queue(snd_seq_port_info_t *info, int queue) info->time_queue = queue; } +/** + * \brief Set the direction of the port + * \param info port_info container + * \param direction the port direction + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_direction() + */ +void snd_seq_port_info_set_direction(snd_seq_port_info_t *info, int direction) +{ + assert(info); + info->direction = direction; +} + +/** + * \brief Set the UMP Group assigned to the port + * \param info port_info container + * \param ump_group 0 for no conversion, or the (1-based) UMP Group number + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_ump_group() + */ +void snd_seq_port_info_set_ump_group(snd_seq_port_info_t *info, int ump_group) +{ + assert(info); + info->ump_group = ump_group; +} /** * \brief create a sequencer port on the current client @@ -3874,7 +4116,9 @@ ssize_t snd_seq_event_length(snd_seq_event_t *ev) { ssize_t len = sizeof(snd_seq_event_t); assert(ev); - if (snd_seq_ev_is_variable(ev)) + if (snd_seq_ev_is_ump(ev)) + len = sizeof(snd_seq_ump_event_t); + else if (snd_seq_ev_is_variable(ev)) len += ev->data.ext.len; return len; } @@ -3917,6 +4161,13 @@ int snd_seq_event_output(snd_seq_t *seq, snd_seq_event_t *ev) return result; } +/* workaround for broken legacy apps that set UMP event bit unexpectedly */ +static void clear_ump_for_legacy_apps(snd_seq_t *seq, snd_seq_event_t *ev) +{ + if (!seq->midi_version && snd_seq_ev_is_ump(ev)) + ev->flags &= ~SNDRV_SEQ_EVENT_UMP; +} + /** * \brief output an event onto the lib buffer without draining buffer * \param seq sequencer handle @@ -3925,12 +4176,16 @@ int snd_seq_event_output(snd_seq_t *seq, snd_seq_event_t *ev) * * This function doesn't drain buffer unlike snd_seq_event_output(). * - * \sa snd_seq_event_output() + * \note + * For a UMP event, use snd_seq_ump_event_output_buffer() instead. + * + * \sa snd_seq_event_output(), snd_seq_ump_event_output_buffer() */ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) { int len; assert(seq && ev); + clear_ump_for_legacy_apps(seq, ev); len = snd_seq_event_length(ev); if (len < 0) return -EINVAL; @@ -3938,12 +4193,15 @@ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) return -EINVAL; if ((seq->obufsize - seq->obufused) < (size_t) len) return -EAGAIN; - memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); - seq->obufused += sizeof(snd_seq_event_t); - if (snd_seq_ev_is_variable(ev)) { - memcpy(seq->obuf + seq->obufused, ev->data.ext.ptr, ev->data.ext.len); - seq->obufused += ev->data.ext.len; + if (snd_seq_ev_is_ump(ev)) { + memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_ump_event_t)); + } else { + memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); + if (snd_seq_ev_is_variable(ev)) + memcpy(seq->obuf + seq->obufused + sizeof(snd_seq_event_t), + ev->data.ext.ptr, ev->data.ext.len); } + seq->obufused += len; return seq->obufused; } @@ -3988,10 +4246,11 @@ int snd_seq_event_output_direct(snd_seq_t *seq, snd_seq_event_t *ev) ssize_t len; void *buf; + clear_ump_for_legacy_apps(seq, ev); len = snd_seq_event_length(ev); if (len < 0) return len; - else if (len == sizeof(*ev)) { + if (snd_seq_ev_is_ump(ev) || !snd_seq_ev_is_variable(ev)) { buf = ev; } else { if (alloc_tmpbuf(seq, (size_t)len) < 0) @@ -4063,20 +4322,20 @@ int snd_seq_drain_output(snd_seq_t *seq) int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) { size_t len, olen; - snd_seq_event_t ev; assert(seq); if (ev_res) *ev_res = NULL; if ((olen = seq->obufused) < sizeof(snd_seq_event_t)) return -ENOENT; - memcpy(&ev, seq->obuf, sizeof(snd_seq_event_t)); - len = snd_seq_event_length(&ev); + len = snd_seq_event_length((snd_seq_event_t *)seq->obuf); + if (olen < len) + return -ENOENT; if (ev_res) { /* extract the event */ if (alloc_tmpbuf(seq, len) < 0) return -ENOMEM; memcpy(seq->tmpbuf, seq->obuf, len); - *ev_res = seq->tmpbuf; + *ev_res = (snd_seq_event_t *)seq->tmpbuf; } seq->obufused = olen - len; memmove(seq->obuf, seq->obuf + len, seq->obufused); @@ -4094,32 +4353,36 @@ int snd_seq_extract_output(snd_seq_t *seq, snd_seq_event_t **ev_res) */ static ssize_t snd_seq_event_read_buffer(snd_seq_t *seq) { + size_t packet_size = get_packet_size(seq); ssize_t len; - len = (seq->ops->read)(seq, seq->ibuf, seq->ibufsize * sizeof(snd_seq_event_t)); + + len = (seq->ops->read)(seq, seq->ibuf, seq->ibufsize * packet_size); if (len < 0) return len; - seq->ibuflen = len / sizeof(snd_seq_event_t); + seq->ibuflen = len / packet_size; seq->ibufptr = 0; return seq->ibuflen; } static int snd_seq_event_retrieve_buffer(snd_seq_t *seq, snd_seq_event_t **retp) { + size_t packet_size = get_packet_size(seq); size_t ncells; snd_seq_event_t *ev; - *retp = ev = &seq->ibuf[seq->ibufptr]; + *retp = ev = (snd_seq_event_t *)(seq->ibuf + seq->ibufptr * packet_size); + clear_ump_for_legacy_apps(seq, ev); seq->ibufptr++; seq->ibuflen--; if (! snd_seq_ev_is_variable(ev)) return 1; - ncells = (ev->data.ext.len + sizeof(snd_seq_event_t) - 1) / sizeof(snd_seq_event_t); + ncells = (ev->data.ext.len + packet_size - 1) / packet_size; if (seq->ibuflen < ncells) { seq->ibuflen = 0; /* clear buffer */ *retp = NULL; return -EINVAL; } - ev->data.ext.ptr = ev + 1; + ev->data.ext.ptr = (char *)ev + packet_size; seq->ibuflen -= ncells; seq->ibufptr += ncells; return 1; @@ -4212,6 +4475,111 @@ int snd_seq_event_input_pending(snd_seq_t *seq, int fetch_sequencer) /*----------------------------------------------------------------*/ +/* + * I/O for UMP packets + */ + +/** + * \brief output a UMP event + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the number of remaining events or a negative error code + * + * Just like snd_seq_event_output(), it puts an event onto the buffer, + * draining the buffer automatically when needed, but the event is + * snd_seq_ump_event_t type instead snd_seq_event_t. + * + * Calling this function is allowed only when the client is set to + * \c SND_SEQ_CLIENT_UMP_MIDI_1_0 or \c SND_SEQ_CLIENT_UMP_MIDI_2_0. + * + * The flushing and clearing of the buffer is done via the same functions, + * snd_seq_event_drain_output() and snd_seq_drop_output(). + * + * \sa snd_seq_event_output() + */ +int snd_seq_ump_event_output(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief output an event onto the lib buffer without draining buffer + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the byte size of remaining events. \c -EAGAIN if the buffer becomes full. + * + * This is a UMP event version of snd_seq_event_output_buffer(). + * + * \sa snd_seq_event_output_buffer(), snd_seq_ump_event_output() + */ +int snd_seq_ump_event_output_buffer(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output_buffer(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief extract the first UMP event in output buffer + * \param seq sequencer handle + * \param ev_res UMP event pointer to be extracted + * \return 0 on success otherwise a negative error code + * + * This is a UMP event version of snd_seq_extract_output(). + * + * \sa snd_seq_extract_output(), snd_seq_ump_event_output() + */ +int snd_seq_ump_extract_output(snd_seq_t *seq, snd_seq_ump_event_t **ev_res) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_extract_output(seq, (snd_seq_event_t **)ev_res); +} + +/** + * \brief output a UMP event directly to the sequencer NOT through output buffer + * \param seq sequencer handle + * \param ev UMP event to be output + * \return the byte size sent to sequencer or a negative error code + * + * This is a UMP event version of snd_seq_event_output_direct(). + * + * \sa snd_seq_event_output_direct() + */ +int snd_seq_ump_event_output_direct(snd_seq_t *seq, snd_seq_ump_event_t *ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_output_direct(seq, (snd_seq_event_t *)ev); +} + +/** + * \brief retrieve a UMP event from sequencer + * \param seq sequencer handle + * \param ev UMP event pointer to be stored + * + * Like snd_seq_event_input(), this reads out the input event, but in + * snd_seq_ump_event_t type instead of snd_seq_event_t type. + * + * Calling this function is allowed only when the client is set to + * \c SND_SEQ_CLIENT_UMP_MIDI_1_0 or \c SND_SEQ_CLIENT_UMP_MIDI_2_0. + * + * For other input operations, the same function like + * snd_seq_event_input_pending() or snd_seq_drop_input() can be still used. + * + * \sa snd_seq_event_input() + */ +int snd_seq_ump_event_input(snd_seq_t *seq, snd_seq_ump_event_t **ev) +{ + if (!seq->midi_version) + return -EBADFD; + return snd_seq_event_input(seq, (snd_seq_event_t **)ev); +} + +/*----------------------------------------------------------------*/ + /* * clear event buffers */ diff --git a/src/seq/seq_hw.c b/src/seq/seq_hw.c index e4b4d2a0..eeaf26e1 100644 --- a/src/seq/seq_hw.c +++ b/src/seq/seq_hw.c @@ -20,9 +20,9 @@ * */ +#include "seq_local.h" #include #include -#include "seq_local.h" #ifndef PIC /* entry for static linking */ @@ -32,7 +32,6 @@ const char *_snd_module_seq_hw = ""; #ifndef DOC_HIDDEN #define SNDRV_FILE_SEQ ALSA_DEVICE_DIRECTORY "seq" #define SNDRV_FILE_ALOADSEQ ALOAD_DEVICE_DIRECTORY "aloadSEQ" -#define SNDRV_SEQ_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 2) typedef struct { int fd; @@ -94,6 +93,20 @@ static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info) return 0; } +static void update_midi_version(snd_seq_t *seq, snd_seq_client_info_t *info) +{ + snd_seq_hw_t *hw = seq->private_data; + + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) <= hw->version && + seq->midi_version != (int)info->midi_version) { + seq->midi_version = info->midi_version; + if (info->midi_version > 0) + seq->packet_size = sizeof(snd_seq_ump_event_t); + else + seq->packet_size = sizeof(snd_seq_event_t); + } +} + static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; @@ -111,10 +124,57 @@ static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * in static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info) { snd_seq_hw_t *hw = seq->private_data; + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) { /*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/ return -errno; } + update_midi_version(seq, info); + return 0; +} + +static int snd_seq_hw_get_ump_info(snd_seq_t *seq, int client, int type, void *info) +{ + snd_seq_hw_t *hw = seq->private_data; + struct snd_seq_client_ump_info buf; + size_t size; + + if (type < 0 || type >= SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + 32) + return -EINVAL; + if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 3)) + return -ENOTTY; + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + size = sizeof(struct snd_ump_endpoint_info); + else + size = sizeof(struct snd_ump_block_info); + buf.client = client; + buf.type = type; + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_UMP_INFO, &buf) < 0) + return -errno; + memcpy(info, buf.info, size); + return 0; +} + +static int snd_seq_hw_set_ump_info(snd_seq_t *seq, int type, const void *info) +{ + snd_seq_hw_t *hw = seq->private_data; + struct snd_seq_client_ump_info buf; + size_t size; + + if (type < 0 || type >= SNDRV_SEQ_CLIENT_UMP_INFO_BLOCK + 32) + return -EINVAL; + if (hw->version < SNDRV_PROTOCOL_VERSION(1, 0, 3)) + return -ENOTTY; + if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT) + size = sizeof(struct snd_ump_endpoint_info); + else + size = sizeof(struct snd_ump_block_info); + buf.client = seq->client; + buf.type = type; + memcpy(buf.info, info, size); + *(int *)buf.info = -1; /* invalidate the card number */ + if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO, &buf) < 0) + return -errno; return 0; } @@ -396,6 +456,8 @@ static const snd_seq_ops_t snd_seq_hw_ops = { .system_info = snd_seq_hw_system_info, .get_client_info = snd_seq_hw_get_client_info, .set_client_info = snd_seq_hw_set_client_info, + .get_ump_info = snd_seq_hw_get_ump_info, + .set_ump_info = snd_seq_hw_set_ump_info, .create_port = snd_seq_hw_create_port, .delete_port = snd_seq_hw_delete_port, .get_port_info = snd_seq_hw_get_port_info, @@ -472,10 +534,15 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) close(fd); return ret; } - if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION_MAX)) { + if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION)) { close(fd); return -SND_ERROR_INCOMPATIBLE_VERSION; } + if (SNDRV_PROTOCOL_VERSION(1, 0, 3) <= ver) { + /* inform the protocol version we're supporting */ + unsigned int user_ver = SNDRV_SEQ_VERSION; + ioctl(fd, SNDRV_SEQ_IOCTL_USER_PVERSION, &user_ver); + } hw = calloc(1, sizeof(snd_seq_hw_t)); if (hw == NULL) { close(fd); @@ -500,7 +567,7 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) } } if (streams & SND_SEQ_OPEN_INPUT) { - seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); + seq->ibuf = (char *) calloc(sizeof(snd_seq_ump_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE); if (!seq->ibuf) { free(seq->obuf); free(hw); @@ -519,6 +586,7 @@ int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode) seq->poll_fd = fd; seq->ops = &snd_seq_hw_ops; seq->private_data = hw; + seq->packet_size = sizeof(snd_seq_event_t); client = snd_seq_hw_client_id(seq); if (client < 0) { snd_seq_close(seq); diff --git a/src/seq/seq_local.h b/src/seq/seq_local.h index f97a5200..46824806 100644 --- a/src/seq/seq_local.h +++ b/src/seq/seq_local.h @@ -23,10 +23,10 @@ #ifndef __SEQ_LOCAL_H #define __SEQ_LOCAL_H +#include "local.h" #include #include #include -#include "local.h" #define SND_SEQ_OBUF_SIZE (16*1024) /* default size */ #define SND_SEQ_IBUF_SIZE 500 /* in event_size aligned */ @@ -41,6 +41,8 @@ typedef struct { int (*system_info)(snd_seq_t *seq, snd_seq_system_info_t * info); int (*get_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); int (*set_client_info)(snd_seq_t *seq, snd_seq_client_info_t * info); + int (*get_ump_info)(snd_seq_t *seq, int client, int type, void *info); + int (*set_ump_info)(snd_seq_t *seq, int type, const void *info); int (*create_port)(snd_seq_t *seq, snd_seq_port_info_t * port); int (*delete_port)(snd_seq_t *seq, snd_seq_port_info_t * port); int (*get_port_info)(snd_seq_t *seq, snd_seq_port_info_t * info); @@ -84,12 +86,14 @@ struct _snd_seq { char *obuf; /* output buffer */ size_t obufsize; /* output buffer size */ size_t obufused; /* output buffer used size */ - snd_seq_event_t *ibuf; /* input buffer */ + char *ibuf; /* input buffer */ size_t ibufptr; /* current pointer of input buffer */ size_t ibuflen; /* queued length */ size_t ibufsize; /* input buffer size */ snd_seq_event_t *tmpbuf; /* temporary event for extracted event */ size_t tmpbufsize; /* size of errbuf */ + size_t packet_size; /* input packet alignment size */ + int midi_version; /* current protocol version */ }; int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode); diff --git a/src/seq/seq_midi_event.c b/src/seq/seq_midi_event.c index 5a12a18c..95a44e9b 100644 --- a/src/seq/seq_midi_event.c +++ b/src/seq/seq_midi_event.c @@ -28,8 +28,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include "local.h" +#if HAVE_MALLOC_H +#include +#endif #ifndef DOC_HIDDEN diff --git a/src/seq/seqmid.c b/src/seq/seqmid.c index 75061a57..9ec93ee8 100644 --- a/src/seq/seqmid.c +++ b/src/seq/seqmid.c @@ -20,14 +20,12 @@ * */ -#include -#include +#include "seq_local.h" #include #include #include #include #include -#include "seq_local.h" /** * \brief queue controls - start/stop/continue @@ -255,6 +253,44 @@ int snd_seq_set_client_event_filter(snd_seq_t *seq, int event_type) return snd_seq_set_client_info(seq, &info); } +/** + * \brief set client MIDI protocol version + * \param seq sequencer handle + * \param midi_version MIDI protocol version to set + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_info() + */ +int snd_seq_set_client_midi_version(snd_seq_t *seq, int midi_version) +{ + snd_seq_client_info_t info; + int err; + + if ((err = snd_seq_get_client_info(seq, &info)) < 0) + return err; + snd_seq_client_info_set_midi_version(&info, midi_version); + return snd_seq_set_client_info(seq, &info); +} + +/** + * \brief enable/disable client's automatic conversion of UMP/legacy events + * \param seq sequencer handle + * \param enable 0 or 1 to disable/enable the conversion + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_info() + */ +int snd_seq_set_client_ump_conversion(snd_seq_t *seq, int enable) +{ + snd_seq_client_info_t info; + int err; + + if ((err = snd_seq_get_client_info(seq, &info)) < 0) + return err; + snd_seq_client_info_set_ump_conversion(&info, enable); + return snd_seq_set_client_info(seq, &info); +} + /** * \brief change the output pool size of the given client * \param seq sequencer handle diff --git a/src/shmarea.c b/src/shmarea.c index 18937d9e..4e621f7c 100644 --- a/src/shmarea.c +++ b/src/shmarea.c @@ -18,13 +18,26 @@ * */ +/** + * \file shmarea.c + * \ingroup Global + * \brief shared memory helpers + * \author Jaroslav Kysela + * \date 2001 + * + * Shared memory helpers + */ + #include "config.h" /* These funcs are only used by pcm_mmap when sys/shm.h is available. */ #ifdef HAVE_SYS_SHM_H #include +#include +#if HAVE_MALLOC_H #include +#endif #include #include #include @@ -99,6 +112,7 @@ int snd_shm_area_destroy(struct snd_shm_area *area) return 0; } +#ifndef DOC_HIDDEN void snd_shm_area_destructor(void) __attribute__ ((destructor)); void snd_shm_area_destructor(void) @@ -111,5 +125,6 @@ void snd_shm_area_destructor(void) shmdt(area->ptr); } } +#endif /* DOC_HIDDEN */ #endif diff --git a/src/socket.c b/src/socket.c index 99da23e7..c68fa300 100644 --- a/src/socket.c +++ b/src/socket.c @@ -25,6 +25,7 @@ * */ +#include "local.h" #include #include #include @@ -37,7 +38,6 @@ #include #include #include -#include "local.h" #ifndef DOC_HIDDEN int snd_send_fd(int sock, void *data, size_t len, int fd) diff --git a/src/timer/timer.c b/src/timer/timer.c index 6fc710b9..0f8491b8 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -56,7 +56,7 @@ Events are read via snd_timer_read() function. The full featured examples with cross-links: \par Simple timer test program -\ref example_test_timer "example code" +\link example_test_timer example code \endlink \par This example shows opening a timer device and reading of timer events. diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 9f48891f..e0b78373 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -1,7 +1,8 @@ +EXTRA_DIST = Versions COMPATNUM=@LIBTOOL_VERSION_INFO@ if VERSIONED_SYMBOLS -VSYMS = -Wl,--version-script=../Versions +VSYMS = -Wl,--version-script=$(srcdir)/Versions else VSYMS = endif diff --git a/src/topology/Versions b/src/topology/Versions new file mode 100644 index 00000000..9c2ed843 --- /dev/null +++ b/src/topology/Versions @@ -0,0 +1,7 @@ +ALSA_0.9 { + global: + snd_*; + + local: + *; +}; diff --git a/src/topology/builder.c b/src/topology/builder.c index f8aba830..9c52c9cc 100644 --- a/src/topology/builder.c +++ b/src/topology/builder.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* write a block, track the position */ diff --git a/src/topology/channel.c b/src/topology/channel.c index ebdff469..9239c3b2 100644 --- a/src/topology/channel.c +++ b/src/topology/channel.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of channel text names to types */ diff --git a/src/topology/ctl.c b/src/topology/ctl.c index dd05424d..67622957 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -17,15 +17,16 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" +#ifndef DOC_HIDDEN #define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) struct ctl_access_elem { const char *name; unsigned int value; }; +#endif /* DOC_HIDDEN */ /* CTL access strings and codes */ /* place the multi-bit values on top - like read_write - for save */ diff --git a/src/topology/dapm.c b/src/topology/dapm.c index f6a84a60..55bb2fab 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of widget text names to types */ @@ -605,6 +604,17 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; + + widget->ignore_suspend = ival; + + tplg_dbg("\t%s: %s", id, val); + continue; + } + if (strcmp(id, "subseq") == 0) { if (tplg_get_integer(n, &ival, 0)) return -EINVAL; @@ -700,6 +710,9 @@ int tplg_save_dapm_widget(snd_tplg_t *tplg ATTRIBUTE_UNUSED, if (err >= 0 && widget->invert) err = tplg_save_printf(dst, pfx, "\tinvert %u\n", widget->invert); + if (err >= 0 && widget->ignore_suspend) + err = tplg_save_printf(dst, pfx, "\tignore_suspend %u\n", + widget->ignore_suspend); if (err >= 0 && widget->subseq) err = tplg_save_printf(dst, pfx, "\tsubseq %u\n", widget->subseq); diff --git a/src/topology/data.c b/src/topology/data.c index 972b4b7a..e1611aea 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" #include @@ -713,11 +712,13 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) return 0; } +#ifndef DOC_HIDDEN struct tuple_type { unsigned int type; const char *name; unsigned int size; }; +#endif /* DOC_HIDDEN */ static struct tuple_type tuple_types[] = { { diff --git a/src/topology/decoder.c b/src/topology/decoder.c index 66ebe5d8..c8df7e35 100644 --- a/src/topology/decoder.c +++ b/src/topology/decoder.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" int tplg_decode_template(snd_tplg_t *tplg, diff --git a/src/topology/elem.c b/src/topology/elem.c index cbd7f4b6..2e31da5d 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" struct tplg_table tplg_table[] = { @@ -482,10 +481,12 @@ struct tplg_elem* tplg_elem_new_common(snd_tplg_t *tplg, return elem; } +#ifndef DOC_HIDDEN struct tplg_alloc { struct list_head list; void *data[0]; }; +#endif /* DOC_HIDDEN */ void *tplg_calloc(struct list_head *heap, size_t size) { diff --git a/src/topology/log.c b/src/topology/log.c index 1ca36528..b06e0898 100644 --- a/src/topology/log.c +++ b/src/topology/log.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" /* verbose output detailing each object size and file position */ diff --git a/src/topology/ops.c b/src/topology/ops.c index daab3577..74f7cbab 100644 --- a/src/topology/ops.c +++ b/src/topology/ops.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" /* mapping of kcontrol text names to types */ diff --git a/src/topology/parser.c b/src/topology/parser.c index 01c95afa..874ec524 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -17,28 +17,8 @@ Liam Girdwood */ -#include -#include "list.h" #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; -} +#include /* * Get integer value diff --git a/src/topology/pcm.c b/src/topology/pcm.c index 76085d05..acec27f9 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -17,7 +17,6 @@ Liam Girdwood */ -#include "list.h" #include "tplg_local.h" #define RATE(v) [SND_PCM_RATE_##v] = #v @@ -812,15 +811,17 @@ static int parse_flag(snd_config_t *n, unsigned int mask_in, static int save_flags(unsigned int flags, unsigned int mask, struct tplg_buf *dst, const char *pfx) { - static unsigned int flag_masks[3] = { + static unsigned int flag_masks[4] = { SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, }; - static const char *flag_ids[3] = { + static const char *flag_ids[4] = { "symmetric_rates", "symmetric_channels", "symmetric_sample_bits", + "ignore_suspend", }; unsigned int i; int err = 0; @@ -929,6 +930,15 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &pcm->flag_mask, &pcm->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); @@ -1066,6 +1076,15 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &dai->flag_mask, &dai->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); @@ -1220,6 +1239,15 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, continue; } + if (strcmp(id, "ignore_suspend") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_VOICE_WAKEUP, + &link->flag_mask, &link->flags); + if (err < 0) + return err; + continue; + } + /* private data */ if (strcmp(id, "data") == 0) { err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); @@ -1336,10 +1364,12 @@ int tplg_save_cc(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return err; } +#ifndef DOC_HIDDEN struct audio_hw_format { unsigned int type; const char *name; }; +#endif /* DOC_HIDDEN */ static struct audio_hw_format audio_hw_formats[] = { { @@ -1450,7 +1480,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, provider_legacy = false; if (strcmp(id, "bclk_master") == 0) { - SNDERR("deprecated option %s, please use 'bclk'\n", id); + SNDERR("deprecated option %s, please use 'bclk'", id); provider_legacy = true; } @@ -1502,7 +1532,7 @@ int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, provider_legacy = false; if (strcmp(id, "fsync_master") == 0) { - SNDERR("deprecated option %s, please use 'fsync'\n", id); + SNDERR("deprecated option %s, please use 'fsync'", id); provider_legacy = true; } diff --git a/src/topology/save.c b/src/topology/save.c index fecbc6a5..59f4759b 100644 --- a/src/topology/save.c +++ b/src/topology/save.c @@ -15,7 +15,6 @@ Authors: Jaroslav Kysela */ -#include "list.h" #include "tplg_local.h" #define SAVE_ALLOC_SHIFT (13) /* 8192 bytes */ diff --git a/src/topology/text.c b/src/topology/text.c index b07feb99..47abb8d8 100644 --- a/src/topology/text.c +++ b/src/topology/text.c @@ -18,7 +18,6 @@ */ -#include "list.h" #include "tplg_local.h" #define TEXT_SIZE_MAX \ diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 1cb8b694..63639274 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -10,11 +10,12 @@ * Lesser General Public License for more details. */ +#include "local.h" + #include #include #include -#include "local.h" #include "list.h" #include "bswap.h" #include "topology.h" diff --git a/src/ucm/main.c b/src/ucm/main.c index 078cfd64..3a3c9c15 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -59,6 +59,13 @@ static int get_value3(snd_use_case_mgr_t *uc_mgr, struct list_head *value_list2, struct list_head *value_list3); +static int execute_sequence(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, + struct list_head *seq, + struct list_head *value_list1, + struct list_head *value_list2, + struct list_head *value_list3); + static int execute_component_seq(snd_use_case_mgr_t *uc_mgr, struct component_sequence *cmpt_seq, struct list_head *value_list1, @@ -66,6 +73,10 @@ static int execute_component_seq(snd_use_case_mgr_t *uc_mgr, struct list_head *value_list3, char *cdev); +static inline struct use_case_device * + find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + const char *device_name, int check_supported); + static int check_identifier(const char *identifier, const char *prefix) { int len; @@ -155,7 +166,7 @@ static int read_tlv_file(unsigned int **res, { int err = 0; int fd; - struct stat st; + struct stat64 st; size_t sz; ssize_t sz_read; struct snd_ctl_tlv *tlv; @@ -165,7 +176,7 @@ static int read_tlv_file(unsigned int **res, err = -errno; return err; } - if (fstat(fd, &st) == -1) { + if (fstat64(fd, &st) == -1) { err = -errno; goto __fail; } @@ -207,7 +218,7 @@ static int binary_file_parse(snd_ctl_elem_value_t *dst, { int err = 0; int fd; - struct stat st; + struct stat64 st; size_t sz; ssize_t sz_read; char *res; @@ -225,7 +236,7 @@ static int binary_file_parse(snd_ctl_elem_value_t *dst, err = -errno; return err; } - if (stat(filepath, &st) == -1) { + if (stat64(filepath, &st) == -1) { err = -errno; goto __fail; } @@ -296,7 +307,7 @@ static const char *parse_uint(const char *p, const char *prefix, size_t len, uc_error("unable to parse '%s'", prefix); return NULL; } - if (v < min || v > max) { + if ((unsigned int)v < min || (unsigned int)v > max) { uc_error("value '%s' out of range %u-%u %(%ld)", min, max, v); return NULL; } @@ -496,7 +507,7 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type) free(value); if (info2) { if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED) - free((void *)info2->value.enumerated.names_ptr); + free((void *)(size_t)info2->value.enumerated.names_ptr); free(info2); } free(info); @@ -561,16 +572,18 @@ static int execute_sysw(const char *sysw) wlen = write(fd, value, len); myerrno = errno; close(fd); - free(s); if (ignore_error) - return 0; + goto __end; if (wlen != (ssize_t)len) { uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno)); + free(s); return -EINVAL; } +__end: + free(s); return 0; } @@ -660,6 +673,54 @@ static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, ch return 0; } +static int run_device_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + const char *name, bool enable) +{ + struct use_case_device *device; + + if (verb == NULL) { + uc_error("error: enadev2 / disdev2 must be executed inside the verb context"); + return -ENOENT; + } + + device = find_device(uc_mgr, verb, name, 0); + if (device == NULL) { + uc_error("error: unable to find device '%s'\n", name); + return -ENOENT; + } + + return execute_sequence(uc_mgr, verb, + enable ? &device->enable_list : &device->disable_list, + &device->value_list, + &verb->value_list, + &uc_mgr->value_list); +} + +static int run_device_all_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb) +{ + struct use_case_device *device; + struct list_head *pos; + int err; + + if (verb == NULL) { + uc_error("error: disdevall must be executed inside the verb context"); + return -ENOENT; + } + + list_for_each(pos, &verb->device_list) { + device = list_entry(pos, struct use_case_device, list); + + err = execute_sequence(uc_mgr, verb, + &device->disable_list, + &device->value_list, + &verb->value_list, + &uc_mgr->value_list); + if (err < 0) + return err; + } + return 0; +} + /** * \brief Execute the sequence * \param uc_mgr Use case manager @@ -667,6 +728,7 @@ static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, ch * \return zero on success, otherwise a negative error code */ static int execute_sequence(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, struct list_head *seq, struct list_head *value_list1, struct list_head *value_list2, @@ -680,6 +742,11 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr, bool ignore_error; int err = 0; + if (uc_mgr->sequence_hops > 100) { + uc_error("error: too many inner sequences!"); + return -EINVAL; + } + uc_mgr->sequence_hops++; list_for_each(pos, seq) { s = list_entry(pos, struct sequence_element, list); switch (s->type) { @@ -819,17 +886,31 @@ shell_retry: if (err < 0) goto __fail; break; + case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ: + case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ: + err = run_device_sequence(uc_mgr, verb, s->data.device, + s->type == SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ); + if (err < 0) + goto __fail; + break; + case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL: + err = run_device_all_sequence(uc_mgr, verb); + if (err < 0) + goto __fail; + break; default: uc_error("unknown sequence command %i", s->type); break; } } free(cdev); + uc_mgr->sequence_hops--; return 0; __fail_nomem: err = -ENOMEM; __fail: free(cdev); + uc_mgr->sequence_hops--; return err; } @@ -865,7 +946,7 @@ static int execute_component_seq(snd_use_case_mgr_t *uc_mgr, seq = &device->disable_list; /* excecute the sequence of the component dev */ - err = execute_sequence(uc_mgr, seq, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq, &device->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); @@ -919,15 +1000,16 @@ static int add_auto_values(snd_use_case_mgr_t *uc_mgr) /** * \brief execute default commands * \param uc_mgr Use case manager + * \param force Force run * \return zero on success, otherwise a negative error code */ -static int set_defaults(snd_use_case_mgr_t *uc_mgr) +static int set_defaults(snd_use_case_mgr_t *uc_mgr, bool force) { int err; - if (uc_mgr->default_list_executed) + if (!force && uc_mgr->default_list_executed) return 0; - err = execute_sequence(uc_mgr, &uc_mgr->default_list, + err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list, &uc_mgr->value_list, NULL, NULL); if (err < 0) { uc_error("Unable to execute default sequence"); @@ -1272,14 +1354,14 @@ static int set_verb(snd_use_case_mgr_t *uc_mgr, int err; if (enable) { - err = set_defaults(uc_mgr); + err = set_defaults(uc_mgr, false); if (err < 0) return err; seq = &verb->enable_list; } else { seq = &verb->disable_list; } - err = execute_sequence(uc_mgr, seq, + err = execute_sequence(uc_mgr, verb, seq, &verb->value_list, &uc_mgr->value_list, NULL); @@ -1310,7 +1392,7 @@ static int set_modifier(snd_use_case_mgr_t *uc_mgr, } else { seq = &modifier->disable_list; } - err = execute_sequence(uc_mgr, seq, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq, &modifier->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); @@ -1344,7 +1426,7 @@ static int set_device(snd_use_case_mgr_t *uc_mgr, } else { seq = &device->disable_list; } - err = execute_sequence(uc_mgr, seq, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq, &device->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); @@ -1357,11 +1439,72 @@ static int set_device(snd_use_case_mgr_t *uc_mgr, } /** - * \brief Init sound card use case manager. - * \param uc_mgr Returned use case manager pointer - * \param card_name name of card to open + * \brief Do the full reset + * \param uc_mgr Use case manager * \return zero on success, otherwise a negative error code */ +static int do_reset(snd_use_case_mgr_t *uc_mgr) +{ + int err; + + err = set_defaults(uc_mgr, true); + INIT_LIST_HEAD(&uc_mgr->active_modifiers); + INIT_LIST_HEAD(&uc_mgr->active_devices); + uc_mgr->active_verb = NULL; + return err; +} + +/** + * \brief Parse open arguments + * \param uc_mgr Use case manager + * \param name name of card to open + * \return the rest of the card name to open + */ +const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name) +{ + const char *end, *id; + char *args, *var; + snd_config_t *cfg, *n; + snd_config_iterator_t i, next; + char vname[128]; + size_t l; + int err; + + end = strstr(name, ">>>"); + if (end == NULL) + return name; + l = end - name - 3; + args = alloca(l + 1); + strncpy(args, name + 3, l); + args[l] = '\0'; + + err = snd_config_load_string(&cfg, args, 0); + if (err < 0) { + uc_error("error: open arguments are not valid (%s)", args); + goto skip; + } + + /* set arguments */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + goto skip; + err = snd_config_get_ascii(n, &var); + if (err < 0) + goto skip; + snprintf(vname, sizeof(vname), "@%s", id); + err = uc_mgr_set_variable(uc_mgr, vname, var); + free(var); + if (err < 0) + goto skip; + } + +skip: + snd_config_delete(cfg); + return end + 3; +} + int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name) { @@ -1388,16 +1531,15 @@ int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, mgr->suppress_nodev_errors = 1; } + if (card_name && card_name[0] == '<' && card_name[1] == '<' && card_name[2] == '<') + card_name = parse_open_variables(mgr, card_name); + 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) { err = -ENOMEM; @@ -1429,17 +1571,14 @@ _err: return err; } -/** - * \brief Reload and reparse all use case files. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) { int err; pthread_mutex_lock(&uc_mgr->mutex); + do_reset(uc_mgr); + uc_mgr_free_verb(uc_mgr); uc_mgr->default_list_executed = 0; @@ -1456,11 +1595,6 @@ int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) return err; } -/** - * \brief Close use case manager. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr) { uc_mgr_card_close(uc_mgr); @@ -1504,27 +1638,17 @@ static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr) } uc_mgr->active_verb = NULL; - err = execute_sequence(uc_mgr, &uc_mgr->default_list, - &uc_mgr->value_list, NULL, NULL); + err = set_defaults(uc_mgr, true); return err; } -/** - * \brief Reset sound card controls to default values. - * \param uc_mgr Use case manager - * \return zero on success, otherwise a negative error code - */ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) { int err; pthread_mutex_lock(&uc_mgr->mutex); - err = execute_sequence(uc_mgr, &uc_mgr->default_list, - &uc_mgr->value_list, NULL, NULL); - INIT_LIST_HEAD(&uc_mgr->active_modifiers); - INIT_LIST_HEAD(&uc_mgr->active_devices); - uc_mgr->active_verb = NULL; + err = do_reset(uc_mgr); pthread_mutex_unlock(&uc_mgr->mutex); return err; } @@ -1950,13 +2074,6 @@ static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr, name); } -/** - * \brief Obtain a list of entries - * \param uc_mgr Use case manager (may be NULL - card list) - * \param identifier (may be NULL - card list) - * \param list Returned allocated list - * \return Number of list entries if success, otherwise a negative error code - */ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **list[]) @@ -2167,16 +2284,6 @@ static int get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str) return 0; } -/** - * \brief Get current - string - * \param uc_mgr Use case manager - * \param identifier - * \param value Value pointer - * \return Zero if success, otherwise a negative error code - * - * Note: String is dynamically allocated, use free() to - * deallocate this string. - */ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, const char *identifier, const char **value) @@ -2270,19 +2377,31 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, return err; } - -/** - * \brief Get current - integer - * \param uc_mgr Use case manager - * \param identifier - * \return Value if success, otherwise a negative error code +/* + * a helper macro to obtain status and existence */ +#define geti(uc_mgr, status, ifind, str, value) ({ \ + long val = -EINVAL; \ + if (str) { \ + val = (status)((uc_mgr), (str)); \ + if (val >= 0) { \ + if ((ifind)((uc_mgr), (uc_mgr)->active_verb, (str), 0)) { \ + *(value) = val; \ + val = 0; \ + } else { \ + val = -ENOENT; \ + } \ + } \ + } \ + ; val; /* return value */ \ +}) + int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, const char *identifier, long *value) { char *str, *str1; - long err; + int err; pthread_mutex_lock(&uc_mgr->mutex); if (0) { @@ -2299,31 +2418,15 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, str = NULL; } if (check_identifier(identifier, "_devstatus")) { - if (!str) { - err = -EINVAL; - goto __end; - } - err = device_status(uc_mgr, str); - if (err >= 0) { - *value = err; - err = 0; - } + err = geti(uc_mgr, device_status, find_device, str, value); } else if (check_identifier(identifier, "_modstatus")) { - if (!str) { - err = -EINVAL; - goto __end; - } - err = modifier_status(uc_mgr, str); - if (err >= 0) { - *value = err; - err = 0; - } + err = geti(uc_mgr, modifier_status, find_modifier, str, value); #if 0 /* * enable this block if the else clause below is expanded to query * user-supplied values */ - } else if (identifier[0] == '_') + } else if (identifier[0] == '_') { err = -ENOENT; #endif } else @@ -2347,7 +2450,7 @@ static int set_fixedboot_user(snd_use_case_mgr_t *uc_mgr, } if (list_empty(&uc_mgr->fixedboot_list)) return -ENOENT; - err = execute_sequence(uc_mgr, &uc_mgr->fixedboot_list, + err = execute_sequence(uc_mgr, NULL, &uc_mgr->fixedboot_list, &uc_mgr->value_list, NULL, NULL); if (err < 0) { uc_error("Unable to execute force boot sequence"); @@ -2367,7 +2470,7 @@ static int set_boot_user(snd_use_case_mgr_t *uc_mgr, } if (list_empty(&uc_mgr->boot_list)) return -ENOENT; - err = execute_sequence(uc_mgr, &uc_mgr->boot_list, + err = execute_sequence(uc_mgr, NULL, &uc_mgr->boot_list, &uc_mgr->value_list, NULL, NULL); if (err < 0) { uc_error("Unable to execute boot sequence"); @@ -2383,7 +2486,7 @@ static int set_defaults_user(snd_use_case_mgr_t *uc_mgr, uc_error("error: wrong value for _defaults (%s)", value); return -EINVAL; } - return set_defaults(uc_mgr); + return set_defaults(uc_mgr, false); } static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, @@ -2396,7 +2499,8 @@ static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, 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, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, + &trans->transition_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list, NULL); @@ -2507,7 +2611,8 @@ static int switch_device(snd_use_case_mgr_t *uc_mgr, 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, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, + &trans->transition_list, &xold->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); @@ -2559,7 +2664,8 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr, 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, + err = execute_sequence(uc_mgr, uc_mgr->active_verb, + &trans->transition_list, &xold->value_list, &uc_mgr->active_verb->value_list, &uc_mgr->value_list); @@ -2582,13 +2688,6 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr, return err; } -/** - * \brief Set new - * \param uc_mgr Use case manager - * \param identifier - * \param value Value - * \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) @@ -2641,7 +2740,7 @@ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, /** * \brief Parse control element identifier - * \param elem_id Element identifier + * \param dst Element identifier * \param ucm_id Use case identifier * \param value String value to be parsed * \return Zero if success, otherwise a negative error code @@ -2661,7 +2760,7 @@ int snd_use_case_parse_ctl_elem_id(snd_ctl_elem_id_t *dst, strcmp(ucm_id, "CaptureSwitch")) return -EINVAL; snd_ctl_elem_id_clear(dst); - if (strcasestr(ucm_id, "name=")) + if (strcasestr(value, "name=")) return __snd_ctl_ascii_elem_id_parse(dst, value, NULL); iface = SND_CTL_ELEM_IFACE_MIXER; if (jack_control) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 48790057..98acd97e 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -31,11 +31,12 @@ */ #include "ucm_local.h" +#include #include #include #include -static int filename_filter(const struct dirent *dirent); +static int filename_filter(const struct dirent64 *dirent); static int parse_sequence(snd_use_case_mgr_t *uc_mgr, struct list_head *base, @@ -93,7 +94,7 @@ static char *replace_string(char **dst, const char *value) /* * Parse string */ -int parse_string(snd_config_t *n, char **res) +static int parse_string(snd_config_t *n, char **res) { int err; @@ -109,7 +110,7 @@ int parse_string(snd_config_t *n, char **res) /* * Parse string and substitute */ -int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, +static int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res) { const char *str; @@ -128,7 +129,7 @@ int parse_string_substitute(snd_use_case_mgr_t *uc_mgr, /* * Parse string and substitute */ -int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, +static int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, char **res) { if (uc_mgr->conf_format < 3) @@ -139,7 +140,7 @@ int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr, /* * Parse integer with substitution */ -int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, +static int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res) { char *s1, *s2; @@ -159,7 +160,7 @@ int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr, /* * Parse integer with substitution */ -int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, +static int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, long *res) { char *s1, *s2; @@ -183,7 +184,7 @@ int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr, /* * Parse safe ID */ -int parse_is_name_safe(const char *name) +static int parse_is_name_safe(const char *name) { if (strchr(name, '.')) { uc_error("char '.' not allowed in '%s'", name); @@ -192,7 +193,7 @@ int parse_is_name_safe(const char *name) return 1; } -int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) +static int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) { if (uc_mgr->conf_format < 3) { *s = strdup(s1); @@ -203,7 +204,7 @@ int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s) 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, +static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, const char *alt, char **name) { const char *id; @@ -245,6 +246,36 @@ static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) return -ENXIO; } +/* + * + */ +static int parse_syntax_field(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, const char *filename) +{ + snd_config_t *n; + long l; + int err; + + 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 %ld in %s", l, filename); + return -EINVAL; + } + /* delete this field to optimize strcmp() call in the parsing loop */ + snd_config_delete(n); + uc_mgr->conf_format = l; + return l; +} + /* * Evaluate variable regex definitions (in-place delete) */ @@ -277,6 +308,10 @@ static int evaluate_regex(snd_use_case_mgr_t *uc_mgr, err = snd_config_get_id(n, &id); if (err < 0) return err; + if (id[0] == '@') { + uc_error("error: value names starting with '@' are reserved for application variables"); + return -EINVAL; + } err = uc_mgr_define_regex(uc_mgr, id, n); if (err < 0) return err; @@ -326,8 +361,15 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, free(var); if (err < 0) return err; - uc_mgr_set_variable(uc_mgr, id, s); + if (id[0] == '@') { + free(s); + uc_error("error: value names starting with '@' are reserved for application variables"); + return -EINVAL; + } + err = uc_mgr_set_variable(uc_mgr, id, s); free(s); + if (err < 0) + return err; } snd_config_delete(d); @@ -335,6 +377,167 @@ static int evaluate_define(snd_use_case_mgr_t *uc_mgr, return evaluate_regex(uc_mgr, cfg); } +/* + * Evaluate macro definitions (in-place delete) + */ +static int evaluate_define_macro(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_t *d; + int err; + + err = snd_config_search(cfg, "DefineMacro", &d); + if (err == -ENOENT) + return 1; + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineMacro"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 6) { + uc_error("DefineMacro is supported in v6+ syntax"); + return -EINVAL; + } + + err = snd_config_merge(uc_mgr->macros, d, 0); + if (err < 0) + return err; + return 0; +} + +static int evaluate_macro1(snd_use_case_mgr_t *uc_mgr, + snd_config_t *dst, + snd_config_t *args) +{ + snd_config_iterator_t i, next; + snd_config_t *m, *mc, *a, *n; + const char *mid, *id; + char name[128], *var; + const char *s; + int err; + + err = snd_config_get_id(args, &mid); + if (err < 0) + return err; + err = snd_config_search(uc_mgr->macros, mid, &m); + if (err < 0) { + uc_error("Macro '%s' is not defined", mid); + return err; + } + + a = args; + if (snd_config_get_type(args) == SND_CONFIG_TYPE_STRING) { + err = snd_config_get_string(args, &s); + if (err < 0) + return err; + err = snd_config_load_string(&a, s, 0); + if (err < 0) + return err; + } else if (snd_config_get_type(args) != SND_CONFIG_TYPE_COMPOUND) { + return -EINVAL; + } + + /* set arguments */ + snd_config_for_each(i, next, a) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + goto __err_path; + err = snd_config_get_ascii(n, &var); + if (err < 0) + goto __err_path; + snprintf(name, sizeof(name), "__%s", id); + err = uc_mgr_set_variable(uc_mgr, name, var); + free(var); + if (err < 0) + goto __err_path; + } + + /* merge + substitute variables */ + err = snd_config_copy(&mc, m); + if (err < 0) + goto __err_path; + err = uc_mgr_evaluate_inplace(uc_mgr, mc); + if (err < 0) { + snd_config_delete(mc); + goto __err_path; + } + err = uc_mgr_config_tree_merge(uc_mgr, dst, mc, NULL, NULL); + snd_config_delete(mc); + + /* delete arguments */ + snd_config_for_each(i, next, a) { + n = snd_config_iterator_entry(i); + err = snd_config_get_id(n, &id); + if (err < 0) + goto __err_path; + snprintf(name, sizeof(name), "__%s", id); + err = uc_mgr_delete_variable(uc_mgr, name); + if (err < 0) + goto __err_path; + } + +__err_path: + if (a != args) + snd_config_delete(a); + return err; +} + +/* + * Evaluate macro definitions and instances (in-place delete) + */ +static int evaluate_macro(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg) +{ + snd_config_iterator_t i, i2, next, next2; + snd_config_t *d, *n, *n2; + int err, ret; + + ret = evaluate_define_macro(uc_mgr, cfg); + if (ret < 0) + return ret; + + err = snd_config_search(cfg, "Macro", &d); + if (err == -ENOENT) + return ret; + if (err < 0) + return err; + + if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for DefineMacro"); + return -EINVAL; + } + + if (uc_mgr->conf_format < 6) { + uc_error("Macro is supported in v6+ syntax"); + return -EINVAL; + } + + snd_config_for_each(i, next, d) { + n = snd_config_iterator_entry(i); + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { + const char *id; + if (snd_config_get_id(n, &id)) + id = ""; + uc_error("compound type expected for Macro.%s", id); + return -EINVAL; + } + snd_config_for_each(i2, next2, n) { + n2 = snd_config_iterator_entry(i2); + err = evaluate_macro1(uc_mgr, cfg, n2); + if (err < 0) + return err; + } + } + + snd_config_delete(d); + + return 0; +} + /* * Evaluate include (in-place) */ @@ -358,8 +561,7 @@ static int evaluate_include(snd_use_case_mgr_t *uc_mgr, /* * Evaluate condition (in-place) */ -static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, - snd_config_t *cfg) +static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) { snd_config_t *n; int err; @@ -375,15 +577,73 @@ static int evaluate_condition(snd_use_case_mgr_t *uc_mgr, return err; } +/* + * Evaluate variant (in-place) + */ +static int evaluate_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n, *c; + const char *id; + int err; + + err = snd_config_search(cfg, "Variant", &c); + if (err == -ENOENT) + return 1; + if (err < 0) + return err; + + if (uc_mgr->conf_format < 6) { + uc_error("Variant is supported in v6+ syntax"); + return -EINVAL; + } + + if (uc_mgr->parse_master_section) + return 1; + + if (uc_mgr->parse_variant == NULL) + goto __ret; + + snd_config_for_each(i, next, c) { + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + return -EINVAL; + + if (strcmp(id, uc_mgr->parse_variant)) + continue; + + err = uc_mgr_evaluate_inplace(uc_mgr, n); + if (err < 0) + return err; + + err = uc_mgr_config_tree_merge(uc_mgr, cfg, n, NULL, NULL); + if (err < 0) + return err; + snd_config_delete(c); + return 0; + } + +__ret: + snd_config_delete(c); + return 1; +} + /* * 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; + long iterations = 10000; + int err1 = 0, err2 = 0, err3 = 0, err4 = 0, err5 = 0; - while (err1 == 0 || err2 == 0 || err3 == 0) { + while (err1 == 0 || err2 == 0 || err3 == 0 || err4 == 0 || err5 == 0) { + if (iterations == 0) { + uc_error("Maximal inplace evaluation iterations number reached (recursive references?)"); + return -EINVAL; + } + iterations--; /* variables at first */ err1 = evaluate_define(uc_mgr, cfg); if (err1 < 0) @@ -392,13 +652,29 @@ int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr, err2 = evaluate_include(uc_mgr, cfg); if (err2 < 0) return err2; - /* include may define another variables */ + /* include or macro may define another variables */ /* conditions may depend on them */ if (err2 == 0) continue; - err3 = evaluate_condition(uc_mgr, cfg); + err3 = evaluate_variant(uc_mgr, cfg); if (err3 < 0) return err3; + if (err3 == 0) + continue; + uc_mgr->macro_hops++; + if (uc_mgr->macro_hops > 100) { + uc_error("Maximal macro hops reached!"); + return -EINVAL; + } + err4 = evaluate_macro(uc_mgr, cfg); + uc_mgr->macro_hops--; + if (err4 < 0) + return err4; + if (err4 == 0) + continue; + err5 = evaluate_condition(uc_mgr, cfg); + if (err5 < 0) + return err5; } return 0; } @@ -830,30 +1106,41 @@ cset: continue; } - if (strcmp(cmd, "enadev") == 0) { - /* need to enable a component device */ + if (strcmp(cmd, "enadev") == 0 || + strcmp(cmd, "disdev") == 0) { + /* need to enable or disable a component device */ curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ; - err = parse_component_seq(uc_mgr, n, 1, + err = parse_component_seq(uc_mgr, n, + strcmp(cmd, "enadev") == 0, &curr->data.cmpt_seq); if (err < 0) { - uc_error("error: enadev requires a valid device!"); + uc_error("error: %s requires a valid device!", cmd); return err; } continue; } - if (strcmp(cmd, "disdev") == 0) { - /* need to disable a component device */ - curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ; - err = parse_component_seq(uc_mgr, n, 0, - &curr->data.cmpt_seq); + if (strcmp(cmd, "enadev2") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ; + goto device; + } + + if (strcmp(cmd, "disdev2") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ; +device: + err = parse_string_substitute3(uc_mgr, n, &curr->data.device); if (err < 0) { - uc_error("error: disdev requires a valid device!"); + uc_error("error: %s requires a valid device!", cmd); return err; } continue; } + if (strcmp(cmd, "disdevall") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL; + continue; + } + if (strcmp(cmd, "cset-bin-file") == 0) { curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE; goto cset; @@ -1808,6 +2095,65 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, return err; } +/* + * Parse variant information + */ +static int parse_variant(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, + char **_vfile, char **_vcomment) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + char *file = NULL, *comment = NULL; + int err; + + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* get use case verb file name */ + if (strcmp(id, "File") == 0) { + if (_vfile) { + err = parse_string_substitute3(uc_mgr, n, &file); + if (err < 0) { + uc_error("failed to get File"); + goto __error; + } + } + continue; + } + + /* get optional use case comment */ + if (strncmp(id, "Comment", 7) == 0) { + if (_vcomment) { + err = parse_string_substitute3(uc_mgr, n, &comment); + if (err < 0) { + uc_error("error: failed to get Comment"); + goto __error; + } + } + continue; + } + + uc_error("unknown field '%s' in Variant section", id); + err = -EINVAL; + goto __error; + } + + if (_vfile) + *_vfile = file; + if (_vcomment) + *_vcomment = comment; + return 0; + +__error: + free(file); + free(comment); + return err; +} + /* * Parse master section for "Use Case" and "File" tags. */ @@ -1816,8 +2162,9 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data2 ATTRIBUTE_UNUSED) { snd_config_iterator_t i, next; - snd_config_t *n; + snd_config_t *n, *variant = NULL; char *use_case_name, *file = NULL, *comment = NULL; + bool variant_ok = false; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -1832,7 +2179,9 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, } /* in-place evaluation */ + uc_mgr->parse_master_section = 1; err = uc_mgr_evaluate_inplace(uc_mgr, cfg); + uc_mgr->parse_master_section = 0; if (err < 0) goto __error; @@ -1863,20 +2212,69 @@ static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, continue; } - uc_error("unknown field %s in master section"); + if (uc_mgr->conf_format >= 6 && strcmp(id, "Variant") == 0) { + snd_config_iterator_t i2, next2; + variant = n; + snd_config_for_each(i2, next2, n) { + const char *id2; + snd_config_t *n2; + n2 = snd_config_iterator_entry(i2); + if (snd_config_get_id(n2, &id2) < 0) + continue; + err = uc_mgr_evaluate_inplace(uc_mgr, n2); + if (err < 0) + goto __error; + if (strcmp(use_case_name, id2) == 0) + variant_ok = true; + } + continue; + } + + uc_error("unknown field '%s' in SectionUseCase", id); } - uc_dbg("use_case_name %s file '%s'", use_case_name, file); - - /* do we have both use case name and file ? */ - if (!file) { - uc_error("error: use case missing file"); + if (variant && !variant_ok) { + uc_error("error: undefined variant '%s'", use_case_name); err = -EINVAL; goto __error; } - /* parse verb file */ - err = parse_verb_file(uc_mgr, use_case_name, comment, file); + if (!variant) { + uc_dbg("use_case_name %s file '%s'", use_case_name, file); + + /* do we have both use case name and file ? */ + if (!file) { + uc_error("error: use case missing file"); + err = -EINVAL; + goto __error; + } + + /* parse verb file */ + err = parse_verb_file(uc_mgr, use_case_name, comment, file); + } else { + /* parse variants */ + snd_config_for_each(i, next, variant) { + char *vfile, *vcomment; + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + if (!parse_is_name_safe(id)) { + err = -EINVAL; + goto __error; + } + err = parse_variant(uc_mgr, n, &vfile, &vcomment); + if (err < 0) + break; + uc_mgr->parse_variant = id; + err = parse_verb_file(uc_mgr, id, + vcomment ? vcomment : comment, + vfile ? vfile : file); + uc_mgr->parse_variant = NULL; + free(vfile); + free(vcomment); + } + } __error: free(use_case_name); @@ -1998,7 +2396,6 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) snd_config_iterator_t i, next; snd_config_t *n; const char *id; - long l; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -2007,23 +2404,9 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) } if (uc_mgr->conf_format >= 2) { - err = snd_config_search(cfg, "Syntax", &n); - if (err < 0) { - uc_error("Syntax field not found in %s", uc_mgr->conf_file_name); - return -EINVAL; - } - err = snd_config_get_integer(n, &l); - if (err < 0) { - uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name); + err = parse_syntax_field(uc_mgr, cfg, uc_mgr->conf_file_name); + if (err < 0) return err; - } - if (l < 2 || l > SYNTAX_VERSION_MAX) { - uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name); - return -EINVAL; - } - /* delete this field to avoid strcmp() call in the loop */ - snd_config_delete(n); - uc_mgr->conf_format = l; } /* in-place evaluation */ @@ -2105,6 +2488,10 @@ static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) if (strcmp(id, "Error") == 0) return error_node(uc_mgr, n); + /* skip further Syntax value updates (Include) */ + if (strcmp(id, "Syntax") == 0) + continue; + uc_error("unknown master file field %s", id); } return 0; @@ -2186,6 +2573,7 @@ static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, *n2; const char *id; char *dir = NULL, *file = NULL, fn[PATH_MAX]; + struct stat64 st; long version; int err; @@ -2260,23 +2648,51 @@ static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr, } 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; + if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) { + if (S_ISLNK(st.st_mode)) { + ssize_t r; + char *link, *dir2, *p; + + link = malloc(PATH_MAX); + if (link == NULL) + goto __enomem; + r = readlink(fn, link, PATH_MAX - 1); + if (r <= 0) { + free(link); + goto __next; + } + link[r] = '\0'; + p = strrchr(link, '/'); + if (p) { + *p = '\0'; + dir2 = malloc(PATH_MAX); + if (dir2 == NULL) { + free(link); + goto __enomem; + } + strncpy(dir2, dir, PATH_MAX - 1); + strncat(dir2, "/", PATH_MAX - 1); + strncat(dir2, link, PATH_MAX - 1); + fn[PATH_MAX - 1] = '\0'; + free(dir); + dir = dir2; + } + free(link); } + if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL) + goto __enomem; + if (replace_string(&uc_mgr->conf_file_name, file) == NULL) + goto __enomem; strncpy(filename, fn, PATH_MAX); + filename[PATH_MAX - 1] = '\0'; uc_mgr->conf_format = version; goto __ok; } __next: free(file); - free(dir); + if (dir != fn) + free(dir); dir = NULL; file = NULL; } @@ -2284,11 +2700,16 @@ __next: err = -ENOENT; goto __error; +__enomem: + err = -ENOMEM; + goto __error; + __ok: err = 0; __error: free(file); - free(dir); + if (dir != fn) + free(dir); return err; } @@ -2299,7 +2720,6 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, snd_config_iterator_t i, next; snd_config_t *n; const char *id; - long l; int err; if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { @@ -2307,23 +2727,9 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, return -EINVAL; } - 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); + err = parse_syntax_field(uc_mgr, cfg, filename); + if (err < 0) 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); @@ -2354,6 +2760,10 @@ static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr, continue; } + /* skip further Syntax value updates (Include) */ + if (strcmp(id, "Syntax") == 0) + continue; + uc_error("unknown toplevel field %s", id); } @@ -2405,6 +2815,14 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) const char *name; int err; + err = snd_config_top(&uc_mgr->local_config); + if (err < 0) + return err; + + err = snd_config_top(&uc_mgr->macros); + if (err < 0) + return err; + name = uc_mgr->card_name; if (strncmp(name, "hw:", 3) == 0) { err = get_by_card(uc_mgr, name); @@ -2423,6 +2841,10 @@ int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) goto __error; err = parse_master_file(uc_mgr, cfg); + if (uc_mgr->macros) { + snd_config_delete(uc_mgr->macros); + uc_mgr->macros = NULL; + } snd_config_delete(cfg); if (err < 0) { uc_mgr_free_ctl_list(uc_mgr); @@ -2437,7 +2859,7 @@ __error: return err; } -static int filename_filter(const struct dirent *dirent) +static int filename_filter(const struct dirent64 *dirent) { if (dirent == NULL) return 0; @@ -2466,12 +2888,26 @@ int uc_mgr_scan_master_configs(const char **_list[]) { char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX]; char *env = getenv(ALSA_CONFIG_UCM2_VAR); + snd_use_case_mgr_t *uc_mgr; const char **list, *d_name; + char *s; snd_config_t *cfg, *c; - int i, j, cnt, err; + int i, j, cnt, err, cards; long l; ssize_t ss; - struct dirent **namelist; + struct dirent64 **namelist; + + i = -1; + cards = 0; + while (1) { + err = snd_card_next(&i); + if (err < 0) + return err; + if (i < 0) + break; + cards++; + } + cards += 4; /* plug-and-play */ if (env) snprintf(filename, sizeof(filename), "%s/conf.virt.d", env); @@ -2479,12 +2915,12 @@ int uc_mgr_scan_master_configs(const char **_list[]) snprintf(filename, sizeof(filename), "%s/ucm2/conf.virt.d", snd_config_topdir()); -#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID) -#define SORTFUNC versionsort +#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__sun) && !defined(__ANDROID__) +#define SORTFUNC versionsort64 #else -#define SORTFUNC alphasort +#define SORTFUNC alphasort64 #endif - err = scandir(filename, &namelist, filename_filter, SORTFUNC); + err = scandir64(filename, &namelist, filename_filter, SORTFUNC); if (err < 0) { err = -errno; uc_error("error: could not scan directory %s: %s", @@ -2507,13 +2943,46 @@ int uc_mgr_scan_master_configs(const char **_list[]) } } - list = calloc(1, cnt * 2 * sizeof(char *)); + j = 0; + list = calloc(1, (cards + cnt) * 2 * sizeof(char *)); if (list == NULL) { err = -ENOMEM; goto __err; } - for (i = j = 0; i < cnt; i++) { + i = -1; + while (j / 2 < cards) { + err = snd_card_next(&i); + if (err < 0) + goto __err; + if (i < 0) + break; + snprintf(fn, sizeof(fn), "-hw:%d", i); + err = snd_use_case_mgr_open(&uc_mgr, fn); + if (err == -ENOENT || err == -ENXIO) + continue; + if (err < 0) { + uc_error("Unable to open '%s': %s", fn, snd_strerror(err)); + goto __err; + } + err = snd_use_case_get(uc_mgr, "comment", (const char **)&s); + if (err < 0) { + err = snd_card_get_longname(i, &s); + if (err < 0) + goto __err; + } + snd_use_case_mgr_close(uc_mgr); + list[j] = strdup(fn + 1); + if (list[j] == NULL) { + free(s); + err = -ENOMEM; + goto __err; + } + list[j + 1] = s; + j += 2; + } + + for (i = 0; i < cnt; i++) { d_name = namelist[i]->d_name; @@ -2570,23 +3039,21 @@ int uc_mgr_scan_master_configs(const char **_list[]) } j += 2; } - err = j; + err = 0; __err: - for (i = 0; i < cnt; i++) { + for (i = 0; i < cnt; i++) free(namelist[i]); - if (err < 0) { + free(namelist); + if (err < 0) { + for (i = 0; i < j; i++) { free((void *)list[i * 2]); free((void *)list[i * 2 + 1]); } - } - free(namelist); - - if (err >= 0) { - *_list = list; - } else { free(list); + return err; } - return err; + *_list = list; + return j; } diff --git a/src/ucm/ucm_confdoc.h b/src/ucm/ucm_confdoc.h index 7241f5e8..7a907934 100644 --- a/src/ucm/ucm_confdoc.h +++ b/src/ucm/ucm_confdoc.h @@ -63,6 +63,8 @@ use case verbs for that sound card. i.e.: # Example master file for blah sound card # By Joe Blogs +Syntax 6 + # Use Case name for user interface Comment "Nice Abstracted Soundcard" @@ -91,7 +93,11 @@ ValueDefaults { # ALSA card controls which may be modified by user after initial settings. BootSequence [ - cset "name='My control' on" + 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" ] # Define fixed boot sequence @@ -117,23 +123,16 @@ 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" + disdevall "" # run DisableSequence for all devices ] 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" + cset "name='Power Save' on" ] # Optional transition verb TransitionSequence."ToCaseName" [ + disdevall "" # run DisableSequence for all devices msleep 1 ] @@ -212,16 +211,25 @@ SectionModifier."Capture Voice" { Value { TQ Voice CapturePCM "hw:${CardId},11" + PlaybackMixerElem "Master" PlaybackVolume "name='Master Playback Volume',index=2" PlaybackSwitch "name='Master Playback Switch',index=2" } } ~~~ +### Sequence graphs + +\image html ucm-seq-verb.svg +\image html ucm-seq-device.svg + ### Sequence commands Command name | Description ---------------|---------------------------------------------- +enadev2 ARG | execute device enable sequence +disdev2 ARG | execute device disable sequence +disdevall "" | execute device disable sequence for all devices in verb 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 @@ -275,12 +283,25 @@ configuration like volumes or switches. The alsactl ensures the persistency (sto the state of the controls to the /var tree and loads the previous state in the next boot). +\image html ucm-seq-boot.svg + ### 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 +to set the fixed values for the volume settings in 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*. +values). The default volume settings should be set in the *BootSequence*. The purpose +for this scheme is to allow users to override defaults using the alsactl sound card +state management. + +Checklist: + +1. Set default volume in BootSequence +2. Verb's EnableSequence should ensure that all devices are turned off (mixer paths) + to avoid simultaneous device use - the previous state is unknown (see *disdevall* + and *disdev2* commands or create a new custom command sequence) + +\image html ucm-volume.svg ### Dynamic configuration tree @@ -376,12 +397,18 @@ ${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 +${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 + +#### Special whole string substitution + +Substituted string | Value +---------------------|--------------------- +${evali:\} | Evaluate expression like *($var+2)/3* [**Syntax 6**]; target node will be integer; substituted only in the LibraryConfig subtree #### Find card substitution @@ -446,6 +473,41 @@ substrings are stored to a separate variable with the sequence number postfix. Variables can be substituted using the `${var:rval1}` reference for example. +### Macros + +Macros were added for *Syntax* version *6*. The *DefineMacro* defines new +macro like: + +~~~{.html} +DefineMacro.macro1 { + Define.a "${var:__arg1}" + Define.b "${var:__other}" + # Device or any other block may be defined here... +} +~~~ + +The arguments in the macro are refered as the variables with the double +underscore name prefix (like *__variable*). The configuration block in +the DefineMacro subtree is always evaluated (including arguments and variables) +at the time of the instantiation. + +The macros can be instantiated (expanded) using: + +~~~{.html} +# short version +Macro.id1.macro1 "arg1='something 1',other='other x'" + +# long version +Macro.id1.macro1 { + arg1 'something 1' + other 'other x' +} +~~~ + +The second identifier (in example as *id1*) must be unique, but the contents +is ignored. It just differentiate the items in the subtree (allowing multiple +instances for one macro). + ### Conditions The configuration tree evaluation supports the conditions - *If* blocks. Each *If* blocks @@ -523,6 +585,41 @@ If.fmic { } ~~~ +### Variants + +To avoid duplication of the many configuration files for the cases with +minimal configuration changes, there is the variant extension. Variants were +added for *Syntax* version *6*. + +The bellow example will create two verbs - "HiFi" and "HiFi 7.1" with +the different playback channels (2 and 8) for the "Speaker" device. + +Example (main configuration file): + +~~~{.html} +SectionUseCase."HiFi" { + File "HiFi.conf" + Variant."HiFi" { + Comment "HiFi" + } + Variant."HiFi 7+1" { + Comment "HiFi 7.1" + } +} +~~~ + +Example (verb configuration file - HiFi.conf): + +~~~{.html} +SectionDevice."Speaker" { + Value { + PlaybackChannels 2 + } + Variant."HiFi 7+1".Value { + PlaybackChannels 8 + } +} +~~~ */ diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c index 4ddf5d15..276cf592 100644 --- a/src/ucm/ucm_exec.c +++ b/src/ucm/ucm_exec.c @@ -33,6 +33,15 @@ #include #include +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) +#include +#if defined(__DragonFly__) +#define environ NULL /* XXX */ +#else +extern char **environ; +#endif +#endif + static pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER; /* @@ -44,10 +53,10 @@ static int find_exec(const char *name, char *out, size_t len) char bin[PATH_MAX]; char *path, *tmp, *tmp2 = NULL; DIR *dir; - struct dirent *de; - struct stat st; + struct dirent64 *de; + struct stat64 st; if (name[0] == '/') { - if (lstat(name, &st)) + if (lstat64(name, &st)) return 0; if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) return 0; @@ -63,12 +72,12 @@ static int find_exec(const char *name, char *out, size_t len) tmp = strtok_r(path, ":", &tmp2); while (tmp && !ret) { if ((dir = opendir(tmp))) { - while ((de = readdir(dir))) { + while ((de = readdir64(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)) + if (lstat64(bin, &st)) continue; if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC)) diff --git a/src/ucm/ucm_include.c b/src/ucm/ucm_include.c index 0b55890e..7def2d31 100644 --- a/src/ucm/ucm_include.c +++ b/src/ucm/ucm_include.c @@ -305,13 +305,14 @@ int uc_mgr_evaluate_include(snd_use_case_mgr_t *uc_mgr, if (a == NULL) continue; err = uc_mgr_evaluate_inplace(uc_mgr, a); - if (err < 0) + if (err < 0) { + snd_config_delete(a); return err; + } err = uc_mgr_config_tree_merge(uc_mgr, parent, a, before, after); + snd_config_delete(a); if (err < 0) return err; - snd_config_delete(a); - } return 0; } diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index e6ebe0f3..fc249058 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -40,7 +40,7 @@ #include #include "use-case.h" -#define SYNTAX_VERSION_MAX 5 +#define SYNTAX_VERSION_MAX 6 #define MAX_CARD_SHORT_NAME 32 #define MAX_CARD_LONG_NAME 80 @@ -57,6 +57,9 @@ #define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 10 #define SEQUENCE_ELEMENT_TYPE_SYSSET 11 #define SEQUENCE_ELEMENT_TYPE_CFGSAVE 12 +#define SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ 13 +#define SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ 14 +#define SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL 15 struct ucm_value { struct list_head list; @@ -80,6 +83,7 @@ struct sequence_element { char *exec; char *sysw; char *cfgsave; + char *device; struct component_sequence cmpt_seq; /* component sequence */ } data; }; @@ -228,6 +232,9 @@ struct snd_use_case_mgr { int conf_format; unsigned int ucm_card_number; int suppress_nodev_errors; + const char *parse_variant; + int parse_master_section; + int sequence_hops; /* UCM cards list */ struct list_head cards_list; @@ -262,6 +269,10 @@ struct snd_use_case_mgr { /* list of opened control devices */ struct list_head ctl_list; + /* tree with macros */ + snd_config_t *macros; + int macro_hops; + /* local library configuration */ snd_config_t *local_config; @@ -334,6 +345,8 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, const char *val); +int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name); + int uc_mgr_get_substituted_value(snd_use_case_mgr_t *uc_mgr, char **_rvalue, const char *value); diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index 0ed400d1..2b010338 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -191,6 +191,7 @@ static char *rval_card_id_by_name(snd_use_case_mgr_t *uc_mgr, const char *id) return strdup(snd_ctl_card_info_get_id(ctl_list->ctl_info)); } +#ifndef DOC_HIDDEN 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 *); @@ -212,6 +213,7 @@ struct lookup_iterate { struct ctl_list *ctl_list; void *info; }; +#endif /* DOC_HIDDEN */ static char *rval_lookup_main(snd_use_case_mgr_t *uc_mgr, const char *query, @@ -490,7 +492,13 @@ static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *i { char *e; - e = getenv(id); + if (*id == '-') { + e = getenv(id + 1); + if (e == NULL) + e = ""; + } else { + e = getenv(id); + } if (e) return strdup(e); return NULL; @@ -499,7 +507,7 @@ static char *rval_env(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *i static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char *id) { char path[PATH_MAX], link[PATH_MAX + 1]; - struct stat sb; + struct stat64 sb; ssize_t len; const char *e; int fd; @@ -510,7 +518,7 @@ static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char if (id[0] == '/') id++; snprintf(path, sizeof(path), "%s/%s", e, id); - if (lstat(path, &sb) != 0) + if (lstat64(path, &sb) != 0) return NULL; if (S_ISLNK(sb.st_mode)) { len = readlink(path, link, sizeof(link) - 1); @@ -549,13 +557,22 @@ static char *rval_sysfs(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, const char static char *rval_var(snd_use_case_mgr_t *uc_mgr, const char *id) { const char *v; + bool ignore_not_found = false; if (uc_mgr->conf_format < 3) { uc_error("variable substitution is supported in v3+ syntax"); return NULL; } + if (id[0] == '-') { + ignore_not_found = true; + id++; + } else if (id[0] == '@') { + ignore_not_found = true; + } v = uc_mgr_get_variable(uc_mgr, id); + if (v == NULL && ignore_not_found) + v = ""; if (v) return strdup(v); return NULL; @@ -582,7 +599,7 @@ static char *rval_eval(snd_use_case_mgr_t *uc_mgr, const char *e) int err; if (uc_mgr->conf_format < 5) { - uc_error("variable substitution is supported in v5+ syntax"); + uc_error("variable evaluation is supported in v5+ syntax"); return NULL; } err = _snd_eval_string(&dst, e, rval_eval_var_cb, uc_mgr); @@ -597,6 +614,41 @@ static char *rval_eval(snd_use_case_mgr_t *uc_mgr, const char *e) return r; } +static int rval_evali(snd_use_case_mgr_t *uc_mgr, snd_config_t *node, const char *e) +{ + snd_config_t *dst; + const char *id; + char *s; + size_t l; + int err; + + if (uc_mgr->conf_format < 6) { + uc_error("variable evaluation is supported in v6+ syntax"); + return -EINVAL; + } + err = snd_config_get_id(node, &id); + if (err < 0) + return err; + l = strlen(e); + if (e[l-1] != '}') + return -EINVAL; + s = malloc(l + 1); + if (s == NULL) + return -ENOMEM; + strcpy(s, e); + s[l-1] = '\0'; + err = _snd_eval_string(&dst, s + 8, rval_eval_var_cb, uc_mgr); + free(s); + if (err < 0) { + uc_error("unable to evaluate '%s'", e); + return err; + } + err = snd_config_set_id(dst, id); + if (err < 0) + return err; + return snd_config_substitute(node, dst); +} + #define MATCH_VARIABLE(name, id, fcn, empty_ok) \ if (strncmp((name), (id), sizeof(id) - 1) == 0) { \ rval = fcn(uc_mgr); \ @@ -727,6 +779,8 @@ __match2: strncpy_with_escape(v2, value + idsize, rvalsize); idsize += rvalsize + 1; if (*v2 == '$' && uc_mgr->conf_format >= 3) { + if (strncmp(value, "${eval:", 7) == 0) + goto __direct_fcn2; tmp = uc_mgr_get_variable(uc_mgr, v2 + 1); if (tmp == NULL) { uc_error("define '%s' is not reachable in this context!", v2 + 1); @@ -735,6 +789,7 @@ __match2: rval = fcn2(uc_mgr, tmp); } } else { +__direct_fcn2: rval = fcn2(uc_mgr, v2); } goto __rval; @@ -816,6 +871,8 @@ int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node) return err; if (!uc_mgr_substitute_check(s2)) return 0; + if (strncmp(s2, "${evali:", 8) == 0) + return rval_evali(uc_mgr, node, s2); err = uc_mgr_get_substituted_value(uc_mgr, &s, s2); if (err < 0) return err; @@ -826,6 +883,12 @@ int uc_mgr_substitute_tree(snd_use_case_mgr_t *uc_mgr, snd_config_t *node) } return 0; } + /* exception - macros are evaluated when instantied */ + err = snd_config_get_id(node, &id); + if (err < 0) + return err; + if (id && strcmp(id, "DefineMacro") == 0) + return 0; snd_config_for_each(i, next, node) { n = snd_config_iterator_entry(i); err = uc_mgr_substitute_tree(uc_mgr, n); diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 598b02ab..bc33ee5a 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -400,6 +400,14 @@ int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) return 0; } +static void uc_mgr_free_value1(struct ucm_value *val) +{ + free(val->name); + free(val->data); + list_del(&val->list); + free(val); +} + void uc_mgr_free_value(struct list_head *base) { struct list_head *pos, *npos; @@ -407,10 +415,7 @@ void uc_mgr_free_value(struct list_head *base) list_for_each_safe(pos, npos, base) { val = list_entry(pos, struct ucm_value, list); - free(val->name); - free(val->data); - list_del(&val->list); - free(val); + uc_mgr_free_value1(val); } } @@ -515,6 +520,10 @@ void uc_mgr_free_sequence_element(struct sequence_element *seq) case SEQUENCE_ELEMENT_TYPE_CFGSAVE: free(seq->data.cfgsave); break; + case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ: + case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ: + free(seq->data.device); + break; default: break; } @@ -704,11 +713,35 @@ int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, return 0; } +int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name) +{ + struct list_head *pos; + struct ucm_value *curr; + + list_for_each(pos, &uc_mgr->variable_list) { + curr = list_entry(pos, struct ucm_value, list); + if (strcmp(curr->name, name) == 0) { + uc_mgr_free_value1(curr); + return 0; + } + } + + return -ENOENT; +} + void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) { struct list_head *pos, *npos; struct use_case_verb *verb; + if (uc_mgr->local_config) { + snd_config_delete(uc_mgr->local_config); + uc_mgr->local_config = NULL; + } + if (uc_mgr->macros) { + snd_config_delete(uc_mgr->macros); + uc_mgr->macros = NULL; + } list_for_each_safe(pos, npos, &uc_mgr->verb_list) { verb = list_entry(pos, struct use_case_verb, list); free(verb->name); @@ -743,7 +776,6 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) 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); diff --git a/src/userfile.c b/src/userfile.c index 4a740834..492ea9cb 100644 --- a/src/userfile.c +++ b/src/userfile.c @@ -18,7 +18,7 @@ * */ -#include +#include "config.h" #include #include #include diff --git a/test/audio_time.c b/test/audio_time.c index 919bebe3..00619966 100644 --- a/test/audio_time.c +++ b/test/audio_time.c @@ -4,8 +4,11 @@ * helpful to verify the information reported by drivers. */ +#include "config.h" #include +#if HAVE_MALLOC_H #include +#endif #include #include #include diff --git a/test/chmap.c b/test/chmap.c index ad3b305b..52f29a76 100644 --- a/test/chmap.c +++ b/test/chmap.c @@ -2,6 +2,7 @@ * channel mapping API test program */ +#include "config.h" #include #include #include diff --git a/test/control.c b/test/control.c index f4b437ee..b0a3877e 100644 --- a/test/control.c +++ b/test/control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include "../include/asoundlib.h" diff --git a/test/latency.c b/test/latency.c index 298bab8a..c40b2ae7 100644 --- a/test/latency.c +++ b/test/latency.c @@ -27,16 +27,30 @@ * */ +#include "config.h" #include #include #include #include #include #include +#include #include "../include/asoundlib.h" #include #include +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + +#if defined(__OpenBSD__) +#define sched_getparam(pid, parm) (-1) +#define sched_setscheduler(pid, policy, parm) (-1) +#endif + +typedef struct timespec timestamp_t; + +char *sched_policy = "rr"; char *pdevice = "hw:0,0"; char *cdevice = "hw:0,0"; snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; @@ -49,11 +63,42 @@ int latency_max = 2048; /* in frames / 2 */ int loop_sec = 30; /* seconds */ int block = 0; /* block mode */ int use_poll = 0; +int usleep_val = 0; int resample = 1; +int sys_latency = 0; /* data I/O: use system timings instead driver wakeups */ +int pos_dump = 0; /* dump positions */ +int realtime_check = 0; unsigned long loop_limit; +snd_pcm_uframes_t playback_buffer_size; snd_output_t *output = NULL; +static inline long long frames_to_micro(size_t frames) +{ + return (long long)((frames * 1000000LL) + (rate / 2)) / rate; +} + +void timestamp_now(timestamp_t *tstamp) +{ + if (clock_gettime(CLOCK_MONOTONIC_RAW, tstamp)) + printf("clock_gettime() failed\n"); +} + +long long timestamp_diff_micro(timestamp_t *tstamp) +{ + timestamp_t now, diff; + timestamp_now(&now); + if (tstamp->tv_nsec > now.tv_nsec) { + diff.tv_sec = now.tv_sec - tstamp->tv_sec - 1; + diff.tv_nsec = (now.tv_nsec + 1000000000L) - tstamp->tv_nsec; + } else { + diff.tv_sec = now.tv_sec - tstamp->tv_sec; + diff.tv_nsec = now.tv_nsec - tstamp->tv_nsec; + } + /* microseconds */ + return (diff.tv_sec * 1000000) + ((diff.tv_nsec + 500L) / 1000L); +} + int setparams_stream(snd_pcm_t *handle, snd_pcm_hw_params_t *params, const char *id) @@ -96,6 +141,14 @@ int setparams_stream(snd_pcm_t *handle, printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); return -EINVAL; } + /* we do not want driver wakeups */ + if (sys_latency > 0 && snd_pcm_hw_params_can_disable_period_wakeup(params)) { + err = snd_pcm_hw_params_set_period_wakeup(handle, params, 0); + if (err < 0) { + printf("Cannot disable period wakeups for %s\n", id); + return err; + } + } return 0; } @@ -207,11 +260,11 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) return -1; __set_it: if ((err = setparams_bufsize(phandle, p_params, pt_params, *bufsize, "playback")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for playback stream: %s\n", snd_strerror(err)); exit(0); } if ((err = setparams_bufsize(chandle, c_params, ct_params, *bufsize, "capture")) < 0) { - printf("Unable to set sw parameters for playback stream: %s\n", snd_strerror(err)); + printf("Unable to set hw parameters for capture stream: %s\n", snd_strerror(err)); exit(0); } @@ -227,6 +280,7 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) goto __again; snd_pcm_hw_params_get_buffer_size(p_params, &p_size); + playback_buffer_size = p_size; if (p_psize * 2 < p_size) { snd_pcm_hw_params_get_periods_min(p_params, &val, NULL); if (val > 2) { @@ -311,18 +365,27 @@ void gettimestamp(snd_pcm_t *handle, snd_timestamp_t *timestamp) void setscheduler(void) { struct sched_param sched_param; + int policy = SCHED_RR; + const char *spolicy = "Round Robin"; + if (strcasecmp(sched_policy, "fifo") == 0) { + policy = SCHED_FIFO; + spolicy = "FIFO"; + } else if (strcasecmp(sched_policy, "other") == 0) { + policy = SCHED_OTHER; + spolicy = "OTHER"; + } if (sched_getparam(0, &sched_param) < 0) { printf("Scheduler getparam failed...\n"); return; } - sched_param.sched_priority = sched_get_priority_max(SCHED_RR); - if (!sched_setscheduler(0, SCHED_RR, &sched_param)) { - printf("Scheduler set to Round Robin with priority %i...\n", sched_param.sched_priority); + sched_param.sched_priority = sched_get_priority_max(policy); + if (!sched_setscheduler(0, policy, &sched_param)) { + printf("Scheduler set to %s with priority %i...\n", spolicy, sched_param.sched_priority); fflush(stdout); return; } - printf("!!!Scheduler set to Round Robin with priority %i FAILED!!!\n", sched_param.sched_priority); + printf("!!!Scheduler set to %s with priority %i FAILED!!!\n", spolicy, sched_param.sched_priority); } long timediff(snd_timestamp_t t1, snd_timestamp_t t2) @@ -354,7 +417,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames, size_t *max } // printf("read = %li\n", r); } else { - int frame_bytes = (snd_pcm_format_width(format) / 8) * channels; + int frame_bytes = (snd_pcm_format_physical_width(format) / 8) * channels; do { r = snd_pcm_readi(handle, buf, len); if (r > 0) { @@ -374,7 +437,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames, size_t *max long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) { long r; - int frame_bytes = (snd_pcm_format_width(format) / 8) * channels; + int frame_bytes = (snd_pcm_format_physical_width(format) / 8) * channels; while (len > 0) { r = snd_pcm_writei(handle, buf, len); @@ -390,7 +453,7 @@ long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) } return 0; } - + #define FILTERSWEEP_LFO_CENTER 2000. #define FILTERSWEEP_LFO_DEPTH 1800. #define FILTERSWEEP_LFO_FREQ 0.2 @@ -434,6 +497,19 @@ void applyeffect(char* buffer,int r) } } +static ssize_t get_avail(snd_pcm_t *pcm) +{ + ssize_t avail; + + while (1) { + avail = snd_pcm_avail(pcm); + if (avail == -EAGAIN) + continue; + break; + } + return avail; +} + void help(void) { int k; @@ -444,6 +520,7 @@ void help(void) "-C,--cdevice capture device\n" "-m,--min minimum latency in frames\n" "-M,--max maximum latency in frames\n" +"-U,--updates I/O updates in milliseconds (0 = off)\n" "-F,--frames frames to transfer\n" "-f,--format sample format\n" "-c,--channels channels\n" @@ -453,7 +530,12 @@ void help(void) "-s,--seconds duration of test in seconds\n" "-b,--block block mode\n" "-p,--poll use poll (wait for event - reduces CPU usage)\n" +"-y,--usleep sleep for the specified amount of microseconds between\n" +" stream updates (default 0 - off)\n" "-e,--effect apply an effect (bandpass filter sweep)\n" +"-x,--posdump dump buffer positions\n" +"-X,--realtime do a realtime check (buffering)\n" +"-O,--policy set scheduler policy (RR, FIFO or OTHER)\n" ); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { @@ -480,6 +562,7 @@ int main(int argc, char *argv[]) {"cdevice", 1, NULL, 'C'}, {"min", 1, NULL, 'm'}, {"max", 1, NULL, 'M'}, + {"updates", 1, NULL, 'U'}, {"frames", 1, NULL, 'F'}, {"format", 1, NULL, 'f'}, {"channels", 1, NULL, 'c'}, @@ -489,7 +572,11 @@ int main(int argc, char *argv[]) {"seconds", 1, NULL, 's'}, {"block", 0, NULL, 'b'}, {"poll", 0, NULL, 'p'}, + {"usleep", 1, NULL, 'y'}, {"effect", 0, NULL, 'e'}, + {"posdump", 0, NULL, 'x'}, + {"realtime", 0, NULL, 'X'}, + {"policy", 1, NULL, 'O'}, {NULL, 0, NULL, 0}, }; snd_pcm_t *phandle, *chandle; @@ -497,13 +584,14 @@ int main(int argc, char *argv[]) int err, latency, morehelp; int ok; snd_timestamp_t p_tstamp, c_tstamp; - ssize_t r; + ssize_t r, cap_avail, cap_avail_max, pbk_fill, pbk_fill_min; size_t frames_in, frames_out, in_max; + timestamp_t tstamp_start; int effect = 0; morehelp = 0; while (1) { int c; - if ((c = getopt_long(argc, argv, "hP:C:m:M:F:f:c:r:B:E:s:bpen", long_option, NULL)) < 0) + if ((c = getopt_long(argc, argv, "hP:C:m:M:U:F:f:c:r:B:E:s:y:O:bpenxX", long_option, NULL)) < 0) break; switch (c) { case 'h': @@ -525,6 +613,10 @@ int main(int argc, char *argv[]) err = atoi(optarg) / 2; latency_max = latency_min > err ? latency_min : err; break; + case 'U': + err = atoi(optarg); + sys_latency = err <= 0 ? 0 : err; + break; case 'f': format = snd_pcm_format_value(optarg); if (format == SND_PCM_FORMAT_UNKNOWN) { @@ -558,12 +650,24 @@ int main(int argc, char *argv[]) case 'p': use_poll = 1; break; + case 'y': + usleep_val = atoi(optarg); + break; case 'e': effect = 1; break; case 'n': resample = 0; break; + case 'x': + pos_dump = 1; + break; + case 'X': + realtime_check = 1; + break; + case 'O': + sched_policy = optarg; + break; } } @@ -579,15 +683,29 @@ int main(int argc, char *argv[]) loop_limit = loop_sec * rate; latency = latency_min - 4; - buffer = malloc((latency_max * snd_pcm_format_width(format) / 8) * 2); + buffer = malloc((latency_max * 2 * snd_pcm_format_physical_width(format) / 8) * channels); + + /* I/O updates based on a system timer */ + if (sys_latency > 0) { + block = 0; + use_poll = 0; + } setscheduler(); printf("Playback device is %s\n", pdevice); printf("Capture device is %s\n", cdevice); - printf("Parameters are %iHz, %s, %i channels, %s mode\n", rate, snd_pcm_format_name(format), channels, block ? "blocking" : "non-blocking"); - printf("Poll mode: %s\n", use_poll ? "yes" : "no"); - printf("Loop limit is %lu frames, minimum latency = %i, maximum latency = %i\n", loop_limit, latency_min * 2, latency_max * 2); + printf("Parameters are %iHz, %s, %i channels, %s mode, use poll %s\n", + rate, snd_pcm_format_name(format), + channels, block ? "blocking" : "non-blocking", + use_poll ? "yes" : "no"); + printf("Loop limit is %lu frames, minimum latency = %i, maximum latency = %i", + loop_limit, latency_min * 2, latency_max * 2); + if (sys_latency > 0) + printf(", I/O updates %ims", sys_latency); + else if (!block && !use_poll) + printf(", I/O usleep %ius", usleep_val); + printf("\n"); if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK, block ? 0 : SND_PCM_NONBLOCK)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); @@ -613,6 +731,9 @@ int main(int argc, char *argv[]) y[1] = (float*) malloc(channels*sizeof(float)); y[2] = (float*) malloc(channels*sizeof(float)); } + + cap_avail_max = 0; + pbk_fill_min = latency * 2; while (1) { frames_in = frames_out = 0; @@ -623,7 +744,7 @@ int main(int argc, char *argv[]) printf("Streams link error: %s\n", snd_strerror(err)); exit(0); } - if (snd_pcm_format_set_silence(format, buffer, latency*channels) < 0) { + if (snd_pcm_format_set_silence(format, buffer, latency * channels) < 0) { fprintf(stderr, "silence error\n"); break; } @@ -636,10 +757,14 @@ int main(int argc, char *argv[]) break; } + if (realtime_check) + timestamp_now(&tstamp_start); if ((err = snd_pcm_start(chandle)) < 0) { printf("Go error: %s\n", snd_strerror(err)); exit(0); } + if (realtime_check) + printf("[%lldus] Stream start\n", timestamp_diff_micro(&tstamp_start)); gettimestamp(phandle, &p_tstamp); gettimestamp(chandle, &c_tstamp); #if 0 @@ -652,15 +777,48 @@ int main(int argc, char *argv[]) ok = 1; in_max = 0; while (ok && frames_in < loop_limit) { - if (use_poll) { + cap_avail = latency; + if (sys_latency > 0) { + poll(NULL, 0, sys_latency); + cap_avail = get_avail(chandle); + if (cap_avail < 0) { + printf("Avail failed: %s\n", snd_strerror(cap_avail)); + ok = 0; + break; + } + } else if (use_poll) { /* use poll to wait for next event */ snd_pcm_wait(chandle, 1000); + } else if (!block && usleep_val > 0) { + usleep(usleep_val); } - if ((r = readbuf(chandle, buffer, latency, &frames_in, &in_max)) < 0) + if (pos_dump || realtime_check) { + if (sys_latency <= 0) + cap_avail = get_avail(chandle); + pbk_fill = get_avail(phandle); + if (pbk_fill >= 0) + pbk_fill = playback_buffer_size - pbk_fill; + if (cap_avail > cap_avail_max) + cap_avail_max = cap_avail; + if (pbk_fill >= 0 && pbk_fill < pbk_fill_min) + pbk_fill_min = pbk_fill; + if (realtime_check) { + long long diff = timestamp_diff_micro(&tstamp_start); + long long cap_pos = frames_to_micro(frames_in + cap_avail); + long long pbk_pos = frames_to_micro(frames_out - pbk_fill); + printf("[%lldus] POS: p=%zd (min=%zd, rt=%lldus) c=%zd (max=%zd, rt=%lldus)\n", + diff, pbk_fill, pbk_fill_min, pbk_pos - diff, + cap_avail, cap_avail_max, cap_pos - diff); + } else if (pos_dump) { + printf("POS: p=%zd (min=%zd), c=%zd (max=%zd)\n", + pbk_fill, pbk_fill_min, cap_avail, cap_avail_max); + } + } + if ((r = readbuf(chandle, buffer, cap_avail, &frames_in, &in_max)) < 0) ok = 0; else { if (effect) - applyeffect(buffer,r); + applyeffect(buffer, r); if (writebuf(phandle, buffer, r, &frames_out) < 0) ok = 0; } @@ -677,6 +835,13 @@ int main(int argc, char *argv[]) if (p_tstamp.tv_sec == c_tstamp.tv_sec && p_tstamp.tv_usec == c_tstamp.tv_usec) printf("Hardware sync\n"); + if (realtime_check) { + long long diff = timestamp_diff_micro(&tstamp_start); + long long mtime = frames_to_micro(frames_in); + printf("Elapsed real time: %lldus\n", diff); + printf("Elapsed device time: %lldus\n", mtime); + printf("Test time diff (device - real): %lldus\n", mtime - diff); + } snd_pcm_drop(chandle); snd_pcm_nonblock(phandle, 0); snd_pcm_drain(phandle); @@ -684,9 +849,9 @@ int main(int argc, char *argv[]) if (ok) { #if 1 printf("Playback time = %li.%i, Record time = %li.%i, diff = %li\n", - p_tstamp.tv_sec, + (long)p_tstamp.tv_sec, (int)p_tstamp.tv_usec, - c_tstamp.tv_sec, + (long)c_tstamp.tv_sec, (int)c_tstamp.tv_usec, timediff(p_tstamp, c_tstamp)); #endif @@ -698,5 +863,10 @@ int main(int argc, char *argv[]) } snd_pcm_close(phandle); snd_pcm_close(chandle); + snd_output_close(output); + snd_config_update_free_global(); + free(buffer); + free(pdevice); + free(cdevice); return 0; } diff --git a/test/midifile.c b/test/midifile.c index 3d72b9f9..4862b199 100644 --- a/test/midifile.c +++ b/test/midifile.c @@ -71,7 +71,7 @@ #endif #include -#include +#include #include /*void exit(), free();*/ @@ -148,7 +148,7 @@ static void msginit (); static int msgleng (); static void msgadd (); static void biggermsg (); -static int eputc (unsigned char c); +static int eputc (); double mf_ticks2sec (unsigned long ticks, int division, unsigned long tempo); int mf_write_meta_event (); @@ -328,7 +328,7 @@ readtrack () /* read a track chunk */ if (Mf_interactive) { - Mf_toberead = MAXINT; + Mf_toberead = INT_MAX; } else { diff --git a/test/namehint.c b/test/namehint.c index e978d5ca..18bad1d8 100644 --- a/test/namehint.c +++ b/test/namehint.c @@ -4,7 +4,8 @@ int main(int argc, char *argv[]) { const char *iface = "pcm"; - char **hints, **n; + void **hints; + char **n; int err; if (argc > 1) @@ -12,7 +13,7 @@ int main(int argc, char *argv[]) err = snd_device_name_hint(-1, iface, &hints); if (err < 0) errx(1, "snd_device_name_hint error: %s", snd_strerror(err)); - n = hints; + n = (char **)hints; while (*n != NULL) { printf("%s\n", *n); n++; diff --git a/test/oldapi.c b/test/oldapi.c index 7abc98e0..82450478 100644 --- a/test/oldapi.c +++ b/test/oldapi.c @@ -31,7 +31,7 @@ #include "../include/asoundlib.h" #include -typedef void (myfcn)(void *); +typedef int (myfcn)(const snd_pcm_hw_params_t *); int main(int argc ATTRIBUTE_UNUSED, char *argv[] ATTRIBUTE_UNUSED) { diff --git a/test/pcm-multi-thread.c b/test/pcm-multi-thread.c index da1b87c6..de178899 100644 --- a/test/pcm-multi-thread.c +++ b/test/pcm-multi-thread.c @@ -37,7 +37,7 @@ static char mode_suffix[] = { 'a', 's', 'h', 't', 'd', 'r' }; -static const char *devname = "default"; +static const char *pcmdev = "default"; static int stream = SND_PCM_STREAM_PLAYBACK; static int num_threads = 1; static int periodsize = 16 * 1024; @@ -127,7 +127,7 @@ static int parse_options(int argc, char **argv) while ((c = getopt(argc, argv, "D:r:f:p:b:s:t:m:vq")) >= 0) { switch (c) { case 'D': - devname = optarg; + pcmdev = optarg; break; case 'r': rate = atoi(optarg); @@ -213,9 +213,9 @@ int main(int argc, char **argv) if (parse_options(argc, argv)) return 1; - err = snd_pcm_open(&pcm, devname, stream, 0); + err = snd_pcm_open(&pcm, pcmdev, stream, 0); if (err < 0) { - fprintf(stderr, "cannot open pcm %s\n", devname); + fprintf(stderr, "cannot open pcm %s\n", pcmdev); return 1; } diff --git a/test/pcm.c b/test/pcm.c index b8d4fe69..223edafc 100644 --- a/test/pcm.c +++ b/test/pcm.c @@ -2,6 +2,8 @@ * This small demo sends a simple sinusoidal wave to your speakers. */ +#include "config.h" + #include #include #include @@ -12,6 +14,10 @@ #include #include +#ifndef ESTRPIPE +#define ESTRPIPE ESPIPE +#endif + static char *device = "plughw:0,0"; /* playback device */ static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ static unsigned int rate = 44100; /* stream rate */ diff --git a/test/playmidi1.c b/test/playmidi1.c index f178279a..831e9578 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -34,6 +34,8 @@ * */ +#include "config.h" + #include #include #include diff --git a/test/queue_timer.c b/test/queue_timer.c index c4ffb192..4e1fa967 100644 --- a/test/queue_timer.c +++ b/test/queue_timer.c @@ -100,9 +100,9 @@ main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) prevdiff = diff; fprintf(stderr, " real time: %12ld sec %8ld usec\nqueue time: %12ld sec %8ld usec\n diff: %12ld sec %8ld usec\n diffdiff: %12ld sec %8ld usec\n", - tv.tv_sec, tv.tv_usec, + (long)tv.tv_sec, tv.tv_usec, (long)rtime->tv_sec, (long)rtime->tv_nsec / 1000, - diff.tv_sec, diff.tv_usec, + (long)diff.tv_sec, diff.tv_usec, (long)diffdiff.tv_sec, (long)diffdiff.tv_usec); if (diffdiff.tv_usec > 5000 || diff --git a/test/seq.c b/test/seq.c index 34b000fd..04f7e5d4 100644 --- a/test/seq.c +++ b/test/seq.c @@ -1,3 +1,5 @@ +#include "config.h" + #include #include #include diff --git a/test/timer.c b/test/timer.c index b05eb2f0..63ea4a54 100644 --- a/test/timer.c +++ b/test/timer.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/test/user-ctl-element-set.c b/test/user-ctl-element-set.c index fee130e2..ceccdba7 100644 --- a/test/user-ctl-element-set.c +++ b/test/user-ctl-element-set.c @@ -7,6 +7,7 @@ * Licensed under the terms of the GNU General Public License, version 2. */ +#include "config.h" #include "../include/asoundlib.h" #include #include diff --git a/utils/alsa.m4 b/utils/alsa.m4 index 40e50a19..461d8d5e 100644 --- a/utils/alsa.m4 +++ b/utils/alsa.m4 @@ -85,6 +85,7 @@ 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_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include +#include ]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_MAJOR) && defined(SOUNDLIB_VERSION_MAJOR) @@ -130,6 +131,7 @@ AC_MSG_CHECKING([for libatopology (sound headers version > 1.1.9)]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include +#include ]], [[ /* ensure backward compatibility */ #if !defined(SND_LIB_VERSION) diff --git a/utils/buildrpm b/utils/buildrpm old mode 100755 new mode 100644