mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-22 06:06:30 +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
|
*.ok
|
||||||
*.gmo
|
*.gmo
|
||||||
*.o
|
*.o
|
||||||
|
*~
|
||||||
.deps
|
.deps
|
||||||
|
|
||||||
alsactl/alsactl
|
alsactl/alsactl
|
||||||
|
alsactl/alsactl_init.7
|
||||||
alsaconf/alsaconf
|
alsaconf/alsaconf
|
||||||
alsamixer/alsamixer
|
alsamixer/alsamixer
|
||||||
amidi/amidi
|
amidi/amidi
|
||||||
amixer/amixer
|
amixer/amixer
|
||||||
aplay/aplay
|
aplay/aplay
|
||||||
|
aplay/arecord.1
|
||||||
iecset/iecset
|
iecset/iecset
|
||||||
seq/aconnect/aconnect
|
seq/aconnect/aconnect
|
||||||
seq/aplaymidi/aplaymidi
|
seq/aplaymidi/aplaymidi
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
sbin_PROGRAMS=alsactl
|
sbin_PROGRAMS=alsactl
|
||||||
man_MANS=alsactl.1
|
man_MANS=alsactl.1 alsactl_init.7
|
||||||
EXTRA_DIST=alsactl.1
|
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
|
noinst_HEADERS=alsactl.h
|
||||||
|
|
||||||
|
%.7: %.xml
|
||||||
|
xmlto man $?
|
||||||
|
|
|
@ -40,19 +40,25 @@ char *command;
|
||||||
static void help(void)
|
static void help(void)
|
||||||
{
|
{
|
||||||
printf("Usage: alsactl <options> command\n");
|
printf("Usage: alsactl <options> command\n");
|
||||||
printf("\nAvailable options:\n");
|
printf("\nAvailable global options:\n");
|
||||||
printf(" -h,--help this help\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,--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(" -F,--force try to restore the matching controls as much as possible\n");
|
||||||
printf(" (default mode)\n");
|
printf(" (default mode)\n");
|
||||||
printf(" -P,--pedantic don't restore mismatching controls (old default)\n");
|
printf(" -P,--pedantic don't restore mismatching controls (old default)\n");
|
||||||
printf(" -d,--debug debug mode\n");
|
printf("\nAvailable init options:\n");
|
||||||
printf(" -v,--version print version of this program\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("\nAvailable commands:\n");
|
||||||
printf(" store <card #> save current driver setup for one or each soundcards\n");
|
printf(" store <card #> save current driver setup for one or each soundcards\n");
|
||||||
printf(" to configuration file\n");
|
printf(" to configuration file\n");
|
||||||
printf(" restore <card #> load current driver setup for one or each soundcards\n");
|
printf(" restore <card #> load current driver setup for one or each soundcards\n");
|
||||||
printf(" from configuration file\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(" names <card #> dump information about all the known present (sub-)devices\n");
|
||||||
printf(" into configuration file (DEPRECATED)\n");
|
printf(" into configuration file (DEPRECATED)\n");
|
||||||
}
|
}
|
||||||
|
@ -63,6 +69,8 @@ int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
{"help", 0, NULL, 'h'},
|
{"help", 0, NULL, 'h'},
|
||||||
{"file", 1, NULL, 'f'},
|
{"file", 1, NULL, 'f'},
|
||||||
|
{"env", 1, NULL, 'E'},
|
||||||
|
{"initfile", 1, NULL, 'i'},
|
||||||
{"force", 0, NULL, 'F'},
|
{"force", 0, NULL, 'F'},
|
||||||
{"pedantic", 0, NULL, 'P'},
|
{"pedantic", 0, NULL, 'P'},
|
||||||
{"debug", 0, NULL, 'd'},
|
{"debug", 0, NULL, 'd'},
|
||||||
|
@ -70,13 +78,14 @@ int main(int argc, char *argv[])
|
||||||
{NULL, 0, NULL, 0},
|
{NULL, 0, NULL, 0},
|
||||||
};
|
};
|
||||||
char *cfgfile = SYS_ASOUNDRC;
|
char *cfgfile = SYS_ASOUNDRC;
|
||||||
|
char *initfile = DATADIR "/init/00main";
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
command = argv[0];
|
command = argv[0];
|
||||||
while (1) {
|
while (1) {
|
||||||
int c;
|
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;
|
break;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -88,6 +97,15 @@ int main(int argc, char *argv[])
|
||||||
case 'F':
|
case 'F':
|
||||||
force_restore = 1;
|
force_restore = 1;
|
||||||
break;
|
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':
|
case 'P':
|
||||||
force_restore = 0;
|
force_restore = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -111,8 +129,11 @@ int main(int argc, char *argv[])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(argv[optind], "store")) {
|
if (!strcmp(argv[optind], "init")) {
|
||||||
res = save_state(cfgfile,
|
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);
|
argc - optind > 1 ? argv[optind + 1] : NULL);
|
||||||
} else if (!strcmp(argv[optind], "restore")) {
|
} else if (!strcmp(argv[optind], "restore")) {
|
||||||
res = load_state(cfgfile,
|
res = load_state(cfgfile,
|
||||||
|
|
|
@ -2,6 +2,20 @@ extern int debugflag;
|
||||||
extern int force_restore;
|
extern int force_restore;
|
||||||
extern char *command;
|
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)
|
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
|
||||||
#define error(...) do {\
|
#define error(...) do {\
|
||||||
fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
|
fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
|
||||||
|
@ -16,7 +30,43 @@ extern char *command;
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#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 save_state(const char *file, const char *cardname);
|
||||||
int load_state(const char *file, const char *cardname);
|
int load_state(const char *file, const char *cardname);
|
||||||
int power(const char *argv[], int argc);
|
int power(const char *argv[], int argc);
|
||||||
int generate_names(const char *cfgfile);
|
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;
|
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)
|
static unsigned int *str_to_tlv(const char *s)
|
||||||
{
|
{
|
||||||
int i, j, c, len;
|
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