diff options
Diffstat (limited to 'alsa-utils/alsactl')
-rw-r--r-- | alsa-utils/alsactl/Makefile.am | 14 | ||||
-rw-r--r-- | alsa-utils/alsactl/Makefile.in | 670 | ||||
-rw-r--r-- | alsa-utils/alsactl/alsactl.1 | 118 | ||||
-rw-r--r-- | alsa-utils/alsactl/alsactl.c | 192 | ||||
-rw-r--r-- | alsa-utils/alsactl/alsactl.h | 94 | ||||
-rw-r--r-- | alsa-utils/alsactl/alsactl_init.xml | 590 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/00main | 46 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/Makefile.am | 7 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/Makefile.in | 366 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/default | 145 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/hda | 34 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/help | 8 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/info | 22 | ||||
-rw-r--r-- | alsa-utils/alsactl/init/test | 270 | ||||
-rw-r--r-- | alsa-utils/alsactl/init_parse.c | 1739 | ||||
-rw-r--r-- | alsa-utils/alsactl/init_sysdeps.c | 63 | ||||
-rw-r--r-- | alsa-utils/alsactl/init_sysfs.c | 158 | ||||
-rw-r--r-- | alsa-utils/alsactl/init_utils_run.c | 247 | ||||
-rw-r--r-- | alsa-utils/alsactl/init_utils_string.c | 194 | ||||
-rw-r--r-- | alsa-utils/alsactl/list.h | 289 | ||||
-rw-r--r-- | alsa-utils/alsactl/state.c | 1653 | ||||
-rw-r--r-- | alsa-utils/alsactl/utils.c | 98 |
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); +} |