Initial 'alsactl init' implementation

See 'man 7 alsactl_init' for more details.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2008-07-31 15:45:08 +02:00
parent d84146df87
commit b402cf543a
18 changed files with 3484 additions and 22 deletions

3
.gitignore vendored
View file

@ -20,14 +20,17 @@ ABOUT-NLS
*.ok
*.gmo
*.o
*~
.deps
alsactl/alsactl
alsactl/alsactl_init.7
alsaconf/alsaconf
alsamixer/alsamixer
amidi/amidi
amixer/amixer
aplay/aplay
aplay/arecord.1
iecset/iecset
seq/aconnect/aconnect
seq/aplaymidi/aplaymidi

View file

@ -1,6 +1,9 @@
sbin_PROGRAMS=alsactl
man_MANS=alsactl.1
EXTRA_DIST=alsactl.1
man_MANS=alsactl.1 alsactl_init.7
EXTRA_DIST=alsactl.1 alsactl_init.xml
alsactl_SOURCES=alsactl.c state.c names.c
alsactl_SOURCES=alsactl.c state.c names.c utils.c init_parse.c
noinst_HEADERS=alsactl.h
%.7: %.xml
xmlto man $?

View file

@ -40,19 +40,25 @@ char *command;
static void help(void)
{
printf("Usage: alsactl <options> command\n");
printf("\nAvailable options:\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 " or " SYS_ASOUNDNAMES ")\n");
printf(" -F,--force try to restore the matching controls as much as possible\n");
printf(" (default mode)\n");
printf(" -P,--pedantic don't restore mismatching controls (old default)\n");
printf(" -d,--debug debug mode\n");
printf(" -v,--version print version of this program\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");
}
@ -63,6 +69,8 @@ int main(int argc, char *argv[])
{
{"help", 0, NULL, 'h'},
{"file", 1, NULL, 'f'},
{"env", 1, NULL, 'E'},
{"initfile", 1, NULL, 'i'},
{"force", 0, NULL, 'F'},
{"pedantic", 0, NULL, 'P'},
{"debug", 0, NULL, 'd'},
@ -70,13 +78,14 @@ int main(int argc, char *argv[])
{NULL, 0, NULL, 0},
};
char *cfgfile = SYS_ASOUNDRC;
char *initfile = DATADIR "/init/00main";
int res;
command = argv[0];
while (1) {
int c;
if ((c = getopt_long(argc, argv, "hf:Fdv", long_option, NULL)) < 0)
if ((c = getopt_long(argc, argv, "hdvf:FE:i:", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
@ -88,6 +97,15 @@ int main(int argc, char *argv[])
case 'F':
force_restore = 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 'P':
force_restore = 0;
break;
@ -111,7 +129,10 @@ int main(int argc, char *argv[])
return 0;
}
if (!strcmp(argv[optind], "store")) {
if (!strcmp(argv[optind], "init")) {
res = init(initfile,
argc - optind > 1 ? argv[optind + 1] : NULL);
} else if (!strcmp(argv[optind], "store")) {
res = save_state(cfgfile,
argc - optind > 1 ? argv[optind + 1] : NULL);
} else if (!strcmp(argv[optind], "restore")) {

View file

@ -2,6 +2,20 @@ extern int debugflag;
extern int force_restore;
extern char *command;
#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__); \
@ -16,7 +30,43 @@ extern char *command;
} 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 *cardname);
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);
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;
}

534
alsactl/alsactl_init.xml Normal file
View file

@ -0,0 +1,534 @@
<?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>items</option></term>
<listitem>
<para>Enumerated value - number of text items</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>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
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>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>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>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).</para>
</listitem>
</varlistentry>
</variablelist>
<para>The <option>PROGRAM</option>, <option>CTL{value}</option>,
<option>PRINT</option>, <option>ERROR</option>,
<option>EXIT</option>, <option>SYSFS_DEVICE</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>

16
alsactl/init/00main Normal file
View file

@ -0,0 +1,16 @@
# 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
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}=="test", INCLUDE="test", GOTO="00main_end"
ENV{CMD}=="*", ERROR="Unknown command '$env{CMD}'\n", GOTO="00main_end"
# include files with real configuration
CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="00main_end"
CARDINFO{driver}=="Test", INCLUDE="test", GOTO="00main_end"
LABEL="00main_end"

21
alsactl/init/hda Normal file
View file

@ -0,0 +1,21 @@
# Configuration for HDA Intel driver (High Definition Audio - Azalia)
CARDINFO{mixername}=="Realtek ALC880", \
CARDINFO{components}=="*HDA:10ec0880 HDA:11c13026*", \
GOTO="Acer Travelmate 8100"
ERROR="Unknown hardware: \"$cardinfo{mixername}\" \"$cardinfo{components}\"\n"
ERROR="Hardware is left uninitialized"
EXIT="99"
LABEL="Acer Travelmate 8100"
# playback
CTL{reset}="mixer"
CTL{name}="Headphone Playback Switch", CTL{value}="on,on"
CTL{name}="Front Playback Volume", CTL{value}="35,35"
CTL{name}="Front Playback Switch", CTL{value}="on,on"
CTL{name}="PCM Playback Volume", CTL{value}="150,150"
# capture
CTL{name}="Input Source", CTL{value}="0"
CTL{name}="Capture Volume", CTL{value}="65,65"
CTL{name}="Capture Switch", CTL{value}="on,on"
EXIT="0"

7
alsactl/init/help Normal file
View file

@ -0,0 +1,7 @@
# help page
PRINT="Available commands (identified by the environment variable CMD):\n\n"
PRINT=" (not set) Do a soundcard initialization\n"
PRINT=" help Show this information\n"
PRINT=" info Print all available hardware identification\n"
PRINT=" test Do alsactl utility parser tests\n"

22
alsactl/init/info Normal file
View file

@ -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"

256
alsactl/init/test Normal file
View file

@ -0,0 +1,256 @@
# 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"
LABEL="skip_switch_search"
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{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{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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{step}=\"$ctl{step}\"\n"
PRINT=" CTL{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{items}=\"$ctl{items}\"\n"
PRINT=" CTL{value}=\"$ctl{value}\"\n"
LABEL="skip_first_ten_search"
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{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", CTL{value}="32,32", \
PRINT=" result=$result\n"
PRINT="\nAll tests done..\n"

1512
alsactl/init_parse.c Normal file

File diff suppressed because it is too large Load diff

63
alsactl/init_sysdeps.c Normal file
View file

@ -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.
*
*/
#ifdef __GLIBC__
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__ */

158
alsactl/init_sysfs.c Normal file
View file

@ -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;
}

247
alsactl/init_utils_run.c Normal file
View file

@ -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);
}

194
alsactl/init_utils_string.c Normal file
View file

@ -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;
}

289
alsactl/list.h Normal file
View file

@ -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 */

View file

@ -151,19 +151,6 @@ static char *tlv_to_str(unsigned int *tlv)
return s;
}
static 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;
}
static unsigned int *str_to_tlv(const char *s)
{
int i, j, c, len;

79
alsactl/utils.c Normal file
View file

@ -0,0 +1,79 @@
/*
* 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 "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;
}