Alsa git updates for alsa-{driver,utils}, uploaded initial -zendisk1 for rc3
[gentoo-diskmaster-overlay.git] / media-sound / alsa-utils / files / git-update.patch
blob177319c68cbcddc1b504c2a9f1ac902a1ac8e9e4
1 diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am
2 index d21a496..0e29c14 100644
3 --- a/alsactl/Makefile.am
4 +++ b/alsactl/Makefile.am
5 @@ -1,6 +1,11 @@
6 +SUBDIRS = init
8 sbin_PROGRAMS=alsactl
9 -man_MANS=alsactl.1
10 -EXTRA_DIST=alsactl.1
11 +man_MANS=alsactl.1 alsactl_init.7
12 +EXTRA_DIST=alsactl.1 alsactl_init.xml
14 -alsactl_SOURCES=alsactl.c state.c names.c
15 +alsactl_SOURCES=alsactl.c state.c names.c utils.c init_parse.c
16 noinst_HEADERS=alsactl.h
18 +%.7: %.xml
19 + xmltoman $?
20 diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c
21 index 78c6cb1..8d3987a 100644
22 --- a/alsactl/alsactl.c
23 +++ b/alsactl/alsactl.c
24 @@ -40,19 +40,25 @@ char *command;
25 static void help(void)
27 printf("Usage: alsactl <options> command\n");
28 - printf("\nAvailable options:\n");
29 + printf("\nAvailable global options:\n");
30 printf(" -h,--help this help\n");
31 + printf(" -d,--debug debug mode\n");
32 + printf(" -v,--version print version of this program\n");
33 + printf("\nAvailable state options:\n");
34 printf(" -f,--file # configuration file (default " SYS_ASOUNDRC " or " SYS_ASOUNDNAMES ")\n");
35 printf(" -F,--force try to restore the matching controls as much as possible\n");
36 printf(" (default mode)\n");
37 printf(" -P,--pedantic don't restore mismatching controls (old default)\n");
38 - printf(" -d,--debug debug mode\n");
39 - printf(" -v,--version print version of this program\n");
40 + printf("\nAvailable init options:\n");
41 + printf(" -E,--env #=# set environment variable for init phase (NAME=VALUE)\n");
42 + printf(" -i,--initfile # main configuation file for init phase (default " DATADIR "/init/00main)\n");
43 + printf("\n");
44 printf("\nAvailable commands:\n");
45 printf(" store <card #> save current driver setup for one or each soundcards\n");
46 printf(" to configuration file\n");
47 printf(" restore <card #> load current driver setup for one or each soundcards\n");
48 printf(" from configuration file\n");
49 + printf(" init <card #> initialize driver to a default state\n");
50 printf(" names <card #> dump information about all the known present (sub-)devices\n");
51 printf(" into configuration file (DEPRECATED)\n");
53 @@ -63,6 +69,8 @@ int main(int argc, char *argv[])
55 {"help", 0, NULL, 'h'},
56 {"file", 1, NULL, 'f'},
57 + {"env", 1, NULL, 'E'},
58 + {"initfile", 1, NULL, 'i'},
59 {"force", 0, NULL, 'F'},
60 {"pedantic", 0, NULL, 'P'},
61 {"debug", 0, NULL, 'd'},
62 @@ -70,13 +78,14 @@ int main(int argc, char *argv[])
63 {NULL, 0, NULL, 0},
65 char *cfgfile = SYS_ASOUNDRC;
66 + char *initfile = DATADIR "/init/00main";
67 int res;
69 command = argv[0];
70 while (1) {
71 int c;
73 - if ((c = getopt_long(argc, argv, "hf:Fdv", long_option, NULL)) < 0)
74 + if ((c = getopt_long(argc, argv, "hdvf:FE:i:", long_option, NULL)) < 0)
75 break;
76 switch (c) {
77 case 'h':
78 @@ -88,6 +97,15 @@ int main(int argc, char *argv[])
79 case 'F':
80 force_restore = 1;
81 break;
82 + case 'E':
83 + if (putenv(optarg)) {
84 + fprintf(stderr, "environment string '%s' is wrong\n", optarg);
85 + return EXIT_FAILURE;
86 + }
87 + break;
88 + case 'i':
89 + initfile = optarg;
90 + break;
91 case 'P':
92 force_restore = 0;
93 break;
94 @@ -111,8 +129,11 @@ int main(int argc, char *argv[])
95 return 0;
98 - if (!strcmp(argv[optind], "store")) {
99 - res = save_state(cfgfile,
100 + if (!strcmp(argv[optind], "init")) {
101 + res = init(initfile,
102 + argc - optind > 1 ? argv[optind + 1] : NULL);
103 + } else if (!strcmp(argv[optind], "store")) {
104 + res = save_state(cfgfile,
105 argc - optind > 1 ? argv[optind + 1] : NULL);
106 } else if (!strcmp(argv[optind], "restore")) {
107 res = load_state(cfgfile,
108 diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h
109 index f1e2b41..51396da 100644
110 --- a/alsactl/alsactl.h
111 +++ b/alsactl/alsactl.h
112 @@ -3,6 +3,20 @@ extern int force_restore;
113 extern char *command;
115 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
116 +#define info(...) do {\
117 + fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
118 + fprintf(stdout, __VA_ARGS__); \
119 + putc('\n', stdout); \
120 +} while (0)
121 +#else
122 +#define info(args...) do {\
123 + fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
124 + fprintf(stdout, ##args); \
125 + putc('\n', stdout); \
126 +} while (0)
127 +#endif
129 +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
130 #define error(...) do {\
131 fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
132 fprintf(stderr, __VA_ARGS__); \
133 @@ -16,7 +30,43 @@ extern char *command;
134 } while (0)
135 #endif
137 +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
138 +#define dbg(...) do {\
139 + if (!debugflag) break; \
140 + fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
141 + fprintf(stderr, __VA_ARGS__); \
142 + putc('\n', stderr); \
143 +} while (0)
144 +#else
145 +#define dbg(args...) do {\
146 + if (!debugflag) break; \
147 + fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
148 + fprintf(stderr, ##args); \
149 + putc('\n', stderr); \
150 +} while (0)
151 +#endif
153 +int init(const char *file, const char *cardname);
154 int save_state(const char *file, const char *cardname);
155 int load_state(const char *file, const char *cardname);
156 int power(const char *argv[], int argc);
157 int generate_names(const char *cfgfile);
159 +/* utils */
161 +int file_map(const char *filename, char **buf, size_t *bufsize);
162 +void file_unmap(void *buf, size_t bufsize);
163 +size_t line_width(const char *buf, size_t bufsize, size_t pos);
165 +static inline int hextodigit(int c)
167 + if (c >= '0' && c <= '9')
168 + c -= '0';
169 + else if (c >= 'a' && c <= 'f')
170 + c = c - 'a' + 10;
171 + else if (c >= 'A' && c <= 'F')
172 + c = c - 'A' + 10;
173 + else
174 + return -1;
175 + return c;
177 diff --git a/alsactl/alsactl_init.xml b/alsactl/alsactl_init.xml
178 new file mode 100644
179 index 0000000..f0ed9a4
180 --- /dev/null
181 +++ b/alsactl/alsactl_init.xml
182 @@ -0,0 +1,562 @@
183 +<?xml version='1.0'?>
184 +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
185 + "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
187 +<article>
188 + <section>
189 + <title>alsactl init</title>
190 + <refentry>
191 + <refentryinfo>
192 + <title>alsactl init</title>
193 + <date>July 2008</date>
194 + <productname>alsactl</productname>
195 + </refentryinfo>
197 + <refmeta>
198 + <refentrytitle>alsactl_init</refentrytitle>
199 + <manvolnum>7</manvolnum>
200 + <refmiscinfo class="version"></refmiscinfo>
201 + </refmeta>
203 + <refnamediv>
204 + <refname>alsactl init</refname>
205 + <refpurpose>alsa control management - initialization</refpurpose>
206 + </refnamediv>
208 + <refsect1><title>DESCRIPTION</title>
209 + <para>"alsactl init" provides soundcard specific initialization.</para>
210 + </refsect1>
212 + <refsect1><title>CONFIGURATION</title>
213 + <para>All "alsactl init" configuration files are placed in
214 + <filename>/usr/share/alsa/init/</filename> directory. The top level
215 + configuration file is <filename>/usr/share/alsa/init/00main</filename>.
216 + The default top-level file can be also specified using -i or
217 + --initfile parameter for the alsactl tool.
218 + Every file consists of a set of lines of text. All empty lines or
219 + lines beginning with '#' will be ignored.</para>
221 + <refsect2><title>Rules files</title>
222 + <para>The "alsactl init" rules are read from the files located
223 + in the <filename>/usr/share/alsa/init/*</filename>. The top
224 + level configuration file is <filename>/usr/share/alsa/init/00main</filename>.
225 + Every line in the rules file contains at least one key value pair.
226 + There are two kind of keys, match and assignment keys. If all match
227 + keys are matching against its value, the rule gets applied and the
228 + assign keys get the specified value assigned.</para>
230 + <para>A rule may consists of a list of one or more key value pairs
231 + separated by a comma. Each key has a distinct operation, depending
232 + on the used operator. Valid operators are:</para>
233 + <variablelist>
234 + <varlistentry>
235 + <term><option>==</option></term>
236 + <listitem>
237 + <para>Compare for equality.</para>
238 + </listitem>
239 + </varlistentry>
241 + <varlistentry>
242 + <term><option>!=</option></term>
243 + <listitem>
244 + <para>Compare for non-equality.</para>
245 + </listitem>
246 + </varlistentry>
248 + <varlistentry>
249 + <term><option>=</option></term>
250 + <listitem>
251 + <para>Assign a value to a key. Keys that represent a list,
252 + are reset and only this single value is assigned.</para>
253 + </listitem>
254 + </varlistentry>
256 + <varlistentry>
257 + <term><option>+=</option></term>
258 + <listitem>
259 + <para>Add the value to a key that holds a list
260 + of entries.</para>
261 + </listitem>
262 + </varlistentry>
264 + <varlistentry>
265 + <term><option>:=</option></term>
266 + <listitem>
267 + <para>Assign a value to a key finally; disallow any
268 + later changes, which may be used to prevent changes by
269 + any later rules.</para>
270 + </listitem>
271 + </varlistentry>
272 + </variablelist>
274 + <para>The following key names can be used to match against device
275 + properties:</para>
276 + <variablelist>
277 + <varlistentry>
278 + <term><option>CARDINDEX</option></term>
279 + <listitem>
280 + <para>Match the card index of the ALSA driver.</para>
281 + </listitem>
282 + </varlistentry>
284 + <varlistentry>
285 + <term><option>CTL{<replaceable>attribute</replaceable>}</option></term>
286 + <listitem>
287 + <para>Set or test universal control attribute. Possible
288 + attributes:</para>
289 + <variablelist>
290 + <varlistentry>
291 + <term><option>numid</option></term>
292 + <listitem>
293 + <para>Numeric control identification.</para>
294 + </listitem>
295 + </varlistentry>
296 + <varlistentry>
297 + <term><option>iface</option>, <option>interface</option></term>
298 + <listitem>
299 + <para>Control interface name (CARD, HWEDEP, MIXER, PCM, RAWMIDI, TIMER, SEQUENCER)</para>
300 + </listitem>
301 + </varlistentry>
302 + <varlistentry>
303 + <term><option>subdev</option>, <option>subdevice</option></term>
304 + <listitem>
305 + <para>Subdevice number.</para>
306 + </listitem>
307 + </varlistentry>
308 + <varlistentry>
309 + <term><option>name</option></term>
310 + <listitem>
311 + <para>Control name</para>
312 + </listitem>
313 + </varlistentry>
314 + <varlistentry>
315 + <term><option>index</option></term>
316 + <listitem>
317 + <para>Control index</para>
318 + </listitem>
319 + </varlistentry>
320 + <varlistentry>
321 + <term><option>type</option></term>
322 + <listitem>
323 + <para>Control type (BOOLEAN, INTEGER, INTEGER64, ENUMERATED, BYTES, IEC958)</para>
324 + </listitem>
325 + </varlistentry>
326 + <varlistentry>
327 + <term><option>attr</option>, <option>attribute</option></term>
328 + <listitem>
329 + <para>Attributes (stored in a string - use match characters * and ?):</para>
330 + <variablelist>
331 + <varlistentry>
332 + <term><option>r</option></term>
333 + <listitem>
334 + <para>control is readable</para>
335 + </listitem>
336 + </varlistentry>
337 + <varlistentry>
338 + <term><option>w</option></term>
339 + <listitem>
340 + <para>control is writable</para>
341 + </listitem>
342 + </varlistentry>
343 + <varlistentry>
344 + <term><option>v</option></term>
345 + <listitem>
346 + <para>control is volatile</para>
347 + </listitem>
348 + </varlistentry>
349 + <varlistentry>
350 + <term><option>i</option></term>
351 + <listitem>
352 + <para>control is inactive</para>
353 + </listitem>
354 + </varlistentry>
355 + <varlistentry>
356 + <term><option>l</option></term>
357 + <listitem>
358 + <para>control is locked</para>
359 + </listitem>
360 + </varlistentry>
361 + <varlistentry>
362 + <term><option>R</option></term>
363 + <listitem>
364 + <para>control is TLV readable</para>
365 + </listitem>
366 + </varlistentry>
367 + <varlistentry>
368 + <term><option>W</option></term>
369 + <listitem>
370 + <para>control is TLV writable</para>
371 + </listitem>
372 + </varlistentry>
373 + <varlistentry>
374 + <term><option>C</option></term>
375 + <listitem>
376 + <para>control is TLV commandable</para>
377 + </listitem>
378 + </varlistentry>
379 + <varlistentry>
380 + <term><option>o</option></term>
381 + <listitem>
382 + <para>process is owner of this control</para>
383 + </listitem>
384 + </varlistentry>
385 + <varlistentry>
386 + <term><option>u</option></term>
387 + <listitem>
388 + <para>control created in user space</para>
389 + </listitem>
390 + </varlistentry>
391 + </variablelist>
392 + </listitem>
393 + </varlistentry>
394 + <varlistentry>
395 + <term><option>owner</option></term>
396 + <listitem>
397 + <para>Control owner process PID number</para>
398 + </listitem>
399 + </varlistentry>
400 + <varlistentry>
401 + <term><option>count</option></term>
402 + <listitem>
403 + <para>Control count of values</para>
404 + </listitem>
405 + </varlistentry>
406 + <varlistentry>
407 + <term><option>min</option></term>
408 + <listitem>
409 + <para>Value range - minimum value</para>
410 + </listitem>
411 + </varlistentry>
412 + <varlistentry>
413 + <term><option>max</option></term>
414 + <listitem>
415 + <para>Value range - maximum value</para>
416 + </listitem>
417 + </varlistentry>
418 + <varlistentry>
419 + <term><option>step</option></term>
420 + <listitem>
421 + <para>Value range - step value</para>
422 + </listitem>
423 + </varlistentry>
424 + <varlistentry>
425 + <term><option>items</option></term>
426 + <listitem>
427 + <para>Enumerated value - number of text items</para>
428 + </listitem>
429 + </varlistentry>
430 + <varlistentry>
431 + <term><option>value</option></term>
432 + <listitem>
433 + <para>Value of control stored to a string delimited by
434 + comma (,).</para>
435 + </listitem>
436 + </varlistentry>
437 + </variablelist>
438 + </listitem>
439 + </varlistentry>
441 + <varlistentry>
442 + <term><option>CONFIG{sysfs_device}</option></term>
443 + <listitem>
444 + <para>The relative path to sysfs subsystem specifying
445 + the root directory of a soundcard device. Usually,
446 + it should be set to "/class/sound/controlC$cardinfo{card}/device".
447 + </para>
448 + </listitem>
449 + </varlistentry>
451 + <varlistentry>
452 + <term><option>ATTR{<replaceable>filename</replaceable>}</option></term>
453 + <listitem>
454 + <para>Match sysfs attribute values of the soundcard device.
455 + The relative path to sysfs tree must be defined by
456 + CONFIG{sysfs_device} key. Trailing whitespace in the attribute
457 + values is ignored, if the specified match value does
458 + not contain trailing whitespace itself. Depending on
459 + the type of operator, this key is also used to set
460 + the value of a sysfs attribute.
461 + </para>
462 + </listitem>
463 + </varlistentry>
465 + <varlistentry>
466 + <term><option>ENV{<replaceable>key</replaceable>}</option></term>
467 + <listitem>
468 + <para>Match against the value of an environment variable. Up
469 + to five <option>ENV</option> keys can be specified per rule.
470 + Depending on the type of operator, this key is also used
471 + to export a variable to the environment.</para>
472 + </listitem>
473 + </varlistentry>
475 + <varlistentry>
476 + <term><option>PROGRAM</option></term>
477 + <listitem>
478 + <para>Execute external program. The key is true, if
479 + the program returns without exit code zero. The whole event
480 + environment is available to the executed program. The
481 + program's output printed to stdout is available for
482 + the RESULT key.</para>
483 + <para>Several buildin commands are available:</para>
484 + <variablelist>
485 + <varlistentry>
486 + <term><option>__ctl_search</option></term>
487 + <listitem>
488 + <para>Search for a control. The CTL{name} key might
489 + contain match characters * and ?. An control index
490 + might be specified as first argument starting from
491 + zero (e.g. PROGRAM="__ctl_search 2").</para>
492 + </listitem>
493 + </varlistentry>
494 + <varlistentry>
495 + <term><option>__ctl_count</option></term>
496 + <listitem>
497 + <para>Search for a controls and return total count
498 + of matched ones. The CTL{name} key might contain match
499 + characters * and ?.</para>
500 + </listitem>
501 + </varlistentry>
502 + </variablelist>
503 + </listitem>
504 + </varlistentry>
506 + <varlistentry>
507 + <term><option>RESULT</option></term>
508 + <listitem>
509 + <para>Match the returned string of the last PROGRAM call.
510 + This key can be used in the same or in any later rule
511 + after a PROGRAM call.</para>
512 + </listitem>
513 + </varlistentry>
515 + </variablelist>
517 + <para>Most of the fields support a shell style pattern matching.
518 + The following pattern characters are supported:</para>
519 + <variablelist>
520 + <varlistentry>
521 + <term><option>*</option></term>
522 + <listitem>
523 + <para>Matches zero, or any number of characters.</para>
524 + </listitem>
525 + </varlistentry>
526 + <varlistentry>
527 + <term><option>?</option></term>
528 + <listitem>
529 + <para>Matches any single character.</para>
530 + </listitem>
531 + </varlistentry>
532 + <varlistentry>
533 + <term><option>[]</option></term>
534 + <listitem>
535 + <para>Matches any single character specified within
536 + the brackets. For example, the pattern string 'tty[SR]'
537 + would match either 'ttyS' or 'ttyR'. Ranges are also
538 + supported within this match with the '-' character.
539 + For example, to match on the range of all digits,
540 + the pattern [0-9] would be used. If the first character
541 + following the '[' is a '!', any characters
542 + not enclosed are matched.</para>
543 + </listitem>
544 + </varlistentry>
545 + </variablelist>
547 + <para>The following keys can get values assigned:</para>
548 + <variablelist>
549 + <varlistentry>
550 + <term><option>CTL{numid}</option>, <option>CTL{iface}</option>,
551 + <option>CTL{device}</option>, <option>CTL{subdev}</option>,
552 + <option>CTL{name}</option>, <option>CTL{index}</option>,
553 + </term>
554 + <listitem>
555 + <para>Select universal control element.</para>
556 + </listitem>
557 + </varlistentry>
558 + <varlistentry>
559 + <term><option>CTL{value}</option></term>
560 + <listitem>
561 + <para>Value is set (written) also to soundcard's control
562 + device and RESULT key is set to errno code. The result of
563 + set operation is always true (it means continue with
564 + next key on line).</para>
565 + </listitem>
566 + </varlistentry>
568 + <varlistentry>
569 + <term><option>ENV{<replaceable>key</replaceable>}</option></term>
570 + <listitem>
571 + <para>Export a variable to the environment. Depending on the type of operator,
572 + this key is also to match against an environment variable.</para>
573 + </listitem>
574 + </varlistentry>
576 + <varlistentry>
577 + <term><option>RESULT</option></term>
578 + <listitem>
579 + <para>Set RESULT variable. Note that PROGRAM also sets
580 + this variable, but setting this variable manually
581 + might be useful to change code execution order (included
582 + files).</para>
583 + </listitem>
584 + </varlistentry>
586 + <varlistentry>
587 + <term><option>LABEL</option></term>
588 + <listitem>
589 + <para>Named label where a GOTO can jump to.</para>
590 + </listitem>
591 + </varlistentry>
593 + <varlistentry>
594 + <term><option>GOTO</option></term>
595 + <listitem>
596 + <para>Jumps to the next LABEL with a matching name</para>
597 + </listitem>
598 + </varlistentry>
600 + <varlistentry>
601 + <term><option>INCLUDE</option></term>
602 + <listitem>
603 + <para>Include specified filename or all files in specified directory</para>
604 + </listitem>
605 + </varlistentry>
607 + <varlistentry>
608 + <term><option>ACCESS</option></term>
609 + <listitem>
610 + <para>Check if specified file or directory exists</para>
611 + </listitem>
612 + </varlistentry>
614 + <varlistentry>
615 + <term><option>CONFIG{sysfs_device}</option></term>
616 + <listitem>
617 + <para>The relative path to sysfs subsystem specifying
618 + the root directory of a soundcard device. Usually,
619 + it should be set to "/class/sound/controlC$cardinfo{card}/device".
620 + </para>
621 + </listitem>
622 + </varlistentry>
624 + <varlistentry>
625 + <term><option>PRINT</option></term>
626 + <listitem>
627 + <para>PRINT value to stdout.</para>
628 + </listitem>
629 + </varlistentry>
631 + <varlistentry>
632 + <term><option>ERROR</option></term>
633 + <listitem>
634 + <para>PRINT value to stderr.</para>
635 + </listitem>
636 + </varlistentry>
638 + <varlistentry>
639 + <term><option>EXIT</option></term>
640 + <listitem>
641 + <para>Exit immediately and set program exit code to value
642 + (should be integer). If value is "return" string,
643 + parser leaves current included file and returns to parent
644 + configuration file.</para>
645 + </listitem>
646 + </varlistentry>
648 + </variablelist>
650 + <para>The <option>PROGRAM</option>, <option>RESULT</option>,
651 + <option>CTL{value}</option>,
652 + <option>PRINT</option>, <option>ERROR</option>,
653 + <option>EXIT</option>, <option>CONFIG{}</option>
654 + fields support simple printf-like string substitutions.
655 + It allows the use of the complete environment set by earlier matching
656 + rules. For all other fields, substitutions are applied while the individual rule is
657 + being processed. The available substitutions are:</para>
658 + <variablelist>
659 + <varlistentry>
660 + <term><option>$cardinfo{<replaceable>attribute</replaceable>}</option>, <option>%i{<replaceable>attribute</replaceable>}</option></term>
661 + <listitem>
662 + <para>See CARDINFO{} for more details.</para>
663 + </listitem>
664 + </varlistentry>
666 + <varlistentry>
667 + <term><option>$ctl{<replaceable>attribute</replaceable>}</option>, <option>%C{<replaceable>attribute</replaceable>}</option></term>
668 + <listitem>
669 + <para>See CTL{} for more details.</para>
670 + </listitem>
671 + </varlistentry>
673 + <varlistentry>
674 + <term><option>$attr{<replaceable>file</replaceable>}</option>, <option>%s{<replaceable>file</replaceable>}</option></term>
675 + <listitem>
676 + <para>The value of a sysfs attribute found at the device, where
677 + all keys of the rule have matched.
678 + If the attribute is a symlink, the last element of the symlink target is
679 + returned as the value.</para>
680 + </listitem>
681 + </varlistentry>
683 + <varlistentry>
684 + <term><option>$env{<replaceable>key</replaceable>}</option>, <option>%E{<replaceable>key</replaceable>}</option></term>
685 + <listitem>
686 + <para>The value of an environment variable.</para>
687 + </listitem>
688 + </varlistentry>
690 + <varlistentry>
691 + <term><option>$result</option>, <option>%c</option></term>
692 + <listitem>
693 + <para>The string returned by the external program requested with PROGRAM.
694 + A single part of the string, separated by a space character may be selected
695 + by specifying the part number as an attribute: <option>%c{N}</option>.
696 + If the number is followed by the '+' char this part plus all remaining parts
697 + of the result string are substituted: <option>%c{N+}</option></para>
698 + </listitem>
699 + </varlistentry>
701 + <varlistentry>
702 + <term><option>$sysfsroot</option>, <option>%r</option></term>
703 + <listitem>
704 + <para>Root directory where sysfs file-system is mounted.
705 + Ususally, this value is just "/sys".</para>
706 + </listitem>
707 + </varlistentry>
709 + <varlistentry>
710 + <term><option>%%</option></term>
711 + <listitem>
712 + <para>The '%' character itself.</para>
713 + </listitem>
714 + </varlistentry>
716 + <varlistentry>
717 + <term><option>$$</option></term>
718 + <listitem>
719 + <para>The '$' character itself.</para>
720 + </listitem>
721 + </varlistentry>
722 + </variablelist>
723 + <para>The count of characters to be substituted may be limited
724 + by specifying the format length value. For example, '%3s{file}'
725 + will only insert the first three characters of the sysfs
726 + attribute</para>
727 + </refsect2>
728 + </refsect1>
730 + <refsect1><title>AUTHOR</title>
731 + <para>Written by Jaroslav Kysela <email>perex@perex.cz</email></para>
732 + <para>Some portions are written by Greg Kroah-Hartman <email>greg@kroah.com</email> and
733 + Kay Sievers <email>kay.sievers@vrfy.org</email>.</para>
734 + </refsect1>
736 + <refsect1>
737 + <title>SEE ALSO</title>
738 + <para><citerefentry>
739 + <refentrytitle>alsactl</refentrytitle><manvolnum>1</manvolnum>
740 + </citerefentry></para>
741 + </refsect1>
742 + </refentry>
743 + </section>
744 +</article>
745 diff --git a/alsactl/init/00main b/alsactl/init/00main
746 new file mode 100644
747 index 0000000..3d289cb
748 --- /dev/null
749 +++ b/alsactl/init/00main
750 @@ -0,0 +1,43 @@
751 +# This is toplevel configuration for for 'alsactl init'.
752 +# See 'man alsactl_init' for syntax.
754 +# set root device directory in sysfs for soundcard for ATTR{} command
755 +CONFIG{sysfs_device}="/class/sound/controlC$cardinfo{card}/device"
757 +# test for extra commands
758 +ENV{CMD}=="help", INCLUDE="help", GOTO="00main_end"
759 +ENV{CMD}=="info", INCLUDE="info", GOTO="00main_end"
760 +ENV{CMD}=="test", INCLUDE="test", GOTO="00main_end"
761 +ENV{CMD}=="*", ERROR="Unknown command '$env{CMD}'\n", GOTO="00main_end"
763 +# include files with real configuration
765 +# steps are:
766 +# 1) look for preinit subdirectory and parse all files in it
767 +# 2) if RESULT=="skip", skip ALSA standard configuration files
768 +# 3) do ALSA standard configuration
769 +# 4) look for postinit subdirectory and parse all files in it
770 +# 5) if RESULT!="true", print an error message and return with exit code 99
771 +# 6) return with exit code 0 (success)
774 +RESULT="unknown"
775 +ACCESS=="preinit", INCLUDE="preinit"
776 +RESULT=="skip", GOTO="init_end"
778 +# real ALSA configuration database
779 +CARDINFO{driver}=="HDA-Intel", INCLUDE="hda", GOTO="init_end"
780 +CARDINFO{driver}=="Test", INCLUDE="test", GOTO="init_end"
782 +LABEL="init_end"
783 +ACCESS=="postinit", INCLUDE="postinit"
784 +RESULT=="true", GOTO="00_mainend"
785 +ERROR="Unknown hardware: \"$cardinfo{driver}\" \"$cardinfo{mixername}\" \"$cardinfo{components}\" \"$attr{subsystem_vendor}\" \"$attr{subsystem_device}\"\n"
786 +ERROR="Hardware is left uninitialized\n"
787 +EXIT="99"
790 +# label identifying end of main file
793 +LABEL="00main_end"
794 diff --git a/alsactl/init/Makefile.am b/alsactl/init/Makefile.am
795 new file mode 100644
796 index 0000000..2a7e13f
797 --- /dev/null
798 +++ b/alsactl/init/Makefile.am
799 @@ -0,0 +1,7 @@
801 +init_files = \
802 + 00main help info test \
803 + hda
804 +EXTRA_DIRST = $(init_files)
805 +alsainitdir = $(datadir)/alsa/init
806 +alsainit_DATA = $(init_files)
807 diff --git a/alsactl/init/hda b/alsactl/init/hda
808 new file mode 100644
809 index 0000000..9c33123
810 --- /dev/null
811 +++ b/alsactl/init/hda
812 @@ -0,0 +1,34 @@
813 +# Configuration for HDA Intel driver (High Definition Audio - Azalia)
815 +CARDINFO{mixername}=="Realtek ALC880", \
816 + ATTR{subsystem_vendor}=="0x1025", ATTR{subsystem_device}=="0x0070", \
817 + GOTO="Acer Travelmate 8100"
818 +CARDINFO{mixername}=="Analog Devices AD1984", \
819 + ATTR{subsystem_vendor}=="0x17aa", ATTR{subsystem_device}=="0x20ac", \
820 + GOTO="Lenovo T61"
821 +RESULT="false", EXIT="return"
823 +LABEL="Acer Travelmate 8100"
824 +# playback
825 +CTL{reset}="mixer"
826 +CTL{name}="Headphone Playback Switch", CTL{value}="on,on"
827 +CTL{name}="Front Playback Volume", CTL{value}="35,35"
828 +CTL{name}="Front Playback Switch", CTL{value}="on,on"
829 +CTL{name}="PCM Playback Volume", CTL{value}="150,150"
830 +# capture
831 +CTL{name}="Input Source", CTL{value}="0"
832 +CTL{name}="Capture Volume", CTL{value}="65,65"
833 +CTL{name}="Capture Switch", CTL{value}="on,on"
834 +RESULT="true", EXIT="return"
836 +LABEL="Lenovo T61"
837 +# playback
838 +CTL{reset}="mixer"
839 +CTL{name}="Headphone Playback Switch", CTL{value}="on,on"
840 +CTL{name}="PCM Playback Volume", CTL{value}="30,30"
841 +# capture (Internal Mic)
842 +CTL{name}="Input Source", CTL{value}="1"
843 +CTL{name}="Internal Mic Boost", CTL{value}="1"
844 +CTL{name}="Capture Volume", CTL{value}="45,45"
845 +CTL{name}="Capture Switch", CTL{value}="on,on"
846 +RESULT="true", EXIT="return"
847 diff --git a/alsactl/init/help b/alsactl/init/help
848 new file mode 100644
849 index 0000000..60d9b1c
850 --- /dev/null
851 +++ b/alsactl/init/help
852 @@ -0,0 +1,7 @@
853 +# help page
855 +PRINT="Available commands (identified by the environment variable CMD):\n\n"
856 +PRINT=" (not set) Do a soundcard initialization\n"
857 +PRINT=" help Show this information\n"
858 +PRINT=" info Print all available hardware identification\n"
859 +PRINT=" test Do alsactl utility parser tests\n"
860 diff --git a/alsactl/init/info b/alsactl/init/info
861 new file mode 100644
862 index 0000000..a4fea19
863 --- /dev/null
864 +++ b/alsactl/init/info
865 @@ -0,0 +1,22 @@
866 +# show information about card
868 +PRINT="CARDINFO:\n"
869 +PRINT=" CARDINFO{id}=\"$CARDINFO{id}\"\n"
870 +PRINT=" CARDINFO{card}=\"$CARDINFO{card}\"\n"
871 +PRINT=" CARDINFO{driver}=\"$CARDINFO{driver}\"\n"
872 +PRINT=" CARDINFO{name}=\"$CARDINFO{name}\"\n"
873 +PRINT=" CARDINFO{longname}=\"$CARDINFO{longname}\"\n"
874 +PRINT=" CARDINFO{mixername}=\"$CARDINFO{mixername}\"\n"
875 +PRINT=" CARDINFO{components}=\"$CARDINFO{components}\"\n"
877 +# sysfs stuff
878 +PRINT="sysfs:\n"
879 +ATTR{bus}=="*", PRINT=" ATTR{bus}=\"$ATTR{bus}\"\n"
880 +ATTR{class}=="*", PRINT=" ATTR{class}=\"$ATTR{class}\"\n"
881 +ATTR{driver}=="*", PRINT=" ATTR{driver}=\"$ATTR{driver}\"\n"
882 +ATTR{vendor}=="*", PRINT=" ATTR{vendor}=\"$ATTR{vendor}\"\n"
883 +ATTR{device}=="*", PRINT=" ATTR{device}=\"$ATTR{device}\"\n"
884 +ATTR{subsystem_vendor}=="*", \
885 + PRINT=" ATTR{subsystem_vendor}=\"$ATTR{subsystem_vendor}\"\n"
886 +ATTR{subsystem_device}=="*", \
887 + PRINT=" ATTR{subsystem_device}=\"$ATTR{subsystem_device}\"\n"
888 diff --git a/alsactl/init/test b/alsactl/init/test
889 new file mode 100644
890 index 0000000..d1f4f11
891 --- /dev/null
892 +++ b/alsactl/init/test
893 @@ -0,0 +1,256 @@
894 +# Test code
895 +# Just for debugging purposes
897 +PRINT="Default CTL:\n"
898 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
899 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
900 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
901 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
902 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
903 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
905 +CTL{reset}="mixer"
907 +PRINT="After CTL{reset}=\"mixer\":\n"
908 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
909 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
910 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
911 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
912 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
913 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
915 +CTL{numid}="987"
916 +CTL{iface}="sequencer"
917 +CTL{device}="10"
918 +CTL{subdevice}="20"
919 +CTL{name}="Just Test"
920 +CTL{index}="999"
922 +PRINT="After test sequence:\n"
923 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
924 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
925 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
926 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
927 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
928 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
930 +ERROR="Ignore following error:\n "
931 +PROGRAM="__just_test"
933 +PRINT="__ctl_count test:\n"
934 +CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM="__ctl_count", \
935 + PRINT=" *Switch* count result: $result\n"
937 +PRINT="__ctl_search test:\n"
938 +CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search", GOTO="skip_switch_search"
939 +PRINT=" *Switch 0* search result: $result\n"
940 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
941 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
942 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
943 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
944 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
945 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
946 +CTL{search}="mixer", CTL{name}="*Switch*", PROGRAM!="__ctl_search 1", GOTO="skip_switch_search"
947 +PRINT=" *Switch 1* search result: $result\n"
948 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
949 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
950 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
951 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
952 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
953 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
954 +LABEL="skip_switch_search"
956 +PRINT="First ten elements:\n"
957 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 0", GOTO="skip_first_ten_search"
958 +PRINT=" Element #0:\n"
959 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
960 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
961 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
962 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
963 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
964 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
965 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
966 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
967 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
968 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
969 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
970 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
971 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
972 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
973 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
974 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 1", GOTO="skip_first_ten_search"
975 +PRINT=" Element #1:\n"
976 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
977 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
978 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
979 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
980 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
981 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
982 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
983 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
984 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
985 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
986 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
987 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
988 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
989 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
990 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
991 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 2", GOTO="skip_first_ten_search"
992 +PRINT=" Element #2:\n"
993 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
994 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
995 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
996 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
997 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
998 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
999 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1000 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1001 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1002 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1003 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1004 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1005 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1006 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1007 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1008 +LABEL="skip_first_ten_search"
1009 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 3", GOTO="skip_first_ten_search"
1010 +PRINT=" Element #3:\n"
1011 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1012 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1013 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1014 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1015 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1016 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1017 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1018 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1019 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1020 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1021 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1022 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1023 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1024 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1025 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1026 +LABEL="skip_first_ten_search"
1027 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 4", GOTO="skip_first_ten_search"
1028 +PRINT=" Element #4:\n"
1029 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1030 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1031 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1032 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1033 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1034 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1035 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1036 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1037 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1038 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1039 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1040 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1041 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1042 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1043 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1044 +LABEL="skip_first_ten_search"
1045 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 5", GOTO="skip_first_ten_search"
1046 +PRINT=" Element #5:\n"
1047 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1048 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1049 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1050 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1051 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1052 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1053 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1054 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1055 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1056 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1057 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1058 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1059 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1060 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1061 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1062 +LABEL="skip_first_ten_search"
1063 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 6", GOTO="skip_first_ten_search"
1064 +PRINT=" Element #6:\n"
1065 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1066 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1067 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1068 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1069 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1070 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1071 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1072 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1073 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1074 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1075 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1076 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1077 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1078 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1079 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1080 +LABEL="skip_first_ten_search"
1081 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 7", GOTO="skip_first_ten_search"
1082 +PRINT=" Element #7:\n"
1083 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1084 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1085 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1086 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1087 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1088 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1089 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1090 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1091 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1092 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1093 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1094 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1095 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1096 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1097 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1098 +LABEL="skip_first_ten_search"
1099 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 8", GOTO="skip_first_ten_search"
1100 +PRINT=" Element #8:\n"
1101 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1102 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1103 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1104 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1105 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1106 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1107 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1108 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1109 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1110 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1111 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1112 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1113 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1114 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1115 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1116 +LABEL="skip_first_ten_search"
1117 +CTL{search}="mixer", CTL{name}="*", PROGRAM!="__ctl_search 9", GOTO="skip_first_ten_search"
1118 +PRINT=" Element #9:\n"
1119 +PRINT=" CTL{numid}=\"$ctl{numid}\"\n"
1120 +PRINT=" CTL{iface}=\"$ctl{iface}\"\n"
1121 +PRINT=" CTL{device}=\"$ctl{device}\"\n"
1122 +PRINT=" CTL{subdevice}=\"$ctl{subdevice}\"\n"
1123 +PRINT=" CTL{name}=\"$ctl{name}\"\n"
1124 +PRINT=" CTL{index}=\"$ctl{index}\"\n"
1125 +PRINT=" CTL{type}=\"$ctl{type}\"\n"
1126 +PRINT=" CTL{attr}=\"$ctl{attr}\"\n"
1127 +PRINT=" CTL{owner}=\"$ctl{owner}\"\n"
1128 +PRINT=" CTL{count}=\"$ctl{count}\"\n"
1129 +PRINT=" CTL{min}=\"$ctl{min}\"\n"
1130 +PRINT=" CTL{max}=\"$ctl{max}\"\n"
1131 +PRINT=" CTL{step}=\"$ctl{step}\"\n"
1132 +PRINT=" CTL{items}=\"$ctl{items}\"\n"
1133 +PRINT=" CTL{value}=\"$ctl{value}\"\n"
1134 +LABEL="skip_first_ten_search"
1136 +PRINT="Elements write test #1:\n", \
1137 + CTL{search}="mixer", CTL{name}="Front Playback Switch", \
1138 + PROGRAM="__ctl_search", CTL{value}="on,on", \
1139 + PRINT=" result=$result\n"
1140 +PRINT="Elements write test #2:\n", \
1141 + CTL{search}="mixer", CTL{name}="Front Playback Volume", \
1142 + PROGRAM="__ctl_search", CTL{value}="32,32", \
1143 + PRINT=" result=$result\n"
1144 +PRINT="Elements write test #3:\n", \
1145 + CTL{search}="mixer", CTL{name}="Front Playback Volume Error", \
1146 + PROGRAM="__ctl_search", CTL{value}="32,32", \
1147 + PRINT=" result=$result\n"
1149 +PRINT="\nAll tests done..\n"
1150 diff --git a/alsactl/init_parse.c b/alsactl/init_parse.c
1151 new file mode 100644
1152 index 0000000..96caf46
1153 --- /dev/null
1154 +++ b/alsactl/init_parse.c
1155 @@ -0,0 +1,1612 @@
1157 + * Advanced Linux Sound Architecture Control Program - Parse initialization files
1158 + * Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
1159 + * Greg Kroah-Hartman <greg@kroah.com>,
1160 + * Kay Sievers <kay.sievers@vrfy.org>
1163 + * This program is free software; you can redistribute it and/or modify
1164 + * it under the terms of the GNU General Public License as published by
1165 + * the Free Software Foundation; either version 2 of the License, or
1166 + * (at your option) any later version.
1168 + * This program is distributed in the hope that it will be useful,
1169 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1170 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1171 + * GNU General Public License for more details.
1173 + * You should have received a copy of the GNU General Public License
1174 + * along with this program; if not, write to the Free Software
1175 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1177 + */
1179 +#include <stdlib.h>
1180 +#include <stdio.h>
1181 +#include <stddef.h>
1182 +#include <unistd.h>
1183 +#include <string.h>
1184 +#include <fcntl.h>
1185 +#include <ctype.h>
1186 +#include <errno.h>
1187 +#include <fnmatch.h>
1188 +#include <sys/stat.h>
1189 +#include <sys/un.h>
1190 +#include <sys/wait.h>
1191 +#include <sys/select.h>
1192 +#include <sys/types.h>
1193 +#include <dirent.h>
1194 +#include <alsa/asoundlib.h>
1195 +#include "aconfig.h"
1196 +#include "alsactl.h"
1197 +#include "list.h"
1199 +#define PATH_SIZE 512
1200 +#define NAME_SIZE 128
1201 +#define EJUSTRETURN 0x7fffffff
1203 +enum key_op {
1204 + KEY_OP_UNSET,
1205 + KEY_OP_MATCH,
1206 + KEY_OP_NOMATCH,
1207 + KEY_OP_ADD,
1208 + KEY_OP_ASSIGN,
1209 + KEY_OP_ASSIGN_FINAL
1212 +struct pair {
1213 + char *key;
1214 + char *value;
1215 + struct pair *next;
1218 +struct space {
1219 + struct pair *pairs;
1220 + char *rootdir;
1221 + char *go_to;
1222 + char *program_result;
1223 + const char *filename;
1224 + int linenum;
1225 + int log_run;
1226 + int exit_code;
1227 + int quit;
1228 + unsigned int ctl_id_changed;
1229 + snd_hctl_t *ctl_handle;
1230 + snd_ctl_card_info_t *ctl_card_info;
1231 + snd_ctl_elem_id_t *ctl_id;
1232 + snd_ctl_elem_info_t *ctl_info;
1233 + snd_ctl_elem_value_t *ctl_value;
1236 +static void Perror(struct space *space, const char *fmt, ...)
1238 + va_list arg;
1239 + va_start(arg, fmt);
1240 + fprintf(stderr, "%s:%i: ", space->filename, space->linenum);
1241 + vfprintf(stderr, fmt, arg);
1242 + putc('\n', stderr);
1243 + va_end(arg);
1246 +#include "init_sysdeps.c"
1247 +#include "init_utils_string.c"
1248 +#include "init_utils_run.c"
1249 +#include "init_sysfs.c"
1251 +static void free_space(struct space *space)
1253 + struct pair *pair = space->pairs;
1254 + struct pair *next = pair;
1256 + while (next) {
1257 + pair = next;
1258 + next = pair->next;
1259 + free(pair->value);
1260 + free(pair->key);
1261 + free(pair);
1263 + space->pairs = NULL;
1264 + if (space->ctl_value) {
1265 + snd_ctl_elem_value_free(space->ctl_value);
1266 + space->ctl_value = NULL;
1268 + if (space->ctl_info) {
1269 + snd_ctl_elem_info_free(space->ctl_info);
1270 + space->ctl_info = NULL;
1272 + if (space->ctl_id) {
1273 + snd_ctl_elem_id_free(space->ctl_id);
1274 + space->ctl_id = NULL;
1276 + if (space->ctl_card_info) {
1277 + snd_ctl_card_info_free(space->ctl_card_info);
1278 + space->ctl_card_info = NULL;
1280 + if (space->ctl_handle) {
1281 + free(space->ctl_handle);
1282 + space->ctl_handle = NULL;
1284 + if (space->rootdir)
1285 + free(space->rootdir);
1286 + if (space->program_result)
1287 + free(space->program_result);
1290 +struct pair *value_find(struct space *space, const char *key)
1292 + struct pair *pair = space->pairs;
1294 + while (pair && strcmp(pair->key, key) != 0)
1295 + pair = pair->next;
1296 + return pair;
1299 +static int value_set(struct space *space, const char *key, const char *value)
1301 + struct pair *pair;
1303 + pair = value_find(space, key);
1304 + if (pair) {
1305 + free(pair->value);
1306 + pair->value = strdup(value);
1307 + if (pair->value == NULL)
1308 + return -ENOMEM;
1309 + } else {
1310 + pair = malloc(sizeof(struct pair));
1311 + if (pair == NULL)
1312 + return -ENOMEM;
1313 + pair->key = strdup(key);
1314 + if (pair->key == NULL) {
1315 + free(pair);
1316 + return -ENOMEM;
1318 + pair->value = strdup(value);
1319 + if (pair->value == NULL) {
1320 + free(pair->key);
1321 + free(pair);
1322 + return -ENOMEM;
1324 + pair->next = space->pairs;
1325 + space->pairs = pair;
1327 + return 0;
1330 +static int init_space(struct space **space, int card)
1332 + struct space *res;
1333 + char device[16];
1334 + int err;
1336 + res = calloc(1, sizeof(struct space));
1337 + if (res == NULL)
1338 + return -ENOMEM;
1339 + res->ctl_id_changed = ~0;
1340 + res->linenum = -1;
1341 + sprintf(device, "hw:%u", card);
1342 + err = snd_hctl_open(&res->ctl_handle, device, 0);
1343 + if (err < 0)
1344 + goto error;
1345 + err = snd_hctl_load(res->ctl_handle);
1346 + if (err < 0)
1347 + goto error;
1348 + err = snd_ctl_card_info_malloc(&res->ctl_card_info);
1349 + if (err < 0)
1350 + goto error;
1351 + err = snd_ctl_card_info(snd_hctl_ctl(res->ctl_handle), res->ctl_card_info);
1352 + if (err < 0)
1353 + goto error;
1354 + err = snd_ctl_elem_id_malloc(&res->ctl_id);
1355 + if (err < 0)
1356 + goto error;
1357 + err = snd_ctl_elem_info_malloc(&res->ctl_info);
1358 + if (err < 0)
1359 + goto error;
1360 + err = snd_ctl_elem_value_malloc(&res->ctl_value);
1361 + if (err < 0)
1362 + goto error;
1363 + *space = res;
1364 + return 0;
1365 + error:
1366 + free_space(res);
1367 + return err;
1370 +static const char *cardinfo_get(struct space *space, const char *attr)
1372 + if (strncasecmp(attr, "CARD", 4) == 0) {
1373 + static char res[16];
1374 + sprintf(res, "%u", snd_ctl_card_info_get_card(space->ctl_card_info));
1375 + return res;
1377 + if (strncasecmp(attr, "ID", 2) == 0)
1378 + return snd_ctl_card_info_get_id(space->ctl_card_info);
1379 + if (strncasecmp(attr, "DRIVER", 6) == 0)
1380 + return snd_ctl_card_info_get_driver(space->ctl_card_info);
1381 + if (strncasecmp(attr, "NAME", 4) == 0)
1382 + return snd_ctl_card_info_get_name(space->ctl_card_info);
1383 + if (strncasecmp(attr, "LONGNAME", 8) == 0)
1384 + return snd_ctl_card_info_get_longname(space->ctl_card_info);
1385 + if (strncasecmp(attr, "MIXERNAME", 9) == 0)
1386 + return snd_ctl_card_info_get_mixername(space->ctl_card_info);
1387 + if (strncasecmp(attr, "COMPONENTS", 10) == 0)
1388 + return snd_ctl_card_info_get_components(space->ctl_card_info);
1389 + Perror(space, "unknown cardinfo{} attribute '%s'", attr);
1390 + return NULL;
1393 +static int check_id_changed(struct space *space, unsigned int what)
1395 + snd_hctl_elem_t *elem;
1396 + int err;
1398 + if ((space->ctl_id_changed & what & 1) != 0) {
1399 + snd_ctl_elem_id_set_numid(space->ctl_id, 0);
1400 + elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
1401 + if (!elem)
1402 + return -ENOENT;
1403 + err = snd_hctl_elem_info(elem, space->ctl_info);
1404 + if (err == 0)
1405 + space->ctl_id_changed &= ~1;
1406 + return err;
1408 + if ((space->ctl_id_changed & what & 2) != 0) {
1409 + snd_ctl_elem_id_set_numid(space->ctl_id, 0);
1410 + elem = snd_hctl_find_elem(space->ctl_handle, space->ctl_id);
1411 + if (!elem)
1412 + return -ENOENT;
1413 + err = snd_hctl_elem_read(elem, space->ctl_value);
1414 + if (err == 0)
1415 + space->ctl_id_changed &= ~2;
1416 + return err;
1418 + return 0;
1421 +static const char *get_ctl_value(struct space *space)
1423 + snd_ctl_elem_type_t type;
1424 + unsigned int idx, count;
1425 + static char res[1024], tmp[16];
1426 + static const char *hex = "0123456789abcdef";
1427 + char *pos;
1428 + const char *pos1;
1430 + type = snd_ctl_elem_info_get_type(space->ctl_info);
1431 + count = snd_ctl_elem_info_get_count(space->ctl_info);
1432 + res[0] = '\0';
1433 + switch (type) {
1434 + case SND_CTL_ELEM_TYPE_BOOLEAN:
1435 + for (idx = 0; idx < count; idx++) {
1436 + if (idx > 0)
1437 + strlcat(res, ",", sizeof(res));
1438 + strlcat(res, snd_ctl_elem_value_get_boolean(space->ctl_value, idx) ? "on" : "off", sizeof(res));
1440 + break;
1441 + case SND_CTL_ELEM_TYPE_INTEGER:
1442 + for (idx = 0; idx < count; idx++) {
1443 + if (idx > 0)
1444 + strlcat(res, ",", sizeof(res));
1445 + snprintf(tmp, sizeof(tmp), "%li", snd_ctl_elem_value_get_integer(space->ctl_value, idx));
1446 + strlcat(res, tmp, sizeof(res));
1448 + break;
1449 + case SND_CTL_ELEM_TYPE_INTEGER64:
1450 + for (idx = 0; idx < count; idx++) {
1451 + if (idx > 0)
1452 + strlcat(res, ",", sizeof(res));
1453 + snprintf(tmp, sizeof(tmp), "%lli", snd_ctl_elem_value_get_integer64(space->ctl_value, idx));
1454 + strlcat(res, tmp, sizeof(res));
1456 + break;
1457 + case SND_CTL_ELEM_TYPE_ENUMERATED:
1458 + for (idx = 0; idx < count; idx++) {
1459 + if (idx > 0)
1460 + strlcat(res, ",", sizeof(res));
1461 + snprintf(tmp, sizeof(tmp), "%u", snd_ctl_elem_value_get_enumerated(space->ctl_value, idx));
1462 + strlcat(res, tmp, sizeof(res));
1464 + break;
1465 + case SND_CTL_ELEM_TYPE_BYTES:
1466 + case SND_CTL_ELEM_TYPE_IEC958:
1467 + if (type == SND_CTL_ELEM_TYPE_IEC958)
1468 + count = sizeof(snd_aes_iec958_t);
1469 + if (count > (sizeof(res)-1)/2)
1470 + count = (sizeof(res)-1/2);
1471 + pos = res;
1472 + pos1 = snd_ctl_elem_value_get_bytes(space->ctl_value);
1473 + while (count > 0) {
1474 + idx = *pos1++;
1475 + *pos++ = hex[idx >> 4];
1476 + *pos++ = hex[idx & 0x0f];
1477 + count++;
1479 + *pos++ = '\0';
1480 + break;
1481 + default:
1482 + Perror(space, "unknown element type '%i'", type);
1483 + return NULL;
1485 + return res;
1488 +static int set_ctl_value(struct space *space, const char *value)
1490 + snd_ctl_elem_type_t type;
1491 + unsigned int idx, count;
1492 + const char *pos;
1493 + int val;
1495 + type = snd_ctl_elem_info_get_type(space->ctl_info);
1496 + count = snd_ctl_elem_info_get_count(space->ctl_info);
1497 + switch (type) {
1498 + case SND_CTL_ELEM_TYPE_BOOLEAN:
1499 + for (idx = 0; idx < count; idx++) {
1500 + while (*value == ' ')
1501 + value++;
1502 + if (*value == '\0')
1503 + goto missing;
1504 + val = strncasecmp(value, "true", 4) == 0 ||
1505 + strncasecmp(value, "yes", 3) == 0 ||
1506 + strncasecmp(value, "on", 2) == 0 ||
1507 + strncasecmp(value, "1", 1) == 0;
1508 + snd_ctl_elem_value_set_boolean(space->ctl_value, idx, val);
1509 + pos = strchr(value, ',');
1510 + value = pos ? pos + 1 : value + strlen(value) - 1;
1512 + break;
1513 + case SND_CTL_ELEM_TYPE_INTEGER:
1514 + for (idx = 0; idx < count; idx++) {
1515 + while (*value == ' ')
1516 + value++;
1517 + snd_ctl_elem_value_set_integer(space->ctl_value, idx, strtol(value, NULL, 0));
1518 + pos = strchr(value, ',');
1519 + value = pos ? pos + 1 : value + strlen(value) - 1;
1521 + break;
1522 + case SND_CTL_ELEM_TYPE_INTEGER64:
1523 + for (idx = 0; idx < count; idx++) {
1524 + while (*value == ' ')
1525 + value++;
1526 + snd_ctl_elem_value_set_integer64(space->ctl_value, idx, strtoll(value, NULL, 0));
1527 + pos = strchr(value, ',');
1528 + value = pos ? pos + 1 : value + strlen(value) - 1;
1530 + break;
1531 + case SND_CTL_ELEM_TYPE_ENUMERATED:
1532 + for (idx = 0; idx < count; idx++) {
1533 + while (*value == ' ')
1534 + value++;
1535 + snd_ctl_elem_value_set_enumerated(space->ctl_value, idx, strtol(value, NULL, 0));
1536 + pos = strchr(value, ',');
1537 + value = pos ? pos + 1 : value + strlen(value) - 1;
1539 + break;
1540 + case SND_CTL_ELEM_TYPE_BYTES:
1541 + case SND_CTL_ELEM_TYPE_IEC958:
1542 + if (type == SND_CTL_ELEM_TYPE_IEC958)
1543 + count = sizeof(snd_aes_iec958_t);
1544 + while (*value == ' ')
1545 + value++;
1546 + if (strlen(value) != count * 2) {
1547 + Perror(space, "bad ctl value hexa length (should be %u bytes, line %i)", count, space->linenum);
1548 + return -EINVAL;
1550 + for (idx = 0; idx < count; idx += 2) {
1551 + val = hextodigit(*(value++)) << 4;
1552 + val |= hextodigit(*(value++));
1553 + if (val > 255) {
1554 + Perror(space, "bad ctl hexa value (line %i)", space->linenum);
1555 + return -EINVAL;
1557 + snd_ctl_elem_value_set_byte(space->ctl_value, idx, val);
1559 + break;
1560 + default:
1561 + Perror(space, "unknown element type '%i'", type);
1562 + return -EINVAL;
1564 + return 0;
1565 + missing:
1566 + printf("%i %i\n", type, count);
1567 + Perror(space, "missing some ctl values (line %i)", space->linenum);
1568 + return -EINVAL;
1571 +static const char *elemid_get(struct space *space, const char *attr)
1573 + long long val;
1574 + snd_ctl_elem_type_t type;
1575 + static char res[16];
1577 + if (strncasecmp(attr, "numid", 5) == 0) {
1578 + val = snd_ctl_elem_id_get_numid(space->ctl_id);
1579 + goto value;
1581 + if (strncasecmp(attr, "iface", 5) == 0 ||
1582 + strncasecmp(attr, "interface", 9) == 0)
1583 + return snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(space->ctl_id));
1584 + if (strncasecmp(attr, "device", 6) == 0) {
1585 + val = snd_ctl_elem_id_get_device(space->ctl_id);
1586 + goto value;
1588 + if (strncasecmp(attr, "subdev", 6) == 0) {
1589 + val = snd_ctl_elem_id_get_subdevice(space->ctl_id);
1590 + goto value;
1592 + if (strncasecmp(attr, "name", 4) == 0)
1593 + return snd_ctl_elem_id_get_name(space->ctl_id);
1594 + if (strncasecmp(attr, "index", 5) == 0) {
1595 + val = snd_ctl_elem_id_get_index(space->ctl_id);
1596 + goto value;
1598 + if (strncasecmp(attr, "type", 4) == 0) {
1599 + if (check_id_changed(space, 1))
1600 + return NULL;
1601 + return snd_ctl_elem_type_name(snd_ctl_elem_info_get_type(space->ctl_info));
1603 + if (strncasecmp(attr, "attr", 4) == 0) {
1604 + if (check_id_changed(space, 1))
1605 + return NULL;
1606 + res[0] = '\0';
1607 + if (snd_ctl_elem_info_is_readable(space->ctl_info))
1608 + strcat(res, "r");
1609 + if (snd_ctl_elem_info_is_writable(space->ctl_info))
1610 + strcat(res, "w");
1611 + if (snd_ctl_elem_info_is_volatile(space->ctl_info))
1612 + strcat(res, "v");
1613 + if (snd_ctl_elem_info_is_inactive(space->ctl_info))
1614 + strcat(res, "i");
1615 + if (snd_ctl_elem_info_is_locked(space->ctl_info))
1616 + strcat(res, "l");
1617 + if (snd_ctl_elem_info_is_tlv_readable(space->ctl_info))
1618 + strcat(res, "R");
1619 + if (snd_ctl_elem_info_is_tlv_writable(space->ctl_info))
1620 + strcat(res, "W");
1621 + if (snd_ctl_elem_info_is_tlv_commandable(space->ctl_info))
1622 + strcat(res, "C");
1623 + if (snd_ctl_elem_info_is_owner(space->ctl_info))
1624 + strcat(res, "o");
1625 + if (snd_ctl_elem_info_is_user(space->ctl_info))
1626 + strcat(res, "u");
1627 + return res;
1629 + if (strncasecmp(attr, "owner", 5) == 0) {
1630 + if (check_id_changed(space, 1))
1631 + return NULL;
1632 + val = snd_ctl_elem_info_get_owner(space->ctl_info);
1633 + goto value;
1635 + if (strncasecmp(attr, "count", 5) == 0) {
1636 + if (check_id_changed(space, 1))
1637 + return NULL;
1638 + val = snd_ctl_elem_info_get_count(space->ctl_info);
1639 + goto value;
1641 + if (strncasecmp(attr, "min", 3) == 0) {
1642 + if (check_id_changed(space, 1))
1643 + return NULL;
1644 + type = snd_ctl_elem_info_get_type(space->ctl_info);
1645 + if (type == SND_CTL_ELEM_TYPE_INTEGER64)
1646 + val = snd_ctl_elem_info_get_min64(space->ctl_info);
1647 + else if (type == SND_CTL_ELEM_TYPE_INTEGER)
1648 + val = snd_ctl_elem_info_get_min(space->ctl_info);
1649 + else
1650 + goto empty;
1651 + goto value;
1653 + if (strncasecmp(attr, "max", 3) == 0) {
1654 + if (check_id_changed(space, 1))
1655 + return NULL;
1656 + type = snd_ctl_elem_info_get_type(space->ctl_info);
1657 + if (type == SND_CTL_ELEM_TYPE_INTEGER64)
1658 + val = snd_ctl_elem_info_get_max64(space->ctl_info);
1659 + else if (type == SND_CTL_ELEM_TYPE_INTEGER)
1660 + val = snd_ctl_elem_info_get_max(space->ctl_info);
1661 + else
1662 + goto empty;
1663 + goto value;
1665 + if (strncasecmp(attr, "step", 3) == 0) {
1666 + if (check_id_changed(space, 1))
1667 + return NULL;
1668 + type = snd_ctl_elem_info_get_type(space->ctl_info);
1669 + if (type == SND_CTL_ELEM_TYPE_INTEGER64)
1670 + val = snd_ctl_elem_info_get_step64(space->ctl_info);
1671 + else if (type == SND_CTL_ELEM_TYPE_INTEGER)
1672 + val = snd_ctl_elem_info_get_step(space->ctl_info);
1673 + else
1674 + goto empty;
1675 + goto value;
1677 + if (strncasecmp(attr, "items", 5) == 0) {
1678 + if (check_id_changed(space, 1))
1679 + return NULL;
1680 + if (snd_ctl_elem_info_get_type(space->ctl_info) == SND_CTL_ELEM_TYPE_ENUMERATED)
1681 + val = snd_ctl_elem_info_get_items(space->ctl_info);
1682 + else {
1683 + empty:
1684 + res[0] = '\0';
1685 + return res;
1687 + goto value;
1689 + if (strncasecmp(attr, "value", 5) == 0) {
1690 + if (check_id_changed(space, 3))
1691 + return NULL;
1692 + return get_ctl_value(space);
1694 + Perror(space, "unknown ctl{} attribute '%s'", attr);
1695 + return NULL;
1696 + value:
1697 + sprintf(res, "%lli", val);
1698 + return res;
1701 +static int elemid_set(struct space *space, const char *attr, const char *value)
1703 + unsigned int val;
1704 + void (*fcn)(snd_ctl_elem_id_t *, unsigned int);
1705 + snd_ctl_elem_iface_t iface;
1706 + int err;
1708 + if (strncasecmp(attr, "numid", 5) == 0) {
1709 + fcn = snd_ctl_elem_id_set_numid;
1710 + goto value;
1712 + if (strncasecmp(attr, "iface", 5) == 0 ||
1713 + strncasecmp(attr, "interface", 9) == 0 ||
1714 + strncasecmp(attr, "reset", 5) == 0 ||
1715 + strncasecmp(attr, "search", 6) == 0) {
1716 + if (strlen(value) == 0 && strncasecmp(attr, "search", 6) == 0) {
1717 + iface = 0;
1718 + goto search;
1720 + for (iface = 0; iface <= SND_CTL_ELEM_IFACE_LAST; iface++) {
1721 + if (strcasecmp(value, snd_ctl_elem_iface_name(iface)) == 0) {
1722 + if (strncasecmp(attr, "reset", 5) == 0)
1723 + snd_ctl_elem_id_clear(space->ctl_id);
1724 + if (strncasecmp(attr, "search", 5) == 0) {
1725 + search:
1726 + snd_ctl_elem_id_clear(space->ctl_id);
1727 + /* -1 means all */
1728 + snd_ctl_elem_id_set_interface(space->ctl_id, -1);
1729 + snd_ctl_elem_id_set_device(space->ctl_id, -1);
1730 + snd_ctl_elem_id_set_subdevice(space->ctl_id, -1);
1731 + snd_ctl_elem_id_set_name(space->ctl_id, "*");
1732 + snd_ctl_elem_id_set_index(space->ctl_id, -1);
1733 + if (strlen(value) == 0)
1734 + return 0;
1736 + snd_ctl_elem_id_set_interface(space->ctl_id, iface);
1737 + space->ctl_id_changed = ~0;
1738 + return 0;
1741 + Perror(space, "unknown control interface name '%s'", value);
1742 + return -EINVAL;
1744 + if (strncasecmp(attr, "device", 6) == 0) {
1745 + fcn = snd_ctl_elem_id_set_device;
1746 + goto value;
1748 + if (strncasecmp(attr, "subdev", 6) == 0) {
1749 + fcn = snd_ctl_elem_id_set_subdevice;
1750 + goto value;
1752 + if (strncasecmp(attr, "name", 4) == 0) {
1753 + snd_ctl_elem_id_set_name(space->ctl_id, value);
1754 + space->ctl_id_changed = ~0;
1755 + return 0;
1757 + if (strncasecmp(attr, "index", 5) == 0) {
1758 + fcn = snd_ctl_elem_id_set_index;
1759 + goto value;
1761 + if (strncasecmp(attr, "value", 5) == 0) {
1762 + err = check_id_changed(space, 1);
1763 + if (err < 0) {
1764 + Perror(space, "control element not found");
1765 + return err;
1767 + err = set_ctl_value(space, value);
1768 + if (err < 0) {
1769 + space->ctl_id_changed |= 2;
1770 + } else {
1771 + space->ctl_id_changed &= ~2;
1772 + snd_ctl_elem_value_set_id(space->ctl_value, space->ctl_id);
1773 + err = snd_ctl_elem_write(snd_hctl_ctl(space->ctl_handle), space->ctl_value);
1774 + if (err < 0) {
1775 + Perror(space, "value write error: %s", snd_strerror(err));
1776 + return err;
1779 + return err;
1781 + Perror(space, "unknown CTL{} attribute '%s'", attr);
1782 + return -EINVAL;
1783 + value:
1784 + val = (unsigned int)strtol(value, NULL, 0);
1785 + fcn(space->ctl_id, val);
1786 + space->ctl_id_changed = ~0;
1787 + return 0;
1790 +static int get_key(char **line, char **key, enum key_op *op, char **value)
1792 + char *linepos;
1793 + char *temp;
1795 + linepos = *line;
1796 + if (linepos == NULL && linepos[0] == '\0')
1797 + return -EINVAL;
1799 + /* skip whitespace */
1800 + while (isspace(linepos[0]) || linepos[0] == ',')
1801 + linepos++;
1803 + /* get the key */
1804 + if (linepos[0] == '\0')
1805 + return -EINVAL;
1806 + *key = linepos;
1808 + while (1) {
1809 + linepos++;
1810 + if (linepos[0] == '\0')
1811 + return -1;
1812 + if (isspace(linepos[0]))
1813 + break;
1814 + if (linepos[0] == '=')
1815 + break;
1816 + if (linepos[0] == '+')
1817 + break;
1818 + if (linepos[0] == '!')
1819 + break;
1820 + if (linepos[0] == ':')
1821 + break;
1824 + /* remember end of key */
1825 + temp = linepos;
1827 + /* skip whitespace after key */
1828 + while (isspace(linepos[0]))
1829 + linepos++;
1830 + if (linepos[0] == '\0')
1831 + return -EINVAL;
1833 + /* get operation type */
1834 + if (linepos[0] == '=' && linepos[1] == '=') {
1835 + *op = KEY_OP_MATCH;
1836 + linepos += 2;
1837 + dbg("operator=match");
1838 + } else if (linepos[0] == '!' && linepos[1] == '=') {
1839 + *op = KEY_OP_NOMATCH;
1840 + linepos += 2;
1841 + dbg("operator=nomatch");
1842 + } else if (linepos[0] == '+' && linepos[1] == '=') {
1843 + *op = KEY_OP_ADD;
1844 + linepos += 2;
1845 + dbg("operator=add");
1846 + } else if (linepos[0] == '=') {
1847 + *op = KEY_OP_ASSIGN;
1848 + linepos++;
1849 + dbg("operator=assign");
1850 + } else if (linepos[0] == ':' && linepos[1] == '=') {
1851 + *op = KEY_OP_ASSIGN_FINAL;
1852 + linepos += 2;
1853 + dbg("operator=assign_final");
1854 + } else
1855 + return -EINVAL;
1857 + /* terminate key */
1858 + temp[0] = '\0';
1859 + dbg("key='%s'", *key);
1861 + /* skip whitespace after operator */
1862 + while (isspace(linepos[0]))
1863 + linepos++;
1864 + if (linepos[0] == '\0')
1865 + return -EINVAL;
1867 + /* get the value*/
1868 + if (linepos[0] != '"')
1869 + return -EINVAL;
1870 + linepos++;
1871 + *value = linepos;
1873 + while (1) {
1874 + temp = strchr(linepos, '"');
1875 + if (temp && temp[-1] == '\\') {
1876 + linepos = temp + 1;
1877 + continue;
1879 + break;
1881 + if (!temp)
1882 + return -EINVAL;
1883 + temp[0] = '\0';
1884 + temp++;
1885 + dbg("value='%s'", *value);
1887 + /* move line to next key */
1888 + *line = temp;
1890 + return 0;
1893 +/* extract possible KEY{attr} */
1894 +static char *get_key_attribute(struct space *space, char *str, char *res, size_t ressize)
1896 + char *pos;
1897 + char *attr;
1899 + attr = strchr(str, '{');
1900 + if (attr != NULL) {
1901 + attr++;
1902 + pos = strchr(attr, '}');
1903 + if (pos == NULL) {
1904 + Perror(space, "missing closing brace for format");
1905 + return NULL;
1907 + pos[0] = '\0';
1908 + strlcpy(res, attr, ressize);
1909 + pos[0] = '}';
1910 + dbg("attribute='%s'", res);
1911 + return res;
1914 + return NULL;
1917 +/* extract possible {attr} and move str behind it */
1918 +static char *get_format_attribute(struct space *space, char **str)
1920 + char *pos;
1921 + char *attr = NULL;
1923 + if (*str[0] == '{') {
1924 + pos = strchr(*str, '}');
1925 + if (pos == NULL) {
1926 + Perror(space, "missing closing brace for format");
1927 + return NULL;
1929 + pos[0] = '\0';
1930 + attr = *str+1;
1931 + *str = pos+1;
1932 + dbg("attribute='%s', str='%s'", attr, *str);
1934 + return attr;
1937 +/* extract possible format length and move str behind it*/
1938 +static int get_format_len(struct space *space, char **str)
1940 + int num;
1941 + char *tail;
1943 + if (isdigit(*str[0])) {
1944 + num = (int) strtoul(*str, &tail, 10);
1945 + if (num > 0) {
1946 + *str = tail;
1947 + dbg("format length=%i", num);
1948 + return num;
1949 + } else {
1950 + Perror(space, "format parsing error '%s'", *str);
1953 + return -1;
1956 +static void apply_format(struct space *space, char *string, size_t maxsize)
1958 + char temp[PATH_SIZE];
1959 + char temp2[PATH_SIZE];
1960 + char *head, *tail, *pos, *cpos, *attr, *rest;
1961 + struct pair *pair;
1962 + int len;
1963 + int i;
1964 + int count;
1965 + enum subst_type {
1966 + SUBST_UNKNOWN,
1967 + SUBST_CARDINFO,
1968 + SUBST_CTL,
1969 + SUBST_RESULT,
1970 + SUBST_ATTR,
1971 + SUBST_SYSFSROOT,
1972 + SUBST_ENV,
1973 + };
1974 + static const struct subst_map {
1975 + char *name;
1976 + char fmt;
1977 + enum subst_type type;
1978 + } map[] = {
1979 + { .name = "cardinfo", .fmt = 'i', .type = SUBST_CARDINFO },
1980 + { .name = "ctl", .fmt = 'C', .type = SUBST_CTL },
1981 + { .name = "result", .fmt = 'c', .type = SUBST_RESULT },
1982 + { .name = "attr", .fmt = 's', .type = SUBST_ATTR },
1983 + { .name = "sysfsroot", .fmt = 'r', .type = SUBST_SYSFSROOT },
1984 + { .name = "env", .fmt = 'E', .type = SUBST_ENV },
1985 + { NULL, '\0', 0 }
1986 + };
1987 + enum subst_type type;
1988 + const struct subst_map *subst;
1990 + head = string;
1991 + while (1) {
1992 + len = -1;
1993 + while (head[0] != '\0') {
1994 + if (head[0] == '$') {
1995 + /* substitute named variable */
1996 + if (head[1] == '\0')
1997 + break;
1998 + if (head[1] == '$') {
1999 + strlcpy(temp, head+2, sizeof(temp));
2000 + strlcpy(head+1, temp, maxsize);
2001 + head++;
2002 + continue;
2004 + head[0] = '\0';
2005 + for (subst = map; subst->name; subst++) {
2006 + if (strncasecmp(&head[1], subst->name, strlen(subst->name)) == 0) {
2007 + type = subst->type;
2008 + tail = head + strlen(subst->name)+1;
2009 + dbg("will substitute format name '%s'", subst->name);
2010 + goto found;
2013 + } else if (head[0] == '%') {
2014 + /* substitute format char */
2015 + if (head[1] == '\0')
2016 + break;
2017 + if (head[1] == '%') {
2018 + strlcpy(temp, head+2, sizeof(temp));
2019 + strlcpy(head+1, temp, maxsize);
2020 + head++;
2021 + continue;
2023 + head[0] = '\0';
2024 + tail = head+1;
2025 + len = get_format_len(space, &tail);
2026 + for (subst = map; subst->name; subst++) {
2027 + if (tail[0] == subst->fmt) {
2028 + type = subst->type;
2029 + tail++;
2030 + dbg("will substitute format char '%c'", subst->fmt);
2031 + goto found;
2035 + head++;
2037 + break;
2038 +found:
2039 + attr = get_format_attribute(space, &tail);
2040 + strlcpy(temp, tail, sizeof(temp));
2041 + dbg("format=%i, string='%s', tail='%s'", type ,string, tail);
2043 + switch (type) {
2044 + case SUBST_CARDINFO:
2045 + if (attr == NULL)
2046 + Perror(space, "missing identification parametr for cardinfo");
2047 + else {
2048 + const char *value = cardinfo_get(space, attr);
2049 + if (value == NULL)
2050 + break;
2051 + strlcat(string, value, maxsize);
2052 + dbg("substitute cardinfo{%s} '%s'", attr, value);
2054 + break;
2055 + case SUBST_CTL:
2056 + if (attr == NULL)
2057 + Perror(space, "missing identification parametr for ctl");
2058 + else {
2059 + const char *value = elemid_get(space, attr);
2060 + if (value == NULL)
2061 + break;
2062 + strlcat(string, value, maxsize);
2063 + dbg("substitute ctl{%s} '%s'", attr, value);
2065 + break;
2066 + case SUBST_RESULT:
2067 + if (space->program_result == NULL)
2068 + break;
2069 + /* get part part of the result string */
2070 + i = 0;
2071 + if (attr != NULL)
2072 + i = strtoul(attr, &rest, 10);
2073 + if (i > 0) {
2074 + dbg("request part #%d of result string", i);
2075 + cpos = space->program_result;
2076 + while (--i) {
2077 + while (cpos[0] != '\0' && !isspace(cpos[0]))
2078 + cpos++;
2079 + while (isspace(cpos[0]))
2080 + cpos++;
2082 + if (i > 0) {
2083 + Perror(space, "requested part of result string not found");
2084 + break;
2086 + strlcpy(temp2, cpos, sizeof(temp2));
2087 + /* %{2+}c copies the whole string from the second part on */
2088 + if (rest[0] != '+') {
2089 + cpos = strchr(temp2, ' ');
2090 + if (cpos)
2091 + cpos[0] = '\0';
2093 + strlcat(string, temp2, maxsize);
2094 + dbg("substitute part of result string '%s'", temp2);
2095 + } else {
2096 + strlcat(string, space->program_result, maxsize);
2097 + dbg("substitute result string '%s'", space->program_result);
2099 + break;
2100 + case SUBST_ATTR:
2101 + if (attr == NULL)
2102 + Perror(space, "missing file parameter for attr");
2103 + else {
2104 + const char *value = NULL;
2105 + size_t size;
2107 + pair = value_find(space, "sysfs_device");
2108 + if (pair == NULL)
2109 + break;
2110 + value = sysfs_attr_get_value(pair->value, attr);
2112 + if (value == NULL)
2113 + break;
2115 + /* strip trailing whitespace and replace untrusted characters of sysfs value */
2116 + size = strlcpy(temp2, value, sizeof(temp2));
2117 + if (size >= sizeof(temp2))
2118 + size = sizeof(temp2)-1;
2119 + while (size > 0 && isspace(temp2[size-1]))
2120 + temp2[--size] = '\0';
2121 + count = replace_untrusted_chars(temp2);
2122 + if (count > 0)
2123 + Perror(space, "%i untrusted character(s) replaced" , count);
2124 + strlcat(string, temp2, maxsize);
2125 + dbg("substitute sysfs value '%s'", temp2);
2127 + break;
2128 + case SUBST_SYSFSROOT:
2129 + strlcat(string, sysfs_path, maxsize);
2130 + dbg("substitute sysfs_path '%s'", sysfs_path);
2131 + break;
2132 + case SUBST_ENV:
2133 + if (attr == NULL) {
2134 + dbg("missing attribute");
2135 + break;
2137 + pos = getenv(attr);
2138 + if (pos == NULL) {
2139 + dbg("env '%s' not available", attr);
2140 + break;
2142 + dbg("substitute env '%s=%s'", attr, pos);
2143 + strlcat(string, pos, maxsize);
2144 + break;
2145 + default:
2146 + Perror(space, "unknown substitution type=%i", type);
2147 + break;
2149 + /* possibly truncate to format-char specified length */
2150 + if (len != -1) {
2151 + head[len] = '\0';
2152 + dbg("truncate to %i chars, subtitution string becomes '%s'", len, head);
2154 + strlcat(string, temp, maxsize);
2156 + /* unescape strings */
2157 + head = tail = string;
2158 + while (*head != '\0') {
2159 + if (*head == '\\') {
2160 + head++;
2161 + if (*head == '\0')
2162 + break;
2163 + switch (*head) {
2164 + case 'a': *tail++ = '\a'; break;
2165 + case 'b': *tail++ = '\b'; break;
2166 + case 'n': *tail++ = '\n'; break;
2167 + case 'r': *tail++ = '\r'; break;
2168 + case 't': *tail++ = '\t'; break;
2169 + case 'v': *tail++ = '\v'; break;
2170 + case '\\': *tail++ = '\\'; break;
2171 + default: *tail++ = *head; break;
2173 + head++;
2174 + continue;
2176 + if (*head)
2177 + *tail++ = *head++;
2179 + *tail = 0;
2182 +static int do_match(const char *key, enum key_op op,
2183 + const char *key_value, const char *value)
2185 + int match;
2187 + if (value == NULL)
2188 + return 0;
2189 + dbg("match %s '%s' <-> '%s'", key, key_value, value);
2190 + match = fnmatch(key_value, value, 0) == 0;
2191 + if (match && op == KEY_OP_MATCH) {
2192 + dbg("%s is true (matching value)", key);
2193 + return 1;
2195 + if (!match && op == KEY_OP_NOMATCH) {
2196 + dbg("%s is true (non-matching value)", key);
2197 + return 1;
2199 + dbg("%s is false", key);
2200 + return 0;
2203 +static int ctl_match(snd_ctl_elem_id_t *pattern, snd_ctl_elem_id_t *id)
2205 + if (snd_ctl_elem_id_get_interface(pattern) != -1 &&
2206 + snd_ctl_elem_id_get_interface(pattern) != snd_ctl_elem_id_get_interface(id))
2207 + return 0;
2208 + if (snd_ctl_elem_id_get_device(pattern) != -1 &&
2209 + snd_ctl_elem_id_get_device(pattern) != snd_ctl_elem_id_get_device(id))
2210 + return 0;
2211 + if (snd_ctl_elem_id_get_subdevice(pattern) != -1 &&
2212 + snd_ctl_elem_id_get_subdevice(pattern) != snd_ctl_elem_id_get_subdevice(id))
2213 + return 0;
2214 + if (snd_ctl_elem_id_get_index(pattern) != -1 &&
2215 + snd_ctl_elem_id_get_index(pattern) != snd_ctl_elem_id_get_index(id))
2216 + return 0;
2217 + if (fnmatch(snd_ctl_elem_id_get_name(pattern), snd_ctl_elem_id_get_name(id), 0) != 0)
2218 + return 0;
2219 + return 1;
2222 +static
2223 +int run_program1(struct space *space,
2224 + const char *command0, char *result,
2225 + size_t ressize, size_t *reslen, int log)
2227 + char *pos = strchr(command0, ' ');
2228 + int cmdlen = pos ? pos - command0 : strlen(command0);
2229 + int err, index;
2230 + snd_hctl_elem_t *elem;
2231 + snd_ctl_elem_id_t *id;
2233 + if (cmdlen == 12 && strncmp(command0, "__ctl_search", 12) == 0) {
2234 + index = 0;
2235 + if (pos)
2236 + index = strtol(pos, NULL, 0);
2237 + err = snd_ctl_elem_id_malloc(&id);
2238 + if (err < 0)
2239 + return EXIT_FAILURE;
2240 + elem = snd_hctl_first_elem(space->ctl_handle);
2241 + while (elem) {
2242 + snd_hctl_elem_get_id(elem, id);
2243 + if (!ctl_match(space->ctl_id, id))
2244 + goto next_search;
2245 + if (index > 0) {
2246 + index--;
2247 + goto next_search;
2249 + strlcpy(result, "0", ressize);
2250 + snd_ctl_elem_id_copy(space->ctl_id, id);
2251 + snd_ctl_elem_id_free(id);
2252 + dbg("__ctl_search found a control");
2253 + return EXIT_SUCCESS;
2254 + next_search:
2255 + elem = snd_hctl_elem_next(elem);
2257 + snd_ctl_elem_id_free(id);
2258 + return EXIT_FAILURE;
2260 + if (cmdlen == 11 && strncmp(command0, "__ctl_count", 11) == 0) {
2261 + index = 0;
2262 + err = snd_ctl_elem_id_malloc(&id);
2263 + if (err < 0)
2264 + return EXIT_FAILURE;
2265 + elem = snd_hctl_first_elem(space->ctl_handle);
2266 + while (elem) {
2267 + snd_hctl_elem_get_id(elem, id);
2268 + if (!ctl_match(space->ctl_id, id))
2269 + goto next_count;
2270 + index++;
2271 + next_count:
2272 + elem = snd_hctl_elem_next(elem);
2274 + snd_ctl_elem_id_free(id);
2275 + if (index > 0) {
2276 + snprintf(result, ressize, "%u", index);
2277 + dbg("__ctl_count found %s controls", result);
2278 + return EXIT_SUCCESS;
2280 + dbg("__ctl_count no match");
2281 + return EXIT_FAILURE;
2283 + if (cmdlen == 11 && strncmp(command0, "__ctl_write", 11) == 0) {
2285 + Perror(space, "unknown buildin command '%s'", command0);
2286 + return EXIT_FAILURE;
2289 +static int parse(struct space *space, const char *filename);
2291 +static char *new_root_dir(const char *filename)
2293 + char *res, *tmp;
2295 + res = strdup(filename);
2296 + if (res) {
2297 + tmp = rindex(res, '/');
2298 + if (tmp)
2299 + *tmp = '\0';
2301 + dbg("new_root_dir '%s' '%s'", filename, res);
2302 + return res;
2305 +static int parse_line(struct space *space, char *line, size_t linesize)
2307 + char *linepos;
2308 + char *key, *value, *attr, *temp;
2309 + struct pair *pair;
2310 + enum key_op op;
2311 + int err = 0, count;
2312 + char string[PATH_SIZE];
2313 + char result[PATH_SIZE];
2315 + linepos = line;
2316 + while (*linepos != '\0') {
2317 + op = KEY_OP_UNSET;
2319 + err = get_key(&linepos, &key, &op, &value);
2320 + if (err < 0)
2321 + break;
2323 + if (strncasecmp(key, "LABEL", 5) == 0) {
2324 + if (op != KEY_OP_ASSIGN) {
2325 + Perror(space, "invalid LABEL operation");
2326 + goto invalid;
2328 + if (space->go_to && strcmp(space->go_to, value) == 0) {
2329 + free(space->go_to);
2330 + space->go_to = NULL;
2332 + continue;
2335 + if (space->go_to) {
2336 + dbg("skip (GOTO '%s')", space->go_to);
2337 + break; /* not for us */
2340 + if (strncasecmp(key, "CTL{", 4) == 0) {
2341 + attr = get_key_attribute(space, key + 3, string, sizeof(string));
2342 + if (attr == NULL) {
2343 + Perror(space, "error parsing CTL attribute");
2344 + goto invalid;
2346 + if (op == KEY_OP_ASSIGN) {
2347 + strlcpy(result, value, sizeof(result));
2348 + apply_format(space, result, sizeof(result));
2349 + dbg("ctl assign: '%s' '%s'", value, attr);
2350 + err = elemid_set(space, attr, result);
2351 + if (space->program_result) {
2352 + free(space->program_result);
2353 + space->program_result = NULL;
2355 + snprintf(string, sizeof(string), "%i", err);
2356 + space->program_result = strdup(string);
2357 + if (err < 0 || space->program_result == NULL) {
2358 + err = 0;
2359 + break;
2361 + } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2362 + dbg("ctl match: '%s' '%s'", value, attr);
2363 + temp = (char *)elemid_get(space, attr);
2364 + if (!do_match(key, op, value, temp))
2365 + break;
2366 + } else {
2367 + Perror(space, "invalid CTL{} operation");
2368 + goto invalid;
2370 + continue;
2372 + if (strcasecmp(key, "RESULT") == 0) {
2373 + if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2374 + if (!do_match(key, op, value, space->program_result))
2375 + break;
2376 + } else if (op == KEY_OP_ASSIGN) {
2377 + if (space->program_result) {
2378 + free(space->program_result);
2379 + space->program_result = NULL;
2381 + strlcpy(string, value, sizeof(string));
2382 + apply_format(space, string, sizeof(string));
2383 + space->program_result = strdup(string);
2384 + if (space->program_result == NULL)
2385 + break;
2386 + } else {
2387 + Perror(space, "invalid RESULT operation");
2388 + goto invalid;
2390 + continue;
2392 + if (strcasecmp(key, "PROGRAM") == 0) {
2393 + if (op == KEY_OP_UNSET)
2394 + continue;
2395 + strlcpy(string, value, sizeof(string));
2396 + apply_format(space, string, sizeof(string));
2397 + if (space->program_result) {
2398 + free(space->program_result);
2399 + space->program_result = NULL;
2401 + if (run_program(space, string, result, sizeof(result), NULL, space->log_run) != 0) {
2402 + dbg("PROGRAM '%s' is false", string);
2403 + if (op != KEY_OP_NOMATCH)
2404 + break;
2405 + } else {
2406 + remove_trailing_chars(result, '\n');
2407 + count = replace_untrusted_chars(result);
2408 + if (count)
2409 + info("%i untrusted character(s) replaced", count);
2410 + dbg("PROGRAM '%s' result is '%s'", string, result);
2411 + space->program_result = strdup(result);
2412 + if (space->program_result == NULL)
2413 + break;
2414 + dbg("PROGRAM returned successful");
2415 + if (op == KEY_OP_NOMATCH)
2416 + break;
2418 + dbg("PROGRAM key is true");
2419 + continue;
2421 + if (strncasecmp(key, "CARDINFO{", 9) == 0) {
2422 + attr = get_key_attribute(space, key + 8, string, sizeof(string));
2423 + if (attr == NULL) {
2424 + Perror(space, "error parsing CARDINFO attribute");
2425 + goto invalid;
2427 + if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2428 + dbg("cardinfo: '%s' '%s'", value, attr);
2429 + temp = (char *)cardinfo_get(space, attr);
2430 + if (!do_match(key, op, value, temp))
2431 + break;
2432 + } else {
2433 + Perror(space, "invalid CARDINFO{} operation");
2434 + goto invalid;
2436 + continue;
2438 + if (strncasecmp(key, "ATTR{", 5) == 0) {
2439 + attr = get_key_attribute(space, key + 4, string, sizeof(string));
2440 + if (attr == NULL) {
2441 + Perror(space, "error parsing ATTR attribute");
2442 + goto invalid;
2444 + if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2445 + pair = value_find(space, "sysfs_device");
2446 + if (pair == NULL)
2447 + break;
2448 + dbg("sysfs_attr: '%s' '%s'", pair->value, attr);
2449 + temp = sysfs_attr_get_value(pair->value, attr);
2450 + if (!do_match(key, op, value, temp))
2451 + break;
2452 + } else {
2453 + Perror(space, "invalid ATTR{} operation");
2454 + goto invalid;
2456 + continue;
2458 + if (strncasecmp(key, "ENV{", 4) == 0) {
2459 + attr = get_key_attribute(space, key + 3, string, sizeof(string));
2460 + if (attr == NULL) {
2461 + Perror(space, "error parsing ENV attribute");
2462 + goto invalid;
2464 + if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2465 + temp = getenv(attr);
2466 + dbg("env: '%s' '%s'", attr, temp);
2467 + if (!do_match(key, op, value, temp))
2468 + break;
2469 + } else {
2470 + Perror(space, "invalid ENV{} operation");
2471 + goto invalid;
2473 + continue;
2475 + if (strcasecmp(key, "GOTO") == 0) {
2476 + if (op != KEY_OP_ASSIGN) {
2477 + Perror(space, "invalid GOTO operation");
2478 + goto invalid;
2480 + space->go_to = strdup(value);
2481 + if (space->go_to == NULL) {
2482 + err = -ENOMEM;
2483 + break;
2485 + continue;
2487 + if (strcasecmp(key, "INCLUDE") == 0) {
2488 + char *rootdir, *go_to;
2489 + const char *filename;
2490 + struct dirent *dirent;
2491 + DIR *dir;
2492 + int linenum;
2493 + if (op != KEY_OP_ASSIGN) {
2494 + Perror(space, "invalid INCLUDE operation");
2495 + goto invalid;
2497 + if (value[0] == '/')
2498 + strlcpy(string, value, sizeof(string));
2499 + else {
2500 + strlcpy(string, space->rootdir, sizeof(string));
2501 + strlcat(string, "/", sizeof(string));
2502 + strlcat(string, value, sizeof(string));
2504 + rootdir = space->rootdir;
2505 + go_to = space->go_to;
2506 + filename = space->filename;
2507 + linenum = space->linenum;
2508 + dir = opendir(string);
2509 + if (dir) {
2510 + count = strlen(string);
2511 + while ((dirent = readdir(dir)) != NULL) {
2512 + if (strcmp(dirent->d_name, ".") == 0 ||
2513 + strcmp(dirent->d_name, "..") == 0)
2514 + continue;
2515 + string[count] = '\0';
2516 + strlcat(string, "/", sizeof(string));
2517 + strlcat(string, dirent->d_name, sizeof(string));
2518 + space->go_to = NULL;
2519 + space->rootdir = new_root_dir(string);
2520 + if (space->rootdir) {
2521 + err = parse(space, string);
2522 + free(space->rootdir);
2523 + } else
2524 + err = -ENOMEM;
2525 + if (space->go_to) {
2526 + Perror(space, "unterminated GOTO '%s'", space->go_to);
2527 + free(space->go_to);
2530 + closedir(dir);
2531 + } else {
2532 + space->go_to = NULL;
2533 + space->rootdir = new_root_dir(string);
2534 + if (space->rootdir) {
2535 + err = parse(space, string);
2536 + free(space->rootdir);
2537 + } else
2538 + err = -ENOMEM;
2539 + if (space->go_to) {
2540 + Perror(space, "unterminated GOTO '%s'", space->go_to);
2541 + free(space->go_to);
2544 + space->go_to = go_to;
2545 + space->rootdir = rootdir;
2546 + space->filename = filename;
2547 + space->linenum = linenum;
2548 + if (space->quit)
2549 + break;
2550 + continue;
2552 + if (strncasecmp(key, "ACCESS", 6) == 0) {
2553 + if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2554 + if (value[0] != '/') {
2555 + strlcpy(string, space->rootdir, sizeof(string));
2556 + strlcat(string, "/", sizeof(string));
2557 + strlcat(string, value, sizeof(string));
2558 + } else {
2559 + strlcat(string, value, sizeof(string));
2561 + count = access(string, F_OK);
2562 + dbg("access(%s) = %i", value, count);
2563 + if (op == KEY_OP_MATCH && count != 0)
2564 + break;
2565 + if (op == KEY_OP_NOMATCH && count == 0)
2566 + break;
2567 + } else {
2568 + Perror(space, "invalid ACCESS operation");
2569 + goto invalid;
2571 + continue;
2573 + if (strncasecmp(key, "PRINT", 5) == 0) {
2574 + if (op != KEY_OP_ASSIGN) {
2575 + Perror(space, "invalid PRINT operation");
2576 + goto invalid;
2578 + strlcpy(string, value, sizeof(string));
2579 + apply_format(space, string, sizeof(string));
2580 + fwrite(string, strlen(string), 1, stdout);
2581 + continue;
2583 + if (strncasecmp(key, "ERROR", 5) == 0) {
2584 + if (op != KEY_OP_ASSIGN) {
2585 + Perror(space, "invalid ERROR operation");
2586 + goto invalid;
2588 + strlcpy(string, value, sizeof(string));
2589 + apply_format(space, string, sizeof(string));
2590 + fwrite(string, strlen(string), 1, stderr);
2591 + continue;
2593 + if (strncasecmp(key, "EXIT", 4) == 0) {
2594 + if (op != KEY_OP_ASSIGN) {
2595 + Perror(space, "invalid EXIT operation");
2596 + goto invalid;
2598 + strlcpy(string, value, sizeof(string));
2599 + apply_format(space, string, sizeof(string));
2600 + if (strcmp(string, "return") == 0)
2601 + return -EJUSTRETURN;
2602 + space->exit_code = strtol(string, NULL, 0);
2603 + space->quit = 1;
2604 + break;
2606 + if (strncasecmp(key, "CONFIG{", 7) == 0) {
2607 + attr = get_key_attribute(space, key + 6, string, sizeof(string));
2608 + if (attr == NULL) {
2609 + Perror(space, "error parsing CONFIG attribute");
2610 + goto invalid;
2612 + strlcpy(result, value, sizeof(result));
2613 + apply_format(space, result, sizeof(result));
2614 + if (op == KEY_OP_ASSIGN) {
2615 + err = value_set(space, attr, result);
2616 + dbg("CONFIG{%s}='%s'", attr, result);
2617 + break;
2618 + } else if (op == KEY_OP_MATCH || op == KEY_OP_NOMATCH) {
2619 + pair = value_find(space, attr);
2620 + if (pair == NULL)
2621 + break;
2622 + if (!do_match(key, op, result, pair->value))
2623 + break;
2624 + } else {
2625 + Perror(space, "invalid CONFIG{} operation");
2626 + goto invalid;
2630 + Perror(space, "unknown key '%s'", key);
2632 + return err;
2634 +invalid:
2635 + Perror(space, "invalid rule");
2636 + return -EINVAL;
2639 +static int parse(struct space *space, const char *filename)
2641 + char *buf, *bufline, *line;
2642 + size_t bufsize, pos, count, linesize;
2643 + unsigned int linenum, i, j, linenum_adj;
2644 + int err;
2646 + dbg("start of file '%s'", filename);
2648 + if (file_map(filename, &buf, &bufsize) != 0) {
2649 + err = errno;
2650 + error("Unable to open file '%s': %s", filename, strerror(err));
2651 + return -err;
2654 + err = 0;
2655 + pos = 0;
2656 + linenum = 0;
2657 + linesize = 128;
2658 + line = malloc(linesize);
2659 + if (line == NULL)
2660 + return -ENOMEM;
2661 + space->filename = filename;
2662 + while (!err && pos < bufsize && !space->quit) {
2663 + count = line_width(buf, bufsize, pos);
2664 + bufline = buf + pos;
2665 + pos += count + 1;
2666 + linenum++;
2668 + /* skip whitespaces */
2669 + while (count > 0 && isspace(bufline[0])) {
2670 + bufline++;
2671 + count--;
2673 + if (count == 0)
2674 + continue;
2676 + /* comment check */
2677 + if (bufline[0] == '#')
2678 + continue;
2680 + if (count > linesize - 1) {
2681 + free(line);
2682 + linesize = (count + 127 + 1) & ~127;
2683 + if (linesize > 2048) {
2684 + error("file %s, line %i too long", filename, linenum);
2685 + err = -EINVAL;
2686 + break;
2688 + line = malloc(linesize);
2689 + if (line == NULL) {
2690 + err = -EINVAL;
2691 + break;
2695 + /* skip backslash and newline from multiline rules */
2696 + linenum_adj = 0;
2697 + for (i = j = 0; i < count; i++) {
2698 + if (bufline[i] == '\\' && bufline[i+1] == '\n') {
2699 + linenum_adj++;
2700 + continue;
2702 + line[j++] = bufline[i];
2704 + line[j] = '\0';
2706 + dbg("read (%i) '%s'", linenum, line);
2707 + space->linenum = linenum;
2708 + err = parse_line(space, line, linesize);
2709 + if (err == -EJUSTRETURN) {
2710 + err = 0;
2711 + break;
2713 + linenum += linenum_adj;
2716 + space->filename = NULL;
2717 + space->linenum = -1;
2718 + file_unmap(buf, bufsize);
2719 + dbg("end of file '%s'", filename);
2720 + return err;
2723 +int init(const char *filename, const char *cardname)
2725 + struct space *space;
2726 + int err = 0, card, first;
2728 + sysfs_init();
2729 + if (!cardname) {
2730 + first = 1;
2731 + card = -1;
2732 + while (1) {
2733 + if (snd_card_next(&card) < 0)
2734 + break;
2735 + if (card < 0) {
2736 + if (first) {
2737 + error("No soundcards found...");
2738 + return -ENODEV;
2740 + break;
2742 + first = 0;
2743 + err = init_space(&space, card);
2744 + if (err == 0 &&
2745 + (space->rootdir = new_root_dir(filename)) != NULL)
2746 + err = parse(space, filename);
2747 + free_space(space);
2748 + if (err < 0)
2749 + break;
2751 + } else {
2752 + card = snd_card_get_index(cardname);
2753 + if (card < 0) {
2754 + error("Cannot find soundcard '%s'...", cardname);
2755 + goto error;
2757 + memset(&space, 0, sizeof(space));
2758 + err = init_space(&space, card);
2759 + if (err == 0 &&
2760 + (space->rootdir = new_root_dir(filename)) != NULL)
2761 + err = parse(space, filename);
2762 + free_space(space);
2764 + error:
2765 + sysfs_cleanup();
2766 + return err;
2768 diff --git a/alsactl/init_sysdeps.c b/alsactl/init_sysdeps.c
2769 new file mode 100644
2770 index 0000000..f263138
2771 --- /dev/null
2772 +++ b/alsactl/init_sysdeps.c
2773 @@ -0,0 +1,63 @@
2775 + * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
2776 + * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
2778 + * This program is free software; you can redistribute it and/or modify it
2779 + * under the terms of the GNU General Public License as published by the
2780 + * Free Software Foundation version 2 of the License.
2781 + *
2782 + * This program is distributed in the hope that it will be useful, but
2783 + * WITHOUT ANY WARRANTY; without even the implied warranty of
2784 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2785 + * General Public License for more details.
2786 + *
2787 + * You should have received a copy of the GNU General Public License along
2788 + * with this program; if not, write to the Free Software Foundation, Inc.,
2789 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2791 + */
2793 +#ifdef __GLIBC__
2794 +static size_t strlcpy(char *dst, const char *src, size_t size)
2796 + size_t bytes = 0;
2797 + char *q = dst;
2798 + const char *p = src;
2799 + char ch;
2801 + while ((ch = *p++)) {
2802 + if (bytes+1 < size)
2803 + *q++ = ch;
2804 + bytes++;
2807 + /* If size == 0 there is no space for a final null... */
2808 + if (size)
2809 + *q = '\0';
2810 + return bytes;
2813 +static size_t strlcat(char *dst, const char *src, size_t size)
2815 + size_t bytes = 0;
2816 + char *q = dst;
2817 + const char *p = src;
2818 + char ch;
2820 + while (bytes < size && *q) {
2821 + q++;
2822 + bytes++;
2824 + if (bytes == size)
2825 + return (bytes + strlen(src));
2827 + while ((ch = *p++)) {
2828 + if (bytes+1 < size)
2829 + *q++ = ch;
2830 + bytes++;
2833 + *q = '\0';
2834 + return bytes;
2836 +#endif /* __GLIBC__ */
2837 diff --git a/alsactl/init_sysfs.c b/alsactl/init_sysfs.c
2838 new file mode 100644
2839 index 0000000..0cbada2
2840 --- /dev/null
2841 +++ b/alsactl/init_sysfs.c
2842 @@ -0,0 +1,158 @@
2844 + * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
2845 + * 2008 Jaroslav Kysela <perex@perex.cz>
2847 + * This program is free software; you can redistribute it and/or modify it
2848 + * under the terms of the GNU General Public License as published by the
2849 + * Free Software Foundation version 2 of the License.
2850 + *
2851 + * This program is distributed in the hope that it will be useful, but
2852 + * WITHOUT ANY WARRANTY; without even the implied warranty of
2853 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2854 + * General Public License for more details.
2855 + *
2856 + * You should have received a copy of the GNU General Public License along
2857 + * with this program; if not, write to the Free Software Foundation, Inc.,
2858 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2860 + */
2863 +static char sysfs_path[PATH_SIZE];
2865 +/* attribute value cache */
2866 +static LIST_HEAD(attr_list);
2867 +struct sysfs_attr {
2868 + struct list_head node;
2869 + char path[PATH_SIZE];
2870 + char *value; /* points to value_local if value is cached */
2871 + char value_local[NAME_SIZE];
2874 +static int sysfs_init(void)
2876 + const char *env;
2877 + char sysfs_test[PATH_SIZE];
2879 + env = getenv("SYSFS_PATH");
2880 + if (env) {
2881 + strlcpy(sysfs_path, env, sizeof(sysfs_path));
2882 + remove_trailing_chars(sysfs_path, '/');
2883 + } else
2884 + strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
2885 + dbg("sysfs_path='%s'", sysfs_path);
2887 + strlcpy(sysfs_test, sysfs_path, sizeof(sysfs_test));
2888 + strlcat(sysfs_test, "/kernel/uevent_helper", sizeof(sysfs_test));
2889 + if (access(sysfs_test, F_OK)) {
2890 + error("sysfs path '%s' is invalid\n", sysfs_path);
2891 + return -errno;
2894 + INIT_LIST_HEAD(&attr_list);
2895 + return 0;
2898 +static void sysfs_cleanup(void)
2900 + struct sysfs_attr *attr_loop;
2901 + struct sysfs_attr *attr_temp;
2903 + list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
2904 + list_del(&attr_loop->node);
2905 + free(attr_loop);
2909 +static char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
2911 + char path_full[PATH_SIZE];
2912 + const char *path;
2913 + char value[NAME_SIZE];
2914 + struct sysfs_attr *attr_loop;
2915 + struct sysfs_attr *attr;
2916 + struct stat statbuf;
2917 + int fd;
2918 + ssize_t size;
2919 + size_t sysfs_len;
2921 + dbg("open '%s'/'%s'", devpath, attr_name);
2922 + sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
2923 + path = &path_full[sysfs_len];
2924 + strlcat(path_full, devpath, sizeof(path_full));
2925 + strlcat(path_full, "/", sizeof(path_full));
2926 + strlcat(path_full, attr_name, sizeof(path_full));
2928 + /* look for attribute in cache */
2929 + list_for_each_entry(attr_loop, &attr_list, node) {
2930 + if (strcmp(attr_loop->path, path) == 0) {
2931 + dbg("found in cache '%s'", attr_loop->path);
2932 + return attr_loop->value;
2936 + /* store attribute in cache (also negatives are kept in cache) */
2937 + dbg("new uncached attribute '%s'", path_full);
2938 + attr = malloc(sizeof(struct sysfs_attr));
2939 + if (attr == NULL)
2940 + return NULL;
2941 + memset(attr, 0x00, sizeof(struct sysfs_attr));
2942 + strlcpy(attr->path, path, sizeof(attr->path));
2943 + dbg("add to cache '%s'", path_full);
2944 + list_add(&attr->node, &attr_list);
2946 + if (lstat(path_full, &statbuf) != 0) {
2947 + dbg("stat '%s' failed: %s", path_full, strerror(errno));
2948 + goto out;
2951 + if (S_ISLNK(statbuf.st_mode)) {
2952 + /* links return the last element of the target path */
2953 + char link_target[PATH_SIZE];
2954 + int len;
2955 + const char *pos;
2957 + len = readlink(path_full, link_target, sizeof(link_target));
2958 + if (len > 0) {
2959 + link_target[len] = '\0';
2960 + pos = strrchr(link_target, '/');
2961 + if (pos != NULL) {
2962 + dbg("cache '%s' with link value '%s'", path_full, pos+1);
2963 + strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local));
2964 + attr->value = attr->value_local;
2967 + goto out;
2970 + /* skip directories */
2971 + if (S_ISDIR(statbuf.st_mode))
2972 + goto out;
2974 + /* skip non-readable files */
2975 + if ((statbuf.st_mode & S_IRUSR) == 0)
2976 + goto out;
2978 + /* read attribute value */
2979 + fd = open(path_full, O_RDONLY);
2980 + if (fd < 0) {
2981 + dbg("attribute '%s' does not exist", path_full);
2982 + goto out;
2984 + size = read(fd, value, sizeof(value));
2985 + close(fd);
2986 + if (size < 0)
2987 + goto out;
2988 + if (size == sizeof(value))
2989 + goto out;
2991 + /* got a valid value, store and return it */
2992 + value[size] = '\0';
2993 + remove_trailing_chars(value, '\n');
2994 + dbg("cache '%s' with attribute value '%s'", path_full, value);
2995 + strlcpy(attr->value_local, value, sizeof(attr->value_local));
2996 + attr->value = attr->value_local;
2998 +out:
2999 + return attr->value;
3001 diff --git a/alsactl/init_utils_run.c b/alsactl/init_utils_run.c
3002 new file mode 100644
3003 index 0000000..dde490b
3004 --- /dev/null
3005 +++ b/alsactl/init_utils_run.c
3006 @@ -0,0 +1,247 @@
3008 + * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
3010 + * This program is free software; you can redistribute it and/or modify it
3011 + * under the terms of the GNU General Public License as published by the
3012 + * Free Software Foundation version 2 of the License.
3013 + *
3014 + * This program is distributed in the hope that it will be useful, but
3015 + * WITHOUT ANY WARRANTY; without even the implied warranty of
3016 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3017 + * General Public License for more details.
3018 + *
3019 + * You should have received a copy of the GNU General Public License along
3020 + * with this program; if not, write to the Free Software Foundation, Inc.,
3021 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
3023 + */
3025 +#define MY_MAX(a,b) ((a) > (b) ? (a) : (b))
3027 +#define READ_END 0
3028 +#define WRITE_END 1
3030 +static
3031 +int run_program1(struct space *space,
3032 + const char *command0, char *result,
3033 + size_t ressize, size_t *reslen, int log);
3035 +static
3036 +int run_program0(struct space *space,
3037 + const char *command0, char *result,
3038 + size_t ressize, size_t *reslen, int log)
3040 + int retval = 0;
3041 + int status;
3042 + int outpipe[2] = {-1, -1};
3043 + int errpipe[2] = {-1, -1};
3044 + pid_t pid;
3045 + char arg[PATH_SIZE];
3046 + char program[PATH_SIZE];
3047 + char *argv[(sizeof(arg) / 2) + 1];
3048 + int devnull;
3049 + int i;
3051 + /* build argv from comand */
3052 + strlcpy(arg, command0, sizeof(arg));
3053 + i = 0;
3054 + if (strchr(arg, ' ') != NULL) {
3055 + char *pos = arg;
3057 + while (pos != NULL) {
3058 + if (pos[0] == '\'') {
3059 + /* don't separate if in apostrophes */
3060 + pos++;
3061 + argv[i] = strsep(&pos, "\'");
3062 + while (pos != NULL && pos[0] == ' ')
3063 + pos++;
3064 + } else {
3065 + argv[i] = strsep(&pos, " ");
3067 + dbg("arg[%i] '%s'", i, argv[i]);
3068 + i++;
3070 + argv[i] = NULL;
3071 + } else {
3072 + argv[0] = arg;
3073 + argv[1] = NULL;
3075 + info("'%s'", command0);
3077 + /* prepare pipes from child to parent */
3078 + if (result || log) {
3079 + if (pipe(outpipe) != 0) {
3080 + Perror(space, "pipe failed: %s", strerror(errno));
3081 + return -1;
3084 + if (log) {
3085 + if (pipe(errpipe) != 0) {
3086 + Perror(space, "pipe failed: %s", strerror(errno));
3087 + return -1;
3091 + /* allow programs in /lib/alsa called without the path */
3092 + if (strchr(argv[0], '/') == NULL) {
3093 + strlcpy(program, "/lib/alsa/", sizeof(program));
3094 + strlcat(program, argv[0], sizeof(program));
3095 + argv[0] = program;
3098 + pid = fork();
3099 + switch(pid) {
3100 + case 0:
3101 + /* child closes parent ends of pipes */
3102 + if (outpipe[READ_END] > 0)
3103 + close(outpipe[READ_END]);
3104 + if (errpipe[READ_END] > 0)
3105 + close(errpipe[READ_END]);
3107 + /* discard child output or connect to pipe */
3108 + devnull = open("/dev/null", O_RDWR);
3109 + if (devnull > 0) {
3110 + dup2(devnull, STDIN_FILENO);
3111 + if (outpipe[WRITE_END] < 0)
3112 + dup2(devnull, STDOUT_FILENO);
3113 + if (errpipe[WRITE_END] < 0)
3114 + dup2(devnull, STDERR_FILENO);
3115 + close(devnull);
3116 + } else
3117 + Perror(space, "open /dev/null failed: %s", strerror(errno));
3118 + if (outpipe[WRITE_END] > 0) {
3119 + dup2(outpipe[WRITE_END], STDOUT_FILENO);
3120 + close(outpipe[WRITE_END]);
3122 + if (errpipe[WRITE_END] > 0) {
3123 + dup2(errpipe[WRITE_END], STDERR_FILENO);
3124 + close(errpipe[WRITE_END]);
3126 + execv(argv[0], argv);
3128 + /* we should never reach this */
3129 + Perror(space, "exec of program '%s' failed", argv[0]);
3130 + _exit(1);
3131 + case -1:
3132 + Perror(space, "fork of '%s' failed: %s", argv[0], strerror(errno));
3133 + return -1;
3134 + default:
3135 + /* read from child if requested */
3136 + if (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
3137 + ssize_t count;
3138 + size_t respos = 0;
3140 + /* parent closes child ends of pipes */
3141 + if (outpipe[WRITE_END] > 0)
3142 + close(outpipe[WRITE_END]);
3143 + if (errpipe[WRITE_END] > 0)
3144 + close(errpipe[WRITE_END]);
3146 + /* read child output */
3147 + while (outpipe[READ_END] > 0 || errpipe[READ_END] > 0) {
3148 + int fdcount;
3149 + fd_set readfds;
3151 + FD_ZERO(&readfds);
3152 + if (outpipe[READ_END] > 0)
3153 + FD_SET(outpipe[READ_END], &readfds);
3154 + if (errpipe[READ_END] > 0)
3155 + FD_SET(errpipe[READ_END], &readfds);
3156 + fdcount = select(MY_MAX(outpipe[READ_END], errpipe[READ_END])+1, &readfds, NULL, NULL, NULL);
3157 + if (fdcount < 0) {
3158 + if (errno == EINTR)
3159 + continue;
3160 + retval = -1;
3161 + break;
3164 + /* get stdout */
3165 + if (outpipe[READ_END] > 0 && FD_ISSET(outpipe[READ_END], &readfds)) {
3166 + char inbuf[1024];
3167 + char *pos;
3168 + char *line;
3170 + count = read(outpipe[READ_END], inbuf, sizeof(inbuf)-1);
3171 + if (count <= 0) {
3172 + close(outpipe[READ_END]);
3173 + outpipe[READ_END] = -1;
3174 + if (count < 0) {
3175 + Perror(space, "stdin read failed: %s", strerror(errno));
3176 + retval = -1;
3178 + continue;
3180 + inbuf[count] = '\0';
3182 + /* store result for rule processing */
3183 + if (result) {
3184 + if (respos + count < ressize) {
3185 + memcpy(&result[respos], inbuf, count);
3186 + respos += count;
3187 + } else {
3188 + Perror(space, "ressize %ld too short", (long)ressize);
3189 + retval = -1;
3192 + pos = inbuf;
3193 + while ((line = strsep(&pos, "\n")))
3194 + if (pos || line[0] != '\0')
3195 + info("'%s' (stdout) '%s'", argv[0], line);
3198 + /* get stderr */
3199 + if (errpipe[READ_END] > 0 && FD_ISSET(errpipe[READ_END], &readfds)) {
3200 + char errbuf[1024];
3201 + char *pos;
3202 + char *line;
3204 + count = read(errpipe[READ_END], errbuf, sizeof(errbuf)-1);
3205 + if (count <= 0) {
3206 + close(errpipe[READ_END]);
3207 + errpipe[READ_END] = -1;
3208 + if (count < 0)
3209 + Perror(space, "stderr read failed: %s", strerror(errno));
3210 + continue;
3212 + errbuf[count] = '\0';
3213 + pos = errbuf;
3214 + while ((line = strsep(&pos, "\n")))
3215 + if (pos || line[0] != '\0')
3216 + info("'%s' (stderr) '%s'", argv[0], line);
3219 + if (outpipe[READ_END] > 0)
3220 + close(outpipe[READ_END]);
3221 + if (errpipe[READ_END] > 0)
3222 + close(errpipe[READ_END]);
3224 + /* return the childs stdout string */
3225 + if (result) {
3226 + result[respos] = '\0';
3227 + dbg("result='%s'", result);
3228 + if (reslen)
3229 + *reslen = respos;
3232 + waitpid(pid, &status, 0);
3233 + if (WIFEXITED(status)) {
3234 + info("'%s' returned with status %i", argv[0], WEXITSTATUS(status));
3235 + if (WEXITSTATUS(status) != 0)
3236 + retval = -1;
3237 + } else {
3238 + Perror(space, "'%s' abnormal exit", argv[0]);
3239 + retval = -1;
3243 + return retval;
3246 +static
3247 +int run_program(struct space *space, const char *command0, char *result,
3248 + size_t ressize, size_t *reslen, int log)
3250 + if (command0[0] == '_' && command0[1] == '_')
3251 + return run_program1(space, command0, result, ressize, reslen, log);
3252 + return run_program0(space, command0, result, ressize, reslen, log);
3254 diff --git a/alsactl/init_utils_string.c b/alsactl/init_utils_string.c
3255 new file mode 100644
3256 index 0000000..2598e9f
3257 --- /dev/null
3258 +++ b/alsactl/init_utils_string.c
3259 @@ -0,0 +1,194 @@
3261 + * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org>
3263 + * This program is free software; you can redistribute it and/or modify it
3264 + * under the terms of the GNU General Public License as published by the
3265 + * Free Software Foundation version 2 of the License.
3266 + *
3267 + * This program is distributed in the hope that it will be useful, but
3268 + * WITHOUT ANY WARRANTY; without even the implied warranty of
3269 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3270 + * General Public License for more details.
3271 + *
3272 + * You should have received a copy of the GNU General Public License along
3273 + * with this program; if not, write to the Free Software Foundation, Inc.,
3274 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
3276 + */
3279 +static int string_is_true(const char *str)
3281 + if (strcasecmp(str, "true") == 0)
3282 + return 1;
3283 + if (strcasecmp(str, "yes") == 0)
3284 + return 1;
3285 + if (strcasecmp(str, "1") == 0)
3286 + return 1;
3287 + return 0;
3290 +static void remove_trailing_chars(char *path, char c)
3292 + size_t len;
3294 + len = strlen(path);
3295 + while (len > 0 && path[len-1] == c)
3296 + path[--len] = '\0';
3299 +/* count of characters used to encode one unicode char */
3300 +static int utf8_encoded_expected_len(const char *str)
3302 + unsigned char c = (unsigned char)str[0];
3304 + if (c < 0x80)
3305 + return 1;
3306 + if ((c & 0xe0) == 0xc0)
3307 + return 2;
3308 + if ((c & 0xf0) == 0xe0)
3309 + return 3;
3310 + if ((c & 0xf8) == 0xf0)
3311 + return 4;
3312 + if ((c & 0xfc) == 0xf8)
3313 + return 5;
3314 + if ((c & 0xfe) == 0xfc)
3315 + return 6;
3316 + return 0;
3319 +/* decode one unicode char */
3320 +static int utf8_encoded_to_unichar(const char *str)
3322 + int unichar;
3323 + int len;
3324 + int i;
3326 + len = utf8_encoded_expected_len(str);
3327 + switch (len) {
3328 + case 1:
3329 + return (int)str[0];
3330 + case 2:
3331 + unichar = str[0] & 0x1f;
3332 + break;
3333 + case 3:
3334 + unichar = (int)str[0] & 0x0f;
3335 + break;
3336 + case 4:
3337 + unichar = (int)str[0] & 0x07;
3338 + break;
3339 + case 5:
3340 + unichar = (int)str[0] & 0x03;
3341 + break;
3342 + case 6:
3343 + unichar = (int)str[0] & 0x01;
3344 + break;
3345 + default:
3346 + return -1;
3349 + for (i = 1; i < len; i++) {
3350 + if (((int)str[i] & 0xc0) != 0x80)
3351 + return -1;
3352 + unichar <<= 6;
3353 + unichar |= (int)str[i] & 0x3f;
3356 + return unichar;
3359 +/* expected size used to encode one unicode char */
3360 +static int utf8_unichar_to_encoded_len(int unichar)
3362 + if (unichar < 0x80)
3363 + return 1;
3364 + if (unichar < 0x800)
3365 + return 2;
3366 + if (unichar < 0x10000)
3367 + return 3;
3368 + if (unichar < 0x200000)
3369 + return 4;
3370 + if (unichar < 0x4000000)
3371 + return 5;
3372 + return 6;
3375 +/* check if unicode char has a valid numeric range */
3376 +static int utf8_unichar_valid_range(int unichar)
3378 + if (unichar > 0x10ffff)
3379 + return 0;
3380 + if ((unichar & 0xfffff800) == 0xd800)
3381 + return 0;
3382 + if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
3383 + return 0;
3384 + if ((unichar & 0xffff) == 0xffff)
3385 + return 0;
3386 + return 1;
3389 +/* validate one encoded unicode char and return its length */
3390 +static int utf8_encoded_valid_unichar(const char *str)
3392 + int len;
3393 + int unichar;
3394 + int i;
3396 + len = utf8_encoded_expected_len(str);
3397 + if (len == 0)
3398 + return -1;
3400 + /* ascii is valid */
3401 + if (len == 1)
3402 + return 1;
3404 + /* check if expected encoded chars are available */
3405 + for (i = 0; i < len; i++)
3406 + if ((str[i] & 0x80) != 0x80)
3407 + return -1;
3409 + unichar = utf8_encoded_to_unichar(str);
3411 + /* check if encoded length matches encoded value */
3412 + if (utf8_unichar_to_encoded_len(unichar) != len)
3413 + return -1;
3415 + /* check if value has valid range */
3416 + if (!utf8_unichar_valid_range(unichar))
3417 + return -1;
3419 + return len;
3422 +/* replace everything but whitelisted plain ascii and valid utf8 */
3423 +static int replace_untrusted_chars(char *str)
3425 + size_t i = 0;
3426 + int replaced = 0;
3428 + while (str[i] != '\0') {
3429 + int len;
3431 + /* valid printable ascii char */
3432 + if ((str[i] >= '0' && str[i] <= '9') ||
3433 + (str[i] >= 'A' && str[i] <= 'Z') ||
3434 + (str[i] >= 'a' && str[i] <= 'z') ||
3435 + strchr(" #$%+-./:=?@_,", str[i])) {
3436 + i++;
3437 + continue;
3439 + /* valid utf8 is accepted */
3440 + len = utf8_encoded_valid_unichar(&str[i]);
3441 + if (len > 1) {
3442 + i += len;
3443 + continue;
3446 + /* everything else is garbage */
3447 + str[i] = '_';
3448 + i++;
3449 + replaced++;
3452 + return replaced;
3454 diff --git a/alsactl/list.h b/alsactl/list.h
3455 new file mode 100644
3456 index 0000000..8626630
3457 --- /dev/null
3458 +++ b/alsactl/list.h
3459 @@ -0,0 +1,289 @@
3461 + * Copied from the Linux kernel source tree, version 2.6.0-test1.
3463 + * Licensed under the GPL v2 as per the whole kernel source tree.
3465 + */
3467 +#ifndef _LIST_H
3468 +#define _LIST_H
3470 +/**
3471 + * container_of - cast a member of a structure out to the containing structure
3473 + * @ptr: the pointer to the member.
3474 + * @type: the type of the container struct this is embedded in.
3475 + * @member: the name of the member within the struct.
3477 + */
3478 +#define container_of(ptr, type, member) ({ \
3479 + const typeof( ((type *)0)->member ) *__mptr = (ptr); \
3480 + (type *)( (char *)__mptr - offsetof(type,member) );})
3483 + * These are non-NULL pointers that will result in page faults
3484 + * under normal circumstances, used to verify that nobody uses
3485 + * non-initialized list entries.
3486 + */
3487 +#define LIST_POISON1 ((void *) 0x00100100)
3488 +#define LIST_POISON2 ((void *) 0x00200200)
3491 + * Simple doubly linked list implementation.
3493 + * Some of the internal functions ("__xxx") are useful when
3494 + * manipulating whole lists rather than single entries, as
3495 + * sometimes we already know the next/prev entries and we can
3496 + * generate better code by using them directly rather than
3497 + * using the generic single-entry routines.
3498 + */
3500 +struct list_head {
3501 + struct list_head *next, *prev;
3504 +#define LIST_HEAD_INIT(name) { &(name), &(name) }
3506 +#define LIST_HEAD(name) \
3507 + struct list_head name = LIST_HEAD_INIT(name)
3509 +#define INIT_LIST_HEAD(ptr) do { \
3510 + (ptr)->next = (ptr); (ptr)->prev = (ptr); \
3511 +} while (0)
3514 + * Insert a new entry between two known consecutive entries.
3516 + * This is only for internal list manipulation where we know
3517 + * the prev/next entries already!
3518 + */
3519 +static inline void __list_add(struct list_head *new,
3520 + struct list_head *prev,
3521 + struct list_head *next)
3523 + next->prev = new;
3524 + new->next = next;
3525 + new->prev = prev;
3526 + prev->next = new;
3529 +/**
3530 + * list_add - add a new entry
3531 + * @new: new entry to be added
3532 + * @head: list head to add it after
3534 + * Insert a new entry after the specified head.
3535 + * This is good for implementing stacks.
3536 + */
3537 +static inline void list_add(struct list_head *new, struct list_head *head)
3539 + __list_add(new, head, head->next);
3542 +/**
3543 + * list_add_tail - add a new entry
3544 + * @new: new entry to be added
3545 + * @head: list head to add it before
3547 + * Insert a new entry before the specified head.
3548 + * This is useful for implementing queues.
3549 + */
3550 +static inline void list_add_tail(struct list_head *new, struct list_head *head)
3552 + __list_add(new, head->prev, head);
3556 + * Delete a list entry by making the prev/next entries
3557 + * point to each other.
3559 + * This is only for internal list manipulation where we know
3560 + * the prev/next entries already!
3561 + */
3562 +static inline void __list_del(struct list_head * prev, struct list_head * next)
3564 + next->prev = prev;
3565 + prev->next = next;
3568 +/**
3569 + * list_del - deletes entry from list.
3570 + * @entry: the element to delete from the list.
3571 + * Note: list_empty on entry does not return true after this, the entry is
3572 + * in an undefined state.
3573 + */
3574 +static inline void list_del(struct list_head *entry)
3576 + __list_del(entry->prev, entry->next);
3577 + entry->next = LIST_POISON1;
3578 + entry->prev = LIST_POISON2;
3581 +/**
3582 + * list_del_init - deletes entry from list and reinitialize it.
3583 + * @entry: the element to delete from the list.
3584 + */
3585 +static inline void list_del_init(struct list_head *entry)
3587 + __list_del(entry->prev, entry->next);
3588 + INIT_LIST_HEAD(entry);
3591 +/**
3592 + * list_move - delete from one list and add as another's head
3593 + * @list: the entry to move
3594 + * @head: the head that will precede our entry
3595 + */
3596 +static inline void list_move(struct list_head *list, struct list_head *head)
3598 + __list_del(list->prev, list->next);
3599 + list_add(list, head);
3602 +/**
3603 + * list_move_tail - delete from one list and add as another's tail
3604 + * @list: the entry to move
3605 + * @head: the head that will follow our entry
3606 + */
3607 +static inline void list_move_tail(struct list_head *list,
3608 + struct list_head *head)
3610 + __list_del(list->prev, list->next);
3611 + list_add_tail(list, head);
3614 +/**
3615 + * list_empty - tests whether a list is empty
3616 + * @head: the list to test.
3617 + */
3618 +static inline int list_empty(struct list_head *head)
3620 + return head->next == head;
3623 +static inline void __list_splice(struct list_head *list,
3624 + struct list_head *head)
3626 + struct list_head *first = list->next;
3627 + struct list_head *last = list->prev;
3628 + struct list_head *at = head->next;
3630 + first->prev = head;
3631 + head->next = first;
3633 + last->next = at;
3634 + at->prev = last;
3637 +/**
3638 + * list_splice - join two lists
3639 + * @list: the new list to add.
3640 + * @head: the place to add it in the first list.
3641 + */
3642 +static inline void list_splice(struct list_head *list, struct list_head *head)
3644 + if (!list_empty(list))
3645 + __list_splice(list, head);
3648 +/**
3649 + * list_splice_init - join two lists and reinitialise the emptied list.
3650 + * @list: the new list to add.
3651 + * @head: the place to add it in the first list.
3653 + * The list at @list is reinitialised
3654 + */
3655 +static inline void list_splice_init(struct list_head *list,
3656 + struct list_head *head)
3658 + if (!list_empty(list)) {
3659 + __list_splice(list, head);
3660 + INIT_LIST_HEAD(list);
3664 +/**
3665 + * list_entry - get the struct for this entry
3666 + * @ptr: the &struct list_head pointer.
3667 + * @type: the type of the struct this is embedded in.
3668 + * @member: the name of the list_struct within the struct.
3669 + */
3670 +#define list_entry(ptr, type, member) \
3671 + container_of(ptr, type, member)
3673 +/**
3674 + * list_for_each - iterate over a list
3675 + * @pos: the &struct list_head to use as a loop counter.
3676 + * @head: the head for your list.
3677 + */
3678 +#define list_for_each(pos, head) \
3679 + for (pos = (head)->next; pos != (head); \
3680 + pos = pos->next)
3682 +/**
3683 + * __list_for_each - iterate over a list
3684 + * @pos: the &struct list_head to use as a loop counter.
3685 + * @head: the head for your list.
3687 + * This variant differs from list_for_each() in that it's the
3688 + * simplest possible list iteration code.
3689 + * Use this for code that knows the list to be very short (empty
3690 + * or 1 entry) most of the time.
3691 + */
3692 +#define __list_for_each(pos, head) \
3693 + for (pos = (head)->next; pos != (head); pos = pos->next)
3695 +/**
3696 + * list_for_each_prev - iterate over a list backwards
3697 + * @pos: the &struct list_head to use as a loop counter.
3698 + * @head: the head for your list.
3699 + */
3700 +#define list_for_each_prev(pos, head) \
3701 + for (pos = (head)->prev; pos != (head); pos = pos->prev)
3703 +/**
3704 + * list_for_each_safe - iterate over a list safe against removal of list entry
3705 + * @pos: the &struct list_head to use as a loop counter.
3706 + * @n: another &struct list_head to use as temporary storage
3707 + * @head: the head for your list.
3708 + */
3709 +#define list_for_each_safe(pos, n, head) \
3710 + for (pos = (head)->next, n = pos->next; pos != (head); \
3711 + pos = n, n = pos->next)
3713 +/**
3714 + * list_for_each_entry - iterate over list of given type
3715 + * @pos: the type * to use as a loop counter.
3716 + * @head: the head for your list.
3717 + * @member: the name of the list_struct within the struct.
3718 + */
3719 +#define list_for_each_entry(pos, head, member) \
3720 + for (pos = list_entry((head)->next, typeof(*pos), member); \
3721 + &pos->member != (head); \
3722 + pos = list_entry(pos->member.next, typeof(*pos), member))
3724 +/**
3725 + * list_for_each_entry_reverse - iterate backwards over list of given type.
3726 + * @pos: the type * to use as a loop counter.
3727 + * @head: the head for your list.
3728 + * @member: the name of the list_struct within the struct.
3729 + */
3730 +#define list_for_each_entry_reverse(pos, head, member) \
3731 + for (pos = list_entry((head)->prev, typeof(*pos), member); \
3732 + &pos->member != (head); \
3733 + pos = list_entry(pos->member.prev, typeof(*pos), member))
3735 +/**
3736 + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
3737 + * @pos: the type * to use as a loop counter.
3738 + * @n: another type * to use as temporary storage
3739 + * @head: the head for your list.
3740 + * @member: the name of the list_struct within the struct.
3741 + */
3742 +#define list_for_each_entry_safe(pos, n, head, member) \
3743 + for (pos = list_entry((head)->next, typeof(*pos), member), \
3744 + n = list_entry(pos->member.next, typeof(*pos), member); \
3745 + &pos->member != (head); \
3746 + pos = n, n = list_entry(n->member.next, typeof(*n), member))
3748 +#endif /* _LIST_H */
3749 diff --git a/alsactl/state.c b/alsactl/state.c
3750 index 6c7f853..90e58cd 100644
3751 --- a/alsactl/state.c
3752 +++ b/alsactl/state.c
3753 @@ -151,19 +151,6 @@ static char *tlv_to_str(unsigned int *tlv)
3754 return s;
3757 -static int hextodigit(int c)
3759 - if (c >= '0' && c <= '9')
3760 - c -= '0';
3761 - else if (c >= 'a' && c <= 'f')
3762 - c = c - 'a' + 10;
3763 - else if (c >= 'A' && c <= 'F')
3764 - c = c - 'A' + 10;
3765 - else
3766 - return -1;
3767 - return c;
3770 static unsigned int *str_to_tlv(const char *s)
3772 int i, j, c, len;
3773 diff --git a/alsactl/utils.c b/alsactl/utils.c
3774 new file mode 100644
3775 index 0000000..eb22ecc
3776 --- /dev/null
3777 +++ b/alsactl/utils.c
3778 @@ -0,0 +1,79 @@
3780 + * Advanced Linux Sound Architecture Control Program - Support routines
3781 + * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
3784 + * This program is free software; you can redistribute it and/or modify
3785 + * it under the terms of the GNU General Public License as published by
3786 + * the Free Software Foundation; either version 2 of the License, or
3787 + * (at your option) any later version.
3789 + * This program is distributed in the hope that it will be useful,
3790 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3791 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3792 + * GNU General Public License for more details.
3794 + * You should have received a copy of the GNU General Public License
3795 + * along with this program; if not, write to the Free Software
3796 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3798 + */
3800 +#include <stdlib.h>
3801 +#include <stdio.h>
3802 +#include <stddef.h>
3803 +#include <unistd.h>
3804 +#include <fcntl.h>
3805 +#include <errno.h>
3806 +#include <ctype.h>
3807 +#include <dirent.h>
3808 +#include <sys/stat.h>
3809 +#include <sys/mman.h>
3811 +#include "alsactl.h"
3813 +int file_map(const char *filename, char **buf, size_t *bufsize)
3815 + struct stat stats;
3816 + int fd;
3818 + fd = open(filename, O_RDONLY);
3819 + if (fd < 0) {
3820 + return -1;
3823 + if (fstat(fd, &stats) < 0) {
3824 + close(fd);
3825 + return -1;
3828 + *buf = mmap(NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
3829 + if (*buf == MAP_FAILED) {
3830 + close(fd);
3831 + return -1;
3833 + *bufsize = stats.st_size;
3835 + close(fd);
3837 + return 0;
3840 +void file_unmap(void *buf, size_t bufsize)
3842 + munmap(buf, bufsize);
3845 +size_t line_width(const char *buf, size_t bufsize, size_t pos)
3847 + int esc = 0;
3848 + size_t count;
3850 + for (count = pos; count < bufsize; count++) {
3851 + if (!esc && buf[count] == '\n')
3852 + break;
3853 + esc = buf[count] == '\\';
3856 + return count - pos;
3858 diff --git a/configure.in b/configure.in
3859 index 2484971..d478224 100644
3860 --- a/configure.in
3861 +++ b/configure.in
3862 @@ -93,8 +93,11 @@ case "$dir" in
3863 *) dir="$prefix/share"
3864 esac
3866 -dir="$dir/sounds/alsa"
3867 -AC_DEFINE_UNQUOTED(DATADIR, "$dir", [directory containing sample data])
3868 +soundsdir="$dir/sounds/alsa"
3869 +AC_DEFINE_UNQUOTED(SOUNDSDIR, "$soundsdir", [directory containing sample data])
3871 +mydatadir="$dir/alsa"
3872 +AC_DEFINE_UNQUOTED(DATADIR, "$mydatadir", [directory containing alsa configuration])
3874 AC_ARG_WITH(testsound,
3875 [ --with-testsound=file give the path of test sound file for alsaconf],
3876 @@ -117,7 +120,8 @@ AC_SYS_LARGEFILE
3878 SAVE_UTIL_VERSION
3880 -AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amidi/Makefile amixer/Makefile \
3881 +AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
3882 + alsamixer/Makefile amidi/Makefile amixer/Makefile \
3883 m4/Makefile po/Makefile.in \
3884 alsaconf/alsaconf alsaconf/Makefile \
3885 alsaconf/po/Makefile \
3886 diff --git a/iecset/iecbits.c b/iecset/iecbits.c
3887 index 8c62749..84d439b 100644
3888 --- a/iecset/iecbits.c
3889 +++ b/iecset/iecbits.c
3890 @@ -26,17 +26,39 @@ struct category_str {
3893 static struct category_str con_category[] = {
3894 + { IEC958_AES1_CON_GENERAL, "general" },
3896 + { IEC958_AES1_CON_IEC908_CD, "CD" },
3897 + { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
3898 + { IEC958_AES1_CON_MINI_DISC, "Mini-Disc" },
3899 + { IEC958_AES1_CON_DVD, "DVD" },
3901 + { IEC958_AES1_CON_PCM_CODER, "PCM coder" },
3902 + { IEC958_AES1_CON_MIXER, "digital signal mixer" },
3903 + { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
3904 + { IEC958_AES1_CON_SAMPLER, "sampler" },
3905 + { IEC958_AES1_CON_DSP, "digital sound processor" },
3907 { IEC958_AES1_CON_DAT, "DAT" },
3908 { IEC958_AES1_CON_VCR, "VCR" },
3909 - { IEC958_AES1_CON_MICROPHONE, "microphone" },
3910 + { IEC958_AES1_CON_DCC, "DCC" },
3911 + { IEC958_AES1_CON_MAGNETIC_DISC, "magnetic disc" },
3913 + { IEC958_AES1_CON_DAB_JAPAN, "digital audio broadcast (Japan)" },
3914 + { IEC958_AES1_CON_DAB_EUROPE, "digital audio broadcast (Europe)" },
3915 + { IEC958_AES1_CON_DAB_USA, "digital audio broadcast (USA)" },
3916 + { IEC958_AES1_CON_SOFTWARE, "software delivery" },
3918 { IEC958_AES1_CON_SYNTHESIZER, "synthesizer" },
3919 - { IEC958_AES1_CON_RATE_CONVERTER, "rate converter" },
3920 - { IEC958_AES1_CON_MIXER, "mixer" },
3921 - { IEC958_AES1_CON_SAMPLER, "sampler" },
3922 - { IEC958_AES1_CON_PCM_CODER, "PCM coder" },
3923 - { IEC958_AES1_CON_IEC908_CD, "CD" },
3924 - { IEC958_AES1_CON_NON_IEC908_CD, "non-IEC908 CD" },
3925 - { IEC958_AES1_CON_GENERAL, "general" },
3926 + { IEC958_AES1_CON_MICROPHONE, "microphone" },
3928 + { IEC958_AES1_CON_ADC, "ADC without copyright information" },
3930 + { IEC958_AES1_CON_ADC_COPYRIGHT, "ADC with copyright information" },
3932 + { IEC958_AES1_CON_SOLIDMEM_DIGITAL_RECORDER_PLAYER, "flash memory recorder/player" },
3934 + { IEC958_AES1_CON_EXPERIMENTAL, "experimental" },
3938 @@ -57,14 +79,38 @@ void dump_iec958(snd_aes_iec958_t *iec)
3940 printf("Rate: ");
3941 switch (iec->status[3] & IEC958_AES3_CON_FS) {
3942 + case IEC958_AES3_CON_FS_22050:
3943 + printf("22050 Hz\n");
3944 + break;
3945 + case IEC958_AES3_CON_FS_24000:
3946 + printf("24000 Hz\n");
3947 + break;
3948 + case IEC958_AES3_CON_FS_32000:
3949 + printf("32000 Hz\n");
3950 + break;
3951 case IEC958_AES3_CON_FS_44100:
3952 printf("44100 Hz\n");
3953 break;
3954 case IEC958_AES3_CON_FS_48000:
3955 printf("48000 Hz\n");
3956 break;
3957 - case IEC958_AES3_CON_FS_32000:
3958 - printf("32000 Hz\n");
3959 + case IEC958_AES3_CON_FS_88200:
3960 + printf("88200 Hz\n");
3961 + break;
3962 + case IEC958_AES3_CON_FS_96000:
3963 + printf("96000 Hz\n");
3964 + break;
3965 + case IEC958_AES3_CON_FS_176400:
3966 + printf("176400 Hz\n");
3967 + break;
3968 + case IEC958_AES3_CON_FS_192000:
3969 + printf("192000 Hz\n");
3970 + break;
3971 + case IEC958_AES3_CON_FS_768000:
3972 + printf("768000 Hz\n");
3973 + break;
3974 + case IEC958_AES3_CON_FS_NOTID:
3975 + printf("not indicated\n");
3976 break;
3977 default:
3978 printf("unknown\n");
3979 diff --git a/iecset/iecset.c b/iecset/iecset.c
3980 index a2fe4d1..44c43ab 100644
3981 --- a/iecset/iecset.c
3982 +++ b/iecset/iecset.c
3983 @@ -58,7 +58,7 @@ static struct cmdtbl cmds[] = {
3984 { "aud", IDX_NOAUDIO, CMD_BOOL_INV,
3985 "audio (common)\n\ton = audio mode, off = non-audio mode" },
3986 { "rat", IDX_RATE, CMD_INT,
3987 - "rate (common)\n\tsample rate in Hz" },
3988 + "rate (common)\n\tsample rate in Hz (0 = not indicated)" },
3989 { "emp", IDX_EMP, CMD_INT,
3990 "emphasis (common)\n\t0 = none, 1 = 50/15us, 2 = CCITT" },
3991 { "loc", IDX_UNLOCK, CMD_BOOL_INV,
3992 @@ -194,14 +194,38 @@ static int update_iec958_status(snd_aes_iec958_t *iec958, int *parms)
3993 } else {
3994 iec958->status[3] &= ~IEC958_AES3_CON_FS;
3995 switch (parms[IDX_RATE]) {
3996 + case 22050:
3997 + iec958->status[3] |= IEC958_AES3_CON_FS_22050;
3998 + break;
3999 + case 24000:
4000 + iec958->status[3] |= IEC958_AES3_CON_FS_24000;
4001 + break;
4002 + case 32000:
4003 + iec958->status[3] |= IEC958_AES3_CON_FS_32000;
4004 + break;
4005 case 44100:
4006 iec958->status[3] |= IEC958_AES3_CON_FS_44100;
4007 break;
4008 case 48000:
4009 iec958->status[3] |= IEC958_AES3_CON_FS_48000;
4010 break;
4011 - case 32000:
4012 - iec958->status[3] |= IEC958_AES3_CON_FS_32000;
4013 + case 88200:
4014 + iec958->status[3] |= IEC958_AES3_CON_FS_88200;;
4015 + break;
4016 + case 96000:
4017 + iec958->status[3] |= IEC958_AES3_CON_FS_96000;
4018 + break;
4019 + case 176400:
4020 + iec958->status[3] |= IEC958_AES3_CON_FS_176400;
4021 + break;
4022 + case 192000:
4023 + iec958->status[3] |= IEC958_AES3_CON_FS_192000;
4024 + break;
4025 + case 768000:
4026 + iec958->status[3] |= IEC958_AES3_CON_FS_768000;
4027 + break;
4028 + default:
4029 + iec958->status[3] |= IEC958_AES3_CON_FS_NOTID;
4030 break;
4033 diff --git a/speaker-test/speaker-test.c b/speaker-test/speaker-test.c
4034 index 22d13bf..57a7cbc 100644
4035 --- a/speaker-test/speaker-test.c
4036 +++ b/speaker-test/speaker-test.c
4037 @@ -81,7 +81,7 @@ static pink_noise_t pink;
4038 static snd_pcm_uframes_t buffer_size;
4039 static snd_pcm_uframes_t period_size;
4040 static const char *given_test_wav_file = NULL;
4041 -static char *wav_file_dir = DATADIR;
4042 +static char *wav_file_dir = SOUNDSDIR;
4044 static const char *channel_name[MAX_CHANNELS] = {
4045 N_("Front Left"),