mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-21 19:46:31 +01:00
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:
parent
d84146df87
commit
b402cf543a
18 changed files with 3484 additions and 22 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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 $?
|
||||
|
|
|
@ -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,8 +129,11 @@ int main(int argc, char *argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[optind], "store")) {
|
||||
res = save_state(cfgfile,
|
||||
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")) {
|
||||
res = load_state(cfgfile,
|
||||
|
|
|
@ -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
534
alsactl/alsactl_init.xml
Normal 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
16
alsactl/init/00main
Normal 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
21
alsactl/init/hda
Normal 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
7
alsactl/init/help
Normal 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
22
alsactl/init/info
Normal 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
256
alsactl/init/test
Normal 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
1512
alsactl/init_parse.c
Normal file
File diff suppressed because it is too large
Load diff
63
alsactl/init_sysdeps.c
Normal file
63
alsactl/init_sysdeps.c
Normal 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
158
alsactl/init_sysfs.c
Normal 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
247
alsactl/init_utils_run.c
Normal 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
194
alsactl/init_utils_string.c
Normal 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
289
alsactl/list.h
Normal 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 */
|
|
@ -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
79
alsactl/utils.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue