From 31efc323b123a5c9e01c8d825037eae795a31461 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 10 May 2005 10:55:24 +0000 Subject: [PATCH] split source per command and added command 'names' - sources are splitted to separate files - added initial code for 'names' command --- alsactl/Makefile.am | 3 +- alsactl/alsactl.1 | 21 +- alsactl/alsactl.c | 1416 +------------------------------------------ alsactl/alsactl.h | 22 + alsactl/names.c | 492 +++++++++++++++ alsactl/power.c | 200 ++++++ alsactl/state.c | 1258 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 1998 insertions(+), 1414 deletions(-) create mode 100644 alsactl/alsactl.h create mode 100644 alsactl/names.c create mode 100644 alsactl/power.c create mode 100644 alsactl/state.c diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am index 17a0b0c..0a4b36c 100644 --- a/alsactl/Makefile.am +++ b/alsactl/Makefile.am @@ -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 + diff --git a/alsactl/alsactl.1 b/alsactl/alsactl.1 index 36d3f3e..f01ec49 100644 --- a/alsactl/alsactl.1 +++ b/alsactl/alsactl.1 @@ -4,7 +4,7 @@ alsactl \- advanced controls for ALSA soundcard driver .SH SYNOPSIS -\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP] +\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fInames\fP] .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] +\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fInames\fP] .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), diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c index b5833eb..7e7025c 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c @@ -28,33 +28,21 @@ #include #include #include +#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 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 index 0000000..f1e2b41 --- /dev/null +++ b/alsactl/alsactl.h @@ -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 index 0000000..bcf3bf4 --- /dev/null +++ b/alsactl/names.c @@ -0,0 +1,492 @@ +/* + * Advanced Linux Sound Architecture Control Program - ALSA Device Names + * Copyright (c) 2005 by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#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 index 0000000..ee6472c --- /dev/null +++ b/alsactl/power.c @@ -0,0 +1,200 @@ +/* + * Advanced Linux Sound Architecture Control Program + * Copyright (c) Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#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 index 0000000..70c8585 --- /dev/null +++ b/alsactl/state.c @@ -0,0 +1,1258 @@ +/* + * Advanced Linux Sound Architecture Control Program + * Copyright (c) by Abramo Bagnara + * Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#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; +} -- 2.47.1