]> git.alsa-project.org Git - alsa-utils.git/commitdiff
split source per command and added command 'names'
authorJaroslav Kysela <perex@perex.cz>
Tue, 10 May 2005 10:55:24 +0000 (10:55 +0000)
committerJaroslav Kysela <perex@perex.cz>
Tue, 10 May 2005 10:55:24 +0000 (10:55 +0000)
- sources are splitted to separate files
- added initial code for 'names' command

alsactl/Makefile.am
alsactl/alsactl.1
alsactl/alsactl.c
alsactl/alsactl.h [new file with mode: 0644]
alsactl/names.c [new file with mode: 0644]
alsactl/power.c [new file with mode: 0644]
alsactl/state.c [new file with mode: 0644]

index 17a0b0ce0a246753f869a9a6881fdd6beabe9966..0a4b36cead1b62beed23ef8bf3dc12ce303320f5 100644 (file)
@@ -2,4 +2,5 @@ sbin_PROGRAMS=alsactl
 man_MANS=alsactl.1
 EXTRA_DIST=alsactl.1
 
-alsactl_SOURCES=alsactl.c
+alsactl_SOURCES=alsactl.c state.c power.c names.c
+
index 36d3f3e8b5dfb632387e3bf818395c2ac8b00ef6..f01ec492bbeba7ba864517b46199da7a88b69714 100644 (file)
@@ -4,7 +4,7 @@ alsactl \- advanced controls for ALSA soundcard driver
 
 .SH SYNOPSIS
 
-\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP] <card # or id>
+\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fInames\fP] <card # or id>
 
 .SH DESCRIPTION
 \fBalsactl\fP is used to control advanced settings for the ALSA
@@ -14,14 +14,19 @@ you have come to the right place.
 
 .SH INVOKING
 
-\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP] <card # or id>
+\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fInames\fP] <card # or id>
 
 .SS Commands
 
 \fIstore\fP saves the current driver state for the selected soundcard
 to the configuration file.
 
-\fIrestore\fP loads driver state for the selected soundcard from the configuration file.
+\fIrestore\fP loads driver state for the selected soundcard from the
+configuration file.
+
+\fInames\fP generates list of available device names for applications.
+The card number or id is ignored for this command. The list is always
+generated for all available cards.
 
 If no soundcards are specified, setup for all cards will be saved or
 loaded.
@@ -34,7 +39,8 @@ Help: show available flags and commands.
 
 .TP
 \fI\-f, \-\-file\fP
-Select the configuration file to use. The default is /etc/asound.state
+Select the configuration file to use. The default is /etc/asound.state or
+/etc/asound.names (for the \fInames\fP command).
 
 .TP
 \fI\-F, \-\-force\fP
@@ -63,6 +69,13 @@ necessary for some soundcard features (e.g. enabling/disabling
 automatic mic gain, digital output, joystick/game ports, some future MIDI
 routing options, etc).
 
+\fI/etc/asound.names\fP (or whatever file you specify with the
+\fB\-f\fP flag) is used to store the list of device names available
+in your system. The list does not contain all virtual names, because
+the name space is infinite, but it detects present hardware and
+generates list of common names. The user / system administrator / another
+configuration tool might modify the file to add virtual names as well.
+
 .SH SEE ALSO
 \fB
 amixer(1),
index b5833ebd17971e7d31f8779b0ced9004fc191ed0..7e7025c1a784e47d07bcc7f4700167fb52c196ff 100644 (file)
 #include <assert.h>
 #include <errno.h>
 #include <alsa/asoundlib.h>
+#include "alsactl.h"
 
 #define SYS_ASOUNDRC "/etc/asound.state"
+#define SYS_ASOUNDNAMES "/etc/asound.names"
 
 int debugflag = 0;
 int force_restore = 0;
 char *command;
 
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define error(...) do {\
-       fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
-       fprintf(stderr, __VA_ARGS__); \
-       putc('\n', stderr); \
-} while (0)
-#else
-#define error(args...) do {\
-       fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
-       fprintf(stderr, ##args); \
-       putc('\n', stderr); \
-} while (0)
-#endif 
-
 static void help(void)
 {
        printf("Usage: alsactl <options> command\n");
        printf("\nAvailable options:\n");
        printf("  -h,--help       this help\n");
-       printf("  -f,--file #     configuration file (default " SYS_ASOUNDRC ")\n");
+       printf("  -f,--file #     configuration file (default " SYS_ASOUNDRC " or " SYS_ASOUNDNAMES ")\n");
        printf("  -F,--force      try to restore the matching controls as much as possible\n");
        printf("  -d,--debug      debug mode\n");
        printf("  -v,--version    print version of this program\n");
@@ -67,1400 +55,6 @@ static void help(void)
        printf("                  get/set power state for one or each soundcards\n");
 }
 
-char *id_str(snd_ctl_elem_id_t *id)
-{
-       static char str[128];
-       assert(id);
-       sprintf(str, "%i,%i,%i,%s,%i", 
-               snd_ctl_elem_id_get_interface(id),
-               snd_ctl_elem_id_get_device(id),
-               snd_ctl_elem_id_get_subdevice(id),
-               snd_ctl_elem_id_get_name(id),
-               snd_ctl_elem_id_get_index(id));
-       return str;
-}
-
-char *num_str(long n)
-{
-       static char str[32];
-       sprintf(str, "%ld", n);
-       return str;
-}
-
-static int snd_config_integer_add(snd_config_t *father, char *id, long integer)
-{
-       int err;
-       snd_config_t *leaf;
-       err = snd_config_make_integer(&leaf, id);
-       if (err < 0)
-               return err;
-       err = snd_config_add(father, leaf);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       err = snd_config_set_integer(leaf, integer);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       return 0;
-}
-
-static int snd_config_integer64_add(snd_config_t *father, char *id, long long integer)
-{
-       int err;
-       snd_config_t *leaf;
-       err = snd_config_make_integer64(&leaf, id);
-       if (err < 0)
-               return err;
-       err = snd_config_add(father, leaf);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       err = snd_config_set_integer64(leaf, integer);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       return 0;
-}
-
-static int snd_config_string_add(snd_config_t *father, const char *id, const char *string)
-{
-       int err;
-       snd_config_t *leaf;
-       err = snd_config_make_string(&leaf, id);
-       if (err < 0)
-               return err;
-       err = snd_config_add(father, leaf);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       err = snd_config_set_string(leaf, string);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       return 0;
-}
-
-static int snd_config_compound_add(snd_config_t *father, const char *id, int join,
-                                  snd_config_t **node)
-{
-       int err;
-       snd_config_t *leaf;
-       err = snd_config_make_compound(&leaf, id, join);
-       if (err < 0)
-               return err;
-       err = snd_config_add(father, leaf);
-       if (err < 0) {
-               snd_config_delete(leaf);
-               return err;
-       }
-       *node = leaf;
-       return 0;
-}
-
-static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top)
-{
-       snd_ctl_elem_value_t *ctl;
-       snd_ctl_elem_info_t *info;
-       snd_config_t *control, *comment, *item, *value;
-       const char *s;
-       char buf[256];
-       unsigned int idx;
-       int err;
-       unsigned int device, subdevice, index;
-       const char *name;
-       snd_ctl_elem_type_t type;
-       unsigned int count;
-       snd_ctl_elem_value_alloca(&ctl);
-       snd_ctl_elem_info_alloca(&info);
-       snd_ctl_elem_info_set_id(info, id);
-       err = snd_ctl_elem_info(handle, info);
-       if (err < 0) {
-               error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err));
-               return err;
-       }
-
-       if (!snd_ctl_elem_info_is_readable(info))
-               return 0;
-       snd_ctl_elem_value_set_id(ctl, id);
-       err = snd_ctl_elem_read(handle, ctl);
-       if (err < 0) {
-               error("Cannot read control '%s': %s", id_str(id), snd_strerror(err));
-               return err;
-       }
-
-       err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control);
-       if (err < 0) {
-               error("snd_config_compound_add: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_config_compound_add(control, "comment", 1, &comment);
-       if (err < 0) {
-               error("snd_config_compound_add: %s", snd_strerror(err));
-               return err;
-       }
-
-       buf[0] = '\0';
-       buf[1] = '\0';
-       if (snd_ctl_elem_info_is_readable(info))
-               strcat(buf, " read");
-       if (snd_ctl_elem_info_is_writable(info))
-               strcat(buf, " write");
-       if (snd_ctl_elem_info_is_inactive(info))
-               strcat(buf, " inactive");
-       if (snd_ctl_elem_info_is_volatile(info))
-               strcat(buf, " volatile");
-       if (snd_ctl_elem_info_is_locked(info))
-               strcat(buf, " locked");
-       if (snd_ctl_elem_info_is_user(info))
-               strcat(buf, " user");
-       err = snd_config_string_add(comment, "access", buf + 1);
-       if (err < 0) {
-               error("snd_config_string_add: %s", snd_strerror(err));
-               return err;
-       }
-
-       type = snd_ctl_elem_info_get_type(info);
-       device = snd_ctl_elem_info_get_device(info);
-       subdevice = snd_ctl_elem_info_get_subdevice(info);
-       index = snd_ctl_elem_info_get_index(info);
-       name = snd_ctl_elem_info_get_name(info);
-       count = snd_ctl_elem_info_get_count(info);
-       s = snd_ctl_elem_type_name(type);
-       err = snd_config_string_add(comment, "type", s);
-       if (err < 0) {
-               error("snd_config_string_add: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_config_integer_add(comment, "count", count);
-       if (err < 0) {
-               error("snd_config_integer_add: %s", snd_strerror(err));
-               return err;
-       }
-
-       switch (type) {
-       case SND_CTL_ELEM_TYPE_BOOLEAN:
-               break;
-       case SND_CTL_ELEM_TYPE_INTEGER:
-       {
-               long min = snd_ctl_elem_info_get_min(info);
-               long max = snd_ctl_elem_info_get_max(info);
-               long step = snd_ctl_elem_info_get_step(info);
-               if (step)
-                       sprintf(buf, "%li - %li (step %li)", min, max, step);
-               else
-                       sprintf(buf, "%li - %li", min, max);
-               err = snd_config_string_add(comment, "range", buf);
-               if (err < 0) {
-                       error("snd_config_string_add: %s", snd_strerror(err));
-                       return err;
-               }
-               break;
-       }
-       case SND_CTL_ELEM_TYPE_INTEGER64:
-       {
-               long long min = snd_ctl_elem_info_get_min64(info);
-               long long max = snd_ctl_elem_info_get_max64(info);
-               long long step = snd_ctl_elem_info_get_step64(info);
-               if (step)
-                       sprintf(buf, "%Li - %Li (step %Li)", min, max, step);
-               else
-                       sprintf(buf, "%Li - %Li", min, max);
-               err = snd_config_string_add(comment, "range", buf);
-               if (err < 0) {
-                       error("snd_config_string_add: %s", snd_strerror(err));
-                       return err;
-               }
-               break;
-       }
-       case SND_CTL_ELEM_TYPE_ENUMERATED:
-       {
-               unsigned int items;
-               err = snd_config_compound_add(comment, "item", 1, &item);
-               if (err < 0) {
-                       error("snd_config_compound_add: %s", snd_strerror(err));
-                       return err;
-               }
-               items = snd_ctl_elem_info_get_items(info);
-               for (idx = 0; idx < items; idx++) {
-                       snd_ctl_elem_info_set_item(info, idx);
-                       err = snd_ctl_elem_info(handle, info);
-                       if (err < 0) {
-                               error("snd_ctl_card_info: %s", snd_strerror(err));
-                               return err;
-                       }
-                       err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info));
-                       if (err < 0) {
-                               error("snd_config_string_add: %s", snd_strerror(err));
-                               return err;
-                       }
-               }
-               break;
-       }
-       default:
-               break;
-       }
-       s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info));
-       err = snd_config_string_add(control, "iface", s);
-       if (err < 0) {
-               error("snd_config_string_add: %s", snd_strerror(err));
-               return err;
-       }
-       if (device != 0) {
-               err = snd_config_integer_add(control, "device", device);
-               if (err < 0) {
-                       error("snd_config_integer_add: %s", snd_strerror(err));
-                       return err;
-               }
-       }
-       if (subdevice != 0) {
-               err = snd_config_integer_add(control, "subdevice", subdevice);
-               if (err < 0) {
-                       error("snd_config_integer_add: %s", snd_strerror(err));
-                       return err;
-               }
-       }
-       err = snd_config_string_add(control, "name", name);
-       if (err < 0) {
-               error("snd_config_string_add: %s", snd_strerror(err));
-               return err;
-       }
-       if (index != 0) {
-               err = snd_config_integer_add(control, "index", index);
-               if (err < 0) {
-                       error("snd_config_integer_add: %s", snd_strerror(err));
-                       return err;
-               }
-       }
-
-       switch (type) {
-       case SND_CTL_ELEM_TYPE_BYTES:
-       case SND_CTL_ELEM_TYPE_IEC958:
-       {
-               size_t size = type == SND_CTL_ELEM_TYPE_BYTES ?
-                       count : sizeof(snd_aes_iec958_t);
-               char buf[size * 2 + 1];
-               char *p = buf;
-               char *hex = "0123456789abcdef";
-               const unsigned char *bytes = 
-                 (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl);
-               for (idx = 0; idx < size; idx++) {
-                       int v = bytes[idx];
-                       *p++ = hex[v >> 4];
-                       *p++ = hex[v & 0x0f];
-               }
-               *p = '\0';
-               err = snd_config_string_add(control, "value", buf);
-               if (err < 0) {
-                       error("snd_config_string_add: %s", snd_strerror(err));
-                       return err;
-               }
-               return 0;
-       }
-       default:
-               break;
-       }
-
-       if (count == 1) {
-               switch (type) {
-               case SND_CTL_ELEM_TYPE_BOOLEAN:
-                       err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false");
-                       if (err < 0) {
-                               error("snd_config_string_add: %s", snd_strerror(err));
-                               return err;
-                       }
-                       return 0;
-               case SND_CTL_ELEM_TYPE_INTEGER:
-                       err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0));
-                       if (err < 0) {
-                               error("snd_config_integer_add: %s", snd_strerror(err));
-                               return err;
-                       }
-                       return 0;
-               case SND_CTL_ELEM_TYPE_INTEGER64:
-                       err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0));
-                       if (err < 0) {
-                               error("snd_config_integer64_add: %s", snd_strerror(err));
-                               return err;
-                       }
-                       return 0;
-               case SND_CTL_ELEM_TYPE_ENUMERATED:
-               {
-                       unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0);
-                       snd_config_t *c;
-                       err = snd_config_search(item, num_str(v), &c);
-                       if (err == 0) {
-                               err = snd_config_get_string(c, &s);
-                               assert(err == 0);
-                               err = snd_config_string_add(control, "value", s);
-                       } else {
-                               err = snd_config_integer_add(control, "value", v);
-                       }
-                       if (err < 0)
-                               error("snd_config add: %s", snd_strerror(err));
-                       return 0;
-               }
-               default:
-                       error("Unknown control type: %d\n", type);
-                       return -EINVAL;
-               }
-       }
-
-       err = snd_config_compound_add(control, "value", 1, &value);
-       if (err < 0) {
-               error("snd_config_compound_add: %s", snd_strerror(err));
-               return err;
-       }
-
-       switch (type) {
-       case SND_CTL_ELEM_TYPE_BOOLEAN:
-               for (idx = 0; idx < count; idx++) {
-                       err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false");
-                       if (err < 0) {
-                               error("snd_config_string_add: %s", snd_strerror(err));
-                               return err;
-                       }
-               }
-               break;
-       case SND_CTL_ELEM_TYPE_INTEGER:
-               for (idx = 0; idx < count; idx++) {
-                       err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx));
-                       if (err < 0) {
-                               error("snd_config_integer_add: %s", snd_strerror(err));
-                               return err;
-                       }
-               }
-               break;
-       case SND_CTL_ELEM_TYPE_INTEGER64:
-               for (idx = 0; idx < count; idx++) {
-                       err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx));
-                       if (err < 0) {
-                               error("snd_config_integer64_add: %s", snd_strerror(err));
-                               return err;
-                       }
-               }
-               break;
-       case SND_CTL_ELEM_TYPE_ENUMERATED:
-               for (idx = 0; idx < count; idx++) {
-                       unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx);
-                       snd_config_t *c;
-                       err = snd_config_search(item, num_str(v), &c);
-                       if (err == 0) {
-                               err = snd_config_get_string(c, &s);
-                               assert(err == 0);
-                               err = snd_config_string_add(value, num_str(idx), s);
-                       } else {
-                               err = snd_config_integer_add(value, num_str(idx), v);
-                       }
-                       if (err < 0) {
-                               error("snd_config add: %s", snd_strerror(err));
-                               return err;
-                       }
-               }
-               break;
-       default:
-               error("Unknown control type: %d\n", type);
-               return -EINVAL;
-       }
-       
-       return 0;
-}
-       
-static int get_controls(int cardno, snd_config_t *top)
-{
-       snd_ctl_t *handle;
-       snd_ctl_card_info_t *info;
-       snd_config_t *state, *card, *control;
-       snd_ctl_elem_list_t *list;
-       unsigned int idx;
-       int err;
-       char name[32];
-       unsigned int count;
-       const char *id;
-       snd_ctl_card_info_alloca(&info);
-       snd_ctl_elem_list_alloca(&list);
-
-       sprintf(name, "hw:%d", cardno);
-       err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
-       if (err < 0) {
-               error("snd_ctl_open error: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_ctl_card_info(handle, info);
-       if (err < 0) {
-               error("snd_ctl_card_info error: %s", snd_strerror(err));
-               goto _close;
-       }
-       id = snd_ctl_card_info_get_id(info);
-       err = snd_config_search(top, "state", &state);
-       if (err == 0 &&
-           snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) {
-               error("config state node is not a compound");
-               err = -EINVAL;
-               goto _close;
-       }
-       if (err < 0) {
-               err = snd_config_compound_add(top, "state", 1, &state);
-               if (err < 0) {
-                       error("snd_config_compound_add: %s", snd_strerror(err));
-                       goto _close;
-               }
-       }
-       err = snd_config_search(state, id, &card);
-       if (err == 0 &&
-           snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) {
-               error("config state.%s node is not a compound", id);
-               err = -EINVAL;
-               goto _close;
-       }
-       if (err < 0) {
-               err = snd_config_compound_add(state, id, 0, &card);
-               if (err < 0) {
-                       error("snd_config_compound_add: %s", snd_strerror(err));
-                       goto _close;
-               }
-       }
-       err = snd_config_search(card, "control", &control);
-       if (err == 0) {
-               err = snd_config_delete(control);
-               if (err < 0) {
-                       error("snd_config_delete: %s", snd_strerror(err));
-                       goto _close;
-               }
-       }
-       err = snd_ctl_elem_list(handle, list);
-       if (err < 0) {
-               error("Cannot determine controls: %s", snd_strerror(err));
-               goto _close;
-       }
-       count = snd_ctl_elem_list_get_count(list);
-       if (count < 0) {
-               err = 0;
-               goto _close;
-       }
-       err = snd_config_compound_add(card, "control", count > 0, &control);
-       if (err < 0) {
-               error("snd_config_compound_add: %s", snd_strerror(err));
-               goto _close;
-       }
-       if (count == 0) {
-               err = 0;
-               goto _close;
-       }
-       snd_ctl_elem_list_set_offset(list, 0);
-       if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
-               error("No enough memory...");
-               goto _close;
-       }
-       if ((err = snd_ctl_elem_list(handle, list)) < 0) {
-               error("Cannot determine controls (2): %s", snd_strerror(err));
-               goto _free;
-       }
-       for (idx = 0; idx < count; ++idx) {
-               snd_ctl_elem_id_t *id;
-               snd_ctl_elem_id_alloca(&id);
-               snd_ctl_elem_list_get_id(list, idx, id);
-               err = get_control(handle, id, control);
-               if (err < 0)
-                       goto _free;
-       }               
-               
-       err = 0;
- _free:
-       snd_ctl_elem_list_free_space(list);
- _close:
-       snd_ctl_close(handle);
-       return err;
-}
-
-static long config_iface(snd_config_t *n)
-{
-       long i;
-       long long li;
-       snd_ctl_elem_iface_t idx; 
-       const char *str;
-       switch (snd_config_get_type(n)) {
-       case SND_CONFIG_TYPE_INTEGER:
-               snd_config_get_integer(n, &i);
-               return i;
-       case SND_CONFIG_TYPE_INTEGER64:
-               snd_config_get_integer64(n, &li);
-               return li;
-       case SND_CONFIG_TYPE_STRING:
-               snd_config_get_string(n, &str);
-               break;
-       default:
-               return -1;
-       }
-       for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
-               if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
-                       return idx;
-       }
-       return -1;
-}
-
-static int config_bool(snd_config_t *n)
-{
-       const char *str;
-       long val;
-       long long lval;
-       switch (snd_config_get_type(n)) {
-       case SND_CONFIG_TYPE_INTEGER:
-               snd_config_get_integer(n, &val);
-               if (val < 0 || val > 1)
-                       return -1;
-               return val;
-       case SND_CONFIG_TYPE_INTEGER64:
-               snd_config_get_integer64(n, &lval);
-               if (lval < 0 || lval > 1)
-                       return -1;
-               return (int) lval;
-       case SND_CONFIG_TYPE_STRING:
-               snd_config_get_string(n, &str);
-               break;
-       default:
-               return -1;
-       }
-       if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0)
-               return 1;
-       if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0)
-               return 0;
-       return -1;
-}
-
-static int config_enumerated(snd_config_t *n, snd_ctl_t *handle,
-                            snd_ctl_elem_info_t *info)
-{
-       const char *str;
-       long val;
-       long long lval;
-       unsigned int idx, items;
-       switch (snd_config_get_type(n)) {
-       case SND_CONFIG_TYPE_INTEGER:
-               snd_config_get_integer(n, &val);
-               return val;
-       case SND_CONFIG_TYPE_INTEGER64:
-               snd_config_get_integer64(n, &lval);
-               return (int) lval;
-       case SND_CONFIG_TYPE_STRING:
-               snd_config_get_string(n, &str);
-               break;
-       default:
-               return -1;
-       }
-       items = snd_ctl_elem_info_get_items(info);
-       for (idx = 0; idx < items; idx++) {
-               int err;
-               snd_ctl_elem_info_set_item(info, idx);
-               err = snd_ctl_elem_info(handle, info);
-               if (err < 0) {
-                       error("snd_ctl_elem_info: %s", snd_strerror(err));
-                       return err;
-               }
-               if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
-                       return idx;
-       }
-       return -1;
-}
-
-static int is_user_control(snd_config_t *conf)
-{
-       snd_config_iterator_t i, next;
-
-       snd_config_for_each(i, next, conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id, *s;
-               if (snd_config_get_id(n, &id) < 0)
-                       continue;
-               if (strcmp(id, "access") == 0) {
-                       if (snd_config_get_string(n, &s) < 0)
-                               return 0;
-                       if (strstr(s, "user"))
-                               return 1;
-               }
-       }
-       return 0;
-}
-
-static int add_user_control(snd_ctl_t *handle, snd_ctl_elem_info_t *info, snd_config_t *conf)
-{
-       snd_ctl_elem_id_t *id;
-       snd_config_iterator_t i, next;
-       long imin, imax, istep;
-       snd_ctl_elem_type_t ctype;
-       unsigned int count;
-       int err;
-
-       imin = imax = istep = 0;
-       count = 0;
-       ctype = SND_CTL_ELEM_TYPE_NONE;
-       snd_config_for_each(i, next, conf) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id, *type;
-               if (snd_config_get_id(n, &id) < 0)
-                       continue;
-               if (strcmp(id, "type") == 0) {
-                       if ((err = snd_config_get_string(n, &type)) < 0)
-                               return -EINVAL;
-                       if (strcmp(type, "BOOLEAN") == 0)
-                               ctype = SND_CTL_ELEM_TYPE_BOOLEAN;
-                       else if (strcmp(type, "INTEGER") == 0)
-                               ctype = SND_CTL_ELEM_TYPE_INTEGER;
-                       else if (strcmp(type, "IEC958") == 0)
-                               ctype = SND_CTL_ELEM_TYPE_IEC958;
-                       else
-                               return -EINVAL;
-                       continue;
-               }
-               if (strcmp(id, "range") == 0) {
-                       const char *s;
-                       if ((err = snd_config_get_string(n, &s)) < 0)
-                               return -EINVAL;
-                       switch (ctype) {
-                       case SND_CTL_ELEM_TYPE_INTEGER:
-                               err = sscanf(s, "%li - %li (step %li)", &imin, &imax, &istep);
-                               if (err != 3) {
-                                       istep = 0;
-                                       err = sscanf(s, "%li - %li", &imin, &imax);
-                                       if (err != 2)
-                                               return -EINVAL;
-                               }
-                               break;
-                       default:
-                               return -EINVAL;
-                       }
-                       continue;
-               }
-               if (strcmp(id, "count") == 0) {
-                       long v;
-                       if ((err = snd_config_get_integer(n, &v)) < 0)
-                               return err;
-                       count = v;
-                       continue;
-               }
-       }
-
-       snd_ctl_elem_id_alloca(&id);
-       snd_ctl_elem_info_get_id(info, id);
-       if (count <= 0)
-               count = 1;
-       switch (ctype) {
-       case SND_CTL_ELEM_TYPE_INTEGER:
-               if (imin > imax || istep > imax - imin)
-                       return -EINVAL;
-               err = snd_ctl_elem_add_integer(handle, id, count, imin, imax, istep);
-               break;
-       case SND_CTL_ELEM_TYPE_BOOLEAN:
-               err = snd_ctl_elem_add_boolean(handle, id, count);
-               break;
-       case SND_CTL_ELEM_TYPE_IEC958:
-               err = snd_ctl_elem_add_iec958(handle, id);
-               break;
-       default:
-               err = -EINVAL;
-               break;
-       }
-
-       if (err < 0)
-               return err;
-       return snd_ctl_elem_info(handle, info);
-}
-
-static int set_control(snd_ctl_t *handle, snd_config_t *control)
-{
-       snd_ctl_elem_value_t *ctl;
-       snd_ctl_elem_info_t *info;
-       snd_config_iterator_t i, next;
-       unsigned int numid1;
-       snd_ctl_elem_iface_t iface = -1;
-       int iface1;
-       const char *name1;
-       unsigned int numid;
-       snd_ctl_elem_type_t type;
-       unsigned int count;
-       long device = -1;
-       long device1;
-       long subdevice = -1;
-       long subdevice1;
-       const char *name = NULL;
-       long index1;
-       long index = -1;
-       snd_config_t *value = NULL;
-       snd_config_t *comment = NULL;
-       long val;
-       long long lval;
-       unsigned int idx;
-       int err;
-       char *set;
-       const char *id;
-       snd_ctl_elem_value_alloca(&ctl);
-       snd_ctl_elem_info_alloca(&info);
-       if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
-               error("control is not a compound");
-               return -EINVAL;
-       }
-       err = snd_config_get_id(control, &id);
-       if (err < 0) {
-               error("unable to get id");
-               return -EINVAL;
-       }
-       numid = atoi(id);
-       snd_config_for_each(i, next, control) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *fld;
-               if (snd_config_get_id(n, &fld) < 0)
-                       continue;
-               if (strcmp(fld, "comment") == 0) {
-                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       comment = n;
-                       continue;
-               }
-               if (strcmp(fld, "iface") == 0) {
-                       iface = (snd_ctl_elem_iface_t)config_iface(n);
-                       if (iface < 0) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       continue;
-               }
-               if (strcmp(fld, "device") == 0) {
-                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       snd_config_get_integer(n, &device);
-                       continue;
-               }
-               if (strcmp(fld, "subdevice") == 0) {
-                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       snd_config_get_integer(n, &subdevice);
-                       continue;
-               }
-               if (strcmp(fld, "name") == 0) {
-                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       snd_config_get_string(n, &name);
-                       continue;
-               }
-               if (strcmp(fld, "index") == 0) {
-                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
-                               error("control.%d.%s is invalid", numid, fld);
-                               return -EINVAL;
-                       }
-                       snd_config_get_integer(n, &index);
-                       continue;
-               }
-               if (strcmp(fld, "value") == 0) {
-                       value = n;
-                       continue;
-               }
-               error("unknown control.%d.%s field", numid, fld);
-       }
-       if (!value) {
-               error("missing control.%d.value", numid);
-               return -EINVAL;
-       }
-       if (device < 0)
-               device = 0;
-       if (subdevice < 0)
-               subdevice = 0;
-       if (index < 0)
-               index = 0;
-
-       err = -EINVAL;
-       if (! force_restore) {
-               snd_ctl_elem_info_set_numid(info, numid);
-               err = snd_ctl_elem_info(handle, info);
-       }
-       if (err < 0) {
-               if (iface >= 0 && name) {
-                       snd_ctl_elem_info_set_numid(info, 0);
-                       snd_ctl_elem_info_set_interface(info, iface);
-                       snd_ctl_elem_info_set_device(info, device);
-                       snd_ctl_elem_info_set_subdevice(info, subdevice);
-                       snd_ctl_elem_info_set_name(info, name);
-                       snd_ctl_elem_info_set_index(info, index);
-                       err = snd_ctl_elem_info(handle, info);
-                       if (err < 0 && comment && is_user_control(comment)) {
-                               err = add_user_control(handle, info, comment);
-                               if (err < 0) {
-                                       error("failed to add user control #%d (%s)",
-                                             numid, snd_strerror(err));
-                                       return err;
-                               }
-                       }
-               }
-       }
-       if (err < 0) {
-               error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
-               return -ENOENT;
-       }
-       numid1 = snd_ctl_elem_info_get_numid(info);
-       iface1 = snd_ctl_elem_info_get_interface(info);
-       device1 = snd_ctl_elem_info_get_device(info);
-       subdevice1 = snd_ctl_elem_info_get_subdevice(info);
-       name1 = snd_ctl_elem_info_get_name(info);
-       index1 = snd_ctl_elem_info_get_index(info);
-       count = snd_ctl_elem_info_get_count(info);
-       type = snd_ctl_elem_info_get_type(info);
-       if (err |= numid != numid1 && ! force_restore)
-               error("warning: numid mismatch (%d/%d) for control #%d", 
-                     numid, numid1, numid);
-       if (err |= iface != iface1)
-               error("warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
-       if (err |= device != device1)
-               error("warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
-       if (err |= subdevice != subdevice1)
-               error("warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid);
-       if (err |= strcmp(name, name1))
-               error("warning: name mismatch (%s/%s) for control #%d", name, name1, numid);
-       if (err |= index != index1)
-               error("warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid);
-       if (err < 0) {
-               error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
-               return -ENOENT;
-       }
-
-#if 0
-       if (comment) {
-               check_comment_type(comment, type);
-               if (type == SND_CTL_ELEM_TYPE_INTEGER ||
-                   type == SND_CTL_ELEM_TYPE_INTEGER64)
-                       check_comment_range(comment, info);
-       }
-#endif
-
-       if (!snd_ctl_elem_info_is_writable(info))
-               return 0;
-       snd_ctl_elem_value_set_numid(ctl, numid1);
-
-       if (count == 1) {
-               switch (type) {
-               case SND_CTL_ELEM_TYPE_BOOLEAN:
-                       val = config_bool(value);
-                       if (val >= 0) {
-                               snd_ctl_elem_value_set_boolean(ctl, 0, val);
-                               goto _ok;
-                       }
-                       break;
-               case SND_CTL_ELEM_TYPE_INTEGER:
-                       err = snd_config_get_integer(value, &val);
-                       if (err == 0) {
-                               snd_ctl_elem_value_set_integer(ctl, 0, val);
-                               goto _ok;
-                       }
-                       break;
-               case SND_CTL_ELEM_TYPE_INTEGER64:
-                       err = snd_config_get_integer64(value, &lval);
-                       if (err == 0) {
-                               snd_ctl_elem_value_set_integer64(ctl, 0, lval);
-                               goto _ok;
-                       }
-                       break;
-               case SND_CTL_ELEM_TYPE_ENUMERATED:
-                       val = config_enumerated(value, handle, info);
-                       if (val >= 0) {
-                               snd_ctl_elem_value_set_enumerated(ctl, 0, val);
-                               goto _ok;
-                       }
-                       break;
-               case SND_CTL_ELEM_TYPE_BYTES:
-               case SND_CTL_ELEM_TYPE_IEC958:
-                       break;
-               default:
-                       error("Unknow control type: %d", type);
-                       return -EINVAL;
-               }
-       }
-       switch (type) {
-       case SND_CTL_ELEM_TYPE_BYTES:
-       case SND_CTL_ELEM_TYPE_IEC958:
-       {
-               const char *buf;
-               err = snd_config_get_string(value, &buf);
-               if (err >= 0) {
-                       int c1 = 0;
-                       int len = strlen(buf);
-                       unsigned int idx = 0;
-                       int size = type == SND_CTL_ELEM_TYPE_BYTES ?
-                               count : sizeof(snd_aes_iec958_t);
-                       if (size * 2 != len) {
-                               error("bad control.%d.value contents\n", numid);
-                               return -EINVAL;
-                       }
-                       while (*buf) {
-                               int c = *buf++;
-                               if (c >= '0' && c <= '9')
-                                       c -= '0';
-                               else if (c >= 'a' && c <= 'f')
-                                       c = c - 'a' + 10;
-                               else if (c >= 'A' && c <= 'F')
-                                       c = c - 'A' + 10;
-                               else {
-                                       error("bad control.%d.value contents\n", numid);
-                                       return -EINVAL;
-                               }
-                               if (idx % 2 == 1)
-                                       snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c);
-                               else
-                                       c1 = c;
-                               idx++;
-                       }
-                       goto _ok;
-               }
-       }
-       default:
-               break;
-       }
-       if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) {
-               error("bad control.%d.value type", numid);
-               return -EINVAL;
-       }
-
-       set = (char*) alloca(count);
-       memset(set, 0, count);
-       snd_config_for_each(i, next, value) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               const char *id;
-               if (snd_config_get_id(n, &id) < 0)
-                       continue;
-               idx = atoi(id);
-               if (idx < 0 || idx >= count || 
-                   set[idx]) {
-                       error("bad control.%d.value index", numid);
-                       return -EINVAL;
-               }
-               switch (type) {
-               case SND_CTL_ELEM_TYPE_BOOLEAN:
-                       val = config_bool(n);
-                       if (val < 0) {
-                               error("bad control.%d.value.%d content", numid, idx);
-                               return -EINVAL;
-                       }
-                       snd_ctl_elem_value_set_boolean(ctl, idx, val);
-                       break;
-               case SND_CTL_ELEM_TYPE_INTEGER:
-                       err = snd_config_get_integer(n, &val);
-                       if (err < 0) {
-                               error("bad control.%d.value.%d content", numid, idx);
-                               return -EINVAL;
-                       }
-                       snd_ctl_elem_value_set_integer(ctl, idx, val);
-                       break;
-               case SND_CTL_ELEM_TYPE_INTEGER64:
-                       err = snd_config_get_integer64(n, &lval);
-                       if (err < 0) {
-                               error("bad control.%d.value.%d content", numid, idx);
-                               return -EINVAL;
-                       }
-                       snd_ctl_elem_value_set_integer64(ctl, idx, lval);
-                       break;
-               case SND_CTL_ELEM_TYPE_ENUMERATED:
-                       val = config_enumerated(n, handle, info);
-                       if (val < 0) {
-                               error("bad control.%d.value.%d content", numid, idx);
-                               return -EINVAL;
-                       }
-                       snd_ctl_elem_value_set_enumerated(ctl, idx, val);
-                       break;
-               case SND_CTL_ELEM_TYPE_BYTES:
-               case SND_CTL_ELEM_TYPE_IEC958:
-                       err = snd_config_get_integer(n, &val);
-                       if (err < 0 || val < 0 || val > 255) {
-                               error("bad control.%d.value.%d content", numid, idx);
-                               return -EINVAL;
-                       }
-                       snd_ctl_elem_value_set_byte(ctl, idx, val);
-                       break;
-               default:
-                       break;
-               }
-               set[idx] = 1;
-       }
-       for (idx = 0; idx < count; ++idx) {
-               if (!set[idx]) {
-                       error("control.%d.value.%d is not specified", numid, idx);
-                       return -EINVAL;
-               }
-       }
-
- _ok:
-       err = snd_ctl_elem_write(handle, ctl);
-       if (err < 0) {
-               error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
-               return err;
-       }
-       return 0;
-}
-
-static int set_controls(int card, snd_config_t *top)
-{
-       snd_ctl_t *handle;
-       snd_ctl_card_info_t *info;
-       snd_config_t *control;
-       snd_config_iterator_t i, next;
-       int err;
-       char name[32], tmpid[16];
-       const char *id;
-       snd_ctl_card_info_alloca(&info);
-
-       sprintf(name, "hw:%d", card);
-       err = snd_ctl_open(&handle, name, 0);
-       if (err < 0) {
-               error("snd_ctl_open error: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_ctl_card_info(handle, info);
-       if (err < 0) {
-               error("snd_ctl_card_info error: %s", snd_strerror(err));
-               goto _close;
-       }
-       id = snd_ctl_card_info_get_id(info);
-       err = snd_config_searchv(top, &control, "state", id, "control", 0);
-       if (err < 0) {
-               if (force_restore) {
-                       sprintf(tmpid, "card%d", card);
-                       err = snd_config_searchv(top, &control, "state", tmpid, "control", 0);
-                       if (! err)
-                               id = tmpid;
-               }
-               if (err < 0) {
-                       err = 0;
-                       fprintf(stderr, "No state is present for card %s\n", id);
-                       goto _close;
-               }
-               id = tmpid;
-       }
-       if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
-               error("state.%s.control is not a compound\n", id);
-               return -EINVAL;
-       }
-       snd_config_for_each(i, next, control) {
-               snd_config_t *n = snd_config_iterator_entry(i);
-               err = set_control(handle, n);
-               if (err < 0 && ! force_restore)
-                       goto _close;
-       }
-
- _close:
-       snd_ctl_close(handle);
-       return err;
-}
-
-static int save_state(char *file, const char *cardname)
-{
-       int err;
-       snd_config_t *config;
-       snd_input_t *in;
-       snd_output_t *out;
-       int stdio;
-
-       err = snd_config_top(&config);
-       if (err < 0) {
-               error("snd_config_top error: %s", snd_strerror(err));
-               return err;
-       }
-       stdio = !strcmp(file, "-");
-       if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) {
-               err = snd_config_load(config, in);
-               snd_input_close(in);
-#if 0
-               if (err < 0) {
-                       error("snd_config_load error: %s", snd_strerror(err));
-                       return err;
-               }
-#endif
-       }
-
-       if (!cardname) {
-               int card, first = 1;
-
-               card = -1;
-               /* find each installed soundcards */
-               while (1) {
-                       if (snd_card_next(&card) < 0)
-                               break;
-                       if (card < 0) {
-                               if (first) {
-                                       error("No soundcards found...");
-                                       return -ENODEV;
-                               }
-                               break;
-                       }
-                       first = 0;
-                       if ((err = get_controls(card, config)))
-                               return err;
-               }
-       } else {
-               int cardno;
-
-               cardno = snd_card_get_index(cardname);
-               if (cardno < 0) {
-                       error("Cannot find soundcard '%s'...", cardname);
-                       return cardno;
-               }
-               if ((err = get_controls(cardno, config))) {
-                       return err;
-               }
-       }
-       
-       if (stdio)
-               err = snd_output_stdio_attach(&out, stdout, 0);
-       else
-               err = snd_output_stdio_open(&out, file, "w");
-       if (err < 0) {
-               error("Cannot open %s for writing", file);
-               return -errno;
-       }
-       err = snd_config_save(config, out);
-       snd_output_close(out);
-       if (err < 0)
-               error("snd_config_save: %s", snd_strerror(err));
-       return 0;
-}
-
-int load_state(const char *file, const char *cardname)
-{
-       int err;
-       snd_config_t *config;
-       snd_input_t *in;
-       int stdio;
-
-       err = snd_config_top(&config);
-       if (err < 0) {
-               error("snd_config_top error: %s", snd_strerror(err));
-               return err;
-       }
-       stdio = !strcmp(file, "-");
-       if (stdio)
-               err = snd_input_stdio_attach(&in, stdin, 0);
-       else
-               err = snd_input_stdio_open(&in, file, "r");
-       if (err >= 0) {
-               err = snd_config_load(config, in);
-               snd_input_close(in);
-               if (err < 0) {
-                       error("snd_config_load error: %s", snd_strerror(err));
-                       return err;
-               }
-       }
-
-       if (!cardname) {
-               int card, first = 1;
-
-               card = -1;
-               /* find each installed soundcards */
-               while (1) {
-                       if (snd_card_next(&card) < 0)
-                               break;
-                       if (card < 0) {
-                               if (first) {
-                                       error("No soundcards found...");
-                                       return -ENODEV;
-                               }
-                               break;
-                       }
-                       first = 0;
-                       if ((err = set_controls(card, config)) && ! force_restore)
-                               return err;
-               }
-       } else {
-               int cardno;
-
-               cardno = snd_card_get_index(cardname);
-               if (cardno < 0) {
-                       error("Cannot find soundcard '%s'...", cardname);
-                       return -ENODEV;
-               }
-               if ((err = set_controls(cardno, config)) && ! force_restore) {
-                       return err;
-               }
-       }
-       return 0;
-}
-
-static int get_int_state(const char *str)
-{
-       if (!strcasecmp(str, "on"))
-               return SND_CTL_POWER_D0;
-       if (!strcasecmp(str, "off"))
-               return SND_CTL_POWER_D3hot;
-       if (*str == 'D' || *str == 'd') {
-               str++;
-               if (!strcmp(str, "0"))
-                       return SND_CTL_POWER_D0;
-               if (!strcmp(str, "1"))
-                       return SND_CTL_POWER_D1;
-               if (!strcmp(str, "2"))
-                       return SND_CTL_POWER_D2;
-               if (!strcmp(str, "3"))
-                       return SND_CTL_POWER_D3;
-               if (!strcmp(str, "3hot"))
-                       return SND_CTL_POWER_D3hot;
-               if (!strcmp(str, "3cold"))
-                       return SND_CTL_POWER_D3cold;
-       }
-       return -1;
-}
-
-static const char *get_str_state(int power_state)
-{
-       static char str[16];
-
-       switch (power_state) {
-       case SND_CTL_POWER_D0:
-               return "D0";
-       case SND_CTL_POWER_D1:
-               return "D1";
-       case SND_CTL_POWER_D2:
-               return "D2";
-       // return SND_CTL_POWER_D3;     /* it's same as D3hot */
-       case SND_CTL_POWER_D3hot:
-               return "D3hot";
-       case SND_CTL_POWER_D3cold:
-               return "D3cold";
-       default:
-               sprintf(str, "???0x%x", power_state);
-               return str;
-       }
-}
-
-static int show_power(int cardno)
-{
-       snd_ctl_t *handle;
-       char name[16];
-       unsigned int power_state;
-       int err;
-
-       sprintf(name, "hw:%d", cardno);
-       err = snd_ctl_open(&handle, name, 0);
-       if (err < 0) {
-               error("snd_ctl_open error: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_ctl_get_power_state(handle, &power_state);
-       if (err < 0) {
-               error("snd_ctl_get_power_state error: %s", snd_strerror(err));
-               snd_ctl_close(handle);
-               return err;
-       }
-       snd_ctl_close(handle);
-       printf("Power state for card #%d is %s\n", cardno, get_str_state(power_state));
-       return 0;
-}
-
-static int set_power(int cardno, unsigned int power_state)
-{
-       snd_ctl_t *handle;
-       char name[16];
-       int err;
-
-       sprintf(name, "hw:%d", cardno);
-       err = snd_ctl_open(&handle, name, 0);
-       if (err < 0) {
-               error("snd_ctl_open error: %s", snd_strerror(err));
-               return err;
-       }
-       err = snd_ctl_set_power_state(handle, power_state);
-       if (err < 0) {
-               error("snd_ctl_set_power_state error: %s", snd_strerror(err));
-               snd_ctl_close(handle);
-               return err;
-       }
-       err = snd_ctl_get_power_state(handle, &power_state);
-       if (err < 0) {
-               error("snd_ctl_get_power_state error: %s", snd_strerror(err));
-               snd_ctl_close(handle);
-               return err;
-       }
-       snd_ctl_close(handle);
-       printf("Power state for card #%d is %s\n", cardno, get_str_state(power_state));
-       return 0;
-}
-
-static int power(const char *argv[], int argc)
-{
-       int power_state, err;
-       
-       if (argc == 0) {                /* show status only */
-               int card, first = 1;
-
-               card = -1;
-               /* find each installed soundcards */
-               while (1) {
-                       if (snd_card_next(&card) < 0)
-                               break;
-                       if (card < 0) {
-                               if (first) {
-                                       error("No soundcards found...");
-                                       return -ENODEV;
-                               }
-                               break;
-                       }
-                       first = 0;
-                       if ((err = show_power(card)) < 0)
-                               return err;
-               }
-               return 0;
-       }
-       power_state = get_int_state(argv[0]);
-       if (power_state >= 0) {
-               int card, first = 1;
-
-               card = -1;
-               /* find each installed soundcards */
-               while (1) {
-                       if (snd_card_next(&card) < 0)
-                               break;
-                       if (card < 0) {
-                               if (first) {
-                                       error("No soundcards found...");
-                                       return -ENODEV;
-                               }
-                               break;
-                       }
-                       first = 0;
-                       if ((err = set_power(card, power_state)))
-                               return err;
-               }
-       } else {
-               int cardno;
-
-               cardno = snd_card_get_index(argv[0]);
-               if (cardno < 0) {
-                       error("Cannot find soundcard '%s'...", argv[0]);
-                       return -ENODEV;
-               }
-               if (argc > 1) {
-                       power_state = get_int_state(argv[1]);
-                       if (power_state < 0) {
-                               error("Invalid power state '%s'...", argv[1]);
-                               return -EINVAL;
-                       }
-                       if ((err = set_power(cardno, power_state)) < 0)
-                               return err;
-               } else {
-                       if ((err = show_power(cardno)) < 0)
-                               return err;
-               }
-       }
-       return 0;
-}
-
 int main(int argc, char *argv[])
 {
        struct option long_option[] =
@@ -1517,6 +111,10 @@ int main(int argc, char *argv[])
        } else if (!strcmp(argv[optind], "restore")) {
                res = load_state(cfgfile, 
                   argc - optind > 1 ? argv[optind + 1] : NULL);
+       } else if (!strcmp(argv[optind], "names")) {
+               if (!strcmp(cfgfile, SYS_ASOUNDRC))
+                       cfgfile = SYS_ASOUNDNAMES;
+               res = generate_names(cfgfile);
        } else if (!strcmp(argv[optind], "power")) {
                res = power((const char **)argv + optind + 1, argc - optind - 1);
        } else {
diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h
new file mode 100644 (file)
index 0000000..f1e2b41
--- /dev/null
@@ -0,0 +1,22 @@
+extern int debugflag;
+extern int force_restore;
+extern char *command;
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
+#define error(...) do {\
+       fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+       fprintf(stderr, __VA_ARGS__); \
+       putc('\n', stderr); \
+} while (0)
+#else
+#define error(args...) do {\
+       fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
+       fprintf(stderr, ##args); \
+       putc('\n', stderr); \
+} while (0)
+#endif 
+
+int save_state(const char *file, const char *cardname);
+int load_state(const char *file, const char *cardname);
+int power(const char *argv[], int argc);
+int generate_names(const char *cfgfile);
diff --git a/alsactl/names.c b/alsactl/names.c
new file mode 100644 (file)
index 0000000..bcf3bf4
--- /dev/null
@@ -0,0 +1,492 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program - ALSA Device Names
+ *  Copyright (c) 2005 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+typedef int (probe_single)(int card, snd_ctl_t *ctl, snd_config_t *config);
+
+static int globidx;
+
+static void dummy_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
+{
+}
+
+static int for_each_card(probe_single *fcn, snd_config_t *config)
+{
+       int card = -1, first = 1, err;
+       snd_ctl_t *ctl;
+       char ctlname[16];
+
+       while (1) {
+               if (snd_card_next(&card) < 0)
+                       break;
+               if (card < 0) {
+                       if (first) {
+                               error("No soundcards found...");
+                               return -ENODEV;
+                       }
+                       break;
+               }
+               first = 0;
+               sprintf(ctlname, "hw:%i", card);
+               err = snd_ctl_open(&ctl, ctlname, 0);
+               if (err < 0)
+                       return err;
+               err = (fcn)(card, ctl, config);
+               snd_ctl_close(ctl);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int add_entry(snd_config_t *cfg, const char *name,
+                    const char *cprefix, const char *flag,
+                    const char *comment)
+{
+       int err;
+       snd_config_t *c, *d;
+       char id[16];
+       char xcomment[256];
+       char *flag0 = " (", *flag1 = ")";
+
+       if (cprefix == NULL)
+               cprefix = "";
+       if (flag == NULL) {
+               flag0 = "";
+               flag = "";
+               flag1 = "";
+       }
+       sprintf(xcomment, "%s - %s%s%s%s", cprefix, comment, flag0, flag, flag1);
+       sprintf(id, "alsactl%i", globidx++);
+       err = snd_config_make_compound(&c, id, 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(cfg, c);
+       if (err < 0)
+               return err;
+       err = snd_config_make_string(&d, "name");
+       if (err < 0)
+               return err;
+       err = snd_config_set_string(d, name);
+       if (err < 0)
+               return err;
+       err = snd_config_add(c, d);
+       if (err < 0)
+               return err;
+       err = snd_config_make_string(&d, "comment");
+       if (err < 0)
+               return err;
+       err = snd_config_set_string(d, xcomment);
+       if (err < 0)
+               return err;
+       err = snd_config_add(c, d);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_ctl_card(int card, snd_ctl_t *ctl, snd_config_t *config)
+{
+       int err;
+       snd_ctl_card_info_t * info;
+       char name[16];
+       const char *dname;
+       
+       snd_ctl_card_info_alloca(&info);
+       err = snd_ctl_card_info(ctl, info);
+       if (err < 0) {
+               error("Unable to get info for card %i: %s\n", card, snd_strerror(err));
+               return err;
+       }
+       sprintf(name, "hw:%i", card);
+       dname = snd_ctl_card_info_get_longname(info);
+       err = add_entry(config, name, "Physical Device", NULL, dname);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_ctl(snd_config_t *config)
+{
+       int err;
+       snd_config_t *c;
+
+       err = snd_config_make_compound(&c, "ctl", 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(config, c);
+       if (err < 0)
+               return err;
+       err = for_each_card(probe_ctl_card, c);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_pcm_virtual(int card, snd_ctl_t *ctl, snd_config_t *config,
+                            const char *name, const char *comment)
+{
+       snd_pcm_t *pcm1, *pcm2;
+       int err1, err2, playback, capture, err;
+       char name1[32], name2[32], *flag;
+       
+       if (!debugflag)
+               snd_lib_error_set_handler(dummy_error_handler);
+       sprintf(name1, name, card);
+       sprintf(name2, "plug:%s", name1);
+       err1 = snd_pcm_open(&pcm1, name1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+       err2 = snd_pcm_open(&pcm2, name1, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
+       snd_lib_error_set_handler(NULL);
+       if (err1 >= 0)
+               snd_pcm_close(pcm1);
+       if (err2 >= 0)
+               snd_pcm_close(pcm2);
+       playback = (err1 == 0 || err1 == -EBUSY);
+       capture = (err2 == 0 || err2 == -EBUSY);
+       if (playback && capture)
+               flag = "Duplex";
+       else if (playback)
+               flag = "Playback";
+       else if (capture)
+               flag = "Capture";
+       else
+               return 0;
+       err = add_entry(config, name1, "Abstract Device", flag, comment);
+       if (err >= 0)
+               err = add_entry(config, name2, "Abstract Device With Conversions", flag, comment);
+       return err;
+}
+
+static int probe_pcm_card(int card, snd_ctl_t *ctl, snd_config_t *config)
+{
+       int dev = -1, err, err1, err2;
+       snd_pcm_info_t * info1, * info2;
+       snd_pcm_class_t class;
+       char name[16];
+       const char *dname;
+       char *flag;
+       int first = 1, idx;
+       static const char *vnames1[] = {
+               "default:%i", "Default Device",
+               "front:%i", "Front Speakers",
+               "rear:%i", "Rear Speakers",
+               NULL
+       };
+       static const char *vnames2[] = {
+               "surround40:%i", "Front and Rear Speakers",
+               "surround51:%i", "Front, Rear, Center and Woofer",
+               "surround71:%i", "Front, Rear, Side, Center and Woofer",
+               "spdif:%i", "S/PDIF (IEC958) Optical or Coaxial Wire",
+               NULL
+       };
+       
+       snd_pcm_info_alloca(&info1);
+       snd_pcm_info_alloca(&info2);
+       while (1) {
+               err = snd_ctl_pcm_next_device(ctl, &dev);
+               if (err < 0)
+                       return err;
+               if (dev < 0)
+                       break;
+               memset(info1, 0, snd_pcm_info_sizeof());
+               snd_pcm_info_set_device(info1, dev);
+               snd_pcm_info_set_stream(info1, SND_PCM_STREAM_PLAYBACK);
+               err1 = snd_ctl_pcm_info(ctl, info1);
+               memset(info2, 0, snd_pcm_info_sizeof());
+               snd_pcm_info_set_device(info2, dev);
+               snd_pcm_info_set_stream(info2, SND_PCM_STREAM_CAPTURE);
+               err2 = snd_ctl_pcm_info(ctl, info2);
+               if (err1 < 0 && err2 < 0) {
+                       error("Unable to get info for pcm device %i:%i: %s\n", card, dev, snd_strerror(err1));
+                       continue;
+               }
+               dname = snd_pcm_info_get_name(info1);
+               class = snd_pcm_info_get_class(info1);
+               if (err1 == 0 && err2 == 0)
+                       flag = "Duplex";
+               else if (err1 == 0)
+                       flag = "Playback";
+               else {
+                       flag = "Capture";
+                       dname = snd_pcm_info_get_name(info2);
+                       class = snd_pcm_info_get_class(info2);
+               }
+               if (class != SND_PCM_CLASS_GENERIC &&
+                   class != SND_PCM_CLASS_MULTI)       /* skip this */
+                       continue;
+               if (first) {
+                       for (idx = 0; vnames1[idx] != NULL; idx += 2)
+                               probe_pcm_virtual(card, ctl, config, vnames1[idx], vnames1[idx+1]);
+               }
+               first = 0;
+               sprintf(name, "hw:%i,%i", card, dev);
+               err = add_entry(config, name, "Physical Device", flag, dname);
+               if (err < 0)
+                       return err;
+               sprintf(name, "plughw:%i,%i", card, dev);
+               err = add_entry(config, name, "Physical Device With Conversions", flag, dname);
+               if (err < 0)
+                       return err;
+       }
+       if (!first) {
+               for (idx = 0; vnames2[idx] != NULL; idx += 2)
+                       probe_pcm_virtual(card, ctl, config, vnames2[idx], vnames2[idx+1]);
+       }
+       return 0;
+}
+
+static int probe_pcm(snd_config_t *config)
+{
+       int err;
+       snd_config_t *c;
+
+       err = snd_config_make_compound(&c, "pcm", 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(config, c);
+       if (err < 0)
+               return err;
+       err = for_each_card(probe_pcm_card, c);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_rawmidi_card(int card, snd_ctl_t *ctl, snd_config_t *config)
+{
+       int dev = -1, err, err1, err2;
+       snd_rawmidi_info_t * info1, * info2;
+       char name[16];
+       const char *dname;
+       char *flag;
+       int first = 1, idx;
+       static const char *vnames1[] = {
+               "default:%i", "Default Device",
+               NULL
+       };
+       
+       snd_rawmidi_info_alloca(&info1);
+       snd_rawmidi_info_alloca(&info2);
+       while (1) {
+               err = snd_ctl_rawmidi_next_device(ctl, &dev);
+               if (err < 0)
+                       return err;
+               if (dev < 0)
+                       break;
+               memset(info1, 0, snd_rawmidi_info_sizeof());
+               snd_rawmidi_info_set_device(info1, dev);
+               snd_rawmidi_info_set_stream(info1, SND_RAWMIDI_STREAM_OUTPUT);
+               err1 = snd_ctl_rawmidi_info(ctl, info1);
+               memset(info2, 0, snd_rawmidi_info_sizeof());
+               snd_rawmidi_info_set_device(info2, dev);
+               snd_rawmidi_info_set_stream(info2, SND_RAWMIDI_STREAM_INPUT);
+               err2 = snd_ctl_rawmidi_info(ctl, info2);
+               if (err1 < 0 && err2 < 0) {
+                       error("Unable to get info for rawmidi device %i:%i: %s\n", card, dev, snd_strerror(err1));
+                       continue;
+               }
+               dname = snd_rawmidi_info_get_name(info1);
+               if (err1 == 0 && err2 == 0)
+                       flag = "Duplex";
+               else if (err1 == 0)
+                       flag = "Output";
+               else {
+                       flag = "Input";
+                       dname = snd_rawmidi_info_get_name(info2);
+               }
+               if (first) {
+                       for (idx = 0; vnames1[idx] != NULL; idx += 2)
+                               probe_pcm_virtual(card, ctl, config, vnames1[idx], vnames1[idx+1]);
+               }
+               first = 0;
+               sprintf(name, "hw:%i,%i", card, dev);
+               err = add_entry(config, name, "Physical Device", flag, dname);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int probe_rawmidi(snd_config_t *config)
+{
+       int err;
+       snd_config_t *c;
+
+       err = snd_config_make_compound(&c, "rawmidi", 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(config, c);
+       if (err < 0)
+               return err;
+       err = for_each_card(probe_rawmidi_card, c);
+       if (err < 0)
+               return err;
+       err = add_entry(c, "virtual", "Virtual Device", "Duplex", "Sequencer");
+       if (err < 0)
+               return err;
+       err = add_entry(c, "virtual:MERGE=0", "Virtual Device", "Duplex", "Sequencer (No Merge)");
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_timers(snd_config_t *config)
+{
+       int err;
+       snd_timer_query_t *handle;
+       snd_timer_id_t *id;
+       snd_timer_ginfo_t *info;
+       char name[64];
+       const char *dname;
+
+       err = snd_timer_query_open(&handle, "default", 0);
+       if (err < 0)
+               return err;
+       snd_timer_id_alloca(&id);
+       snd_timer_ginfo_alloca(&info);
+       snd_timer_id_set_class(id, SND_TIMER_CLASS_NONE);
+       while (1) {
+               err = snd_timer_query_next_device(handle, id);
+               if (err < 0)
+                       goto _err;
+               if (snd_timer_id_get_class(id) < 0)
+                       break;
+               if (snd_timer_id_get_class(id) == SND_TIMER_CLASS_PCM)
+                       continue;
+               snd_timer_ginfo_set_tid(info, id);
+               err = snd_timer_query_info(handle, info);
+               if (err < 0)
+                       continue;
+               sprintf(name, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
+                       snd_timer_id_get_class(id),
+                       snd_timer_id_get_sclass(id),
+                       snd_timer_id_get_card(id),
+                       snd_timer_id_get_device(id),
+                       snd_timer_id_get_subdevice(id));
+               dname = snd_timer_ginfo_get_name(info);
+               err = add_entry(config, name, "Physical Device", NULL, dname);
+               if (err < 0)
+                       goto _err;
+       }
+       err = 0;
+       _err:
+       snd_timer_query_close(handle);
+       return err;
+}
+
+static int probe_timer(snd_config_t *config)
+{
+       int err;
+       snd_config_t *c;
+
+       err = snd_config_make_compound(&c, "timer", 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(config, c);
+       if (err < 0)
+               return err;
+       err = probe_timers(c);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+static int probe_seq(snd_config_t *config)
+{
+       int err;
+       snd_config_t *c;
+
+       err = snd_config_make_compound(&c, "seq", 0);
+       if (err < 0)
+               return err;
+       err = snd_config_add(config, c);
+       if (err < 0)
+               return err;
+       err = add_entry(c, "default", "Default Device", "Duplex", "Sequencer");
+       if (err < 0)
+               return err;
+       err = add_entry(c, "seq", "Physical Device", "Duplex", "Sequencer");
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+typedef int (probe_fcn)(snd_config_t *config);
+
+static probe_fcn * probes[] = {
+       probe_ctl,
+       probe_pcm,
+       probe_rawmidi,
+       probe_timer,
+       probe_seq,
+       NULL
+};
+
+int generate_names(const char *cfgfile)
+{
+       int err, idx;
+       snd_config_t *config;
+       snd_output_t *out;
+       int stdio, ok = 0;
+
+       err = snd_config_top(&config);
+       if (err < 0) {
+               error("snd_config_top error: %s", snd_strerror(err));
+               return err;
+       }
+       for (idx = 0; probes[idx]; idx++) {
+               globidx = 1;
+               err = (probes[idx])(config);
+               if (err < 0) {
+                       error("probe %i failed: %s", idx, snd_strerror(err));
+               } else {
+                       ok++;
+               }
+       }
+       if (ok > 0) {
+               stdio = !strcmp(cfgfile, "-");
+               if (stdio) {
+                       err = snd_output_stdio_attach(&out, stdout, 0);
+               } else {
+                       err = snd_output_stdio_open(&out, cfgfile, "w+");
+               }
+               if (err < 0) {
+                       error("Cannot open %s for writing", cfgfile);
+                       return -errno;
+               }
+               err = snd_config_save(config, out);
+               snd_output_close(out);
+               if (err < 0)
+                       error("snd_config_save: %s", snd_strerror(err));
+       } else {
+               return -ENOENT;
+       }
+       return 0;
+}
diff --git a/alsactl/power.c b/alsactl/power.c
new file mode 100644 (file)
index 0000000..ee6472c
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program
+ *  Copyright (c) Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+
+static int get_int_state(const char *str)
+{
+       if (!strcasecmp(str, "on"))
+               return SND_CTL_POWER_D0;
+       if (!strcasecmp(str, "off"))
+               return SND_CTL_POWER_D3hot;
+       if (*str == 'D' || *str == 'd') {
+               str++;
+               if (!strcmp(str, "0"))
+                       return SND_CTL_POWER_D0;
+               if (!strcmp(str, "1"))
+                       return SND_CTL_POWER_D1;
+               if (!strcmp(str, "2"))
+                       return SND_CTL_POWER_D2;
+               if (!strcmp(str, "3"))
+                       return SND_CTL_POWER_D3;
+               if (!strcmp(str, "3hot"))
+                       return SND_CTL_POWER_D3hot;
+               if (!strcmp(str, "3cold"))
+                       return SND_CTL_POWER_D3cold;
+       }
+       return -1;
+}
+
+static const char *get_str_state(int power_state)
+{
+       static char str[16];
+
+       switch (power_state) {
+       case SND_CTL_POWER_D0:
+               return "D0";
+       case SND_CTL_POWER_D1:
+               return "D1";
+       case SND_CTL_POWER_D2:
+               return "D2";
+       // return SND_CTL_POWER_D3;     /* it's same as D3hot */
+       case SND_CTL_POWER_D3hot:
+               return "D3hot";
+       case SND_CTL_POWER_D3cold:
+               return "D3cold";
+       default:
+               sprintf(str, "???0x%x", power_state);
+               return str;
+       }
+}
+
+static int show_power(int cardno)
+{
+       snd_ctl_t *handle;
+       char name[16];
+       unsigned int power_state;
+       int err;
+
+       sprintf(name, "hw:%d", cardno);
+       err = snd_ctl_open(&handle, name, 0);
+       if (err < 0) {
+               error("snd_ctl_open error: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_ctl_get_power_state(handle, &power_state);
+       if (err < 0) {
+               error("snd_ctl_get_power_state error: %s", snd_strerror(err));
+               snd_ctl_close(handle);
+               return err;
+       }
+       snd_ctl_close(handle);
+       printf("Power state for card #%d is %s\n", cardno, get_str_state(power_state));
+       return 0;
+}
+
+static int set_power(int cardno, unsigned int power_state)
+{
+       snd_ctl_t *handle;
+       char name[16];
+       int err;
+
+       sprintf(name, "hw:%d", cardno);
+       err = snd_ctl_open(&handle, name, 0);
+       if (err < 0) {
+               error("snd_ctl_open error: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_ctl_set_power_state(handle, power_state);
+       if (err < 0) {
+               error("snd_ctl_set_power_state error: %s", snd_strerror(err));
+               snd_ctl_close(handle);
+               return err;
+       }
+       err = snd_ctl_get_power_state(handle, &power_state);
+       if (err < 0) {
+               error("snd_ctl_get_power_state error: %s", snd_strerror(err));
+               snd_ctl_close(handle);
+               return err;
+       }
+       snd_ctl_close(handle);
+       printf("Power state for card #%d is %s\n", cardno, get_str_state(power_state));
+       return 0;
+}
+
+
+int power(const char *argv[], int argc)
+{
+       int power_state, err;
+       
+       if (argc == 0) {                /* show status only */
+               int card, first = 1;
+
+               card = -1;
+               /* find each installed soundcards */
+               while (1) {
+                       if (snd_card_next(&card) < 0)
+                               break;
+                       if (card < 0) {
+                               if (first) {
+                                       error("No soundcards found...");
+                                       return -ENODEV;
+                               }
+                               break;
+                       }
+                       first = 0;
+                       if ((err = show_power(card)) < 0)
+                               return err;
+               }
+               return 0;
+       }
+       power_state = get_int_state(argv[0]);
+       if (power_state >= 0) {
+               int card, first = 1;
+
+               card = -1;
+               /* find each installed soundcards */
+               while (1) {
+                       if (snd_card_next(&card) < 0)
+                               break;
+                       if (card < 0) {
+                               if (first) {
+                                       error("No soundcards found...");
+                                       return -ENODEV;
+                               }
+                               break;
+                       }
+                       first = 0;
+                       if ((err = set_power(card, power_state)))
+                               return err;
+               }
+       } else {
+               int cardno;
+
+               cardno = snd_card_get_index(argv[0]);
+               if (cardno < 0) {
+                       error("Cannot find soundcard '%s'...", argv[0]);
+                       return -ENODEV;
+               }
+               if (argc > 1) {
+                       power_state = get_int_state(argv[1]);
+                       if (power_state < 0) {
+                               error("Invalid power state '%s'...", argv[1]);
+                               return -EINVAL;
+                       }
+                       if ((err = set_power(cardno, power_state)) < 0)
+                               return err;
+               } else {
+                       if ((err = show_power(cardno)) < 0)
+                               return err;
+               }
+       }
+       return 0;
+}
diff --git a/alsactl/state.c b/alsactl/state.c
new file mode 100644 (file)
index 0000000..70c8585
--- /dev/null
@@ -0,0 +1,1258 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program
+ *  Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
+ *                   Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+
+char *id_str(snd_ctl_elem_id_t *id)
+{
+       static char str[128];
+       assert(id);
+       sprintf(str, "%i,%i,%i,%s,%i", 
+               snd_ctl_elem_id_get_interface(id),
+               snd_ctl_elem_id_get_device(id),
+               snd_ctl_elem_id_get_subdevice(id),
+               snd_ctl_elem_id_get_name(id),
+               snd_ctl_elem_id_get_index(id));
+       return str;
+}
+
+char *num_str(long n)
+{
+       static char str[32];
+       sprintf(str, "%ld", n);
+       return str;
+}
+
+static int snd_config_integer_add(snd_config_t *father, char *id, long integer)
+{
+       int err;
+       snd_config_t *leaf;
+       err = snd_config_make_integer(&leaf, id);
+       if (err < 0)
+               return err;
+       err = snd_config_add(father, leaf);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       err = snd_config_set_integer(leaf, integer);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       return 0;
+}
+
+static int snd_config_integer64_add(snd_config_t *father, char *id, long long integer)
+{
+       int err;
+       snd_config_t *leaf;
+       err = snd_config_make_integer64(&leaf, id);
+       if (err < 0)
+               return err;
+       err = snd_config_add(father, leaf);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       err = snd_config_set_integer64(leaf, integer);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       return 0;
+}
+
+static int snd_config_string_add(snd_config_t *father, const char *id, const char *string)
+{
+       int err;
+       snd_config_t *leaf;
+       err = snd_config_make_string(&leaf, id);
+       if (err < 0)
+               return err;
+       err = snd_config_add(father, leaf);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       err = snd_config_set_string(leaf, string);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       return 0;
+}
+
+static int snd_config_compound_add(snd_config_t *father, const char *id, int join,
+                                  snd_config_t **node)
+{
+       int err;
+       snd_config_t *leaf;
+       err = snd_config_make_compound(&leaf, id, join);
+       if (err < 0)
+               return err;
+       err = snd_config_add(father, leaf);
+       if (err < 0) {
+               snd_config_delete(leaf);
+               return err;
+       }
+       *node = leaf;
+       return 0;
+}
+
+static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top)
+{
+       snd_ctl_elem_value_t *ctl;
+       snd_ctl_elem_info_t *info;
+       snd_config_t *control, *comment, *item, *value;
+       const char *s;
+       char buf[256];
+       unsigned int idx;
+       int err;
+       unsigned int device, subdevice, index;
+       const char *name;
+       snd_ctl_elem_type_t type;
+       unsigned int count;
+       snd_ctl_elem_value_alloca(&ctl);
+       snd_ctl_elem_info_alloca(&info);
+       snd_ctl_elem_info_set_id(info, id);
+       err = snd_ctl_elem_info(handle, info);
+       if (err < 0) {
+               error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err));
+               return err;
+       }
+
+       if (!snd_ctl_elem_info_is_readable(info))
+               return 0;
+       snd_ctl_elem_value_set_id(ctl, id);
+       err = snd_ctl_elem_read(handle, ctl);
+       if (err < 0) {
+               error("Cannot read control '%s': %s", id_str(id), snd_strerror(err));
+               return err;
+       }
+
+       err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control);
+       if (err < 0) {
+               error("snd_config_compound_add: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_config_compound_add(control, "comment", 1, &comment);
+       if (err < 0) {
+               error("snd_config_compound_add: %s", snd_strerror(err));
+               return err;
+       }
+
+       buf[0] = '\0';
+       buf[1] = '\0';
+       if (snd_ctl_elem_info_is_readable(info))
+               strcat(buf, " read");
+       if (snd_ctl_elem_info_is_writable(info))
+               strcat(buf, " write");
+       if (snd_ctl_elem_info_is_inactive(info))
+               strcat(buf, " inactive");
+       if (snd_ctl_elem_info_is_volatile(info))
+               strcat(buf, " volatile");
+       if (snd_ctl_elem_info_is_locked(info))
+               strcat(buf, " locked");
+       if (snd_ctl_elem_info_is_user(info))
+               strcat(buf, " user");
+       err = snd_config_string_add(comment, "access", buf + 1);
+       if (err < 0) {
+               error("snd_config_string_add: %s", snd_strerror(err));
+               return err;
+       }
+
+       type = snd_ctl_elem_info_get_type(info);
+       device = snd_ctl_elem_info_get_device(info);
+       subdevice = snd_ctl_elem_info_get_subdevice(info);
+       index = snd_ctl_elem_info_get_index(info);
+       name = snd_ctl_elem_info_get_name(info);
+       count = snd_ctl_elem_info_get_count(info);
+       s = snd_ctl_elem_type_name(type);
+       err = snd_config_string_add(comment, "type", s);
+       if (err < 0) {
+               error("snd_config_string_add: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_config_integer_add(comment, "count", count);
+       if (err < 0) {
+               error("snd_config_integer_add: %s", snd_strerror(err));
+               return err;
+       }
+
+       switch (type) {
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               break;
+       case SND_CTL_ELEM_TYPE_INTEGER:
+       {
+               long min = snd_ctl_elem_info_get_min(info);
+               long max = snd_ctl_elem_info_get_max(info);
+               long step = snd_ctl_elem_info_get_step(info);
+               if (step)
+                       sprintf(buf, "%li - %li (step %li)", min, max, step);
+               else
+                       sprintf(buf, "%li - %li", min, max);
+               err = snd_config_string_add(comment, "range", buf);
+               if (err < 0) {
+                       error("snd_config_string_add: %s", snd_strerror(err));
+                       return err;
+               }
+               break;
+       }
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+       {
+               long long min = snd_ctl_elem_info_get_min64(info);
+               long long max = snd_ctl_elem_info_get_max64(info);
+               long long step = snd_ctl_elem_info_get_step64(info);
+               if (step)
+                       sprintf(buf, "%Li - %Li (step %Li)", min, max, step);
+               else
+                       sprintf(buf, "%Li - %Li", min, max);
+               err = snd_config_string_add(comment, "range", buf);
+               if (err < 0) {
+                       error("snd_config_string_add: %s", snd_strerror(err));
+                       return err;
+               }
+               break;
+       }
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+       {
+               unsigned int items;
+               err = snd_config_compound_add(comment, "item", 1, &item);
+               if (err < 0) {
+                       error("snd_config_compound_add: %s", snd_strerror(err));
+                       return err;
+               }
+               items = snd_ctl_elem_info_get_items(info);
+               for (idx = 0; idx < items; idx++) {
+                       snd_ctl_elem_info_set_item(info, idx);
+                       err = snd_ctl_elem_info(handle, info);
+                       if (err < 0) {
+                               error("snd_ctl_card_info: %s", snd_strerror(err));
+                               return err;
+                       }
+                       err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info));
+                       if (err < 0) {
+                               error("snd_config_string_add: %s", snd_strerror(err));
+                               return err;
+                       }
+               }
+               break;
+       }
+       default:
+               break;
+       }
+       s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info));
+       err = snd_config_string_add(control, "iface", s);
+       if (err < 0) {
+               error("snd_config_string_add: %s", snd_strerror(err));
+               return err;
+       }
+       if (device != 0) {
+               err = snd_config_integer_add(control, "device", device);
+               if (err < 0) {
+                       error("snd_config_integer_add: %s", snd_strerror(err));
+                       return err;
+               }
+       }
+       if (subdevice != 0) {
+               err = snd_config_integer_add(control, "subdevice", subdevice);
+               if (err < 0) {
+                       error("snd_config_integer_add: %s", snd_strerror(err));
+                       return err;
+               }
+       }
+       err = snd_config_string_add(control, "name", name);
+       if (err < 0) {
+               error("snd_config_string_add: %s", snd_strerror(err));
+               return err;
+       }
+       if (index != 0) {
+               err = snd_config_integer_add(control, "index", index);
+               if (err < 0) {
+                       error("snd_config_integer_add: %s", snd_strerror(err));
+                       return err;
+               }
+       }
+
+       switch (type) {
+       case SND_CTL_ELEM_TYPE_BYTES:
+       case SND_CTL_ELEM_TYPE_IEC958:
+       {
+               size_t size = type == SND_CTL_ELEM_TYPE_BYTES ?
+                       count : sizeof(snd_aes_iec958_t);
+               char buf[size * 2 + 1];
+               char *p = buf;
+               char *hex = "0123456789abcdef";
+               const unsigned char *bytes = 
+                 (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl);
+               for (idx = 0; idx < size; idx++) {
+                       int v = bytes[idx];
+                       *p++ = hex[v >> 4];
+                       *p++ = hex[v & 0x0f];
+               }
+               *p = '\0';
+               err = snd_config_string_add(control, "value", buf);
+               if (err < 0) {
+                       error("snd_config_string_add: %s", snd_strerror(err));
+                       return err;
+               }
+               return 0;
+       }
+       default:
+               break;
+       }
+
+       if (count == 1) {
+               switch (type) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false");
+                       if (err < 0) {
+                               error("snd_config_string_add: %s", snd_strerror(err));
+                               return err;
+                       }
+                       return 0;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0));
+                       if (err < 0) {
+                               error("snd_config_integer_add: %s", snd_strerror(err));
+                               return err;
+                       }
+                       return 0;
+               case SND_CTL_ELEM_TYPE_INTEGER64:
+                       err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0));
+                       if (err < 0) {
+                               error("snd_config_integer64_add: %s", snd_strerror(err));
+                               return err;
+                       }
+                       return 0;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+               {
+                       unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0);
+                       snd_config_t *c;
+                       err = snd_config_search(item, num_str(v), &c);
+                       if (err == 0) {
+                               err = snd_config_get_string(c, &s);
+                               assert(err == 0);
+                               err = snd_config_string_add(control, "value", s);
+                       } else {
+                               err = snd_config_integer_add(control, "value", v);
+                       }
+                       if (err < 0)
+                               error("snd_config add: %s", snd_strerror(err));
+                       return 0;
+               }
+               default:
+                       error("Unknown control type: %d\n", type);
+                       return -EINVAL;
+               }
+       }
+
+       err = snd_config_compound_add(control, "value", 1, &value);
+       if (err < 0) {
+               error("snd_config_compound_add: %s", snd_strerror(err));
+               return err;
+       }
+
+       switch (type) {
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               for (idx = 0; idx < count; idx++) {
+                       err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false");
+                       if (err < 0) {
+                               error("snd_config_string_add: %s", snd_strerror(err));
+                               return err;
+                       }
+               }
+               break;
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               for (idx = 0; idx < count; idx++) {
+                       err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx));
+                       if (err < 0) {
+                               error("snd_config_integer_add: %s", snd_strerror(err));
+                               return err;
+                       }
+               }
+               break;
+       case SND_CTL_ELEM_TYPE_INTEGER64:
+               for (idx = 0; idx < count; idx++) {
+                       err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx));
+                       if (err < 0) {
+                               error("snd_config_integer64_add: %s", snd_strerror(err));
+                               return err;
+                       }
+               }
+               break;
+       case SND_CTL_ELEM_TYPE_ENUMERATED:
+               for (idx = 0; idx < count; idx++) {
+                       unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx);
+                       snd_config_t *c;
+                       err = snd_config_search(item, num_str(v), &c);
+                       if (err == 0) {
+                               err = snd_config_get_string(c, &s);
+                               assert(err == 0);
+                               err = snd_config_string_add(value, num_str(idx), s);
+                       } else {
+                               err = snd_config_integer_add(value, num_str(idx), v);
+                       }
+                       if (err < 0) {
+                               error("snd_config add: %s", snd_strerror(err));
+                               return err;
+                       }
+               }
+               break;
+       default:
+               error("Unknown control type: %d\n", type);
+               return -EINVAL;
+       }
+       
+       return 0;
+}
+       
+static int get_controls(int cardno, snd_config_t *top)
+{
+       snd_ctl_t *handle;
+       snd_ctl_card_info_t *info;
+       snd_config_t *state, *card, *control;
+       snd_ctl_elem_list_t *list;
+       unsigned int idx;
+       int err;
+       char name[32];
+       unsigned int count;
+       const char *id;
+       snd_ctl_card_info_alloca(&info);
+       snd_ctl_elem_list_alloca(&list);
+
+       sprintf(name, "hw:%d", cardno);
+       err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
+       if (err < 0) {
+               error("snd_ctl_open error: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_ctl_card_info(handle, info);
+       if (err < 0) {
+               error("snd_ctl_card_info error: %s", snd_strerror(err));
+               goto _close;
+       }
+       id = snd_ctl_card_info_get_id(info);
+       err = snd_config_search(top, "state", &state);
+       if (err == 0 &&
+           snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) {
+               error("config state node is not a compound");
+               err = -EINVAL;
+               goto _close;
+       }
+       if (err < 0) {
+               err = snd_config_compound_add(top, "state", 1, &state);
+               if (err < 0) {
+                       error("snd_config_compound_add: %s", snd_strerror(err));
+                       goto _close;
+               }
+       }
+       err = snd_config_search(state, id, &card);
+       if (err == 0 &&
+           snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) {
+               error("config state.%s node is not a compound", id);
+               err = -EINVAL;
+               goto _close;
+       }
+       if (err < 0) {
+               err = snd_config_compound_add(state, id, 0, &card);
+               if (err < 0) {
+                       error("snd_config_compound_add: %s", snd_strerror(err));
+                       goto _close;
+               }
+       }
+       err = snd_config_search(card, "control", &control);
+       if (err == 0) {
+               err = snd_config_delete(control);
+               if (err < 0) {
+                       error("snd_config_delete: %s", snd_strerror(err));
+                       goto _close;
+               }
+       }
+       err = snd_ctl_elem_list(handle, list);
+       if (err < 0) {
+               error("Cannot determine controls: %s", snd_strerror(err));
+               goto _close;
+       }
+       count = snd_ctl_elem_list_get_count(list);
+       if (count < 0) {
+               err = 0;
+               goto _close;
+       }
+       err = snd_config_compound_add(card, "control", count > 0, &control);
+       if (err < 0) {
+               error("snd_config_compound_add: %s", snd_strerror(err));
+               goto _close;
+       }
+       if (count == 0) {
+               err = 0;
+               goto _close;
+       }
+       snd_ctl_elem_list_set_offset(list, 0);
+       if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
+               error("No enough memory...");
+               goto _close;
+       }
+       if ((err = snd_ctl_elem_list(handle, list)) < 0) {
+               error("Cannot determine controls (2): %s", snd_strerror(err));
+               goto _free;
+       }
+       for (idx = 0; idx < count; ++idx) {
+               snd_ctl_elem_id_t *id;
+               snd_ctl_elem_id_alloca(&id);
+               snd_ctl_elem_list_get_id(list, idx, id);
+               err = get_control(handle, id, control);
+               if (err < 0)
+                       goto _free;
+       }               
+               
+       err = 0;
+ _free:
+       snd_ctl_elem_list_free_space(list);
+ _close:
+       snd_ctl_close(handle);
+       return err;
+}
+
+static long config_iface(snd_config_t *n)
+{
+       long i;
+       long long li;
+       snd_ctl_elem_iface_t idx; 
+       const char *str;
+       switch (snd_config_get_type(n)) {
+       case SND_CONFIG_TYPE_INTEGER:
+               snd_config_get_integer(n, &i);
+               return i;
+       case SND_CONFIG_TYPE_INTEGER64:
+               snd_config_get_integer64(n, &li);
+               return li;
+       case SND_CONFIG_TYPE_STRING:
+               snd_config_get_string(n, &str);
+               break;
+       default:
+               return -1;
+       }
+       for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
+               if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
+                       return idx;
+       }
+       return -1;
+}
+
+static int config_bool(snd_config_t *n)
+{
+       const char *str;
+       long val;
+       long long lval;
+       switch (snd_config_get_type(n)) {
+       case SND_CONFIG_TYPE_INTEGER:
+               snd_config_get_integer(n, &val);
+               if (val < 0 || val > 1)
+                       return -1;
+               return val;
+       case SND_CONFIG_TYPE_INTEGER64:
+               snd_config_get_integer64(n, &lval);
+               if (lval < 0 || lval > 1)
+                       return -1;
+               return (int) lval;
+       case SND_CONFIG_TYPE_STRING:
+               snd_config_get_string(n, &str);
+               break;
+       default:
+               return -1;
+       }
+       if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0)
+               return 1;
+       if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0)
+               return 0;
+       return -1;
+}
+
+static int config_enumerated(snd_config_t *n, snd_ctl_t *handle,
+                            snd_ctl_elem_info_t *info)
+{
+       const char *str;
+       long val;
+       long long lval;
+       unsigned int idx, items;
+       switch (snd_config_get_type(n)) {
+       case SND_CONFIG_TYPE_INTEGER:
+               snd_config_get_integer(n, &val);
+               return val;
+       case SND_CONFIG_TYPE_INTEGER64:
+               snd_config_get_integer64(n, &lval);
+               return (int) lval;
+       case SND_CONFIG_TYPE_STRING:
+               snd_config_get_string(n, &str);
+               break;
+       default:
+               return -1;
+       }
+       items = snd_ctl_elem_info_get_items(info);
+       for (idx = 0; idx < items; idx++) {
+               int err;
+               snd_ctl_elem_info_set_item(info, idx);
+               err = snd_ctl_elem_info(handle, info);
+               if (err < 0) {
+                       error("snd_ctl_elem_info: %s", snd_strerror(err));
+                       return err;
+               }
+               if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
+                       return idx;
+       }
+       return -1;
+}
+
+static int is_user_control(snd_config_t *conf)
+{
+       snd_config_iterator_t i, next;
+
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id, *s;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "access") == 0) {
+                       if (snd_config_get_string(n, &s) < 0)
+                               return 0;
+                       if (strstr(s, "user"))
+                               return 1;
+               }
+       }
+       return 0;
+}
+
+static int add_user_control(snd_ctl_t *handle, snd_ctl_elem_info_t *info, snd_config_t *conf)
+{
+       snd_ctl_elem_id_t *id;
+       snd_config_iterator_t i, next;
+       long imin, imax, istep;
+       snd_ctl_elem_type_t ctype;
+       unsigned int count;
+       int err;
+
+       imin = imax = istep = 0;
+       count = 0;
+       ctype = SND_CTL_ELEM_TYPE_NONE;
+       snd_config_for_each(i, next, conf) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id, *type;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               if (strcmp(id, "type") == 0) {
+                       if ((err = snd_config_get_string(n, &type)) < 0)
+                               return -EINVAL;
+                       if (strcmp(type, "BOOLEAN") == 0)
+                               ctype = SND_CTL_ELEM_TYPE_BOOLEAN;
+                       else if (strcmp(type, "INTEGER") == 0)
+                               ctype = SND_CTL_ELEM_TYPE_INTEGER;
+                       else if (strcmp(type, "IEC958") == 0)
+                               ctype = SND_CTL_ELEM_TYPE_IEC958;
+                       else
+                               return -EINVAL;
+                       continue;
+               }
+               if (strcmp(id, "range") == 0) {
+                       const char *s;
+                       if ((err = snd_config_get_string(n, &s)) < 0)
+                               return -EINVAL;
+                       switch (ctype) {
+                       case SND_CTL_ELEM_TYPE_INTEGER:
+                               err = sscanf(s, "%li - %li (step %li)", &imin, &imax, &istep);
+                               if (err != 3) {
+                                       istep = 0;
+                                       err = sscanf(s, "%li - %li", &imin, &imax);
+                                       if (err != 2)
+                                               return -EINVAL;
+                               }
+                               break;
+                       default:
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(id, "count") == 0) {
+                       long v;
+                       if ((err = snd_config_get_integer(n, &v)) < 0)
+                               return err;
+                       count = v;
+                       continue;
+               }
+       }
+
+       snd_ctl_elem_id_alloca(&id);
+       snd_ctl_elem_info_get_id(info, id);
+       if (count <= 0)
+               count = 1;
+       switch (ctype) {
+       case SND_CTL_ELEM_TYPE_INTEGER:
+               if (imin > imax || istep > imax - imin)
+                       return -EINVAL;
+               err = snd_ctl_elem_add_integer(handle, id, count, imin, imax, istep);
+               break;
+       case SND_CTL_ELEM_TYPE_BOOLEAN:
+               err = snd_ctl_elem_add_boolean(handle, id, count);
+               break;
+       case SND_CTL_ELEM_TYPE_IEC958:
+               err = snd_ctl_elem_add_iec958(handle, id);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+
+       if (err < 0)
+               return err;
+       return snd_ctl_elem_info(handle, info);
+}
+
+static int set_control(snd_ctl_t *handle, snd_config_t *control)
+{
+       snd_ctl_elem_value_t *ctl;
+       snd_ctl_elem_info_t *info;
+       snd_config_iterator_t i, next;
+       unsigned int numid1;
+       snd_ctl_elem_iface_t iface = -1;
+       int iface1;
+       const char *name1;
+       unsigned int numid;
+       snd_ctl_elem_type_t type;
+       unsigned int count;
+       long device = -1;
+       long device1;
+       long subdevice = -1;
+       long subdevice1;
+       const char *name = NULL;
+       long index1;
+       long index = -1;
+       snd_config_t *value = NULL;
+       snd_config_t *comment = NULL;
+       long val;
+       long long lval;
+       unsigned int idx;
+       int err;
+       char *set;
+       const char *id;
+       snd_ctl_elem_value_alloca(&ctl);
+       snd_ctl_elem_info_alloca(&info);
+       if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
+               error("control is not a compound");
+               return -EINVAL;
+       }
+       err = snd_config_get_id(control, &id);
+       if (err < 0) {
+               error("unable to get id");
+               return -EINVAL;
+       }
+       numid = atoi(id);
+       snd_config_for_each(i, next, control) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *fld;
+               if (snd_config_get_id(n, &fld) < 0)
+                       continue;
+               if (strcmp(fld, "comment") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       comment = n;
+                       continue;
+               }
+               if (strcmp(fld, "iface") == 0) {
+                       iface = (snd_ctl_elem_iface_t)config_iface(n);
+                       if (iface < 0) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       continue;
+               }
+               if (strcmp(fld, "device") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       snd_config_get_integer(n, &device);
+                       continue;
+               }
+               if (strcmp(fld, "subdevice") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       snd_config_get_integer(n, &subdevice);
+                       continue;
+               }
+               if (strcmp(fld, "name") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       snd_config_get_string(n, &name);
+                       continue;
+               }
+               if (strcmp(fld, "index") == 0) {
+                       if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
+                               error("control.%d.%s is invalid", numid, fld);
+                               return -EINVAL;
+                       }
+                       snd_config_get_integer(n, &index);
+                       continue;
+               }
+               if (strcmp(fld, "value") == 0) {
+                       value = n;
+                       continue;
+               }
+               error("unknown control.%d.%s field", numid, fld);
+       }
+       if (!value) {
+               error("missing control.%d.value", numid);
+               return -EINVAL;
+       }
+       if (device < 0)
+               device = 0;
+       if (subdevice < 0)
+               subdevice = 0;
+       if (index < 0)
+               index = 0;
+
+       err = -EINVAL;
+       if (! force_restore) {
+               snd_ctl_elem_info_set_numid(info, numid);
+               err = snd_ctl_elem_info(handle, info);
+       }
+       if (err < 0) {
+               if (iface >= 0 && name) {
+                       snd_ctl_elem_info_set_numid(info, 0);
+                       snd_ctl_elem_info_set_interface(info, iface);
+                       snd_ctl_elem_info_set_device(info, device);
+                       snd_ctl_elem_info_set_subdevice(info, subdevice);
+                       snd_ctl_elem_info_set_name(info, name);
+                       snd_ctl_elem_info_set_index(info, index);
+                       err = snd_ctl_elem_info(handle, info);
+                       if (err < 0 && comment && is_user_control(comment)) {
+                               err = add_user_control(handle, info, comment);
+                               if (err < 0) {
+                                       error("failed to add user control #%d (%s)",
+                                             numid, snd_strerror(err));
+                                       return err;
+                               }
+                       }
+               }
+       }
+       if (err < 0) {
+               error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
+               return -ENOENT;
+       }
+       numid1 = snd_ctl_elem_info_get_numid(info);
+       iface1 = snd_ctl_elem_info_get_interface(info);
+       device1 = snd_ctl_elem_info_get_device(info);
+       subdevice1 = snd_ctl_elem_info_get_subdevice(info);
+       name1 = snd_ctl_elem_info_get_name(info);
+       index1 = snd_ctl_elem_info_get_index(info);
+       count = snd_ctl_elem_info_get_count(info);
+       type = snd_ctl_elem_info_get_type(info);
+       if (err |= numid != numid1 && ! force_restore)
+               error("warning: numid mismatch (%d/%d) for control #%d", 
+                     numid, numid1, numid);
+       if (err |= iface != iface1)
+               error("warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
+       if (err |= device != device1)
+               error("warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
+       if (err |= subdevice != subdevice1)
+               error("warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid);
+       if (err |= strcmp(name, name1))
+               error("warning: name mismatch (%s/%s) for control #%d", name, name1, numid);
+       if (err |= index != index1)
+               error("warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid);
+       if (err < 0) {
+               error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
+               return -ENOENT;
+       }
+
+#if 0
+       if (comment) {
+               check_comment_type(comment, type);
+               if (type == SND_CTL_ELEM_TYPE_INTEGER ||
+                   type == SND_CTL_ELEM_TYPE_INTEGER64)
+                       check_comment_range(comment, info);
+       }
+#endif
+
+       if (!snd_ctl_elem_info_is_writable(info))
+               return 0;
+       snd_ctl_elem_value_set_numid(ctl, numid1);
+
+       if (count == 1) {
+               switch (type) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       val = config_bool(value);
+                       if (val >= 0) {
+                               snd_ctl_elem_value_set_boolean(ctl, 0, val);
+                               goto _ok;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       err = snd_config_get_integer(value, &val);
+                       if (err == 0) {
+                               snd_ctl_elem_value_set_integer(ctl, 0, val);
+                               goto _ok;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER64:
+                       err = snd_config_get_integer64(value, &lval);
+                       if (err == 0) {
+                               snd_ctl_elem_value_set_integer64(ctl, 0, lval);
+                               goto _ok;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       val = config_enumerated(value, handle, info);
+                       if (val >= 0) {
+                               snd_ctl_elem_value_set_enumerated(ctl, 0, val);
+                               goto _ok;
+                       }
+                       break;
+               case SND_CTL_ELEM_TYPE_BYTES:
+               case SND_CTL_ELEM_TYPE_IEC958:
+                       break;
+               default:
+                       error("Unknow control type: %d", type);
+                       return -EINVAL;
+               }
+       }
+       switch (type) {
+       case SND_CTL_ELEM_TYPE_BYTES:
+       case SND_CTL_ELEM_TYPE_IEC958:
+       {
+               const char *buf;
+               err = snd_config_get_string(value, &buf);
+               if (err >= 0) {
+                       int c1 = 0;
+                       int len = strlen(buf);
+                       unsigned int idx = 0;
+                       int size = type == SND_CTL_ELEM_TYPE_BYTES ?
+                               count : sizeof(snd_aes_iec958_t);
+                       if (size * 2 != len) {
+                               error("bad control.%d.value contents\n", numid);
+                               return -EINVAL;
+                       }
+                       while (*buf) {
+                               int c = *buf++;
+                               if (c >= '0' && c <= '9')
+                                       c -= '0';
+                               else if (c >= 'a' && c <= 'f')
+                                       c = c - 'a' + 10;
+                               else if (c >= 'A' && c <= 'F')
+                                       c = c - 'A' + 10;
+                               else {
+                                       error("bad control.%d.value contents\n", numid);
+                                       return -EINVAL;
+                               }
+                               if (idx % 2 == 1)
+                                       snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c);
+                               else
+                                       c1 = c;
+                               idx++;
+                       }
+                       goto _ok;
+               }
+       }
+       default:
+               break;
+       }
+       if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) {
+               error("bad control.%d.value type", numid);
+               return -EINVAL;
+       }
+
+       set = (char*) alloca(count);
+       memset(set, 0, count);
+       snd_config_for_each(i, next, value) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               const char *id;
+               if (snd_config_get_id(n, &id) < 0)
+                       continue;
+               idx = atoi(id);
+               if (idx < 0 || idx >= count || 
+                   set[idx]) {
+                       error("bad control.%d.value index", numid);
+                       return -EINVAL;
+               }
+               switch (type) {
+               case SND_CTL_ELEM_TYPE_BOOLEAN:
+                       val = config_bool(n);
+                       if (val < 0) {
+                               error("bad control.%d.value.%d content", numid, idx);
+                               return -EINVAL;
+                       }
+                       snd_ctl_elem_value_set_boolean(ctl, idx, val);
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER:
+                       err = snd_config_get_integer(n, &val);
+                       if (err < 0) {
+                               error("bad control.%d.value.%d content", numid, idx);
+                               return -EINVAL;
+                       }
+                       snd_ctl_elem_value_set_integer(ctl, idx, val);
+                       break;
+               case SND_CTL_ELEM_TYPE_INTEGER64:
+                       err = snd_config_get_integer64(n, &lval);
+                       if (err < 0) {
+                               error("bad control.%d.value.%d content", numid, idx);
+                               return -EINVAL;
+                       }
+                       snd_ctl_elem_value_set_integer64(ctl, idx, lval);
+                       break;
+               case SND_CTL_ELEM_TYPE_ENUMERATED:
+                       val = config_enumerated(n, handle, info);
+                       if (val < 0) {
+                               error("bad control.%d.value.%d content", numid, idx);
+                               return -EINVAL;
+                       }
+                       snd_ctl_elem_value_set_enumerated(ctl, idx, val);
+                       break;
+               case SND_CTL_ELEM_TYPE_BYTES:
+               case SND_CTL_ELEM_TYPE_IEC958:
+                       err = snd_config_get_integer(n, &val);
+                       if (err < 0 || val < 0 || val > 255) {
+                               error("bad control.%d.value.%d content", numid, idx);
+                               return -EINVAL;
+                       }
+                       snd_ctl_elem_value_set_byte(ctl, idx, val);
+                       break;
+               default:
+                       break;
+               }
+               set[idx] = 1;
+       }
+       for (idx = 0; idx < count; ++idx) {
+               if (!set[idx]) {
+                       error("control.%d.value.%d is not specified", numid, idx);
+                       return -EINVAL;
+               }
+       }
+
+ _ok:
+       err = snd_ctl_elem_write(handle, ctl);
+       if (err < 0) {
+               error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
+               return err;
+       }
+       return 0;
+}
+
+static int set_controls(int card, snd_config_t *top)
+{
+       snd_ctl_t *handle;
+       snd_ctl_card_info_t *info;
+       snd_config_t *control;
+       snd_config_iterator_t i, next;
+       int err;
+       char name[32], tmpid[16];
+       const char *id;
+       snd_ctl_card_info_alloca(&info);
+
+       sprintf(name, "hw:%d", card);
+       err = snd_ctl_open(&handle, name, 0);
+       if (err < 0) {
+               error("snd_ctl_open error: %s", snd_strerror(err));
+               return err;
+       }
+       err = snd_ctl_card_info(handle, info);
+       if (err < 0) {
+               error("snd_ctl_card_info error: %s", snd_strerror(err));
+               goto _close;
+       }
+       id = snd_ctl_card_info_get_id(info);
+       err = snd_config_searchv(top, &control, "state", id, "control", 0);
+       if (err < 0) {
+               if (force_restore) {
+                       sprintf(tmpid, "card%d", card);
+                       err = snd_config_searchv(top, &control, "state", tmpid, "control", 0);
+                       if (! err)
+                               id = tmpid;
+               }
+               if (err < 0) {
+                       err = 0;
+                       fprintf(stderr, "No state is present for card %s\n", id);
+                       goto _close;
+               }
+               id = tmpid;
+       }
+       if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
+               error("state.%s.control is not a compound\n", id);
+               return -EINVAL;
+       }
+       snd_config_for_each(i, next, control) {
+               snd_config_t *n = snd_config_iterator_entry(i);
+               err = set_control(handle, n);
+               if (err < 0 && ! force_restore)
+                       goto _close;
+       }
+
+ _close:
+       snd_ctl_close(handle);
+       return err;
+}
+
+int save_state(const char *file, const char *cardname)
+{
+       int err;
+       snd_config_t *config;
+       snd_input_t *in;
+       snd_output_t *out;
+       int stdio;
+
+       err = snd_config_top(&config);
+       if (err < 0) {
+               error("snd_config_top error: %s", snd_strerror(err));
+               return err;
+       }
+       stdio = !strcmp(file, "-");
+       if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) {
+               err = snd_config_load(config, in);
+               snd_input_close(in);
+#if 0
+               if (err < 0) {
+                       error("snd_config_load error: %s", snd_strerror(err));
+                       return err;
+               }
+#endif
+       }
+
+       if (!cardname) {
+               int card, first = 1;
+
+               card = -1;
+               /* find each installed soundcards */
+               while (1) {
+                       if (snd_card_next(&card) < 0)
+                               break;
+                       if (card < 0) {
+                               if (first) {
+                                       error("No soundcards found...");
+                                       return -ENODEV;
+                               }
+                               break;
+                       }
+                       first = 0;
+                       if ((err = get_controls(card, config)))
+                               return err;
+               }
+       } else {
+               int cardno;
+
+               cardno = snd_card_get_index(cardname);
+               if (cardno < 0) {
+                       error("Cannot find soundcard '%s'...", cardname);
+                       return cardno;
+               }
+               if ((err = get_controls(cardno, config))) {
+                       return err;
+               }
+       }
+       
+       if (stdio)
+               err = snd_output_stdio_attach(&out, stdout, 0);
+       else
+               err = snd_output_stdio_open(&out, file, "w");
+       if (err < 0) {
+               error("Cannot open %s for writing", file);
+               return -errno;
+       }
+       err = snd_config_save(config, out);
+       snd_output_close(out);
+       if (err < 0)
+               error("snd_config_save: %s", snd_strerror(err));
+       return 0;
+}
+
+int load_state(const char *file, const char *cardname)
+{
+       int err;
+       snd_config_t *config;
+       snd_input_t *in;
+       int stdio;
+
+       err = snd_config_top(&config);
+       if (err < 0) {
+               error("snd_config_top error: %s", snd_strerror(err));
+               return err;
+       }
+       stdio = !strcmp(file, "-");
+       if (stdio)
+               err = snd_input_stdio_attach(&in, stdin, 0);
+       else
+               err = snd_input_stdio_open(&in, file, "r");
+       if (err >= 0) {
+               err = snd_config_load(config, in);
+               snd_input_close(in);
+               if (err < 0) {
+                       error("snd_config_load error: %s", snd_strerror(err));
+                       return err;
+               }
+       }
+
+       if (!cardname) {
+               int card, first = 1;
+
+               card = -1;
+               /* find each installed soundcards */
+               while (1) {
+                       if (snd_card_next(&card) < 0)
+                               break;
+                       if (card < 0) {
+                               if (first) {
+                                       error("No soundcards found...");
+                                       return -ENODEV;
+                               }
+                               break;
+                       }
+                       first = 0;
+                       if ((err = set_controls(card, config)) && ! force_restore)
+                               return err;
+               }
+       } else {
+               int cardno;
+
+               cardno = snd_card_get_index(cardname);
+               if (cardno < 0) {
+                       error("Cannot find soundcard '%s'...", cardname);
+                       return -ENODEV;
+               }
+               if ((err = set_controls(cardno, config)) && ! force_restore) {
+                       return err;
+               }
+       }
+       return 0;
+}