summaryrefslogtreecommitdiffstats
path: root/alsa-utils/alsactl
diff options
context:
space:
mode:
Diffstat (limited to 'alsa-utils/alsactl')
-rw-r--r--alsa-utils/alsactl/Makefile.am14
-rw-r--r--alsa-utils/alsactl/Makefile.in670
-rw-r--r--alsa-utils/alsactl/alsactl.1118
-rw-r--r--alsa-utils/alsactl/alsactl.c192
-rw-r--r--alsa-utils/alsactl/alsactl.h94
-rw-r--r--alsa-utils/alsactl/alsactl_init.xml590
-rw-r--r--alsa-utils/alsactl/init/00main46
-rw-r--r--alsa-utils/alsactl/init/Makefile.am7
-rw-r--r--alsa-utils/alsactl/init/Makefile.in366
-rw-r--r--alsa-utils/alsactl/init/default145
-rw-r--r--alsa-utils/alsactl/init/hda34
-rw-r--r--alsa-utils/alsactl/init/help8
-rw-r--r--alsa-utils/alsactl/init/info22
-rw-r--r--alsa-utils/alsactl/init/test270
-rw-r--r--alsa-utils/alsactl/init_parse.c1739
-rw-r--r--alsa-utils/alsactl/init_sysdeps.c63
-rw-r--r--alsa-utils/alsactl/init_sysfs.c158
-rw-r--r--alsa-utils/alsactl/init_utils_run.c247
-rw-r--r--alsa-utils/alsactl/init_utils_string.c194
-rw-r--r--alsa-utils/alsactl/list.h289
-rw-r--r--alsa-utils/alsactl/state.c1653
-rw-r--r--alsa-utils/alsactl/utils.c98
22 files changed, 7017 insertions, 0 deletions
diff --git a/alsa-utils/alsactl/Makefile.am b/alsa-utils/alsactl/Makefile.am
new file mode 100644
index 0000000..359f73a
--- /dev/null
+++ b/alsa-utils/alsactl/Makefile.am
@@ -0,0 +1,14 @@
+SUBDIRS = init
+
+sbin_PROGRAMS=alsactl
+man_MANS=alsactl.1
+if USE_XMLTO
+man_MANS += alsactl_init.7
+endif
+EXTRA_DIST=alsactl.1 alsactl_init.xml
+
+alsactl_SOURCES=alsactl.c state.c utils.c init_parse.c
+noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c init_utils_run.c init_sysfs.c
+
+%.7: %.xml
+ xmlto man $?
diff --git a/alsa-utils/alsactl/Makefile.in b/alsa-utils/alsactl/Makefile.in
new file mode 100644
index 0000000..2d8fe45
--- /dev/null
+++ b/alsa-utils/alsactl/Makefile.in
@@ -0,0 +1,670 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = alsactl$(EXEEXT)
+@USE_XMLTO_TRUE@am__append_1 = alsactl_init.7
+subdir = alsactl
+DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/aconfig.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man1dir)" \
+ "$(DESTDIR)$(man7dir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(sbin_PROGRAMS)
+am_alsactl_OBJECTS = alsactl.$(OBJEXT) state.$(OBJEXT) utils.$(OBJEXT) \
+ init_parse.$(OBJEXT)
+alsactl_OBJECTS = $(am_alsactl_OBJECTS)
+alsactl_LDADD = $(LDADD)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(alsactl_SOURCES)
+DIST_SOURCES = $(alsactl_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-exec-recursive install-info-recursive \
+ install-recursive installcheck-recursive installdirs-recursive \
+ pdf-recursive ps-recursive uninstall-info-recursive \
+ uninstall-recursive
+man1dir = $(mandir)/man1
+man7dir = $(mandir)/man7
+NROFF = nroff
+MANS = $(man_MANS)
+HEADERS = $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSACONF_FALSE = @ALSACONF_FALSE@
+ALSACONF_TRUE = @ALSACONF_TRUE@
+ALSAMIXER_FALSE = @ALSAMIXER_FALSE@
+ALSAMIXER_TRUE = @ALSAMIXER_TRUE@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CURSESINC = @CURSESINC@
+CURSESLIB = @CURSESLIB@
+CURSES_CFLAGS = @CURSES_CFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SND_UTIL_MAJOR = @SND_UTIL_MAJOR@
+SND_UTIL_MINOR = @SND_UTIL_MINOR@
+SND_UTIL_SUBMINOR = @SND_UTIL_SUBMINOR@
+SND_UTIL_VERSION = @SND_UTIL_VERSION@
+STRIP = @STRIP@
+TESTSOUND = @TESTSOUND@
+USE_NLS = @USE_NLS@
+USE_XMLTO_FALSE = @USE_XMLTO_FALSE@
+USE_XMLTO_TRUE = @USE_XMLTO_TRUE@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+ac_ct_CC = @ac_ct_CC@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+ncurses5_config = @ncurses5_config@
+ncursesw5_config = @ncursesw5_config@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+xmlto = @xmlto@
+SUBDIRS = init
+man_MANS = alsactl.1 $(am__append_1)
+EXTRA_DIST = alsactl.1 alsactl_init.xml
+alsactl_SOURCES = alsactl.c state.c utils.c init_parse.c
+noinst_HEADERS = alsactl.h list.h init_sysdeps.c init_utils_string.c init_utils_run.c init_sysfs.c
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign alsactl/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign alsactl/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+alsactl$(EXEEXT): $(alsactl_OBJECTS) $(alsactl_DEPENDENCIES)
+ @rm -f alsactl$(EXEEXT)
+ $(LINK) $(alsactl_LDFLAGS) $(alsactl_OBJECTS) $(alsactl_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alsactl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_parse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+install-man1: $(man1_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(man1dir)"
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.1*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 1*) ;; \
+ *) ext='1' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man1dir)/$$inst"; \
+ done
+install-man7: $(man7_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man7dir)" || $(mkdir_p) "$(DESTDIR)$(man7dir)"
+ @list='$(man7_MANS) $(dist_man7_MANS) $(nodist_man7_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.7*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 7*) ;; \
+ *) ext='7' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst"; \
+ done
+uninstall-man7:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man7_MANS) $(dist_man7_MANS) $(nodist_man7_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.7*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 7*) ;; \
+ *) ext='7' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man7dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man7dir)/$$inst"; \
+ done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(mkdir_p) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(MANS) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man7dir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-man
+
+install-exec-am: install-sbinPROGRAMS
+
+install-info: install-info-recursive
+
+install-man: install-man1 install-man7
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-info-am uninstall-man uninstall-sbinPROGRAMS
+
+uninstall-info: uninstall-info-recursive
+
+uninstall-man: uninstall-man1 uninstall-man7
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \
+ clean clean-generic clean-recursive clean-sbinPROGRAMS ctags \
+ ctags-recursive distclean distclean-compile distclean-generic \
+ distclean-recursive distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-exec install-exec-am install-info \
+ install-info-am install-man install-man1 install-man7 \
+ install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic maintainer-clean-recursive \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \
+ uninstall uninstall-am uninstall-info-am uninstall-man \
+ uninstall-man1 uninstall-man7 uninstall-sbinPROGRAMS
+
+
+%.7: %.xml
+ xmlto man $?
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/alsa-utils/alsactl/alsactl.1 b/alsa-utils/alsactl/alsactl.1
new file mode 100644
index 0000000..eb3cbd1
--- /dev/null
+++ b/alsa-utils/alsactl/alsactl.1
@@ -0,0 +1,118 @@
+.TH ALSACTL 1 "15 May 2001"
+.SH NAME
+alsactl \- advanced controls for ALSA soundcard driver
+
+.SH SYNOPSIS
+
+\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fIinit\fP] <card # or id or device>
+
+.SH DESCRIPTION
+\fBalsactl\fP is used to control advanced settings for the ALSA
+soundcard drivers. It supports multiple soundcards. If your card has
+features that you can't seem to control from a mixer application,
+you have come to the right place.
+
+.SH COMMANDS
+
+\fIstore\fP saves the current driver state for the selected soundcard
+to the configuration file.
+
+\fIrestore\fP loads driver state for the selected soundcard from the
+configuration file. If restoring fails (eventually partly), the init
+action is called.
+
+\fIinit\fP tries to initialize all devices to a default state. If device
+is not known, error code 99 is returned.
+
+If no soundcards are specified, setup for all cards will be saved or
+loaded.
+
+.SH OPTIONS
+
+.TP
+\fI\-h, \-\-help\fP
+Help: show available flags and commands.
+
+.TP
+\fI\-d, \-\-debug\fP
+Use debug mode: a bit more verbose.
+
+.TP
+\fI\-v, \-\-version\fP
+Print alsactl version number.
+
+.TP
+\fI\-f, \-\-file\fP
+Select the configuration file to use. The default is /etc/asound.state.
+
+.TP
+\fI\-F, \-\-force\fP
+Used with restore command. Try to restore the matching control elements
+as much as possible. This option is set as default now.
+
+.TP
+\fI\-g, \-\-ignore\fP
+Used with store and restore commands. Do not show 'No soundcards found'
+and do not set an error exit code when soundcards are not installed.
+
+.TP
+\fI\-P, \-\-pedantic\fP
+Used with restore command. Don't restore mismatching control elements.
+This option was the old default behavior.
+
+.TP
+\fI\-I, \-\-no\-init\-fallback\fP
+Don't initialize cards if restore fails. Since version 1.0.18,
+\fBalsactl\fP tries to initialize the card with the restore operation
+as default. But this can cause incompatibility with the older version.
+The caller may expect that the state won't be touched if no state file
+exists. This option takes the restore behavior back to the older
+version by suppressing the initialization.
+
+.TP
+\fI\-r, \-\-runstate\fP
+Save restore and init state to this file. The file will contain only errors.
+Errors are appended with the soundcard id to the end of file.
+
+.TP
+\fI\-R, \-\-remove\fP
+Remove runstate file at first.
+
+.TP
+\fI\-E, \-\-env\fP #=#
+Set environment variable (useful for init action or you may override
+ALSA_CONFIG_PATH to read different or optimized configuration - may be
+useful for "boot" scripts).
+
+.TP
+\fI\-i, \-\-initfile\fP #=#
+The configuration file for init. By default, PREFIX/share/alsa/init/00main
+is used.
+
+.SH FILES
+\fI/etc/asound.state\fP (or whatever file you specify with the
+\fB\-f\fP flag) is used to store current settings for your
+soundcards. The settings include all the usual soundcard mixer
+settings. More importantly, alsactl is
+capable of controlling other card-specific features that mixer apps
+usually don't know about.
+
+The configuration file is generated automatically by running
+\fBalsactl store\fP. Editing the configuration file by hand may be
+necessary for some soundcard features (e.g. enabling/disabling
+automatic mic gain, digital output, joystick/game ports, some future MIDI
+routing options, etc).
+
+.SH SEE ALSO
+\fB
+amixer(1),
+alsamixer(1),
+aplay(1)
+\fP
+
+.SH BUGS
+None known.
+
+.SH AUTHOR
+\fBalsactl\fP is by Jaroslav Kysela <perex@perex.cz> and Abramo Bagnara
+<abramo@alsa\-project.org>. This document is by Paul Winkler <zarmzarm@erols.com>.
diff --git a/alsa-utils/alsactl/alsactl.c b/alsa-utils/alsactl/alsactl.c
new file mode 100644
index 0000000..e0bc276
--- /dev/null
+++ b/alsa-utils/alsactl/alsactl.c
@@ -0,0 +1,192 @@
+/*
+ * Advanced Linux Sound Architecture Control Program
+ * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ * Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+#define SYS_ASOUNDRC "/etc/asound.state"
+
+int debugflag = 0;
+int force_restore = 1;
+int ignore_nocards = 0;
+char *command;
+char *statefile = NULL;
+
+static void help(void)
+{
+ printf("Usage: alsactl <options> command\n");
+ printf("\nAvailable global options:\n");
+ printf(" -h,--help this help\n");
+ printf(" -d,--debug debug mode\n");
+ printf(" -v,--version print version of this program\n");
+ printf("\nAvailable state options:\n");
+ printf(" -f,--file # configuration file (default " SYS_ASOUNDRC ")\n");
+ printf(" -F,--force try to restore the matching controls as much as possible\n");
+ printf(" (default mode)\n");
+ printf(" -g,--ignore ignore 'No soundcards found' error\n");
+ printf(" -P,--pedantic do not restore mismatching controls (old default)\n");
+ printf(" -I,--no-init-fallback\n"
+ " don't initialize even if restore fails\n");
+ printf(" -r,--runstate # save restore and init state to this file (only errors)\n");
+ printf(" default settings is 'no file set'\n");
+ printf(" -R,--remove remove runstate file at first, otherwise append errors\n");
+ printf("\nAvailable init options:\n");
+ printf(" -E,--env #=# set environment variable for init phase (NAME=VALUE)\n");
+ printf(" -i,--initfile # main configuation file for init phase (default " DATADIR "/init/00main)\n");
+ printf("\n");
+ printf("\nAvailable commands:\n");
+ printf(" store <card #> save current driver setup for one or each soundcards\n");
+ printf(" to configuration file\n");
+ printf(" restore <card #> load current driver setup for one or each soundcards\n");
+ printf(" from configuration file\n");
+ printf(" init <card #> initialize driver to a default state\n");
+ printf(" names <card #> dump information about all the known present (sub-)devices\n");
+ printf(" into configuration file (DEPRECATED)\n");
+}
+
+int main(int argc, char *argv[])
+{
+ static const struct option long_option[] =
+ {
+ {"help", 0, NULL, 'h'},
+ {"file", 1, NULL, 'f'},
+ {"env", 1, NULL, 'E'},
+ {"initfile", 1, NULL, 'i'},
+ {"no-init-fallback", 0, NULL, 'I'},
+ {"force", 0, NULL, 'F'},
+ {"ignore", 0, NULL, 'g'},
+ {"pedantic", 0, NULL, 'P'},
+ {"runstate", 0, NULL, 'r'},
+ {"remove", 0, NULL, 'R'},
+ {"debug", 0, NULL, 'd'},
+ {"version", 0, NULL, 'v'},
+ {NULL, 0, NULL, 0},
+ };
+ static const char *const devfiles[] = {
+ "/dev/snd/controlC",
+ "/dev/snd/pcmC",
+ "/dev/snd/midiC",
+ "/dev/snd/hwC",
+ NULL
+ };
+ char *cfgfile = SYS_ASOUNDRC;
+ char *initfile = DATADIR "/init/00main";
+ char *cardname, **tmp, ncardname[16];
+ int removestate = 0;
+ int init_fallback = 1; /* new default behavior */
+ int res;
+
+ command = argv[0];
+ while (1) {
+ int c;
+
+ if ((c = getopt_long(argc, argv, "hdvf:FgE:i:Pr:R", long_option, NULL)) < 0)
+ break;
+ switch (c) {
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ case 'f':
+ cfgfile = optarg;
+ break;
+ case 'F':
+ force_restore = 1;
+ break;
+ case 'g':
+ ignore_nocards = 1;
+ break;
+ case 'E':
+ if (putenv(optarg)) {
+ fprintf(stderr, "environment string '%s' is wrong\n", optarg);
+ return EXIT_FAILURE;
+ }
+ break;
+ case 'i':
+ initfile = optarg;
+ break;
+ case 'I':
+ init_fallback = 0;
+ break;
+ case 'r':
+ statefile = optarg;
+ break;
+ case 'R':
+ removestate = 1;
+ break;
+ case 'P':
+ force_restore = 0;
+ break;
+ case 'd':
+ debugflag = 1;
+ break;
+ case 'v':
+ printf("alsactl version " SND_UTIL_VERSION_STR "\n");
+ return EXIT_SUCCESS;
+ case '?': // error msg already printed
+ help();
+ return EXIT_FAILURE;
+ break;
+ default: // should never happen
+ fprintf(stderr,
+ "Invalid option '%c' (%d) not handled??\n", c, c);
+ }
+ }
+ if (argc - optind <= 0) {
+ fprintf(stderr, "alsactl: Specify command...\n");
+ return 0;
+ }
+
+ cardname = argc - optind > 1 ? argv[optind + 1] : NULL;
+ for (tmp = devfiles; cardname != NULL && *tmp != NULL; tmp++) {
+ int len = strlen(*tmp);
+ if (!strncmp(cardname, *tmp, len)) {
+ long l = strtol(cardname + len, NULL, 0);
+ sprintf(ncardname, "%li", l);
+ cardname = ncardname;
+ break;
+ }
+ }
+
+ if (!strcmp(argv[optind], "init")) {
+ res = init(initfile, cardname);
+ } else if (!strcmp(argv[optind], "store")) {
+ res = save_state(cfgfile, cardname);
+ } else if (!strcmp(argv[optind], "restore")) {
+ if (removestate)
+ remove(statefile);
+ res = load_state(cfgfile, initfile, cardname, init_fallback);
+ } else {
+ fprintf(stderr, "alsactl: Unknown command '%s'...\n",
+ argv[optind]);
+ res = -ENODEV;
+ }
+
+ snd_config_update_free_global();
+ return res < 0 ? res : 0;
+}
diff --git a/alsa-utils/alsactl/alsactl.h b/alsa-utils/alsactl/alsactl.h
new file mode 100644
index 0000000..89ad295
--- /dev/null
+++ b/alsa-utils/alsactl/alsactl.h
@@ -0,0 +1,94 @@
+extern int debugflag;
+extern int force_restore;
+extern int ignore_nocards;
+extern char *command;
+extern char *statefile;
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define info(...) do {\
+ fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stdout, __VA_ARGS__); \
+ putc('\n', stdout); \
+} while (0)
+#else
+#define info(args...) do {\
+ fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stdout, ##args); \
+ putc('\n', stdout); \
+} while (0)
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define error(...) do {\
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ putc('\n', stderr); \
+} while (0)
+#else
+#define error(args...) do {\
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, ##args); \
+ putc('\n', stderr); \
+} while (0)
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define cerror(cond, ...) do {\
+ if (cond) { \
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ putc('\n', stderr); \
+ } \
+} while (0)
+#else
+#define cerror(cond, args...) do {\
+ if (cond) { \
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, ##args); \
+ putc('\n', stderr); \
+ } \
+} while (0)
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define dbg(...) do {\
+ if (!debugflag) break; \
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, __VA_ARGS__); \
+ putc('\n', stderr); \
+} while (0)
+#else
+#define dbg(args...) do {\
+ if (!debugflag) break; \
+ fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+ fprintf(stderr, ##args); \
+ putc('\n', stderr); \
+} while (0)
+#endif
+
+int init(const char *file, const char *cardname);
+int save_state(const char *file, const char *cardname);
+int load_state(const char *file, const char *initfile, const char *cardname,
+ int do_init);
+int power(const char *argv[], int argc);
+int generate_names(const char *cfgfile);
+
+/* utils */
+
+int file_map(const char *filename, char **buf, size_t *bufsize);
+void file_unmap(void *buf, size_t bufsize);
+size_t line_width(const char *buf, size_t bufsize, size_t pos);
+void initfailed(int cardnumber, const char *reason);
+
+static inline int hextodigit(int c)
+{
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else
+ return -1;
+ return c;
+}
diff --git a/alsa-utils/alsactl/alsactl_init.xml b/alsa-utils/alsactl/alsactl_init.xml
new file mode 100644
index 0000000..0e0d46b
--- /dev/null
+++ b/alsa-utils/alsactl/alsactl_init.xml
@@ -0,0 +1,590 @@
+<?xml version='1.0'?>
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<article>
+ <section>
+ <title>alsactl init</title>
+ <refentry>
+ <refentryinfo>
+ <title>alsactl init</title>
+ <date>July 2008</date>
+ <productname>alsactl</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>alsactl_init</refentrytitle>
+ <manvolnum>7</manvolnum>
+ <refmiscinfo class="version"></refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>alsactl init</refname>
+ <refpurpose>alsa control management - initialization</refpurpose>
+ </refnamediv>
+
+ <refsect1><title>DESCRIPTION</title>
+ <para>"alsactl init" provides soundcard specific initialization.</para>
+ </refsect1>
+
+ <refsect1><title>CONFIGURATION</title>
+ <para>All "alsactl init" configuration files are placed in
+ <filename>/usr/share/alsa/init/</filename> directory. The top level
+ configuration file is <filename>/usr/share/alsa/init/00main</filename>.
+ The default top-level file can be also specified using -i or
+ --initfile parameter for the alsactl tool.
+ Every file consists of a set of lines of text. All empty lines or
+ lines beginning with '#' will be ignored.</para>
+
+ <refsect2><title>Rules files</title>
+ <para>The "alsactl init" rules are read from the files located
+ in the <filename>/usr/share/alsa/init/*</filename>. The top
+ level configuration file is <filename>/usr/share/alsa/init/00main</filename>.
+ Every line in the rules file contains at least one key value pair.
+ There are two kind of keys, match and assignment keys. If all match
+ keys are matching against its value, the rule gets applied and the
+ assign keys get the specified value assigned.</para>
+
+ <para>A rule may consists of a list of one or more key value pairs
+ separated by a comma. Each key has a distinct operation, depending
+ on the used operator. Valid operators are:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>==</option></term>
+ <listitem>
+ <para>Compare for equality.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>!=</option></term>
+ <listitem>
+ <para>Compare for non-equality.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>=</option></term>
+ <listitem>
+ <para>Assign a value to a key. Keys that represent a list,
+ are reset and only this single value is assigned.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>+=</option></term>
+ <listitem>
+ <para>Add the value to a key that holds a list
+ of entries.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>:=</option></term>
+ <listitem>
+ <para>Assign a value to a key finally; disallow any
+ later changes, which may be used to prevent changes by
+ any later rules.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The following key names can be used to match against device
+ properties:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>CARDINDEX</option></term>
+ <listitem>
+ <para>Match the card index of the ALSA driver.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>CTL{<replaceable>attribute</replaceable>}</option></term>
+ <listitem>
+ <para>Set or test universal control attribute. Possible
+ attributes:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>numid</option></term>
+ <listitem>
+ <para>Numeric control identification.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>iface</option>, <option>interface</option></term>
+ <listitem>
+ <para>Control interface name (CARD, HWEDEP, MIXER, PCM, RAWMIDI, TIMER, SEQUENCER)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>subdev</option>, <option>subdevice</option></term>
+ <listitem>
+ <para>Subdevice number.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>name</option></term>
+ <listitem>
+ <para>Control name</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>index</option></term>
+ <listitem>
+ <para>Control index</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>type</option></term>
+ <listitem>
+ <para>Control type (BOOLEAN, INTEGER, INTEGER64, ENUMERATED, BYTES, IEC958)</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>attr</option>, <option>attribute</option></term>
+ <listitem>
+ <para>Attributes (stored in a string - use match characters * and ?):</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>r</option></term>
+ <listitem>
+ <para>control is readable</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>w</option></term>
+ <listitem>
+ <para>control is writable</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>v</option></term>
+ <listitem>
+ <para>control is volatile</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>i</option></term>
+ <listitem>
+ <para>control is inactive</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>l</option></term>
+ <listitem>
+ <para>control is locked</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>R</option></term>
+ <listitem>
+ <para>control is TLV readable</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>W</option></term>
+ <listitem>
+ <para>control is TLV writable</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>C</option></term>
+ <listitem>
+ <para>control is TLV commandable</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>o</option></term>
+ <listitem>
+ <para>process is owner of this control</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>u</option></term>
+ <listitem>
+ <para>control created in user space</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>owner</option></term>
+ <listitem>
+ <para>Control owner process PID number</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>count</option></term>
+ <listitem>
+ <para>Control count of values</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>min</option></term>
+ <listitem>
+ <para>Value range - minimum value</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>max</option></term>
+ <listitem>
+ <para>Value range - maximum value</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>step</option></term>
+ <listitem>
+ <para>Value range - step value</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dBmin</option></term>
+ <listitem>
+ <para>Value range - minimum dB value</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>dBmax</option></term>
+ <listitem>
+ <para>Value range - maximum dB value</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>items</option></term>
+ <listitem>
+ <para>Enumerated value - number of text items</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>enums</option></term>
+ <listitem>
+ <para>Enumerated value - list of text names stored between '|' character</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>value</option></term>
+ <listitem>
+ <para>Value of control stored to a string delimited by
+ comma (,).</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>CONFIG{sysfs_device}</option></term>
+ <listitem>
+ <para>The relative path to sysfs subsystem specifying
+ the root directory of a soundcard device. Usually,
+ it should be set to "/class/sound/controlC$cardinfo{card}/device".
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ATTR{<replaceable>filename</replaceable>}</option></term>
+ <listitem>
+ <para>Match sysfs attribute values of the soundcard device.
+ The relative path to sysfs tree must be defined by
+ CONFIG{sysfs_device} key. Trailing whitespace in the attribute
+ values is ignored, if the specified match value does
+ not contain trailing whitespace itself. Depending on
+ the type of operator, this key is also used to set
+ the value of a sysfs attribute.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ENV{<replaceable>key</replaceable>}</option></term>
+ <listitem>
+ <para>Match against the value of an environment variable. Up
+ to five <option>ENV</option> keys can be specified per rule.
+ Depending on the type of operator, this key is also used
+ to export a variable to the environment.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>PROGRAM</option></term>
+ <listitem>
+ <para>Execute external program. The key is true, if
+ the program returns without exit code zero. The whole event
+ environment is available to the executed program. The
+ program's output printed to stdout is available for
+ the RESULT key.</para>
+ <para>Several buildin commands are available:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>__ctl_search</option></term>
+ <listitem>
+ <para>Search for a control. The CTL{name} key might
+ contain match characters * and ?. An control index
+ might be specified as first argument starting from
+ zero (e.g. PROGRAM="__ctl_search 2").</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>__ctl_count</option></term>
+ <listitem>
+ <para>Search for a controls and return total count
+ of matched ones. The CTL{name} key might contain match
+ characters * and ?.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>RESULT</option></term>
+ <listitem>
+ <para>Match the returned string of the last PROGRAM call.
+ This key can be used in the same or in any later rule
+ after a PROGRAM call.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>Most of the fields support a shell style pattern matching.
+ The following pattern characters are supported:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>*</option></term>
+ <listitem>
+ <para>Matches zero, or any number of characters.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>?</option></term>
+ <listitem>
+ <para>Matches any single character.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>[]</option></term>
+ <listitem>
+ <para>Matches any single character specified within
+ the brackets. For example, the pattern string 'tty[SR]'
+ would match either 'ttyS' or 'ttyR'. Ranges are also
+ supported within this match with the '-' character.
+ For example, to match on the range of all digits,
+ the pattern [0-9] would be used. If the first character
+ following the '[' is a '!', any characters
+ not enclosed are matched.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>The following keys can get values assigned:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>CTL{numid}</option>, <option>CTL{iface}</option>,
+ <option>CTL{device}</option>, <option>CTL{subdev}</option>,
+ <option>CTL{name}</option>, <option>CTL{index}</option>,
+ </term>
+ <listitem>
+ <para>Select universal control element.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>CTL{value}</option></term>
+ <listitem>
+ <para>Value is set (written) also to soundcard's control
+ device and RESULT key is set to errno code. The result of
+ set operation is always true (it means continue with
+ next key on line).</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><option>CTL{values}</option></term>
+ <listitem>
+ <para>Value is set (written) also to soundcard's control
+ device (all control values are set to specified value) and
+ RESULT key is set to errno code. The result of
+ set operation is always true (it means continue with
+ next key on line).</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ENV{<replaceable>key</replaceable>}</option></term>
+ <listitem>
+ <para>Export a variable to the environment. Depending on the type of operator,
+ this key is also to match against an environment variable.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>RESULT</option></term>
+ <listitem>
+ <para>Set RESULT variable. Note that PROGRAM also sets
+ this variable, but setting this variable manually
+ might be useful to change code execution order (included
+ files).</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>LABEL</option></term>
+ <listitem>
+ <para>Named label where a GOTO can jump to.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>GOTO</option></term>
+ <listitem>
+ <para>Jumps to the next LABEL with a matching name</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>INCLUDE</option></term>
+ <listitem>
+ <para>Include specified filename or all files in specified directory</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ACCESS</option></term>
+ <listitem>
+ <para>Check if specified file or directory exists</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>CONFIG{sysfs_device}</option></term>
+ <listitem>
+ <para>The relative path to sysfs subsystem specifying
+ the root directory of a soundcard device. Usually,
+ it should be set to "/class/sound/controlC$cardinfo{card}/device".
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>PRINT</option></term>
+ <listitem>
+ <para>PRINT value to stdout.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>ERROR</option></term>
+ <listitem>
+ <para>PRINT value to stderr.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>EXIT</option></term>
+ <listitem>
+ <para>Exit immediately and set program exit code to value
+ (should be integer). If value is "return" string,
+ parser leaves current included file and returns to parent
+ configuration file.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>The <option>PROGRAM</option>, <option>RESULT</option>,
+ <option>CTL{value}</option>,
+ <option>PRINT</option>, <option>ERROR</option>,
+ <option>EXIT</option>, <option>CONFIG{}</option>
+ fields support simple printf-like string substitutions.
+ It allows the use of the complete environment set by earlier matching
+ rules. For all other fields, substitutions are applied while the individual rule is
+ being processed. The available substitutions are:</para>
+ <variablelist>
+ <varlistentry>
+ <term><option>$cardinfo{<replaceable>attribute</replaceable>}</option>, <option>%i{<replaceable>attribute</replaceable>}</option></term>
+ <listitem>
+ <para>See CARDINFO{} for more details.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$ctl{<replaceable>attribute</replaceable>}</option>, <option>%C{<replaceable>attribute</replaceable>}</option></term>
+ <listitem>
+ <para>See CTL{} for more details.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$attr{<replaceable>file</replaceable>}</option>, <option>%s{<replaceable>file</replaceable>}</option></term>
+ <listitem>
+ <para>The value of a sysfs attribute found at the device, where
+ all keys of the rule have matched.
+ If the attribute is a symlink, the last element of the symlink target is
+ returned as the value.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$env{<replaceable>key</replaceable>}</option>, <option>%E{<replaceable>key</replaceable>}</option></term>
+ <listitem>
+ <para>The value of an environment variable.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$result</option>, <option>%c</option></term>
+ <listitem>
+ <para>The string returned by the external program requested with PROGRAM.
+ A single part of the string, separated by a space character may be selected
+ by specifying the part number as an attribute: <option>%c{N}</option>.
+ If the number is followed by the '+' char this part plus all remaining parts
+ of the result string are substituted: <option>%c{N+}</option></para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$sysfsroot</option>, <option>%r</option></term>
+ <listitem>
+ <para>Root directory where sysfs file-system is mounted.
+ Ususally, this value is just "/sys".</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>%%</option></term>
+ <listitem>
+ <para>The '%' character itself.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>$$</option></term>
+ <listitem>
+ <para>The '$' character itself.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>The count of characters to be substituted may be limited
+ by specifying the format length value. For example, '%3s{file}'
+ will only insert the first three characters of the sysfs
+ attribute</para>
+ </refsect2>
+ </refsect1>
+
+ <refsect1><title>AUTHOR</title>
+ <para>Written by Jaroslav Kysela <email>perex@perex.cz</email></para>
+ <para>Some portions are written by Greg Kroah-Hartman <email>greg@kroah.com</email> and
+ Kay Sievers <email>kay.sievers@vrfy.org</email>.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>alsactl</refentrytitle><manvolnum>1</manvolnum>
+ </citerefentry></para>
+ </refsect1>
+ </refentry>
+ </section>
+</article>
diff --git a/alsa-utils/alsactl/init/00main b/alsa-utils/alsactl/init/00main
new file mode 100644
index 0000000..942f386
--- /dev/null
+++ b/alsa-utils/alsactl/init/00main
@@ -0,0 +1,46 @@
+# This is toplevel configuration for for 'alsactl init'.
+# See 'man alsactl_init' for syntax.
+
+# set root device directory in sysfs for soundcard for ATTR{} command
+CONFIG{sysfs_device}="/class/sound/controlC$cardinfo{card}/device"
+
+# test for extra commands
+ENV{CMD}=="help", INCLUDE="help", GOTO="00main_end"
+ENV{CMD}=="info", INCLUDE="info", GOTO="00main_end"
+ENV{CMD}=="default", INCLUDE="default", GOTO="00main_end"
+ENV{CMD}=="test", INCLUDE="test", GOTO="00main_end"
+ENV{CMD}=="*", ERROR="Unknown command '$env{CMD}'\n", GOTO="00main_end"
+
+# include files with real configuration
+#
+# steps are:
+# 1) look for preinit subdirectory and parse all files in it
+# 2) if RESULT=="skip", skip ALSA standard configuration files
+# 3) do ALSA standard configuration
+# 4) look for postinit subdirectory and parse all files in it
+# 5) if RESULT!="true", initialize hardware using a guess method,
+# print an error message and return with exit code 99
+# 6) return with exit code 0 (success)
+#
+
+RESULT="unknown"
+ACCESS=="preinit", INCLUDE="preinit"
+RESULT=="skip", GOTO="init_end"
+
+# real ALSA configuration database
+CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="init_end"
+CARDINFO{driver}=="Test", INCLUDE="test", GOTO="init_end"
+
+LABEL="init_end"
+ACCESS=="postinit", INCLUDE="postinit"
+RESULT=="true", GOTO="00_mainend"
+ERROR="Unknown hardware: \"$cardinfo{driver}\" \"$cardinfo{mixername}\" \"$cardinfo{components}\" \"$attr{subsystem_vendor}\" \"$attr{subsystem_device}\"\n"
+ERROR="Hardware is initialized using a guess method\n"
+INCLUDE="default"
+EXIT="99"
+
+#
+# label identifying end of main file
+#
+
+LABEL="00main_end"
diff --git a/alsa-utils/alsactl/init/Makefile.am b/alsa-utils/alsactl/init/Makefile.am
new file mode 100644
index 0000000..9e2b83d
--- /dev/null
+++ b/alsa-utils/alsactl/init/Makefile.am
@@ -0,0 +1,7 @@
+
+init_files = \
+ 00main default help info test \
+ hda
+EXTRA_DIST = $(init_files)
+alsainitdir = $(datadir)/alsa/init
+alsainit_DATA = $(init_files)
diff --git a/alsa-utils/alsactl/init/Makefile.in b/alsa-utils/alsactl/init/Makefile.in
new file mode 100644
index 0000000..1cabfb4
--- /dev/null
+++ b/alsa-utils/alsactl/init/Makefile.in
@@ -0,0 +1,366 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ../..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = alsactl/init
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/aconfig.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(alsainitdir)"
+alsainitDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(alsainit_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALSACONF_FALSE = @ALSACONF_FALSE@
+ALSACONF_TRUE = @ALSACONF_TRUE@
+ALSAMIXER_FALSE = @ALSAMIXER_FALSE@
+ALSAMIXER_TRUE = @ALSAMIXER_TRUE@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CURSESINC = @CURSESINC@
+CURSESLIB = @CURSESLIB@
+CURSES_CFLAGS = @CURSES_CFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SND_UTIL_MAJOR = @SND_UTIL_MAJOR@
+SND_UTIL_MINOR = @SND_UTIL_MINOR@
+SND_UTIL_SUBMINOR = @SND_UTIL_SUBMINOR@
+SND_UTIL_VERSION = @SND_UTIL_VERSION@
+STRIP = @STRIP@
+TESTSOUND = @TESTSOUND@
+USE_NLS = @USE_NLS@
+USE_XMLTO_FALSE = @USE_XMLTO_FALSE@
+USE_XMLTO_TRUE = @USE_XMLTO_TRUE@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+ac_ct_CC = @ac_ct_CC@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+ncurses5_config = @ncurses5_config@
+ncursesw5_config = @ncursesw5_config@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+xmlto = @xmlto@
+init_files = \
+ 00main default help info test \
+ hda
+
+EXTRA_DIST = $(init_files)
+alsainitdir = $(datadir)/alsa/init
+alsainit_DATA = $(init_files)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign alsactl/init/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign alsactl/init/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+uninstall-info-am:
+install-alsainitDATA: $(alsainit_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(alsainitdir)" || $(mkdir_p) "$(DESTDIR)$(alsainitdir)"
+ @list='$(alsainit_DATA)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(alsainitDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(alsainitdir)/$$f'"; \
+ $(alsainitDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(alsainitdir)/$$f"; \
+ done
+
+uninstall-alsainitDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(alsainit_DATA)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(alsainitdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(alsainitdir)/$$f"; \
+ done
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(alsainitdir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-alsainitDATA
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-alsainitDATA uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+ distclean-generic distdir dvi dvi-am html html-am info info-am \
+ install install-alsainitDATA install-am install-data \
+ install-data-am install-exec install-exec-am install-info \
+ install-info-am install-man install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
+ pdf-am ps ps-am uninstall uninstall-alsainitDATA uninstall-am \
+ uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/alsa-utils/alsactl/init/default b/alsa-utils/alsactl/init/default
new file mode 100644
index 0000000..66692b3
--- /dev/null
+++ b/alsa-utils/alsactl/init/default
@@ -0,0 +1,145 @@
+#
+# Default ALSA volume levels and setting when initialization database fails.
+#
+# Basic rules are:
+# - keep volumes at minimal level, but sound should be hearable
+# - enable just main speakers for playback and main microphone for recording
+#
+
+# **************************************************************************
+# playback
+# **************************************************************************
+
+ENV{volume}:="-20dB"
+
+CTL{reset}="mixer"
+CTL{name}="Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Master Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Master Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Master Digital Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Master Digital Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Front Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Front Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Headphone Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Headphone Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Speaker Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Speaker Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="PC Speaker Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="PC Speaker Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="PCM Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="PCM Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="PCM Playback Volume",CTL{index}="1",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="PCM Playback Switch",CTL{index}="1",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="DAC Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="DAC Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Synth Playback Volume",,PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Synth Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Wave Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="100%"
+CTL{name}="Wave Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Music Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="100%"
+CTL{name}="Music Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="CD Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="CD Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Mono Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Mono Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="Master Mono Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Master Mono Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{reset}="mixer"
+CTL{name}="AC97 Playback Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="100%"
+CTL{name}="AC97 Playback Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+#
+# Powermacs
+#
+
+CTL{reset}="mixer"
+CTL{name}="DRC Range",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+
+# **************************************************************************
+# capture
+# **************************************************************************
+
+ENV{volume}:="20dB"
+
+CTL{reset}="mixer"
+CTL{name}="Capture Volume",PROGRAM=="__ctl_search", \
+ CTL{values}="$env{volume}",RESULT!="0",CTL{values}="75%"
+CTL{name}="Capture Switch",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
+
+CTL{name}="Input Source",PROGRAM=="__ctl_search", \
+ CTL{enums}=="*|Internal Mic|*",CTL{values}="Internal Mic", \
+ GOTO="end_input_source"
+CTL{name}="Input Source",PROGRAM=="__ctl_search", \
+ CTL{enums}=="*|Mic|*",CTL{values}="Mic"
+LABEL="end_input_source"
+
+CTL{name}="Internal Mic Boost",PROGRAM=="__ctl_search", \
+ CTL{values}="on"
diff --git a/alsa-utils/alsactl/init/hda b/alsa-utils/alsactl/init/hda
new file mode 100644
index 0000000..43dfec2
--- /dev/null
+++ b/alsa-utils/alsactl/init/hda
@@ -0,0 +1,34 @@
+# Configuration for HDA Intel driver (High Definition Audio - Azalia)
+
+CARDINFO{mixername}=="Realtek ALC880", \
+ ATTR{subsystem_vendor}=="0x1025", ATTR{subsystem_device}=="0x0070", \
+ GOTO="Acer Travelmate 8100"
+CARDINFO{mixername}=="Analog Devices AD1984", \
+ ATTR{subsystem_vendor}=="0x17aa", ATTR{subsystem_device}=="0x20ac", \
+ GOTO="Lenovo T61"
+RESULT="false", EXIT="return"
+
+LABEL="Acer Travelmate 8100"
+# playback
+CTL{reset}="mixer"
+CTL{name}="Headphone Playback Switch", CTL{value}="on,on"
+CTL{name}="Front Playback Volume", CTL{value}="-29dB,-29dB"
+CTL{name}="Front Playback Switch", CTL{value}="on,on"
+CTL{name}="PCM Playback Volume", CTL{value}="-21dB,-21dB"
+# capture
+CTL{name}="Input Source", CTL{value}="Mic"
+CTL{name}="Capture Volume", CTL{value}="20dB,20dB"
+CTL{name}="Capture Switch", CTL{value}="on,on"
+RESULT="true", EXIT="return"
+
+LABEL="Lenovo T61"
+# playback
+CTL{reset}="mixer"
+CTL{name}="Headphone Playback Switch", CTL{value}="on,on"
+CTL{name}="PCM Playback Volume", CTL{value}="-13.5dB,-13.5dB"
+# capture
+CTL{name}="Input Source", CTL{value}="Internal Mic"
+CTL{name}="Internal Mic Boost", CTL{value}="1"
+CTL{name}="Capture Volume", CTL{value}="9dB,9dB"
+CTL{name}="Capture Switch", CTL{value}="on,on"
+RESULT="true", EXIT="return"
diff --git a/alsa-utils/alsactl/init/help b/alsa-utils/alsactl/init/help
new file mode 100644
index 0000000..4f0ba70
--- /dev/null
+++ b/alsa-utils/alsactl/init/help
@@ -0,0 +1,8 @@
+# help page
+
+PRINT="Available commands (identified by the environment variable CMD):\n\n"
+PRINT=" (not set) Do a soundcard initialization\n"
+PRINT=" default Do a default (guess method) initialization\n"
+PRINT=" help Show this information\n"
+PRINT=" info Print all available hardware identification\n"
+PRINT=" test Do alsactl utility parser tests\n"
diff --git a/alsa-utils/alsactl/init/info b/alsa-utils/alsactl/init/info
new file mode 100644
index 0000000..a4fea19
--- /dev/null
+++ b/alsa-utils/alsactl/init/info
@@ -0,0 +1,22 @@
+# show information about card
+
+PRINT="CARDINFO:\n"
+PRINT=" CARDINFO{id}=\"$CARDINFO{id}\"\n"
+PRINT=" CARDINFO{card}=\"$CARDINFO{card}\"\n"
+PRINT=" CARDINFO{driver}=\"$CARDINFO{driver}\"\n"
+PRINT=" CARDINFO{name}=\"$CARDINFO{name}\"\n"
+PRINT=" CARDINFO{longname}=\"$CARDINFO{longname}\"\n"
+PRINT=" CARDINFO{mixername}=\"$CARDINFO{mixername}\"\n"
+PRINT=" CARDINFO{components}=\"$CARDINFO{components}\"\n"
+
+# sysfs stuff
+PRINT="sysfs:\n"
+ATTR{bus}=="*", PRINT=" ATTR{bus}=\"$ATTR{bus}\"\n"
+ATTR{class}=="*", PRINT=" ATTR{class}=\"$ATTR{class}\"\n"
+ATTR{driver}=="*", PRINT=" ATTR{driver}=\"$ATTR{driver}\"\n"
+ATTR{vendor}=="*", PRINT=" ATTR{vendor}=\"$ATTR{vendor}\"\n"
+ATTR{device}=="*", PRINT=" ATTR{device}=\"$ATTR{device}\"\n"
+ATTR{subsystem_vendor}=="*", \
+ PRINT=" ATTR{subsystem_vendor}=\"$ATTR{subsystem_vendor}\"\n"
+ATTR{subsystem_device}=="*", \
+ PRINT=" ATTR{subsystem_device}=\"$ATTR{subsystem_device}\"\n"
diff --git a/alsa-utils/alsactl/init/test b/alsa-utils/alsactl/init/test
new file mode 100644
index 0000000..26db2a3
--- /dev/null
+++ b/alsa-utils/alsactl/init/test
@@ -0,0 +1,270 @@
+# Test code
+# Just for debugging purposes
+
+PRINT="Default CTL:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+
+CTL{reset}="mixer"
+
+PRINT="After CTL{reset}=\"mixer\":\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+
+CTL{numid}="987"
+CTL{iface}="sequencer"
+CTL{device}="10"
+CTL{subdevice}="20"
+CTL{name}="Just Test"
+CTL{index}="999"
+
+PRINT="After test sequence:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+
+ERROR="Ignore following error:\n "
+PROGRAM="__just_test"
+
+PRINT="__ctl_count test:\n"
+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM="__ctl_count", \
+ PRINT=" *Switch* count result: $result\n"
+
+PRINT="__ctl_search test:\n"
+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search", GOTO="skip_switch_search"
+PRINT=" *Switch 0* search result: $result\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search 1", GOTO="skip_switch_search"
+PRINT=" *Switch 1* search result: $result\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+
+PRINT="First ten elements:\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 0", GOTO="skip_first_ten_search"
+PRINT=" Element #0:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 1", GOTO="skip_first_ten_search"
+PRINT=" Element #1:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 2", GOTO="skip_first_ten_search"
+PRINT=" Element #2:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 3", GOTO="skip_first_ten_search"
+PRINT=" Element #3:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 4", GOTO="skip_first_ten_search"
+PRINT=" Element #4:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 5", GOTO="skip_first_ten_search"
+PRINT=" Element #5:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 6", GOTO="skip_first_ten_search"
+PRINT=" Element #6:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 7", GOTO="skip_first_ten_search"
+PRINT=" Element #7:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 8", GOTO="skip_first_ten_search"
+PRINT=" Element #8:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 9", GOTO="skip_first_ten_search"
+PRINT=" Element #9:\n"
+PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
+PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
+PRINT=" CTL{device}=\"$ctl{device}\"\n"
+PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
+PRINT=" CTL{name}=\"$ctl{name}\"\n"
+PRINT=" CTL{index}=\"$ctl{index}\"\n"
+PRINT=" CTL{type}=\"$ctl{type}\"\n"
+PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
+PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
+PRINT=" CTL{count}=\"$ctl{count}\"\n"
+PRINT=" CTL{min}=\"$ctl{min}\"\n"
+PRINT=" CTL{max}=\"$ctl{max}\"\n"
+PRINT=" CTL{step}=\"$ctl{step}\"\n"
+PRINT=" CTL{dBmin}=\"$ctl{dBmin}\"\n"
+PRINT=" CTL{dBmax}=\"$ctl{dBmax}\"\n"
+PRINT=" CTL{items}=\"$ctl{items}\"\n"
+PRINT=" CTL{value}=\"$ctl{value}\"\n"
+LABEL="skip_first_ten_search"
+
+PRINT="Elements write test #1:\n", \
+ CTL{search}="mixer", CTL{name}="Front Playback Switch", \
+ PROGRAM="__ctl_search", CTL{value}="on,on", \
+ PRINT=" result=$result\n"
+PRINT="Elements write test #2:\n", \
+ CTL{search}="mixer", CTL{name}="Front Playback Volume", \
+ PROGRAM="__ctl_search", CTL{value}="32,32", \
+ PRINT=" result=$result\n"
+PRINT="Elements write test #3:\n", \
+ CTL{search}="mixer", CTL{name}="Front Playback Volume Error", \
+ PROGRAM="__ctl_search"
+PRINT=" result=$result\n"
+
+#CTL{reset}="mixer", CTL{name}="Input Source", PRINT="***$ctl{enums}\n"
+
+PRINT="\nAll tests done..\n"
diff --git a/alsa-utils/alsactl/init_parse.c b/alsa-utils/alsactl/init_parse.c
new file mode 100644
index 0000000..c82797e
--- /dev/null
+++ b/alsa-utils/alsactl/init_parse.c
@@ -0,0 +1,1739 @@
+/*
+ * Advanced Linux Sound Architecture Control Program - Parse initialization files
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ * Greg Kroah-Hartman <greg@kroah.com>,
+ * Kay Sievers <kay.sievers@vrfy.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <math.h>
+#include <alsa/asoundlib.h>
+#include "aconfig.h"
+#include "alsactl.h"
+#include "list.h"
+
+#define PATH_SIZE 512
+#define NAME_SIZE 128
+#define EJUSTRETURN 0x7fffffff
+
+enum key_op {
+ KEY_OP_UNSET,
+ KEY_OP_MATCH,
+ KEY_OP_NOMATCH,
+ KEY_OP_ADD,
+ KEY_OP_ASSIGN,
+ KEY_OP_ASSIGN_FINAL
+};
+
+struct pair {
+ char *key;
+ char *value;
+ struct pair *next;
+};
+
+struct space {
+ struct pair *pairs;
+ char *rootdir;
+ char *go_to;
+ char *program_result;
+ const char *filename;
+ int linenum;
+ int log_run;
+ int exit_code;
+ int quit;
+ unsigned int ctl_id_changed;
+ snd_hctl_t *ctl_handle;
+ snd_ctl_card_info_t *ctl_card_info;
+ snd_ctl_elem_id_t *ctl_id;
+ snd_ctl_elem_info_t *ctl_info;
+ snd_ctl_elem_value_t *ctl_value;
+};
+
+static void Perror(struct space *space, const char *fmt, ...)
+{
+ va_list arg;
+ va_start(arg, fmt);
+ fprintf(stderr, "%s:%i: ", space->filename, space->linenum);
+ vfprintf(stderr, fmt, arg);
+ putc('\n', stderr);
+ va_end(arg);
+}
+
+#include "init_sysdeps.c"
+#include "init_utils_string.c"
+#include "init_utils_run.c"
+#include "init_sysfs.c"
+
+static void free_space(struct space *space)
+{
+ struct pair *pair = space->pairs;
+ struct pair *next = pair;
+
+ while (next) {
+ pair = next;
+ next = pair->next;
+ free(pair->value);
+ free(pair->key);
+ free(pair);
+ }
+ space->pairs = NULL;
+ if (space->ctl_value) {
+ snd_ctl_elem_value_free(space->ctl_value);
+ space->ctl_value = NULL;
+ }
+ if (space->ctl_info) {
+ snd_ctl_elem_info_free(space->ctl_info);
+ space->ctl_info = NULL;
+ }
+ if (space->ctl_id) {
+ snd_ctl_elem_id_free(space->ctl_id);
+ space->ctl_id = NULL;
+ }
+ if (space->ctl_card_info) {
+ snd_ctl_card_info_free(space->ctl_card_info);
+ space->ctl_card_info = NULL;
+ }
+ if (space->ctl_handle) {
+ snd_hctl_close(space->ctl_handle);
+ space->ctl_handle = NULL;
+ }
+ if (space->rootdir)
+ free(space->rootdir);
+ if (space->program_result)
+ free(space->program_result);
+ if (space->go_to)
+ free(space->go_to);
+ free(space);
+}
+
+static struct pair *value_find(struct space *space, const char *key)
+{
+ struct pair *pair = space->pairs;
+
+ while (pair && strcmp(pair->key, key) != 0)
+ pair = pair->next;
+ return pair;
+}
+
+static int value_set(struct space *space, const char *key, const char *value)
+{
+ struct pair *pair;
+
+ pair = value_find(space, key);
+ if (pair) {
+ free(pair->value);
+ pair->value = strdup(value);
+ if (pair->value == NULL)
+ return -ENOMEM;
+ } else {
+ pair = malloc(sizeof(struct pair));
+ if (pair == NULL)
+ return -ENOMEM;
+ pair->key = strdup(key);
+ if (pair->key == NULL) {
+ free(pair);
+ return -ENOMEM;
+ }
+ pair->value = strdup(value);
+ if (pair->value == NULL) {
+ free(pair->key);
+ free(pair);
+ return -ENOMEM;
+ }
+ pair->next = space->pairs;
+ space->pairs = pair;
+ }
+ return 0;
+}
+
+static int init_space(struct space **space, int card)
+{
+ struct space *res;
+ char device[16];
+ int err;
+
+ res = calloc(1, sizeof(struct space));
+ if (res == NULL)
+ return -ENOMEM;
+ res->ctl_id_changed = ~0;
+ res->linenum = -1;
+ sprintf(device, "hw:%u", card);
+ err = snd_hctl_open(&res->ctl_handle, device, 0);
+ if (err < 0)
+ goto error;
+ err = snd_hctl_load(res->ctl_handle);
+ if (err < 0)
+ goto error;
+ err = snd_ctl_card_info_malloc(&res->ctl_card_info);
+ if (err < 0)
+ goto error;
+ err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info);
+ if (err < 0)
+ goto error;
+ err = snd_ctl_elem_id_malloc(&res->ctl_id);
+ if (err < 0)
+ goto error;
+ err = snd_ctl_elem_info_malloc(&res->ctl_info);
+ if (err < 0)
+ goto error;
+ err = snd_ctl_elem_value_malloc(&res->ctl_value);
+ if (err < 0)
+ goto error;
+ *space = res;
+ return 0;
+ error:
+ free_space(res);
+ return err;
+}
+
+static const char *cardinfo_get(struct space *space, const char *attr)
+{
+ if (strncasecmp(attr, "CARD", 4) == 0) {
+ static char res[16];
+ sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info));
+ return res;
+ }
+ if (strncasecmp(attr, "ID", 2) == 0)
+ return snd_ctl_card_info_get_id(space->ctl_card_info);
+ if (strncasecmp(attr, "DRIVER", 6) == 0)
+ return snd_ctl_card_info_get_driver(space->ctl_card_info);
+ if (strncasecmp(attr, "NAME", 4) == 0)
+ return snd_ctl_card_info_get_name(space->ctl_card_info);
+ if (strncasecmp(attr, "LONGNAME", 8) == 0)
+ return snd_ctl_card_info_get_longname(space->ctl_card_info);
+ if (strncasecmp(attr, "MIXERNAME", 9) == 0)
+ return snd_ctl_card_info_get_mixername(space->ctl_card_info);
+ if (strncasecmp(attr, "COMPONENTS", 10) == 0)
+ return snd_ctl_card_info_get_components(space->ctl_card_info);
+ Perror(space, "unknown cardinfo{} attribute '%s'", attr);
+ return NULL;
+}
+
+static int check_id_changed(struct space *space, unsigned int what)
+{
+ snd_hctl_elem_t *elem;
+ int err;
+
+ if ((space->ctl_id_changed & what & 1) != 0) {
+ snd_ctl_elem_id_set_numid(space->ctl_id, 0);
+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
+ if (!elem)
+ return -ENOENT;
+ err = snd_hctl_elem_info(elem, space->ctl_info);
+ if (err == 0)
+ space->ctl_id_changed &= ~1;
+ return err;
+ }
+ if ((space->ctl_id_changed & what & 2) != 0) {
+ snd_ctl_elem_id_set_numid(space->ctl_id, 0);
+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
+ if (!elem)
+ return -ENOENT;
+ err = snd_hctl_elem_read(elem, space->ctl_value);
+ if (err == 0)
+ space->ctl_id_changed &= ~2;
+ return err;
+ }
+ return 0;
+}
+
+static const char *get_ctl_value(struct space *space)
+{
+ snd_ctl_elem_type_t type;
+ unsigned int idx, count;
+ static char res[1024], tmp[16];
+ static const char hex[] = "0123456789abcdef";
+ char *pos;
+ const char *pos1;
+
+ type = snd_ctl_elem_info_get_type(space->ctl_info);
+ count = snd_ctl_elem_info_get_count(space->ctl_info);
+ res[0] = '\0';
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (idx = 0; idx < count; idx++) {
+ if (idx > 0)
+ strlcat(res, ",", sizeof(res));
+ strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res));
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (idx = 0; idx < count; idx++) {
+ if (idx > 0)
+ strlcat(res, ",", sizeof(res));
+ snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx));
+ strlcat(res, tmp, sizeof(res));
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (idx = 0; idx < count; idx++) {
+ if (idx > 0)
+ strlcat(res, ",", sizeof(res));
+ snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx));
+ strlcat(res, tmp, sizeof(res));
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (idx = 0; idx < count; idx++) {
+ if (idx > 0)
+ strlcat(res, ",", sizeof(res));
+ snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx));
+ strlcat(res, tmp, sizeof(res));
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ if (type == SND_CTL_ELEM_TYPE_IEC958)
+ count = sizeof(snd_aes_iec958_t);
+ if (count > (sizeof(res)-1)/2)
+ count = (sizeof(res)-1/2);
+ pos = res;
+ pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value);
+ while (count > 0) {
+ idx = *pos1++;
+ *pos++ = hex[idx >> 4];
+ *pos++ = hex[idx & 0x0f];
+ count++;
+ }
+ *pos++ = '\0';
+ break;
+ default:
+ Perror(space, "unknown element type '%i'", type);
+ return NULL;
+ }
+ return res;
+}
+
+/* Function to convert from percentage to volume. val = percentage */
+#define convert_prange1(val, min, max) \
+ ceil((val) * ((max) - (min)) * 0.01 + (min))
+
+static int set_ctl_value(struct space *space, const char *value, int all)
+{
+ snd_ctl_elem_type_t type;
+ unsigned int idx, idx2, count, items;
+ const char *pos, *pos2;
+ snd_hctl_elem_t *elem;
+ int val;
+ long lval;
+
+ type = snd_ctl_elem_info_get_type(space->ctl_info);
+ count = snd_ctl_elem_info_get_count(space->ctl_info);
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (idx = 0; idx < count; idx++) {
+ while (*value == ' ')
+ value++;
+ if (*value == '\0')
+ goto missing;
+ val = strncasecmp(value, "true", 4) == 0 ||
+ strncasecmp(value, "yes", 3) == 0 ||
+ strncasecmp(value, "on", 2) == 0 ||
+ strncasecmp(value, "1", 1) == 0;
+ snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val);
+ if (all)
+ continue;
+ pos = strchr(value, ',');
+ value = pos ? pos + 1 : value + strlen(value) - 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (idx = 0; idx < count; idx++) {
+ while (*value == ' ')
+ value++;
+ pos = strchr(value, ',');
+ if (pos)
+ *(char *)pos = '\0';
+ remove_trailing_chars((char *)value, ' ');
+ items = pos ? pos - value : strlen(value);
+ if (items > 1 && value[items-1] == '%') {
+ val = convert_prange1(strtol(value, NULL, 0), snd_ctl_elem_info_get_min(space->ctl_info), snd_ctl_elem_info_get_max(space->ctl_info));
+ snd_ctl_elem_value_set_integer(space->ctl_value, idx, val);
+ } else if (items > 2 && value[items-2] == 'd' && value[items-1] == 'B') {
+ val = strtol(value, NULL, 0) * 100;
+ if ((pos2 = index(value, '.')) != NULL) {
+ if (isdigit(*(pos2-1)) && isdigit(*(pos2-2))) {
+ if (val < 0)
+ val -= strtol(pos2 + 1, NULL, 0);
+ else
+ val += strtol(pos2 + 1, NULL, 0);
+ } else if (isdigit(*(pos2-1))) {
+ if (val < 0)
+ val -= strtol(pos2 + 1, NULL, 0) * 10;
+ else
+ val += strtol(pos2 + 1, NULL, 0) * 10;
+ }
+ }
+ val = snd_ctl_convert_from_dB(snd_hctl_ctl(space->ctl_handle), space->ctl_id, val, &lval, -1);
+ if (val < 0) {
+ Perror(space, "unable to convert dB value '%s' to internal integer range", value);
+ return val;
+ }
+ snd_ctl_elem_value_set_integer(space->ctl_value, idx, lval);
+ } else {
+ snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0));
+ }
+ if (all)
+ continue;
+ value = pos ? pos + 1 : value + strlen(value) - 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (idx = 0; idx < count; idx++) {
+ while (*value == ' ')
+ value++;
+ snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0));
+ if (all)
+ continue;
+ pos = strchr(value, ',');
+ value = pos ? pos + 1 : value + strlen(value) - 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (idx = 0; idx < count; idx++) {
+ while (*value == ' ')
+ value++;
+ pos = strchr(value, ',');
+ if (isdigit(value[0]) || value[0] == '-') {
+ snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0));
+ } else {
+ if (pos)
+ *(char *)pos = '\0';
+ remove_trailing_chars((char *)value, ' ');
+ items = snd_ctl_elem_info_get_items(space->ctl_info);
+ for (idx2 = 0; idx2 < items; idx2++) {
+ snd_ctl_elem_info_set_item(space->ctl_info, idx2);
+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
+ if (elem == NULL)
+ return -ENOENT;
+ val = snd_hctl_elem_info(elem, space->ctl_info);
+ if (val < 0)
+ return val;
+ if (strcasecmp(snd_ctl_elem_info_get_item_name(space->ctl_info), value) == 0) {
+ snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, idx2);
+ break;
+ }
+ }
+ if (idx2 >= items) {
+ Perror(space, "wrong enum identifier '%s'", value);
+ return -EINVAL;
+ }
+ }
+ if (all)
+ continue;
+ value = pos ? pos + 1 : value + strlen(value) - 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ if (type == SND_CTL_ELEM_TYPE_IEC958)
+ count = sizeof(snd_aes_iec958_t);
+ while (*value == ' ')
+ value++;
+ if (strlen(value) != count * 2) {
+ Perror(space, "bad ctl value hexa length (should be %u bytes)", count);
+ return -EINVAL;
+ }
+ for (idx = 0; idx < count; idx += 2) {
+ val = hextodigit(*(value++)) << 4;
+ val |= hextodigit(*(value++));
+ if (val > 255) {
+ Perror(space, "bad ctl hexa value");
+ return -EINVAL;
+ }
+ snd_ctl_elem_value_set_byte(space->ctl_value, idx, val);
+ }
+ break;
+ default:
+ Perror(space, "unknown element type '%i'", type);
+ return -EINVAL;
+ }
+ return 0;
+ missing:
+ printf("%i %i\n", type, count);
+ Perror(space, "missing some ctl values (line %i)", space->linenum);
+ return -EINVAL;
+}
+
+static const char *elemid_get(struct space *space, const char *attr)
+{
+ long long val;
+ snd_ctl_elem_type_t type;
+ static char res[256];
+
+ if (strncasecmp(attr, "numid", 5) == 0) {
+ val = snd_ctl_elem_id_get_numid(space->ctl_id);
+ goto value;
+ }
+ if (strncasecmp(attr, "iface", 5) == 0 ||
+ strncasecmp(attr, "interface", 9) == 0)
+ return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id));
+ if (strncasecmp(attr, "device", 6) == 0) {
+ val = snd_ctl_elem_id_get_device(space->ctl_id);
+ goto value;
+ }
+ if (strncasecmp(attr, "subdev", 6) == 0) {
+ val = snd_ctl_elem_id_get_subdevice(space->ctl_id);
+ goto value;
+ }
+ if (strncasecmp(attr, "name", 4) == 0)
+ return snd_ctl_elem_id_get_name(space->ctl_id);
+ if (strncasecmp(attr, "index", 5) == 0) {
+ val = snd_ctl_elem_id_get_index(space->ctl_id);
+ goto value;
+ }
+ if (strncasecmp(attr, "type", 4) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info));
+ }
+ if (strncasecmp(attr, "attr", 4) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ res[0] = '\0';
+ if (snd_ctl_elem_info_is_readable(space->ctl_info))
+ strcat(res, "r");
+ if (snd_ctl_elem_info_is_writable(space->ctl_info))
+ strcat(res, "w");
+ if (snd_ctl_elem_info_is_volatile(space->ctl_info))
+ strcat(res, "v");
+ if (snd_ctl_elem_info_is_inactive(space->ctl_info))
+ strcat(res, "i");
+ if (snd_ctl_elem_info_is_locked(space->ctl_info))
+ strcat(res, "l");
+ if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info))
+ strcat(res, "R");
+ if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info))
+ strcat(res, "W");
+ if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info))
+ strcat(res, "C");
+ if (snd_ctl_elem_info_is_owner(space->ctl_info))
+ strcat(res, "o");
+ if (snd_ctl_elem_info_is_user(space->ctl_info))
+ strcat(res, "u");
+ return res;
+ }
+ if (strncasecmp(attr, "owner", 5) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ val = snd_ctl_elem_info_get_owner(space->ctl_info);
+ goto value;
+ }
+ if (strncasecmp(attr, "count", 5) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ val = snd_ctl_elem_info_get_count(space->ctl_info);
+ goto value;
+ }
+ if (strncasecmp(attr, "min", 3) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ type = snd_ctl_elem_info_get_type(space->ctl_info);
+ if (type == SND_CTL_ELEM_TYPE_INTEGER64)
+ val = snd_ctl_elem_info_get_min64(space->ctl_info);
+ else if (type == SND_CTL_ELEM_TYPE_INTEGER)
+ val = snd_ctl_elem_info_get_min(space->ctl_info);
+ else
+ goto empty;
+ goto value;
+ }
+ if (strncasecmp(attr, "max", 3) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ type = snd_ctl_elem_info_get_type(space->ctl_info);
+ if (type == SND_CTL_ELEM_TYPE_INTEGER64)
+ val = snd_ctl_elem_info_get_max64(space->ctl_info);
+ else if (type == SND_CTL_ELEM_TYPE_INTEGER)
+ val = snd_ctl_elem_info_get_max(space->ctl_info);
+ else
+ goto empty;
+ goto value;
+ }
+ if (strncasecmp(attr, "step", 3) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ type = snd_ctl_elem_info_get_type(space->ctl_info);
+ if (type == SND_CTL_ELEM_TYPE_INTEGER64)
+ val = snd_ctl_elem_info_get_step64(space->ctl_info);
+ else if (type == SND_CTL_ELEM_TYPE_INTEGER)
+ val = snd_ctl_elem_info_get_step(space->ctl_info);
+ else
+ goto empty;
+ goto value;
+ }
+ if (strncasecmp(attr, "items", 5) == 0) {
+ if (check_id_changed(space, 1))
+ return NULL;
+ if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED)
+ val = snd_ctl_elem_info_get_items(space->ctl_info);
+ else {
+ empty:
+ res[0] = '\0';
+ return res;
+ }
+ goto value;
+ }
+ if (strncasecmp(attr, "value", 5) == 0) {
+ if (check_id_changed(space, 3))
+ return NULL;
+ return get_ctl_value(space);
+ }
+ if (strncasecmp(attr, "dBmin", 5) == 0) {
+ long min, max;
+ if (check_id_changed(space, 1))
+ return NULL;
+ if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
+ goto empty;
+ val = min;
+dbvalue:
+ sprintf(res, "%li.%02idB", (long)(val / 100), (int)abs(val % 100));
+ return res;
+ }
+ if (strncasecmp(attr, "dBmax", 5) == 0) {
+ long min, max;
+ if (check_id_changed(space, 1))
+ return NULL;
+ if (snd_ctl_get_dB_range(snd_hctl_ctl(space->ctl_handle), space->ctl_id, &min, &max) < 0)
+ goto empty;
+ val = max;
+ goto dbvalue;
+ }
+ if (strncasecmp(attr, "enums", 5) == 0) {
+ unsigned int idx, items;
+ snd_hctl_elem_t *elem;
+ if (check_id_changed(space, 1))
+ return NULL;
+ if (snd_ctl_elem_info_get_type(space->ctl_info) != SND_CTL_ELEM_TYPE_ENUMERATED)
+ goto empty;
+ items = snd_ctl_elem_info_get_items(space->ctl_info);
+ strcpy(res, "|");
+ for (idx = 0; idx < items; idx++) {
+ snd_ctl_elem_info_set_item(space->ctl_info, idx);
+ elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
+ if (elem == NULL)
+ break;
+ if (snd_hctl_elem_info(elem, space->ctl_info) < 0)
+ break;
+ strlcat(res, snd_ctl_elem_info_get_item_name(space->ctl_info), sizeof(res));
+ strlcat(res, "|", sizeof(res));
+ }
+ return res;
+ }
+ Perror(space, "unknown ctl{} attribute '%s'", attr);
+ return NULL;
+ value:
+ sprintf(res, "%lli", val);
+ return res;
+}
+
+static int elemid_set(struct space *space, const char *attr, const char *value)
+{
+ unsigned int val;
+ void (*fcn)(snd_ctl_elem_id_t *, unsigned int);
+ snd_ctl_elem_iface_t iface;
+ int err;
+
+ if (strncasecmp(attr, "numid", 5) == 0) {
+ fcn = snd_ctl_elem_id_set_numid;
+ goto value;
+ }
+ if (strncasecmp(attr, "iface", 5) == 0 ||
+ strncasecmp(attr, "interface", 9) == 0 ||
+ strncasecmp(attr, "reset", 5) == 0 ||
+ strncasecmp(attr, "search", 6) == 0) {
+ if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) {
+ iface = 0;
+ goto search;
+ }
+ for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) {
+ if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) {
+ if (strncasecmp(attr, "reset", 5) == 0)
+ snd_ctl_elem_id_clear(space->ctl_id);
+ if (strncasecmp(attr, "search", 5) == 0) {
+ search:
+ snd_ctl_elem_id_clear(space->ctl_id);
+ /* -1 means all */
+ snd_ctl_elem_id_set_interface(space->ctl_id, -1);
+ snd_ctl_elem_id_set_device(space->ctl_id, -1);
+ snd_ctl_elem_id_set_subdevice(space->ctl_id, -1);
+ snd_ctl_elem_id_set_name(space->ctl_id, "*");
+ snd_ctl_elem_id_set_index(space->ctl_id, -1);
+ if (strlen(value) == 0)
+ return 0;
+ }
+ snd_ctl_elem_id_set_interface(space->ctl_id, iface);
+ space->ctl_id_changed = ~0;
+ return 0;
+ }
+ }
+ Perror(space, "unknown control interface name '%s'", value);
+ return -EINVAL;
+ }
+ if (strncasecmp(attr, "device", 6) == 0) {
+ fcn = snd_ctl_elem_id_set_device;
+ goto value;
+ }
+ if (strncasecmp(attr, "subdev", 6) == 0) {
+ fcn = snd_ctl_elem_id_set_subdevice;
+ goto value;
+ }
+ if (strncasecmp(attr, "name", 4) == 0) {
+ snd_ctl_elem_id_set_name(space->ctl_id, value);
+ space->ctl_id_changed = ~0;
+ return 0;
+ }
+ if (strncasecmp(attr, "index", 5) == 0) {
+ fcn = snd_ctl_elem_id_set_index;
+ goto value;
+ }
+ if (strncasecmp(attr, "values", 6) == 0 ||
+ strncasecmp(attr, "value", 5) == 0) {
+ err = check_id_changed(space, 1);
+ if (err < 0) {
+ Perror(space, "control element not found");
+ return err;
+ }
+ err = set_ctl_value(space, value, strncasecmp(attr, "values", 6) == 0);
+ if (err < 0) {
+ space->ctl_id_changed |= 2;
+ } else {
+ space->ctl_id_changed &= ~2;
+ snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id);
+ err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value);
+ if (err < 0) {
+ Perror(space, "value write error: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ return err;
+ }
+ Perror(space, "unknown CTL{} attribute '%s'", attr);
+ return -EINVAL;
+ value:
+ val = (unsigned int)strtol(value, NULL, 0);
+ fcn(space->ctl_id, val);
+ space->ctl_id_changed = ~0;
+ return 0;
+}
+
+static int get_key(char **line, char **key, enum key_op *op, char **value)
+{
+ char *linepos;
+ char *temp;
+
+ linepos = *line;
+ if (linepos == NULL && linepos[0] == '\0')
+ return -EINVAL;
+
+ /* skip whitespace */
+ while (isspace(linepos[0]) || linepos[0] == ',')
+ linepos++;
+
+ /* get the key */
+ if (linepos[0] == '\0')
+ return -EINVAL;
+ *key = linepos;
+
+ while (1) {
+ linepos++;
+ if (linepos[0] == '\0')
+ return -1;
+ if (isspace(linepos[0]))
+ break;
+ if (linepos[0] == '=')
+ break;
+ if (linepos[0] == '+')
+ break;
+ if (linepos[0] == '!')
+ break;
+ if (linepos[0] == ':')
+ break;
+ }
+
+ /* remember end of key */
+ temp = linepos;
+
+ /* skip whitespace after key */
+ while (isspace(linepos[0]))
+ linepos++;
+ if (linepos[0] == '\0')
+ return -EINVAL;
+
+ /* get operation type */
+ if (linepos[0] == '=' && linepos[1] == '=') {
+ *op = KEY_OP_MATCH;
+ linepos += 2;
+ dbg("operator=match");
+ } else if (linepos[0] == '!' && linepos[1] == '=') {
+ *op = KEY_OP_NOMATCH;
+ linepos += 2;
+ dbg("operator=nomatch");
+ } else if (linepos[0] == '+' && linepos[1] == '=') {
+ *op = KEY_OP_ADD;
+ linepos += 2;
+ dbg("operator=add");
+ } else if (linepos[0] == '=') {
+ *op = KEY_OP_ASSIGN;
+ linepos++;
+ dbg("operator=assign");
+ } else if (linepos[0] == ':' && linepos[1] == '=') {
+ *op = KEY_OP_ASSIGN_FINAL;
+ linepos += 2;
+ dbg("operator=assign_final");
+ } else
+ return -EINVAL;
+
+ /* terminate key */
+ temp[0] = '\0';
+ dbg("key='%s'", *key);
+
+ /* skip whitespace after operator */
+ while (isspace(linepos[0]))
+ linepos++;
+ if (linepos[0] == '\0')
+ return -EINVAL;
+
+ /* get the value*/
+ if (linepos[0] != '"')
+ return -EINVAL;
+ linepos++;
+ *value = linepos;
+
+ while (1) {
+ temp = strchr(linepos, '"');
+ if (temp && temp[-1] == '\\') {
+ linepos = temp + 1;
+ continue;
+ }
+ break;
+ }
+ if (!temp)
+ return -EINVAL;
+ temp[0] = '\0';
+ temp++;
+ dbg("value='%s'", *value);
+
+ /* move line to next key */
+ *line = temp;
+
+ return 0;
+}
+
+/* extract possible KEY{attr} */
+static char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize)
+{
+ char *pos;
+ char *attr;
+
+ attr = strchr(str, '{');
+ if (attr != NULL) {
+ attr++;
+ pos = strchr(attr, '}');
+ if (pos == NULL) {
+ Perror(space, "missing closing brace for format");
+ return NULL;
+ }
+ pos[0] = '\0';
+ strlcpy(res, attr, ressize);
+ pos[0] = '}';
+ dbg("attribute='%s'", res);
+ return res;
+ }
+
+ return NULL;
+}
+
+/* extract possible {attr} and move str behind it */
+static char *get_format_attribute(struct space *space, char **str)
+{
+ char *pos;
+ char *attr = NULL;
+
+ if (*str[0] == '{') {
+ pos = strchr(*str, '}');
+ if (pos == NULL) {
+ Perror(space, "missing closing brace for format");
+ return NULL;
+ }
+ pos[0] = '\0';
+ attr = *str+1;
+ *str = pos+1;
+ dbg("attribute='%s', str='%s'", attr, *str);
+ }
+ return attr;
+}
+
+/* extract possible format length and move str behind it*/
+static int get_format_len(struct space *space, char **str)
+{
+ int num;
+ char *tail;
+
+ if (isdigit(*str[0])) {
+ num = (int) strtoul(*str, &tail, 10);
+ if (num > 0) {
+ *str = tail;
+ dbg("format length=%i", num);
+ return num;
+ } else {
+ Perror(space, "format parsing error '%s'", *str);
+ }
+ }
+ return -1;
+}
+
+static void apply_format(struct space *space, char *string, size_t maxsize)
+{
+ char temp[PATH_SIZE];
+ char temp2[PATH_SIZE];
+ char *head, *tail, *pos, *cpos, *attr, *rest;
+ struct pair *pair;
+ int len;
+ int i;
+ int count;
+ enum subst_type {
+ SUBST_UNKNOWN,
+ SUBST_CARDINFO,
+ SUBST_CTL,
+ SUBST_RESULT,
+ SUBST_ATTR,
+ SUBST_SYSFSROOT,
+ SUBST_ENV,
+ };
+ static const struct subst_map {
+ char *name;
+ char fmt;
+ enum subst_type type;
+ } map[] = {
+ { .name = "cardinfo", .fmt = 'i', .type = SUBST_CARDINFO },
+ { .name = "ctl", .fmt = 'C', .type = SUBST_CTL },
+ { .name = "result", .fmt = 'c', .type = SUBST_RESULT },
+ { .name = "attr", .fmt = 's', .type = SUBST_ATTR },
+ { .name = "sysfsroot", .fmt = 'r', .type = SUBST_SYSFSROOT },
+ { .name = "env", .fmt = 'E', .type = SUBST_ENV },
+ { NULL, '\0', 0 }
+ };
+ enum subst_type type;
+ const struct subst_map *subst;
+
+ head = string;
+ while (1) {
+ len = -1;
+ while (head[0] != '\0') {
+ if (head[0] == '$') {
+ /* substitute named variable */
+ if (head[1] == '\0')
+ break;
+ if (head[1] == '$') {
+ strlcpy(temp, head+2, sizeof(temp));
+ strlcpy(head+1, temp, maxsize);
+ head++;
+ continue;
+ }
+ head[0] = '\0';
+ for (subst = map; subst->name; subst++) {
+ if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) {
+ type = subst->type;
+ tail = head + strlen(subst->name)+1;
+ dbg("will substitute format name '%s'", subst->name);
+ goto found;
+ }
+ }
+ } else if (head[0] == '%') {
+ /* substitute format char */
+ if (head[1] == '\0')
+ break;
+ if (head[1] == '%') {
+ strlcpy(temp, head+2, sizeof(temp));
+ strlcpy(head+1, temp, maxsize);
+ head++;
+ continue;
+ }
+ head[0] = '\0';
+ tail = head+1;
+ len = get_format_len(space, &tail);
+ for (subst = map; subst->name; subst++) {
+ if (tail[0] == subst->fmt) {
+ type = subst->type;
+ tail++;
+ dbg("will substitute format char '%c'", subst->fmt);
+ goto found;
+ }
+ }
+ }
+ head++;
+ }
+ break;
+found:
+ attr = get_format_attribute(space, &tail);
+ strlcpy(temp, tail, sizeof(temp));
+ dbg("format=%i, string='%s', tail='%s'", type ,string, tail);
+
+ switch (type) {
+ case SUBST_CARDINFO:
+ if (attr == NULL)
+ Perror(space, "missing identification parametr for cardinfo");
+ else {
+ const char *value = cardinfo_get(space, attr);
+ if (value == NULL)
+ break;
+ strlcat(string, value, maxsize);
+ dbg("substitute cardinfo{%s} '%s'", attr, value);
+ }
+ break;
+ case SUBST_CTL:
+ if (attr == NULL)
+ Perror(space, "missing identification parametr for ctl");
+ else {
+ const char *value = elemid_get(space, attr);
+ if (value == NULL)
+ break;
+ strlcat(string, value, maxsize);
+ dbg("substitute ctl{%s} '%s'", attr, value);
+ }
+ break;
+ case SUBST_RESULT:
+ if (space->program_result == NULL)
+ break;
+ /* get part part of the result string */
+ i = 0;
+ if (attr != NULL)
+ i = strtoul(attr, &rest, 10);
+ if (i > 0) {
+ dbg("request part #%d of result string", i);
+ cpos = space->program_result;
+ while (--i) {
+ while (cpos[0] != '\0' && !isspace(cpos[0]))
+ cpos++;
+ while (isspace(cpos[0]))
+ cpos++;
+ }
+ if (i > 0) {
+ Perror(space, "requested part of result string not found");
+ break;
+ }
+ strlcpy(temp2, cpos, sizeof(temp2));
+ /* %{2+}c copies the whole string from the second part on */
+ if (rest[0] != '+') {
+ cpos = strchr(temp2, ' ');
+ if (cpos)
+ cpos[0] = '\0';
+ }
+ strlcat(string, temp2, maxsize);
+ dbg("substitute part of result string '%s'", temp2);
+ } else {
+ strlcat(string, space->program_result, maxsize);
+ dbg("substitute result string '%s'", space->program_result);
+ }
+ break;
+ case SUBST_ATTR:
+ if (attr == NULL)
+ Perror(space, "missing file parameter for attr");
+ else {
+ const char *value = NULL;
+ size_t size;
+
+ pair = value_find(space, "sysfs_device");
+ if (pair == NULL)
+ break;
+ value = sysfs_attr_get_value(pair->value, attr);
+
+ if (value == NULL)
+ break;
+
+ /* strip trailing whitespace and replace untrusted characters of sysfs value */
+ size = strlcpy(temp2, value, sizeof(temp2));
+ if (size >= sizeof(temp2))
+ size = sizeof(temp2)-1;
+ while (size > 0 && isspace(temp2[size-1]))
+ temp2[--size] = '\0';
+ count = replace_untrusted_chars(temp2);
+ if (count > 0)
+ Perror(space, "%i untrusted character(s) replaced" , count);
+ strlcat(string, temp2, maxsize);
+ dbg("substitute sysfs value '%s'", temp2);
+ }
+ break;
+ case SUBST_SYSFSROOT:
+ strlcat(string, sysfs_path, maxsize);
+ dbg("substitute sysfs_path '%s'", sysfs_path);
+ break;
+ case SUBST_ENV:
+ if (attr == NULL) {
+ dbg("missing attribute");
+ break;
+ }
+ pos = getenv(attr);
+ if (pos == NULL) {
+ dbg("env '%s' not available", attr);
+ break;
+ }
+ dbg("substitute env '%s=%s'", attr, pos);
+ strlcat(string, pos, maxsize);
+ break;
+ default:
+ Perror(space, "unknown substitution type=%i", type);
+ break;
+ }
+ /* possibly truncate to format-char specified length */
+ if (len != -1) {
+ head[len] = '\0';
+ dbg("truncate to %i chars, subtitution string becomes '%s'", len, head);
+ }
+ strlcat(string, temp, maxsize);
+ }
+ /* unescape strings */
+ head = tail = string;
+ while (*head != '\0') {
+ if (*head == '\\') {
+ head++;
+ if (*head == '\0')
+ break;
+ switch (*head) {
+ case 'a': *tail++ = '\a'; break;
+ case 'b': *tail++ = '\b'; break;
+ case 'n': *tail++ = '\n'; break;
+ case 'r': *tail++ = '\r'; break;
+ case 't': *tail++ = '\t'; break;
+ case 'v': *tail++ = '\v'; break;
+ case '\\': *tail++ = '\\'; break;
+ default: *tail++ = *head; break;
+ }
+ head++;
+ continue;
+ }
+ if (*head)
+ *tail++ = *head++;
+ }
+ *tail = 0;
+}
+
+static int do_match(const char *key, enum key_op op,
+ const char *key_value, const char *value)
+{
+ int match;
+
+ if (value == NULL)
+ return 0;
+ dbg("match %s '%s' <-> '%s'", key, key_value, value);
+ match = fnmatch(key_value, value, 0) == 0;
+ if (match && op == KEY_OP_MATCH) {
+ dbg("%s is true (matching value)", key);
+ return 1;
+ }
+ if (!match && op == KEY_OP_NOMATCH) {
+ dbg("%s is true (non-matching value)", key);
+ return 1;
+ }
+ dbg("%s is false", key);
+ return 0;
+}
+
+static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id)
+{
+ if (snd_ctl_elem_id_get_interface(pattern) != -1 &&
+ snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id))
+ return 0;
+ if (snd_ctl_elem_id_get_device(pattern) != -1 &&
+ snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id))
+ return 0;
+ if (snd_ctl_elem_id_get_subdevice(pattern) != -1 &&
+ snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id))
+ return 0;
+ if (snd_ctl_elem_id_get_index(pattern) != -1 &&
+ snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id))
+ return 0;
+ if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0)
+ return 0;
+ return 1;
+}
+
+static
+int run_program1(struct space *space,
+ const char *command0, char *result,
+ size_t ressize, size_t *reslen, int log)
+{
+ char *pos = strchr(command0, ' ');
+ int cmdlen = pos ? pos - command0 : strlen(command0);
+ int err, index;
+ snd_hctl_elem_t *elem;
+ snd_ctl_elem_id_t *id;
+
+ if (cmdlen == 12 && strncmp(command0, "__ctl_search", 12) == 0) {
+ index = 0;
+ if (pos)
+ index = strtol(pos, NULL, 0);
+ err = snd_ctl_elem_id_malloc(&id);
+ if (err < 0)
+ return EXIT_FAILURE;
+ elem = snd_hctl_first_elem(space->ctl_handle);
+ while (elem) {
+ snd_hctl_elem_get_id(elem, id);
+ if (!ctl_match(space->ctl_id, id))
+ goto next_search;
+ if (index > 0) {
+ index--;
+ goto next_search;
+ }
+ strlcpy(result, "0", ressize);
+ snd_ctl_elem_id_copy(space->ctl_id, id);
+ snd_ctl_elem_id_free(id);
+ dbg("__ctl_search found a control");
+ return EXIT_SUCCESS;
+ next_search:
+ elem = snd_hctl_elem_next(elem);
+ }
+ snd_ctl_elem_id_free(id);
+ return EXIT_FAILURE;
+ }
+ if (cmdlen == 11 && strncmp(command0, "__ctl_count", 11) == 0) {
+ index = 0;
+ err = snd_ctl_elem_id_malloc(&id);
+ if (err < 0)
+ return EXIT_FAILURE;
+ elem = snd_hctl_first_elem(space->ctl_handle);
+ while (elem) {
+ snd_hctl_elem_get_id(elem, id);
+ if (!ctl_match(space->ctl_id, id))
+ goto next_count;
+ index++;
+ next_count:
+ elem = snd_hctl_elem_next(elem);
+ }
+ snd_ctl_elem_id_free(id);
+ if (index > 0) {
+ snprintf(result, ressize, "%u", index);
+ dbg("__ctl_count found %s controls", result);
+ return EXIT_SUCCESS;
+ }
+ dbg("__ctl_count no match");
+ return EXIT_FAILURE;
+ }
+ if (cmdlen == 11 && strncmp(command0, "__ctl_write", 11) == 0) {
+ }
+ Perror(space, "unknown buildin command '%s'", command0);
+ return EXIT_FAILURE;
+}
+
+static int parse(struct space *space, const char *filename);
+
+static char *new_root_dir(const char *filename)
+{
+ char *res, *tmp;
+
+ res = strdup(filename);
+ if (res) {
+ tmp = rindex(res, '/');
+ if (tmp)
+ *tmp = '\0';
+ }
+ dbg("new_root_dir '%s' '%s'", filename, res);
+ return res;
+}
+
+static int parse_line(struct space *space, char *line, size_t linesize)
+{
+ char *linepos;
+ char *key, *value, *attr, *temp;
+ struct pair *pair;
+ enum key_op op;
+ int err = 0, count;
+ char string[PATH_SIZE];
+ char result[PATH_SIZE];
+
+ linepos = line;
+ while (*linepos != '\0') {
+ op = KEY_OP_UNSET;
+
+ err = get_key(&linepos, &key, &op, &value);
+ if (err < 0)
+ goto invalid;
+
+ if (strncasecmp(key, "LABEL", 5) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid LABEL operation");
+ goto invalid;
+ }
+ if (space->go_to && strcmp(space->go_to, value) == 0) {
+ free(space->go_to);
+ space->go_to = NULL;
+ }
+ continue;
+ }
+
+ if (space->go_to) {
+ dbg("skip (GOTO '%s')", space->go_to);
+ break; /* not for us */
+ }
+
+ if (strncasecmp(key, "CTL{", 4) == 0) {
+ attr = get_key_attribute(space, key + 3, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing CTL attribute");
+ goto invalid;
+ }
+ if (op == KEY_OP_ASSIGN) {
+ strlcpy(result, value, sizeof(result));
+ apply_format(space, result, sizeof(result));
+ dbg("ctl assign: '%s' '%s'", value, attr);
+ err = elemid_set(space, attr, result);
+ if (space->program_result) {
+ free(space->program_result);
+ space->program_result = NULL;
+ }
+ snprintf(string, sizeof(string), "%i", err);
+ space->program_result = strdup(string);
+ if (err < 0 || space->program_result == NULL) {
+ err = 0;
+ break;
+ }
+ } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ dbg("ctl match: '%s' '%s'", value, attr);
+ temp = (char *)elemid_get(space, attr);
+ if (!do_match(key, op, value, temp))
+ break;
+ } else {
+ Perror(space, "invalid CTL{} operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strcasecmp(key, "RESULT") == 0) {
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ if (!do_match(key, op, value, space->program_result))
+ break;
+ } else if (op == KEY_OP_ASSIGN) {
+ if (space->program_result) {
+ free(space->program_result);
+ space->program_result = NULL;
+ }
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ space->program_result = strdup(string);
+ if (space->program_result == NULL)
+ break;
+ } else {
+ Perror(space, "invalid RESULT operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strcasecmp(key, "PROGRAM") == 0) {
+ if (op == KEY_OP_UNSET)
+ continue;
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ if (space->program_result) {
+ free(space->program_result);
+ space->program_result = NULL;
+ }
+ if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) {
+ dbg("PROGRAM '%s' is false", string);
+ if (op != KEY_OP_NOMATCH)
+ break;
+ } else {
+ remove_trailing_chars(result, '\n');
+ count = replace_untrusted_chars(result);
+ if (count)
+ info("%i untrusted character(s) replaced", count);
+ dbg("PROGRAM '%s' result is '%s'", string, result);
+ space->program_result = strdup(result);
+ if (space->program_result == NULL)
+ break;
+ dbg("PROGRAM returned successful");
+ if (op == KEY_OP_NOMATCH)
+ break;
+ }
+ dbg("PROGRAM key is true");
+ continue;
+ }
+ if (strncasecmp(key, "CARDINFO{", 9) == 0) {
+ attr = get_key_attribute(space, key + 8, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing CARDINFO attribute");
+ goto invalid;
+ }
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ dbg("cardinfo: '%s' '%s'", value, attr);
+ temp = (char *)cardinfo_get(space, attr);
+ if (!do_match(key, op, value, temp))
+ break;
+ } else {
+ Perror(space, "invalid CARDINFO{} operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strncasecmp(key, "ATTR{", 5) == 0) {
+ attr = get_key_attribute(space, key + 4, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing ATTR attribute");
+ goto invalid;
+ }
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ pair = value_find(space, "sysfs_device");
+ if (pair == NULL)
+ break;
+ dbg("sysfs_attr: '%s' '%s'", pair->value, attr);
+ temp = sysfs_attr_get_value(pair->value, attr);
+ if (!do_match(key, op, value, temp))
+ break;
+ } else {
+ Perror(space, "invalid ATTR{} operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strncasecmp(key, "ENV{", 4) == 0) {
+ attr = get_key_attribute(space, key + 3, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing ENV attribute");
+ goto invalid;
+ }
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ temp = getenv(attr);
+ dbg("env: '%s' '%s'", attr, temp);
+ if (!do_match(key, op, value, temp))
+ break;
+ } else if (op == KEY_OP_ASSIGN ||
+ op == KEY_OP_ASSIGN_FINAL) {
+ strlcpy(result, value, sizeof(result));
+ apply_format(space, result, sizeof(result));
+ dbg("env set: '%s' '%s'", attr, result);
+ if (setenv(attr, result, op == KEY_OP_ASSIGN_FINAL))
+ break;
+ } else {
+ Perror(space, "invalid ENV{} operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strcasecmp(key, "GOTO") == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid GOTO operation");
+ goto invalid;
+ }
+ space->go_to = strdup(value);
+ if (space->go_to == NULL) {
+ err = -ENOMEM;
+ break;
+ }
+ continue;
+ }
+ if (strcasecmp(key, "INCLUDE") == 0) {
+ char *rootdir, *go_to;
+ const char *filename;
+ struct dirent *dirent;
+ DIR *dir;
+ int linenum;
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid INCLUDE operation");
+ goto invalid;
+ }
+ if (value[0] == '/')
+ strlcpy(string, value, sizeof(string));
+ else {
+ strlcpy(string, space->rootdir, sizeof(string));
+ strlcat(string, "/", sizeof(string));
+ strlcat(string, value, sizeof(string));
+ }
+ rootdir = space->rootdir;
+ go_to = space->go_to;
+ filename = space->filename;
+ linenum = space->linenum;
+ dir = opendir(string);
+ if (dir) {
+ count = strlen(string);
+ while ((dirent = readdir(dir)) != NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0)
+ continue;
+ string[count] = '\0';
+ strlcat(string, "/", sizeof(string));
+ strlcat(string, dirent->d_name, sizeof(string));
+ space->go_to = NULL;
+ space->rootdir = new_root_dir(string);
+ if (space->rootdir) {
+ err = parse(space, string);
+ free(space->rootdir);
+ } else
+ err = -ENOMEM;
+ if (space->go_to) {
+ Perror(space, "unterminated GOTO '%s'", space->go_to);
+ free(space->go_to);
+ }
+ if (err)
+ break;
+ }
+ closedir(dir);
+ } else {
+ space->go_to = NULL;
+ space->rootdir = new_root_dir(string);
+ if (space->rootdir) {
+ err = parse(space, string);
+ free(space->rootdir);
+ } else
+ err = -ENOMEM;
+ if (space->go_to) {
+ Perror(space, "unterminated GOTO '%s'", space->go_to);
+ free(space->go_to);
+ }
+ }
+ space->go_to = go_to;
+ space->rootdir = rootdir;
+ space->filename = filename;
+ space->linenum = linenum;
+ if (space->quit)
+ break;
+ if (err)
+ break;
+ continue;
+ }
+ if (strncasecmp(key, "ACCESS", 6) == 0) {
+ if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ if (value[0] != '/') {
+ strlcpy(string, space->rootdir, sizeof(string));
+ strlcat(string, "/", sizeof(string));
+ strlcat(string, value, sizeof(string));
+ } else {
+ strlcat(string, value, sizeof(string));
+ }
+ count = access(string, F_OK);
+ dbg("access(%s) = %i", value, count);
+ if (op == KEY_OP_MATCH && count != 0)
+ break;
+ if (op == KEY_OP_NOMATCH && count == 0)
+ break;
+ } else {
+ Perror(space, "invalid ACCESS operation");
+ goto invalid;
+ }
+ continue;
+ }
+ if (strncasecmp(key, "PRINT", 5) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid PRINT operation");
+ goto invalid;
+ }
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ fwrite(string, strlen(string), 1, stdout);
+ continue;
+ }
+ if (strncasecmp(key, "ERROR", 5) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid ERROR operation");
+ goto invalid;
+ }
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ fwrite(string, strlen(string), 1, stderr);
+ continue;
+ }
+ if (strncasecmp(key, "EXIT", 4) == 0) {
+ if (op != KEY_OP_ASSIGN) {
+ Perror(space, "invalid EXIT operation");
+ goto invalid;
+ }
+ strlcpy(string, value, sizeof(string));
+ apply_format(space, string, sizeof(string));
+ if (strcmp(string, "return") == 0)
+ return -EJUSTRETURN;
+ space->exit_code = strtol(string, NULL, 0);
+ space->quit = 1;
+ break;
+ }
+ if (strncasecmp(key, "CONFIG{", 7) == 0) {
+ attr = get_key_attribute(space, key + 6, string, sizeof(string));
+ if (attr == NULL) {
+ Perror(space, "error parsing CONFIG attribute");
+ goto invalid;
+ }
+ strlcpy(result, value, sizeof(result));
+ apply_format(space, result, sizeof(result));
+ if (op == KEY_OP_ASSIGN) {
+ err = value_set(space, attr, result);
+ dbg("CONFIG{%s}='%s'", attr, result);
+ break;
+ } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
+ pair = value_find(space, attr);
+ if (pair == NULL)
+ break;
+ if (!do_match(key, op, result, pair->value))
+ break;
+ } else {
+ Perror(space, "invalid CONFIG{} operation");
+ goto invalid;
+ }
+ }
+
+ Perror(space, "unknown key '%s'", key);
+ }
+ return err;
+
+invalid:
+ Perror(space, "invalid rule");
+ return -EINVAL;
+}
+
+static int parse(struct space *space, const char *filename)
+{
+ char *buf, *bufline, *line;
+ size_t bufsize, pos, count, linesize;
+ unsigned int linenum, i, j, linenum_adj;
+ int err;
+
+ dbg("start of file '%s'", filename);
+
+ if (file_map(filename, &buf, &bufsize) != 0) {
+ err = errno;
+ error("Unable to open file '%s': %s", filename, strerror(err));
+ return -err;
+ }
+
+ err = 0;
+ pos = 0;
+ linenum = 0;
+ linesize = 128;
+ line = malloc(linesize);
+ if (line == NULL)
+ return -ENOMEM;
+ space->filename = filename;
+ while (!err && pos < bufsize && !space->quit) {
+ count = line_width(buf, bufsize, pos);
+ bufline = buf + pos;
+ pos += count + 1;
+ linenum++;
+
+ /* skip whitespaces */
+ while (count > 0 && isspace(bufline[0])) {
+ bufline++;
+ count--;
+ }
+ if (count == 0)
+ continue;
+
+ /* comment check */
+ if (bufline[0] == '#')
+ continue;
+
+ if (count > linesize - 1) {
+ free(line);
+ linesize = (count + 127 + 1) & ~127;
+ if (linesize > 2048) {
+ error("file %s, line %i too long", filename, linenum);
+ err = -EINVAL;
+ break;
+ }
+ line = malloc(linesize);
+ if (line == NULL) {
+ err = -EINVAL;
+ break;
+ }
+ }
+
+ /* skip backslash and newline from multiline rules */
+ linenum_adj = 0;
+ for (i = j = 0; i < count; i++) {
+ if (bufline[i] == '\\' && bufline[i+1] == '\n') {
+ linenum_adj++;
+ continue;
+ }
+ line[j++] = bufline[i];
+ }
+ line[j] = '\0';
+
+ dbg("read (%i) '%s'", linenum, line);
+ space->linenum = linenum;
+ err = parse_line(space, line, linesize);
+ if (err == -EJUSTRETURN) {
+ err = 0;
+ break;
+ }
+ linenum += linenum_adj;
+ }
+
+ free(line);
+ space->filename = NULL;
+ space->linenum = -1;
+ file_unmap(buf, bufsize);
+ dbg("end of file '%s'", filename);
+ return err ? err : -abs(space->exit_code);
+}
+
+int init(const char *filename, const char *cardname)
+{
+ struct space *space;
+ int err = 0, card, first;
+
+ sysfs_init();
+ if (!cardname) {
+ first = 1;
+ card = -1;
+ while (1) {
+ if (snd_card_next(&card) < 0)
+ break;
+ if (card < 0) {
+ if (first) {
+ error("No soundcards found...");
+ return -ENODEV;
+ }
+ break;
+ }
+ first = 0;
+ err = init_space(&space, card);
+ if (err == 0 &&
+ (space->rootdir = new_root_dir(filename)) != NULL)
+ err = parse(space, filename);
+ free_space(space);
+ if (err < 0)
+ break;
+ }
+ } else {
+ card = snd_card_get_index(cardname);
+ if (card < 0) {
+ error("Cannot find soundcard '%s'...", cardname);
+ goto error;
+ }
+ memset(&space, 0, sizeof(space));
+ err = init_space(&space, card);
+ if (err == 0 &&
+ (space->rootdir = new_root_dir(filename)) != NULL)
+ err = parse(space, filename);
+ free_space(space);
+ }
+ error:
+ sysfs_cleanup();
+ return err;
+}
diff --git a/alsa-utils/alsactl/init_sysdeps.c b/alsa-utils/alsactl/init_sysdeps.c
new file mode 100644
index 0000000..3aca1b4
--- /dev/null
+++ b/alsa-utils/alsactl/init_sysdeps.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if defined(__GLIBC__) && !(defined(__UCLIBC__) && defined(__USE_BSD))
+static size_t strlcpy(char *dst, const char *src, size_t size)
+{
+ size_t bytes = 0;
+ char *q = dst;
+ const char *p = src;
+ char ch;
+
+ while ((ch = *p++)) {
+ if (bytes+1 < size)
+ *q++ = ch;
+ bytes++;
+ }
+
+ /* If size == 0 there is no space for a final null... */
+ if (size)
+ *q = '\0';
+ return bytes;
+}
+
+static size_t strlcat(char *dst, const char *src, size_t size)
+{
+ size_t bytes = 0;
+ char *q = dst;
+ const char *p = src;
+ char ch;
+
+ while (bytes < size && *q) {
+ q++;
+ bytes++;
+ }
+ if (bytes == size)
+ return (bytes + strlen(src));
+
+ while ((ch = *p++)) {
+ if (bytes+1 < size)
+ *q++ = ch;
+ bytes++;
+ }
+
+ *q = '\0';
+ return bytes;
+}
+#endif /* __GLIBC__ */
diff --git a/alsa-utils/alsactl/init_sysfs.c b/alsa-utils/alsactl/init_sysfs.c
new file mode 100644
index 0000000..0cbada2
--- /dev/null
+++ b/alsa-utils/alsactl/init_sysfs.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
+ * 2008 Jaroslav Kysela <perex@perex.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+static char sysfs_path[PATH_SIZE];
+
+/* attribute value cache */
+static LIST_HEAD(attr_list);
+struct sysfs_attr {
+ struct list_head node;
+ char path[PATH_SIZE];
+ char *value; /* points to value_local if value is cached */
+ char value_local[NAME_SIZE];
+};
+
+static int sysfs_init(void)
+{
+ const char *env;
+ char sysfs_test[PATH_SIZE];
+
+ env = getenv("SYSFS_PATH");
+ if (env) {
+ strlcpy(sysfs_path, env, sizeof(sysfs_path));
+ remove_trailing_chars(sysfs_path, '/');
+ } else
+ strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
+ dbg("sysfs_path='%s'", sysfs_path);
+
+ strlcpy(sysfs_test, sysfs_path, sizeof(sysfs_test));
+ strlcat(sysfs_test, "/kernel/uevent_helper", sizeof(sysfs_test));
+ if (access(sysfs_test, F_OK)) {
+ error("sysfs path '%s' is invalid\n", sysfs_path);
+ return -errno;
+ }
+
+ INIT_LIST_HEAD(&attr_list);
+ return 0;
+}
+
+static void sysfs_cleanup(void)
+{
+ struct sysfs_attr *attr_loop;
+ struct sysfs_attr *attr_temp;
+
+ list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
+ list_del(&attr_loop->node);
+ free(attr_loop);
+ }
+}
+
+static char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
+{
+ char path_full[PATH_SIZE];
+ const char *path;
+ char value[NAME_SIZE];
+ struct sysfs_attr *attr_loop;
+ struct sysfs_attr *attr;
+ struct stat statbuf;
+ int fd;
+ ssize_t size;
+ size_t sysfs_len;
+
+ dbg("open '%s'/'%s'", devpath, attr_name);
+ sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
+ path = &path_full[sysfs_len];
+ strlcat(path_full, devpath, sizeof(path_full));
+ strlcat(path_full, "/", sizeof(path_full));
+ strlcat(path_full, attr_name, sizeof(path_full));
+
+ /* look for attribute in cache */
+ list_for_each_entry(attr_loop, &attr_list, node) {
+ if (strcmp(attr_loop->path, path) == 0) {
+ dbg("found in cache '%s'", attr_loop->path);
+ return attr_loop->value;
+ }
+ }
+
+ /* store attribute in cache (also negatives are kept in cache) */
+ dbg("new uncached attribute '%s'", path_full);
+ attr = malloc(sizeof(struct sysfs_attr));
+ if (attr == NULL)
+ return NULL;
+ memset(attr, 0x00, sizeof(struct sysfs_attr));
+ strlcpy(attr->path, path, sizeof(attr->path));
+ dbg("add to cache '%s'", path_full);
+ list_add(&attr->node, &attr_list);
+
+ if (lstat(path_full, &statbuf) != 0) {
+ dbg("stat '%s' failed: %s", path_full, strerror(errno));
+ goto out;
+ }
+
+ if (S_ISLNK(statbuf.st_mode)) {
+ /* links return the last element of the target path */
+ char link_target[PATH_SIZE];
+ int len;
+ const char *pos;
+
+ len = readlink(path_full, link_target, sizeof(link_target));
+ if (len > 0) {
+ link_target[len] = '\0';
+ pos = strrchr(link_target, '/');
+ if (pos != NULL) {
+ dbg("cache '%s' with link value '%s'", path_full, pos+1);
+ strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local));
+ attr->value = attr->value_local;
+ }
+ }
+ goto out;
+ }
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode))
+ goto out;
+
+ /* skip non-readable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0)
+ goto out;
+
+ /* read attribute value */
+ fd = open(path_full, O_RDONLY);
+ if (fd < 0) {
+ dbg("attribute '%s' does not exist", path_full);
+ goto out;
+ }
+ size = read(fd, value, sizeof(value));
+ close(fd);
+ if (size < 0)
+ goto out;
+ if (size == sizeof(value))
+ goto out;
+
+ /* got a valid value, store and return it */
+ value[size] = '\0';
+ remove_trailing_chars(value, '\n');
+ dbg("cache '%s' with attribute value '%s'", path_full, value);
+ strlcpy(attr->value_local, value, sizeof(attr->value_local));
+ attr->value = attr->value_local;
+
+out:
+ return attr->value;
+}
diff --git a/alsa-utils/alsactl/init_utils_run.c b/alsa-utils/alsactl/init_utils_run.c
new file mode 100644
index 0000000..dde490b
--- /dev/null
+++ b/alsa-utils/alsactl/init_utils_run.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#define MY_MAX(a,b) ((a) > (b) ? (a) : (b))
+
+#define READ_END 0
+#define WRITE_END 1
+
+static
+int run_program1(struct space *space,
+ const char *command0, char *result,
+ size_t ressize, size_t *reslen, int log);
+
+static
+int run_program0(struct space *space,
+ const char *command0, char *result,
+ size_t ressize, size_t *reslen, int log)
+{
+ int retval = 0;
+ int status;
+ int outpipe[2] = {-1, -1};
+ int errpipe[2] = {-1, -1};
+ pid_t pid;
+ char arg[PATH_SIZE];
+ char program[PATH_SIZE];
+ char *argv[(sizeof(arg) / 2) + 1];
+ int devnull;
+ int i;
+
+ /* build argv from comand */
+ strlcpy(arg, command0, sizeof(arg));
+ i = 0;
+ if (strchr(arg, ' ') != NULL) {
+ char *pos = arg;
+
+ while (pos != NULL) {
+ if (pos[0] == '\'') {
+ /* don't separate if in apostrophes */
+ pos++;
+ argv[i] = strsep(&pos, "\'");
+ while (pos != NULL && pos[0] == ' ')
+ pos++;
+ } else {
+ argv[i] = strsep(&pos, " ");
+ }
+ dbg("arg[%i] '%s'", i, argv[i]);
+ i++;
+ }
+ argv[i] = NULL;
+ } else {
+ argv[0] = arg;
+ argv[1] = NULL;
+ }
+ info("'%s'", command0);
+
+ /* prepare pipes from child to parent */
+ if (result || log) {
+ if (pipe(outpipe) != 0) {
+ Perror(space, "pipe failed: %s", strerror(errno));
+ return -1;
+ }
+ }
+ if (log) {
+ if (pipe(errpipe) != 0) {
+ Perror(space, "pipe failed: %s", strerror(errno));
+ return -1;
+ }
+ }
+
+ /* allow programs in /lib/alsa called without the path */
+ if (strchr(argv[0], '/') == NULL) {
+ strlcpy(program, "/lib/alsa/", sizeof(program));
+ strlcat(program, argv[0], sizeof(program));
+ argv[0] = program;
+ }
+
+ pid = fork();
+ switch(pid) {
+ case 0:
+ /* child closes parent ends of pipes */
+ if (outpipe[READ_END] > 0)
+ close(outpipe[READ_END]);
+ if (errpipe[READ_END] > 0)
+ close(errpipe[READ_END]);
+
+ /* discard child output or connect to pipe */
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull > 0) {
+ dup2(devnull, STDIN_FILENO);
+ if (outpipe[WRITE_END] < 0)
+ dup2(devnull, STDOUT_FILENO);
+ if (errpipe[WRITE_END] < 0)
+ dup2(devnull, STDERR_FILENO);
+ close(devnull);
+ } else
+ Perror(space, "open /dev/null failed: %s", strerror(errno));
+ if (outpipe[WRITE_END] > 0) {
+ dup2(outpipe[WRITE_END], STDOUT_FILENO);
+ close(outpipe[WRITE_END]);
+ }
+ if (errpipe[WRITE_END] > 0) {
+ dup2(errpipe[WRITE_END], STDERR_FILENO);
+ close(errpipe[WRITE_END]);
+ }
+ execv(argv[0], argv);
+
+ /* we should never reach this */
+ Perror(space, "exec of program '%s' failed", argv[0]);
+ _exit(1);
+ case -1:
+ Perror(space, "fork of '%s' failed: %s", argv[0], strerror(errno));
+ return -1;
+ default:
+ /* read from child if requested */
+ if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
+ ssize_t count;
+ size_t respos = 0;
+
+ /* parent closes child ends of pipes */
+ if (outpipe[WRITE_END] > 0)
+ close(outpipe[WRITE_END]);
+ if (errpipe[WRITE_END] > 0)
+ close(errpipe[WRITE_END]);
+
+ /* read child output */
+ while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
+ int fdcount;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ if (outpipe[READ_END] > 0)
+ FD_SET(outpipe[READ_END], &readfds);
+ if (errpipe[READ_END] > 0)
+ FD_SET(errpipe[READ_END], &readfds);
+ fdcount = select(MY_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
+ if (fdcount < 0) {
+ if (errno == EINTR)
+ continue;
+ retval = -1;
+ break;
+ }
+
+ /* get stdout */
+ if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
+ char inbuf[1024];
+ char *pos;
+ char *line;
+
+ count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
+ if (count <= 0) {
+ close(outpipe[READ_END]);
+ outpipe[READ_END] = -1;
+ if (count < 0) {
+ Perror(space, "stdin read failed: %s", strerror(errno));
+ retval = -1;
+ }
+ continue;
+ }
+ inbuf[count] = '\0';
+
+ /* store result for rule processing */
+ if (result) {
+ if (respos + count < ressize) {
+ memcpy(&result[respos], inbuf, count);
+ respos += count;
+ } else {
+ Perror(space, "ressize %ld too short", (long)ressize);
+ retval = -1;
+ }
+ }
+ pos = inbuf;
+ while ((line = strsep(&pos, "\n")))
+ if (pos || line[0] != '\0')
+ info("'%s' (stdout) '%s'", argv[0], line);
+ }
+
+ /* get stderr */
+ if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
+ char errbuf[1024];
+ char *pos;
+ char *line;
+
+ count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
+ if (count <= 0) {
+ close(errpipe[READ_END]);
+ errpipe[READ_END] = -1;
+ if (count < 0)
+ Perror(space, "stderr read failed: %s", strerror(errno));
+ continue;
+ }
+ errbuf[count] = '\0';
+ pos = errbuf;
+ while ((line = strsep(&pos, "\n")))
+ if (pos || line[0] != '\0')
+ info("'%s' (stderr) '%s'", argv[0], line);
+ }
+ }
+ if (outpipe[READ_END] > 0)
+ close(outpipe[READ_END]);
+ if (errpipe[READ_END] > 0)
+ close(errpipe[READ_END]);
+
+ /* return the childs stdout string */
+ if (result) {
+ result[respos] = '\0';
+ dbg("result='%s'", result);
+ if (reslen)
+ *reslen = respos;
+ }
+ }
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ info("'%s' returned with status %i", argv[0], WEXITSTATUS(status));
+ if (WEXITSTATUS(status) != 0)
+ retval = -1;
+ } else {
+ Perror(space, "'%s' abnormal exit", argv[0]);
+ retval = -1;
+ }
+ }
+
+ return retval;
+}
+
+static
+int run_program(struct space *space, const char *command0, char *result,
+ size_t ressize, size_t *reslen, int log)
+{
+ if (command0[0] == '_' && command0[1] == '_')
+ return run_program1(space, command0, result, ressize, reslen, log);
+ return run_program0(space, command0, result, ressize, reslen, log);
+}
diff --git a/alsa-utils/alsactl/init_utils_string.c b/alsa-utils/alsactl/init_utils_string.c
new file mode 100644
index 0000000..2598e9f
--- /dev/null
+++ b/alsa-utils/alsactl/init_utils_string.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+static int string_is_true(const char *str)
+{
+ if (strcasecmp(str, "true") == 0)
+ return 1;
+ if (strcasecmp(str, "yes") == 0)
+ return 1;
+ if (strcasecmp(str, "1") == 0)
+ return 1;
+ return 0;
+}
+
+static void remove_trailing_chars(char *path, char c)
+{
+ size_t len;
+
+ len = strlen(path);
+ while (len > 0 && path[len-1] == c)
+ path[--len] = '\0';
+}
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str)
+{
+ unsigned char c = (unsigned char)str[0];
+
+ if (c < 0x80)
+ return 1;
+ if ((c & 0xe0) == 0xc0)
+ return 2;
+ if ((c & 0xf0) == 0xe0)
+ return 3;
+ if ((c & 0xf8) == 0xf0)
+ return 4;
+ if ((c & 0xfc) == 0xf8)
+ return 5;
+ if ((c & 0xfe) == 0xfc)
+ return 6;
+ return 0;
+}
+
+/* decode one unicode char */
+static int utf8_encoded_to_unichar(const char *str)
+{
+ int unichar;
+ int len;
+ int i;
+
+ len = utf8_encoded_expected_len(str);
+ switch (len) {
+ case 1:
+ return (int)str[0];
+ case 2:
+ unichar = str[0] & 0x1f;
+ break;
+ case 3:
+ unichar = (int)str[0] & 0x0f;
+ break;
+ case 4:
+ unichar = (int)str[0] & 0x07;
+ break;
+ case 5:
+ unichar = (int)str[0] & 0x03;
+ break;
+ case 6:
+ unichar = (int)str[0] & 0x01;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 1; i < len; i++) {
+ if (((int)str[i] & 0xc0) != 0x80)
+ return -1;
+ unichar <<= 6;
+ unichar |= (int)str[i] & 0x3f;
+ }
+
+ return unichar;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar)
+{
+ if (unichar < 0x80)
+ return 1;
+ if (unichar < 0x800)
+ return 2;
+ if (unichar < 0x10000)
+ return 3;
+ if (unichar < 0x200000)
+ return 4;
+ if (unichar < 0x4000000)
+ return 5;
+ return 6;
+}
+
+/* check if unicode char has a valid numeric range */
+static int utf8_unichar_valid_range(int unichar)
+{
+ if (unichar > 0x10ffff)
+ return 0;
+ if ((unichar & 0xfffff800) == 0xd800)
+ return 0;
+ if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
+ return 0;
+ if ((unichar & 0xffff) == 0xffff)
+ return 0;
+ return 1;
+}
+
+/* validate one encoded unicode char and return its length */
+static int utf8_encoded_valid_unichar(const char *str)
+{
+ int len;
+ int unichar;
+ int i;
+
+ len = utf8_encoded_expected_len(str);
+ if (len == 0)
+ return -1;
+
+ /* ascii is valid */
+ if (len == 1)
+ return 1;
+
+ /* check if expected encoded chars are available */
+ for (i = 0; i < len; i++)
+ if ((str[i] & 0x80) != 0x80)
+ return -1;
+
+ unichar = utf8_encoded_to_unichar(str);
+
+ /* check if encoded length matches encoded value */
+ if (utf8_unichar_to_encoded_len(unichar) != len)
+ return -1;
+
+ /* check if value has valid range */
+ if (!utf8_unichar_valid_range(unichar))
+ return -1;
+
+ return len;
+}
+
+/* replace everything but whitelisted plain ascii and valid utf8 */
+static int replace_untrusted_chars(char *str)
+{
+ size_t i = 0;
+ int replaced = 0;
+
+ while (str[i] != '\0') {
+ int len;
+
+ /* valid printable ascii char */
+ if ((str[i] >= '0' && str[i] <= '9') ||
+ (str[i] >= 'A' && str[i] <= 'Z') ||
+ (str[i] >= 'a' && str[i] <= 'z') ||
+ strchr(" #$%+-./:=?@_,", str[i])) {
+ i++;
+ continue;
+ }
+ /* valid utf8 is accepted */
+ len = utf8_encoded_valid_unichar(&str[i]);
+ if (len > 1) {
+ i += len;
+ continue;
+ }
+
+ /* everything else is garbage */
+ str[i] = '_';
+ i++;
+ replaced++;
+ }
+
+ return replaced;
+}
diff --git a/alsa-utils/alsactl/list.h b/alsa-utils/alsactl/list.h
new file mode 100644
index 0000000..8626630
--- /dev/null
+++ b/alsa-utils/alsactl/list.h
@@ -0,0 +1,289 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+#endif /* _LIST_H */
diff --git a/alsa-utils/alsactl/state.c b/alsa-utils/alsactl/state.c
new file mode 100644
index 0000000..635a999
--- /dev/null
+++ b/alsa-utils/alsactl/state.c
@@ -0,0 +1,1653 @@
+/*
+ * Advanced Linux Sound Architecture Control Program
+ * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ * Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a)[0])
+
+
+static char *id_str(snd_ctl_elem_id_t *id)
+{
+ static char str[128];
+ assert(id);
+ sprintf(str, "%i,%i,%i,%s,%i",
+ snd_ctl_elem_id_get_interface(id),
+ snd_ctl_elem_id_get_device(id),
+ snd_ctl_elem_id_get_subdevice(id),
+ snd_ctl_elem_id_get_name(id),
+ snd_ctl_elem_id_get_index(id));
+ return str;
+}
+
+static char *num_str(long n)
+{
+ static char str[32];
+ sprintf(str, "%ld", n);
+ return str;
+}
+
+static int snd_config_integer_add(snd_config_t *father, char *id, long integer)
+{
+ int err;
+ snd_config_t *leaf;
+ err = snd_config_make_integer(&leaf, id);
+ if (err < 0)
+ return err;
+ err = snd_config_add(father, leaf);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ err = snd_config_set_integer(leaf, integer);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ return 0;
+}
+
+static int snd_config_integer64_add(snd_config_t *father, char *id, long long integer)
+{
+ int err;
+ snd_config_t *leaf;
+ err = snd_config_make_integer64(&leaf, id);
+ if (err < 0)
+ return err;
+ err = snd_config_add(father, leaf);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ err = snd_config_set_integer64(leaf, integer);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ return 0;
+}
+
+static int snd_config_string_add(snd_config_t *father, const char *id, const char *string)
+{
+ int err;
+ snd_config_t *leaf;
+ err = snd_config_make_string(&leaf, id);
+ if (err < 0)
+ return err;
+ err = snd_config_add(father, leaf);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ err = snd_config_set_string(leaf, string);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ return 0;
+}
+
+static int snd_config_compound_add(snd_config_t *father, const char *id, int join,
+ snd_config_t **node)
+{
+ int err;
+ snd_config_t *leaf;
+ err = snd_config_make_compound(&leaf, id, join);
+ if (err < 0)
+ return err;
+ err = snd_config_add(father, leaf);
+ if (err < 0) {
+ snd_config_delete(leaf);
+ return err;
+ }
+ *node = leaf;
+ return 0;
+}
+
+#define MAX_USER_TLV_SIZE 64
+
+static char *tlv_to_str(unsigned int *tlv)
+{
+ int i, len = tlv[1] / 4 + 2;
+ char *s, *p;
+
+ if (len >= MAX_USER_TLV_SIZE)
+ return NULL;
+ s = malloc(len * 8 + 1);
+ if (! s)
+ return NULL;
+ p = s;
+ for (i = 0; i < len; i++) {
+ sprintf(p, "%08x", tlv[i]);
+ p += 8;
+ }
+ return s;
+}
+
+static unsigned int *str_to_tlv(const char *s)
+{
+ int i, j, c, len;
+ unsigned int *tlv;
+
+ len = strlen(s);
+ if (len % 8) /* aligned to 4 bytes (= 8 letters) */
+ return NULL;
+ len /= 8;
+ if (len > MAX_USER_TLV_SIZE)
+ return NULL;
+ tlv = malloc(sizeof(int) * len);
+ if (! tlv)
+ return NULL;
+ for (i = 0; i < len; i++) {
+ tlv[i] = 0;
+ for (j = 0; j < 8; j++) {
+ if ((c = hextodigit(*s++)) < 0) {
+ free(tlv);
+ return NULL;
+ }
+ tlv[i] = (tlv[i] << 4) | c;
+ }
+ }
+ return tlv;
+}
+
+/*
+ * add the TLV string and dB ranges to comment fields
+ */
+static int add_tlv_comments(snd_ctl_t *handle, snd_ctl_elem_id_t *id,
+ snd_ctl_elem_info_t *info, snd_config_t *comment)
+{
+ unsigned int tlv[MAX_USER_TLV_SIZE];
+ unsigned int *db;
+ long dbmin, dbmax;
+ int err;
+
+ if (snd_ctl_elem_tlv_read(handle, id, tlv, sizeof(tlv)) < 0)
+ return 0; /* ignore error */
+
+ if (snd_ctl_elem_info_is_tlv_writable(info)) {
+ char *s = tlv_to_str(tlv);
+ if (s) {
+ err = snd_config_string_add(comment, "tlv", s);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ free(s);
+ }
+ }
+
+ err = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &db);
+ if (err <= 0)
+ return 0;
+
+ snd_tlv_get_dB_range(db, snd_ctl_elem_info_get_min(info),
+ snd_ctl_elem_info_get_max(info),
+ &dbmin, &dbmax);
+ if (err < 0)
+ return err;
+ snd_config_integer_add(comment, "dbmin", dbmin);
+ snd_config_integer_add(comment, "dbmax", dbmax);
+ return 0;
+}
+
+static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top)
+{
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_elem_info_t *info;
+ snd_config_t *control, *comment, *item, *value;
+ const char *s;
+ char buf[256];
+ unsigned int idx;
+ int err;
+ unsigned int device, subdevice, index;
+ const char *name;
+ snd_ctl_elem_type_t type;
+ unsigned int count;
+ snd_ctl_elem_value_alloca(&ctl);
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_info_set_id(info, id);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err));
+ return err;
+ }
+
+ if (snd_ctl_elem_info_is_inactive(info) ||
+ !snd_ctl_elem_info_is_readable(info))
+ return 0;
+ snd_ctl_elem_value_set_id(ctl, id);
+ err = snd_ctl_elem_read(handle, ctl);
+ if (err < 0) {
+ error("Cannot read control '%s': %s", id_str(id), snd_strerror(err));
+ return err;
+ }
+
+ err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ return err;
+ }
+ err = snd_config_compound_add(control, "comment", 1, &comment);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ return err;
+ }
+
+ buf[0] = '\0';
+ buf[1] = '\0';
+ if (snd_ctl_elem_info_is_readable(info))
+ strcat(buf, " read");
+ if (snd_ctl_elem_info_is_writable(info))
+ strcat(buf, " write");
+ if (snd_ctl_elem_info_is_inactive(info))
+ strcat(buf, " inactive");
+ if (snd_ctl_elem_info_is_volatile(info))
+ strcat(buf, " volatile");
+ if (snd_ctl_elem_info_is_locked(info))
+ strcat(buf, " locked");
+ if (snd_ctl_elem_info_is_user(info))
+ strcat(buf, " user");
+ err = snd_config_string_add(comment, "access", buf + 1);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+
+ type = snd_ctl_elem_info_get_type(info);
+ device = snd_ctl_elem_info_get_device(info);
+ subdevice = snd_ctl_elem_info_get_subdevice(info);
+ index = snd_ctl_elem_info_get_index(info);
+ name = snd_ctl_elem_info_get_name(info);
+ count = snd_ctl_elem_info_get_count(info);
+ s = snd_ctl_elem_type_name(type);
+ err = snd_config_string_add(comment, "type", s);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ err = snd_config_integer_add(comment, "count", count);
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ {
+ long min = snd_ctl_elem_info_get_min(info);
+ long max = snd_ctl_elem_info_get_max(info);
+ long step = snd_ctl_elem_info_get_step(info);
+ if (step)
+ sprintf(buf, "%li - %li (step %li)", min, max, step);
+ else
+ sprintf(buf, "%li - %li", min, max);
+ err = snd_config_string_add(comment, "range", buf);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ if (snd_ctl_elem_info_is_tlv_readable(info)) {
+ err = add_tlv_comments(handle, id, info, comment);
+ if (err < 0)
+ return err;
+ }
+ break;
+ }
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ {
+ long long min = snd_ctl_elem_info_get_min64(info);
+ long long max = snd_ctl_elem_info_get_max64(info);
+ long long step = snd_ctl_elem_info_get_step64(info);
+ if (step)
+ sprintf(buf, "%Li - %Li (step %Li)", min, max, step);
+ else
+ sprintf(buf, "%Li - %Li", min, max);
+ err = snd_config_string_add(comment, "range", buf);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ break;
+ }
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ {
+ unsigned int items;
+ err = snd_config_compound_add(comment, "item", 1, &item);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ return err;
+ }
+ items = snd_ctl_elem_info_get_items(info);
+ for (idx = 0; idx < items; idx++) {
+ snd_ctl_elem_info_set_item(info, idx);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ error("snd_ctl_card_info: %s", snd_strerror(err));
+ return err;
+ }
+ err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info));
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info));
+ err = snd_config_string_add(control, "iface", s);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ if (device != 0) {
+ err = snd_config_integer_add(control, "device", device);
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ if (subdevice != 0) {
+ err = snd_config_integer_add(control, "subdevice", subdevice);
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ err = snd_config_string_add(control, "name", name);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ if (index != 0) {
+ err = snd_config_integer_add(control, "index", index);
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ {
+ size_t size = type == SND_CTL_ELEM_TYPE_BYTES ?
+ count : sizeof(snd_aes_iec958_t);
+ char buf[size * 2 + 1];
+ char *p = buf;
+ char *hex = "0123456789abcdef";
+ const unsigned char *bytes =
+ (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl);
+ for (idx = 0; idx < size; idx++) {
+ int v = bytes[idx];
+ *p++ = hex[v >> 4];
+ *p++ = hex[v & 0x0f];
+ }
+ *p = '\0';
+ err = snd_config_string_add(control, "value", buf);
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ return 0;
+ }
+ default:
+ break;
+ }
+
+ if (count == 1) {
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false");
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ return 0;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0));
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+ return 0;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0));
+ if (err < 0) {
+ error("snd_config_integer64_add: %s", snd_strerror(err));
+ return err;
+ }
+ return 0;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ {
+ unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0);
+ snd_config_t *c;
+ err = snd_config_search(item, num_str(v), &c);
+ if (err == 0) {
+ err = snd_config_get_string(c, &s);
+ assert(err == 0);
+ err = snd_config_string_add(control, "value", s);
+ } else {
+ err = snd_config_integer_add(control, "value", v);
+ }
+ if (err < 0)
+ error("snd_config add: %s", snd_strerror(err));
+ return 0;
+ }
+ default:
+ error("Unknown control type: %d\n", type);
+ return -EINVAL;
+ }
+ }
+
+ err = snd_config_compound_add(control, "value", 1, &value);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ return err;
+ }
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ for (idx = 0; idx < count; idx++) {
+ err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false");
+ if (err < 0) {
+ error("snd_config_string_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ for (idx = 0; idx < count; idx++) {
+ err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx));
+ if (err < 0) {
+ error("snd_config_integer_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ for (idx = 0; idx < count; idx++) {
+ err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx));
+ if (err < 0) {
+ error("snd_config_integer64_add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ for (idx = 0; idx < count; idx++) {
+ unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx);
+ snd_config_t *c;
+ err = snd_config_search(item, num_str(v), &c);
+ if (err == 0) {
+ err = snd_config_get_string(c, &s);
+ assert(err == 0);
+ err = snd_config_string_add(value, num_str(idx), s);
+ } else {
+ err = snd_config_integer_add(value, num_str(idx), v);
+ }
+ if (err < 0) {
+ error("snd_config add: %s", snd_strerror(err));
+ return err;
+ }
+ }
+ break;
+ default:
+ error("Unknown control type: %d\n", type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int get_controls(int cardno, snd_config_t *top)
+{
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_config_t *state, *card, *control;
+ snd_ctl_elem_list_t *list;
+ unsigned int idx;
+ int err;
+ char name[32];
+ unsigned int count;
+ const char *id;
+ snd_ctl_card_info_alloca(&info);
+ snd_ctl_elem_list_alloca(&list);
+
+ sprintf(name, "hw:%d", cardno);
+ err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
+ if (err < 0) {
+ error("snd_ctl_open error: %s", snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_card_info(handle, info);
+ if (err < 0) {
+ error("snd_ctl_card_info error: %s", snd_strerror(err));
+ goto _close;
+ }
+ id = snd_ctl_card_info_get_id(info);
+ err = snd_config_search(top, "state", &state);
+ if (err == 0 &&
+ snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) {
+ error("config state node is not a compound");
+ err = -EINVAL;
+ goto _close;
+ }
+ if (err < 0) {
+ err = snd_config_compound_add(top, "state", 1, &state);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ goto _close;
+ }
+ }
+ err = snd_config_search(state, id, &card);
+ if (err == 0 &&
+ snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) {
+ error("config state.%s node is not a compound", id);
+ err = -EINVAL;
+ goto _close;
+ }
+ if (err < 0) {
+ err = snd_config_compound_add(state, id, 0, &card);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ goto _close;
+ }
+ }
+ err = snd_config_search(card, "control", &control);
+ if (err == 0) {
+ err = snd_config_delete(control);
+ if (err < 0) {
+ error("snd_config_delete: %s", snd_strerror(err));
+ goto _close;
+ }
+ }
+ err = snd_ctl_elem_list(handle, list);
+ if (err < 0) {
+ error("Cannot determine controls: %s", snd_strerror(err));
+ goto _close;
+ }
+ count = snd_ctl_elem_list_get_count(list);
+ err = snd_config_compound_add(card, "control", count > 0, &control);
+ if (err < 0) {
+ error("snd_config_compound_add: %s", snd_strerror(err));
+ goto _close;
+ }
+ if (count == 0) {
+ err = 0;
+ goto _close;
+ }
+ snd_ctl_elem_list_set_offset(list, 0);
+ if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+ error("No enough memory...");
+ goto _close;
+ }
+ if ((err = snd_ctl_elem_list(handle, list)) < 0) {
+ error("Cannot determine controls (2): %s", snd_strerror(err));
+ goto _free;
+ }
+ for (idx = 0; idx < count; ++idx) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_list_get_id(list, idx, id);
+ err = get_control(handle, id, control);
+ if (err < 0)
+ goto _free;
+ }
+
+ err = 0;
+ _free:
+ snd_ctl_elem_list_free_space(list);
+ _close:
+ snd_ctl_close(handle);
+ return err;
+}
+
+static long config_iface(snd_config_t *n)
+{
+ long i;
+ long long li;
+ snd_ctl_elem_iface_t idx;
+ const char *str;
+ switch (snd_config_get_type(n)) {
+ case SND_CONFIG_TYPE_INTEGER:
+ snd_config_get_integer(n, &i);
+ return i;
+ case SND_CONFIG_TYPE_INTEGER64:
+ snd_config_get_integer64(n, &li);
+ return li;
+ case SND_CONFIG_TYPE_STRING:
+ snd_config_get_string(n, &str);
+ break;
+ default:
+ return -1;
+ }
+ for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
+ if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
+ return idx;
+ }
+ return -1;
+}
+
+static int config_bool(snd_config_t *n, int doit)
+{
+ const char *str;
+ long val;
+ long long lval;
+
+ switch (snd_config_get_type(n)) {
+ case SND_CONFIG_TYPE_INTEGER:
+ snd_config_get_integer(n, &val);
+ if (val < 0 || val > 1)
+ return -1;
+ return val;
+ case SND_CONFIG_TYPE_INTEGER64:
+ snd_config_get_integer64(n, &lval);
+ if (lval < 0 || lval > 1)
+ return -1;
+ return (int) lval;
+ case SND_CONFIG_TYPE_STRING:
+ snd_config_get_string(n, &str);
+ break;
+ case SND_CONFIG_TYPE_COMPOUND:
+ if (!force_restore || !doit)
+ return -1;
+ n = snd_config_iterator_entry(snd_config_iterator_first(n));
+ return config_bool(n, doit);
+ default:
+ return -1;
+ }
+ if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0)
+ return 1;
+ if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0)
+ return 0;
+ return -1;
+}
+
+static int config_enumerated(snd_config_t *n, snd_ctl_t *handle,
+ snd_ctl_elem_info_t *info, int doit)
+{
+ const char *str;
+ long val;
+ long long lval;
+ unsigned int idx, items;
+
+ switch (snd_config_get_type(n)) {
+ case SND_CONFIG_TYPE_INTEGER:
+ snd_config_get_integer(n, &val);
+ return val;
+ case SND_CONFIG_TYPE_INTEGER64:
+ snd_config_get_integer64(n, &lval);
+ return (int) lval;
+ case SND_CONFIG_TYPE_STRING:
+ snd_config_get_string(n, &str);
+ break;
+ case SND_CONFIG_TYPE_COMPOUND:
+ if (!force_restore || !doit)
+ return -1;
+ n = snd_config_iterator_entry(snd_config_iterator_first(n));
+ return config_enumerated(n, handle, info, doit);
+ default:
+ return -1;
+ }
+ items = snd_ctl_elem_info_get_items(info);
+ for (idx = 0; idx < items; idx++) {
+ int err;
+ snd_ctl_elem_info_set_item(info, idx);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0) {
+ error("snd_ctl_elem_info: %s", snd_strerror(err));
+ return err;
+ }
+ if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
+ return idx;
+ }
+ return -1;
+}
+
+static int config_integer(snd_config_t *n, long *val, int doit)
+{
+ int err = snd_config_get_integer(n, val);
+ if (err < 0 && force_restore && doit) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
+ return err;
+ n = snd_config_iterator_entry(snd_config_iterator_first(n));
+ return config_integer(n, val, doit);
+ }
+ return err;
+}
+
+static int config_integer64(snd_config_t *n, long long *val, int doit)
+{
+ int err = snd_config_get_integer64(n, val);
+ if (err < 0 && force_restore && doit) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND)
+ return err;
+ n = snd_config_iterator_entry(snd_config_iterator_first(n));
+ return config_integer64(n, val, doit);
+ }
+ return err;
+}
+
+static int is_user_control(snd_config_t *conf)
+{
+ snd_config_iterator_t i, next;
+
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id, *s;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (strcmp(id, "access") == 0) {
+ if (snd_config_get_string(n, &s) < 0)
+ return 0;
+ if (strstr(s, "user"))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * get the item type from the given comment config
+ */
+static int get_comment_type(snd_config_t *n)
+{
+ static const snd_ctl_elem_type_t types[] = {
+ SND_CTL_ELEM_TYPE_BOOLEAN,
+ SND_CTL_ELEM_TYPE_INTEGER,
+ SND_CTL_ELEM_TYPE_ENUMERATED,
+ SND_CTL_ELEM_TYPE_BYTES,
+ SND_CTL_ELEM_TYPE_IEC958,
+ SND_CTL_ELEM_TYPE_INTEGER64,
+ };
+ const char *type;
+ unsigned int i;
+
+ if (snd_config_get_string(n, &type) < 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(types); ++i)
+ if (strcmp(type, snd_ctl_elem_type_name(types[i])) == 0)
+ return types[i];
+ return -EINVAL;
+}
+
+/*
+ * get the value range from the given comment config
+ */
+static int get_comment_range(snd_config_t *n, int ctype,
+ long *imin, long *imax, long *istep)
+{
+ const char *s;
+ int err;
+
+ if (snd_config_get_string(n, &s) < 0)
+ return -EINVAL;
+ switch (ctype) {
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ err = sscanf(s, "%li - %li (step %li)", imin, imax, istep);
+ if (err != 3) {
+ istep = 0;
+ err = sscanf(s, "%li - %li", imin, imax);
+ if (err != 2)
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int add_user_control(snd_ctl_t *handle, snd_ctl_elem_info_t *info, snd_config_t *conf)
+{
+ snd_ctl_elem_id_t *id;
+ snd_config_iterator_t i, next;
+ long imin, imax, istep;
+ snd_ctl_elem_type_t ctype;
+ unsigned int count;
+ int err;
+ unsigned int *tlv;
+
+ imin = imax = istep = 0;
+ count = 0;
+ ctype = SND_CTL_ELEM_TYPE_NONE;
+ tlv = NULL;
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (strcmp(id, "type") == 0) {
+ err = get_comment_type(n);
+ if (err < 0)
+ return err;
+ ctype = err;
+ continue;
+ }
+ if (strcmp(id, "range") == 0) {
+ err = get_comment_range(n, ctype, &imin, &imax, &istep);
+ if (err < 0)
+ return err;
+ continue;
+ }
+ if (strcmp(id, "count") == 0) {
+ long v;
+ if ((err = snd_config_get_integer(n, &v)) < 0)
+ return err;
+ count = v;
+ continue;
+ }
+ if (strcmp(id, "tlv") == 0) {
+ const char *s;
+ if ((err = snd_config_get_string(n, &s)) < 0)
+ return -EINVAL;
+ if (tlv)
+ free(tlv);
+ if ((tlv = str_to_tlv(s)) == NULL)
+ return -EINVAL;
+ continue;
+ }
+ }
+
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_get_id(info, id);
+ if (count <= 0)
+ count = 1;
+ switch (ctype) {
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ if (imin > imax || istep > imax - imin)
+ return -EINVAL;
+ err = snd_ctl_elem_add_integer(handle, id, count, imin, imax, istep);
+ if (err < 0)
+ goto error;
+ if (tlv)
+ snd_ctl_elem_tlv_write(handle, id, tlv);
+ break;
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ err = snd_ctl_elem_add_boolean(handle, id, count);
+ break;
+ case SND_CTL_ELEM_TYPE_IEC958:
+ err = snd_ctl_elem_add_iec958(handle, id);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ error:
+ free(tlv);
+ if (err < 0)
+ return err;
+ return snd_ctl_elem_info(handle, info);
+}
+
+/*
+ * look for a config node with the given item name
+ */
+static snd_config_t *search_comment_item(snd_config_t *conf, const char *name)
+{
+ snd_config_iterator_t i, next;
+ snd_config_for_each(i, next, conf) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ if (strcmp(id, name) == 0)
+ return n;
+ }
+ return NULL;
+}
+
+/*
+ * check whether the config item has the same of compatible type
+ */
+static int check_comment_type(snd_config_t *conf, int type)
+{
+ snd_config_t *n = search_comment_item(conf, "type");
+ int ctype;
+
+ if (!n)
+ return 0; /* not defined */
+ ctype = get_comment_type(n);
+ if (ctype == type)
+ return 0;
+ if ((ctype == SND_CTL_ELEM_TYPE_BOOLEAN ||
+ ctype == SND_CTL_ELEM_TYPE_INTEGER ||
+ ctype == SND_CTL_ELEM_TYPE_INTEGER64 ||
+ ctype == SND_CTL_ELEM_TYPE_ENUMERATED) &&
+ (type == SND_CTL_ELEM_TYPE_BOOLEAN ||
+ type == SND_CTL_ELEM_TYPE_INTEGER ||
+ type == SND_CTL_ELEM_TYPE_INTEGER64 ||
+ type == SND_CTL_ELEM_TYPE_ENUMERATED))
+ return 0; /* OK, compatible */
+ return -EINVAL;
+}
+
+/*
+ * convert from an old value to a new value with the same dB level
+ */
+static int convert_to_new_db(snd_config_t *value, long omin, long omax,
+ long nmin, long nmax,
+ long odbmin, long odbmax,
+ long ndbmin, long ndbmax,
+ int doit)
+{
+ long val;
+ if (config_integer(value, &val, doit) < 0)
+ return -EINVAL;
+ if (val < omin || val > omax)
+ return -EINVAL;
+ val = ((val - omin) * (odbmax - odbmin)) / (omax - omin) + odbmin;
+ if (val < ndbmin)
+ val = ndbmin;
+ else if (val > ndbmax)
+ val = ndbmax;
+ val = ((val - ndbmin) * (nmax - nmin)) / (ndbmax - ndbmin) + nmin;
+ return snd_config_set_integer(value, val);
+}
+
+/*
+ * compare the current value range with the old range in comments.
+ * also, if dB information is available, try to compare them.
+ * if any change occurs, try to keep the same dB level.
+ */
+static int check_comment_range(snd_ctl_t *handle, snd_config_t *conf,
+ snd_ctl_elem_info_t *info, snd_config_t *value,
+ int doit)
+{
+ snd_config_t *n;
+ long omin, omax, ostep;
+ long nmin, nmax;
+ long odbmin, odbmax;
+ long ndbmin, ndbmax;
+ snd_ctl_elem_id_t *id;
+
+ n = search_comment_item(conf, "range");
+ if (!n)
+ return 0;
+ if (get_comment_range(n, SND_CTL_ELEM_TYPE_INTEGER,
+ &omin, &omax, &ostep) < 0)
+ return 0;
+ nmin = snd_ctl_elem_info_get_min(info);
+ nmax = snd_ctl_elem_info_get_max(info);
+ if (omin != nmin && omax != nmax) {
+ /* Hey, the range mismatches */
+ if (!force_restore || !doit)
+ return -EINVAL;
+ }
+ if (omin >= omax || nmin >= nmax)
+ return 0; /* invalid values */
+
+ n = search_comment_item(conf, "dbmin");
+ if (!n)
+ return 0;
+ if (config_integer(n, &odbmin, doit) < 0)
+ return 0;
+ n = search_comment_item(conf, "dbmax");
+ if (!n)
+ return 0;
+ if (config_integer(n, &odbmax, doit) < 0)
+ return 0;
+ if (odbmin >= odbmax)
+ return 0; /* invalid values */
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_get_id(info, id);
+ if (snd_ctl_get_dB_range(handle, id, &ndbmin, &ndbmax) < 0)
+ return 0;
+ if (ndbmin >= ndbmax)
+ return 0; /* invalid values */
+ if (omin == nmin && omax == nmax &&
+ odbmin == ndbmin && odbmax == ndbmax)
+ return 0; /* OK, identical one */
+
+ /* Let's guess the current value from dB range */
+ if (snd_config_get_type(value) == SND_CONFIG_TYPE_COMPOUND) {
+ snd_config_iterator_t i, next;
+ snd_config_for_each(i, next, value) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ convert_to_new_db(n, omin, omax, nmin, nmax,
+ odbmin, odbmax, ndbmin, ndbmax, doit);
+ }
+ } else
+ convert_to_new_db(value, omin, omax, nmin, nmax,
+ odbmin, odbmax, ndbmin, ndbmax, doit);
+ return 0;
+}
+
+static int restore_config_value(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
+ snd_ctl_elem_iface_t type,
+ snd_config_t *value,
+ snd_ctl_elem_value_t *ctl, int idx,
+ int doit)
+{
+ long val;
+ long long lval;
+ int err;
+
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BOOLEAN:
+ val = config_bool(value, doit);
+ if (val >= 0) {
+ snd_ctl_elem_value_set_boolean(ctl, idx, val);
+ return 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER:
+ err = config_integer(value, &val, doit);
+ if (err == 0) {
+ snd_ctl_elem_value_set_integer(ctl, idx, val);
+ return 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_INTEGER64:
+ err = config_integer64(value, &lval, doit);
+ if (err == 0) {
+ snd_ctl_elem_value_set_integer64(ctl, idx, lval);
+ return 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_ENUMERATED:
+ val = config_enumerated(value, handle, info, doit);
+ if (val >= 0) {
+ snd_ctl_elem_value_set_enumerated(ctl, idx, val);
+ return 1;
+ }
+ break;
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ break;
+ default:
+ cerror(doit, "Unknow control type: %d", type);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info,
+ snd_ctl_elem_iface_t type,
+ snd_config_t *value,
+ snd_ctl_elem_value_t *ctl, int idx,
+ unsigned int numid, int doit)
+{
+ int err = restore_config_value(handle, info, type, value, ctl, idx, doit);
+ long val;
+
+ if (err != 0)
+ return err;
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ err = snd_config_get_integer(value, &val);
+ if (err < 0 || val < 0 || val > 255) {
+ cerror(doit, "bad control.%d.value.%d content", numid, idx);
+ return force_restore && doit ? 0 : -EINVAL;
+ }
+ snd_ctl_elem_value_set_byte(ctl, idx, val);
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int set_control(snd_ctl_t *handle, snd_config_t *control,
+ int *maxnumid, int doit)
+{
+ snd_ctl_elem_value_t *ctl;
+ snd_ctl_elem_info_t *info;
+ snd_config_iterator_t i, next;
+ unsigned int numid1;
+ snd_ctl_elem_iface_t iface = -1;
+ int iface1;
+ const char *name1;
+ unsigned int numid;
+ snd_ctl_elem_type_t type;
+ unsigned int count;
+ long device = -1;
+ long device1;
+ long subdevice = -1;
+ long subdevice1;
+ const char *name = NULL;
+ long index1;
+ long index = -1;
+ snd_config_t *value = NULL;
+ snd_config_t *comment = NULL;
+ unsigned int idx;
+ int err;
+ char *set;
+ const char *id;
+ snd_ctl_elem_value_alloca(&ctl);
+ snd_ctl_elem_info_alloca(&info);
+ if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
+ cerror(doit, "control is not a compound");
+ return -EINVAL;
+ }
+ err = snd_config_get_id(control, &id);
+ if (err < 0) {
+ cerror(doit, "unable to get id");
+ return -EINVAL;
+ }
+ numid = atoi(id);
+ if ((int)numid > *maxnumid)
+ *maxnumid = numid;
+ snd_config_for_each(i, next, control) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *fld;
+ if (snd_config_get_id(n, &fld) < 0)
+ continue;
+ if (strcmp(fld, "comment") == 0) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+ cerror(doit, "control.%d.%s is invalid", numid, fld);
+ return -EINVAL;
+ }
+ comment = n;
+ continue;
+ }
+ if (strcmp(fld, "iface") == 0) {
+ iface = (snd_ctl_elem_iface_t)config_iface(n);
+ continue;
+ }
+ if (strcmp(fld, "device") == 0) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+ cerror(doit, "control.%d.%s is invalid", numid, fld);
+ return -EINVAL;
+ }
+ snd_config_get_integer(n, &device);
+ continue;
+ }
+ if (strcmp(fld, "subdevice") == 0) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+ cerror(doit, "control.%d.%s is invalid", numid, fld);
+ return -EINVAL;
+ }
+ snd_config_get_integer(n, &subdevice);
+ continue;
+ }
+ if (strcmp(fld, "name") == 0) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
+ cerror(doit, "control.%d.%s is invalid", numid, fld);
+ return -EINVAL;
+ }
+ snd_config_get_string(n, &name);
+ continue;
+ }
+ if (strcmp(fld, "index") == 0) {
+ if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+ cerror(doit, "control.%d.%s is invalid", numid, fld);
+ return -EINVAL;
+ }
+ snd_config_get_integer(n, &index);
+ continue;
+ }
+ if (strcmp(fld, "value") == 0) {
+ value = n;
+ continue;
+ }
+ cerror(doit, "unknown control.%d.%s field", numid, fld);
+ }
+ if (!value) {
+ cerror(doit, "missing control.%d.value", numid);
+ return -EINVAL;
+ }
+ if (device < 0)
+ device = 0;
+ if (subdevice < 0)
+ subdevice = 0;
+ if (index < 0)
+ index = 0;
+
+ err = -EINVAL;
+ if (!force_restore) {
+ snd_ctl_elem_info_set_numid(info, numid);
+ err = snd_ctl_elem_info(handle, info);
+ }
+ if (err < 0 && name) {
+ snd_ctl_elem_info_set_numid(info, 0);
+ snd_ctl_elem_info_set_interface(info, iface);
+ snd_ctl_elem_info_set_device(info, device);
+ snd_ctl_elem_info_set_subdevice(info, subdevice);
+ snd_ctl_elem_info_set_name(info, name);
+ snd_ctl_elem_info_set_index(info, index);
+ err = snd_ctl_elem_info(handle, info);
+ if (err < 0 && comment && is_user_control(comment)) {
+ err = add_user_control(handle, info, comment);
+ if (err < 0) {
+ cerror(doit, "failed to add user control #%d (%s)",
+ numid, snd_strerror(err));
+ return err;
+ }
+ }
+ }
+ if (err < 0) {
+ cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
+ return -ENOENT;
+ }
+ numid1 = snd_ctl_elem_info_get_numid(info);
+ iface1 = snd_ctl_elem_info_get_interface(info);
+ device1 = snd_ctl_elem_info_get_device(info);
+ subdevice1 = snd_ctl_elem_info_get_subdevice(info);
+ name1 = snd_ctl_elem_info_get_name(info);
+ index1 = snd_ctl_elem_info_get_index(info);
+ count = snd_ctl_elem_info_get_count(info);
+ type = snd_ctl_elem_info_get_type(info);
+ if (err |= numid != numid1 && !force_restore)
+ cerror(doit, "warning: numid mismatch (%d/%d) for control #%d",
+ numid, numid1, numid);
+ if (err |= iface != iface1)
+ cerror(doit, "warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
+ if (err |= device != device1)
+ cerror(doit, "warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
+ if (err |= subdevice != subdevice1)
+ cerror(doit, "warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid);
+ if (err |= strcmp(name, name1))
+ cerror(doit, "warning: name mismatch (%s/%s) for control #%d", name, name1, numid);
+ if (err |= index != index1)
+ cerror(doit, "warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid);
+ if (err < 0) {
+ cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
+ return -ENOENT;
+ }
+
+ if (comment) {
+ if (check_comment_type(comment, type) < 0)
+ cerror(doit, "incompatible field type for control #%d", numid);
+ if (type == SND_CTL_ELEM_TYPE_INTEGER) {
+ if (check_comment_range(handle, comment, info, value, doit) < 0) {
+ cerror(doit, "value range mismatch for control #%d",
+ numid);
+ return -EINVAL;
+ }
+ }
+ }
+
+ if (snd_ctl_elem_info_is_inactive(info) ||
+ !snd_ctl_elem_info_is_writable(info))
+ return 0;
+ snd_ctl_elem_value_set_numid(ctl, numid1);
+
+ if (count == 1) {
+ err = restore_config_value(handle, info, type, value, ctl, 0, doit);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ goto _ok;
+ }
+ switch (type) {
+ case SND_CTL_ELEM_TYPE_BYTES:
+ case SND_CTL_ELEM_TYPE_IEC958:
+ {
+ const char *buf;
+ err = snd_config_get_string(value, &buf);
+ if (err >= 0) {
+ int c1 = 0;
+ int len = strlen(buf);
+ unsigned int idx = 0;
+ int size = type == SND_CTL_ELEM_TYPE_BYTES ?
+ count : sizeof(snd_aes_iec958_t);
+ if (size * 2 != len) {
+ cerror(doit, "bad control.%d.value contents\n", numid);
+ return -EINVAL;
+ }
+ while (*buf) {
+ int c = *buf++;
+ if ((c = hextodigit(c)) < 0) {
+ cerror(doit, "bad control.%d.value contents\n", numid);
+ return -EINVAL;
+ }
+ if (idx % 2 == 1)
+ snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c);
+ else
+ c1 = c;
+ idx++;
+ }
+ goto _ok;
+ }
+ }
+ default:
+ break;
+ }
+ if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) {
+ if (!force_restore || !doit) {
+ cerror(doit, "bad control.%d.value type", numid);
+ return -EINVAL;
+ }
+ for (idx = 0; idx < count; ++idx) {
+ err = restore_config_value2(handle, info, type, value,
+ ctl, idx, numid, doit);
+ if (err < 0)
+ return err;
+ }
+ goto _ok;
+ }
+
+ set = (char*) alloca(count);
+ memset(set, 0, count);
+ snd_config_for_each(i, next, value) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ const char *id;
+ if (snd_config_get_id(n, &id) < 0)
+ continue;
+ idx = atoi(id);
+ if (idx >= count || set[idx]) {
+ cerror(doit, "bad control.%d.value index", numid);
+ if (!force_restore || !doit)
+ return -EINVAL;
+ continue;
+ }
+ err = restore_config_value2(handle, info, type, n,
+ ctl, idx, numid, doit);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ set[idx] = 1;
+ }
+ for (idx = 0; idx < count; ++idx) {
+ if (!set[idx]) {
+ cerror(doit, "control.%d.value.%d is not specified", numid, idx);
+ if (!force_restore || !doit)
+ return -EINVAL;
+ }
+ }
+
+ _ok:
+ err = doit ? snd_ctl_elem_write(handle, ctl) : 0;
+ if (err < 0) {
+ error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
+ return err;
+ }
+ return 0;
+}
+
+static int set_controls(int card, snd_config_t *top, int doit)
+{
+ snd_ctl_t *handle;
+ snd_ctl_card_info_t *info;
+ snd_config_t *control;
+ snd_config_iterator_t i, next;
+ int err, maxnumid = -1;
+ char name[32], tmpid[16];
+ const char *id;
+ snd_ctl_card_info_alloca(&info);
+
+ sprintf(name, "hw:%d", card);
+ err = snd_ctl_open(&handle, name, 0);
+ if (err < 0) {
+ error("snd_ctl_open error: %s", snd_strerror(err));
+ return err;
+ }
+ err = snd_ctl_card_info(handle, info);
+ if (err < 0) {
+ error("snd_ctl_card_info error: %s", snd_strerror(err));
+ goto _close;
+ }
+ id = snd_ctl_card_info_get_id(info);
+ err = snd_config_searchv(top, &control, "state", id, "control", 0);
+ if (err < 0) {
+ if (force_restore) {
+ sprintf(tmpid, "card%d", card);
+ err = snd_config_searchv(top, &control, "state", tmpid, "control", 0);
+ if (! err)
+ id = tmpid;
+ }
+ if (err < 0) {
+ fprintf(stderr, "No state is present for card %s\n", id);
+ goto _close;
+ }
+ id = tmpid;
+ }
+ if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
+ cerror(doit, "state.%s.control is not a compound\n", id);
+ return -EINVAL;
+ }
+ snd_config_for_each(i, next, control) {
+ snd_config_t *n = snd_config_iterator_entry(i);
+ err = set_control(handle, n, &maxnumid, doit);
+ if (err < 0 && (!force_restore || !doit))
+ goto _close;
+ }
+
+ /* check if we have additional controls in driver */
+ /* in this case we should go through init procedure */
+ if (!doit && maxnumid >= 0) {
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_info_t *info;
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_info_alloca(&info);
+ snd_ctl_elem_info_set_numid(info, maxnumid+1);
+ if (snd_ctl_elem_info(handle, info) == 0) {
+ /* not very informative */
+ /* but value is used for check only */
+ err = -EAGAIN;
+ goto _close;
+ }
+ }
+
+ _close:
+ snd_ctl_close(handle);
+ return err;
+}
+
+int save_state(const char *file, const char *cardname)
+{
+ int err;
+ snd_config_t *config;
+ snd_input_t *in;
+ snd_output_t *out;
+ int stdio;
+
+ err = snd_config_top(&config);
+ if (err < 0) {
+ error("snd_config_top error: %s", snd_strerror(err));
+ return err;
+ }
+ stdio = !strcmp(file, "-");
+ if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) {
+ err = snd_config_load(config, in);
+ snd_input_close(in);
+#if 0
+ if (err < 0) {
+ error("snd_config_load error: %s", snd_strerror(err));
+ return err;
+ }
+#endif
+ }
+
+ if (!cardname) {
+ int card, first = 1;
+
+ card = -1;
+ /* find each installed soundcards */
+ while (1) {
+ if (snd_card_next(&card) < 0)
+ break;
+ if (card < 0) {
+ if (first) {
+ if (ignore_nocards) {
+ return 0;
+ } else {
+ error("No soundcards found...");
+ return -ENODEV;
+ }
+ }
+ break;
+ }
+ first = 0;
+ if ((err = get_controls(card, config)))
+ return err;
+ }
+ } else {
+ int cardno;
+
+ cardno = snd_card_get_index(cardname);
+ if (cardno < 0) {
+ error("Cannot find soundcard '%s'...", cardname);
+ return cardno;
+ }
+ if ((err = get_controls(cardno, config))) {
+ return err;
+ }
+ }
+
+ if (stdio)
+ err = snd_output_stdio_attach(&out, stdout, 0);
+ else
+ err = snd_output_stdio_open(&out, file, "w");
+ if (err < 0) {
+ error("Cannot open %s for writing: %s", file, snd_strerror(err));
+ return -errno;
+ }
+ err = snd_config_save(config, out);
+ snd_output_close(out);
+ if (err < 0)
+ error("snd_config_save: %s", snd_strerror(err));
+ return 0;
+}
+
+int load_state(const char *file, const char *initfile, const char *cardname,
+ int do_init)
+{
+ int err, finalerr = 0;
+ snd_config_t *config;
+ snd_input_t *in;
+ int stdio;
+
+ err = snd_config_top(&config);
+ if (err < 0) {
+ error("snd_config_top error: %s", snd_strerror(err));
+ return err;
+ }
+ stdio = !strcmp(file, "-");
+ if (stdio)
+ err = snd_input_stdio_attach(&in, stdin, 0);
+ else
+ err = snd_input_stdio_open(&in, file, "r");
+ if (err >= 0) {
+ err = snd_config_load(config, in);
+ snd_input_close(in);
+ if (err < 0) {
+ error("snd_config_load error: %s", snd_strerror(err));
+ return err;
+ }
+ } else {
+ int card, first = 1;
+ char cardname1[16];
+
+ error("Cannot open %s for reading: %s", file, snd_strerror(err));
+ finalerr = err;
+ card = -1;
+ /* find each installed soundcards */
+ while (1) {
+ if (snd_card_next(&card) < 0)
+ break;
+ if (card < 0)
+ break;
+ first = 0;
+ if (!do_init)
+ break;
+ sprintf(cardname1, "%i", card);
+ err = init(initfile, cardname1);
+ if (err < 0) {
+ finalerr = err;
+ initfailed(card, "init");
+ }
+ initfailed(card, "restore");
+ }
+ if (first)
+ finalerr = 0; /* no cards, no error code */
+ return finalerr;
+ }
+
+ if (!cardname) {
+ int card, first = 1;
+ char cardname1[16];
+
+ card = -1;
+ /* find each installed soundcards */
+ while (1) {
+ if (snd_card_next(&card) < 0)
+ break;
+ if (card < 0) {
+ if (first) {
+ if (ignore_nocards) {
+ return 0;
+ } else {
+ error("No soundcards found...");
+ return -ENODEV;
+ }
+ }
+ break;
+ }
+ first = 0;
+ /* do a check if controls matches state file */
+ if (do_init && set_controls(card, config, 0)) {
+ sprintf(cardname1, "%i", card);
+ err = init(initfile, cardname1);
+ if (err < 0) {
+ initfailed(card, "init");
+ finalerr = err;
+ }
+ }
+ if ((err = set_controls(card, config, 1))) {
+ if (!force_restore)
+ finalerr = err;
+ initfailed(card, "restore");
+ }
+ }
+ } else {
+ int cardno;
+
+ cardno = snd_card_get_index(cardname);
+ if (cardno < 0) {
+ error("Cannot find soundcard '%s'...", cardname);
+ return -ENODEV;
+ }
+ /* do a check if controls matches state file */
+ if (do_init && set_controls(cardno, config, 0)) {
+ err = init(initfile, cardname);
+ if (err < 0) {
+ initfailed(cardno, "init");
+ return err;
+ }
+ }
+ if ((err = set_controls(cardno, config, 1))) {
+ initfailed(cardno, "restore");
+ if (!force_restore)
+ return err;
+ }
+ }
+ return finalerr;
+}
diff --git a/alsa-utils/alsactl/utils.c b/alsa-utils/alsactl/utils.c
new file mode 100644
index 0000000..ab4dbd4
--- /dev/null
+++ b/alsa-utils/alsactl/utils.c
@@ -0,0 +1,98 @@
+/*
+ * Advanced Linux Sound Architecture Control Program - Support routines
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+int file_map(const char *filename, char **buf, size_t *bufsize)
+{
+ struct stat stats;
+ int fd;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (fstat(fd, &stats) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED) {
+ close(fd);
+ return -1;
+ }
+ *bufsize = stats.st_size;
+
+ close(fd);
+
+ return 0;
+}
+
+void file_unmap(void *buf, size_t bufsize)
+{
+ munmap(buf, bufsize);
+}
+
+size_t line_width(const char *buf, size_t bufsize, size_t pos)
+{
+ int esc = 0;
+ size_t count;
+
+ for (count = pos; count < bufsize; count++) {
+ if (!esc && buf[count] == '\n')
+ break;
+ esc = buf[count] == '\\';
+ }
+
+ return count - pos;
+}
+
+void initfailed(int cardnumber, const char *reason)
+{
+ int fp;
+ char *str;
+
+ if (statefile == NULL)
+ return;
+ if (snd_card_get_name(cardnumber, &str) < 0)
+ return;
+ fp = open(statefile, O_WRONLY|O_CREAT|O_APPEND, 0644);
+ write(fp, str, strlen(str));
+ write(fp, ":", 1);
+ write(fp, reason, strlen(reason));
+ write(fp, "\n", 1);
+ close(fp);
+ free(str);
+}