diff options
Diffstat (limited to 'alsa-utils/alsactl/state.c')
-rw-r--r-- | alsa-utils/alsactl/state.c | 1653 |
1 files changed, 0 insertions, 1653 deletions
diff --git a/alsa-utils/alsactl/state.c b/alsa-utils/alsactl/state.c deleted file mode 100644 index 635a999..0000000 --- a/alsa-utils/alsactl/state.c +++ /dev/null @@ -1,1653 +0,0 @@ -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) by Abramo Bagnara <abramo@alsa-project.org> - * Jaroslav Kysela <perex@perex.cz> - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "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" - - -#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a)[0]) - - -static 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; -} - -static 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; -} - -#define MAX_USER_TLV_SIZE 64 - -static char *tlv_to_str(unsigned int *tlv) -{ - int i, len = tlv[1] / 4 + 2; - char *s, *p; - - if (len >= MAX_USER_TLV_SIZE) - return NULL; - s = malloc(len * 8 + 1); - if (! s) - return NULL; - p = s; - for (i = 0; i < len; i++) { - sprintf(p, "%08x", tlv[i]); - p += 8; - } - return s; -} - -static unsigned int *str_to_tlv(const char *s) -{ - int i, j, c, len; - unsigned int *tlv; - - len = strlen(s); - if (len % 8) /* aligned to 4 bytes (= 8 letters) */ - return NULL; - len /= 8; - if (len > MAX_USER_TLV_SIZE) - return NULL; - tlv = malloc(sizeof(int) * len); - if (! tlv) - return NULL; - for (i = 0; i < len; i++) { - tlv[i] = 0; - for (j = 0; j < 8; j++) { - if ((c = hextodigit(*s++)) < 0) { - free(tlv); - return NULL; - } - tlv[i] = (tlv[i] << 4) | c; - } - } - return tlv; -} - -/* - * add the TLV string and dB ranges to comment fields - */ -static int add_tlv_comments(snd_ctl_t *handle, snd_ctl_elem_id_t *id, - snd_ctl_elem_info_t *info, snd_config_t *comment) -{ - unsigned int tlv[MAX_USER_TLV_SIZE]; - unsigned int *db; - long dbmin, dbmax; - int err; - - if (snd_ctl_elem_tlv_read(handle, id, tlv, sizeof(tlv)) < 0) - return 0; /* ignore error */ - - if (snd_ctl_elem_info_is_tlv_writable(info)) { - char *s = tlv_to_str(tlv); - if (s) { - err = snd_config_string_add(comment, "tlv", s); - if (err < 0) { - error("snd_config_string_add: %s", snd_strerror(err)); - return err; - } - free(s); - } - } - - err = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &db); - if (err <= 0) - return 0; - - snd_tlv_get_dB_range(db, snd_ctl_elem_info_get_min(info), - snd_ctl_elem_info_get_max(info), - &dbmin, &dbmax); - if (err < 0) - return err; - snd_config_integer_add(comment, "dbmin", dbmin); - snd_config_integer_add(comment, "dbmax", dbmax); - 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_inactive(info) || - !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; - } - if (snd_ctl_elem_info_is_tlv_readable(info)) { - err = add_tlv_comments(handle, id, info, comment); - if (err < 0) - 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); - 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, int doit) -{ - 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; - case SND_CONFIG_TYPE_COMPOUND: - if (!force_restore || !doit) - return -1; - n = snd_config_iterator_entry(snd_config_iterator_first(n)); - return config_bool(n, doit); - 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, int doit) -{ - 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; - case SND_CONFIG_TYPE_COMPOUND: - if (!force_restore || !doit) - return -1; - n = snd_config_iterator_entry(snd_config_iterator_first(n)); - return config_enumerated(n, handle, info, doit); - 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 config_integer(snd_config_t *n, long *val, int doit) -{ - int err = snd_config_get_integer(n, val); - if (err < 0 && force_restore && doit) { - if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) - return err; - n = snd_config_iterator_entry(snd_config_iterator_first(n)); - return config_integer(n, val, doit); - } - return err; -} - -static int config_integer64(snd_config_t *n, long long *val, int doit) -{ - int err = snd_config_get_integer64(n, val); - if (err < 0 && force_restore && doit) { - if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) - return err; - n = snd_config_iterator_entry(snd_config_iterator_first(n)); - return config_integer64(n, val, doit); - } - return err; -} - -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; -} - -/* - * get the item type from the given comment config - */ -static int get_comment_type(snd_config_t *n) -{ - static const snd_ctl_elem_type_t types[] = { - SND_CTL_ELEM_TYPE_BOOLEAN, - SND_CTL_ELEM_TYPE_INTEGER, - SND_CTL_ELEM_TYPE_ENUMERATED, - SND_CTL_ELEM_TYPE_BYTES, - SND_CTL_ELEM_TYPE_IEC958, - SND_CTL_ELEM_TYPE_INTEGER64, - }; - const char *type; - unsigned int i; - - if (snd_config_get_string(n, &type) < 0) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(types); ++i) - if (strcmp(type, snd_ctl_elem_type_name(types[i])) == 0) - return types[i]; - return -EINVAL; -} - -/* - * get the value range from the given comment config - */ -static int get_comment_range(snd_config_t *n, int ctype, - long *imin, long *imax, long *istep) -{ - const char *s; - int err; - - if (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; - } - 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; - unsigned int *tlv; - - imin = imax = istep = 0; - count = 0; - ctype = SND_CTL_ELEM_TYPE_NONE; - tlv = NULL; - snd_config_for_each(i, next, conf) { - snd_config_t *n = snd_config_iterator_entry(i); - const char *id; - if (snd_config_get_id(n, &id) < 0) - continue; - if (strcmp(id, "type") == 0) { - err = get_comment_type(n); - if (err < 0) - return err; - ctype = err; - continue; - } - if (strcmp(id, "range") == 0) { - err = get_comment_range(n, ctype, &imin, &imax, &istep); - if (err < 0) - return err; - continue; - } - if (strcmp(id, "count") == 0) { - long v; - if ((err = snd_config_get_integer(n, &v)) < 0) - return err; - count = v; - continue; - } - if (strcmp(id, "tlv") == 0) { - const char *s; - if ((err = snd_config_get_string(n, &s)) < 0) - return -EINVAL; - if (tlv) - free(tlv); - if ((tlv = str_to_tlv(s)) == NULL) - return -EINVAL; - 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); - if (err < 0) - goto error; - if (tlv) - snd_ctl_elem_tlv_write(handle, id, tlv); - 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; - } - - error: - free(tlv); - if (err < 0) - return err; - return snd_ctl_elem_info(handle, info); -} - -/* - * look for a config node with the given item name - */ -static snd_config_t *search_comment_item(snd_config_t *conf, const char *name) -{ - 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; - if (snd_config_get_id(n, &id) < 0) - continue; - if (strcmp(id, name) == 0) - return n; - } - return NULL; -} - -/* - * check whether the config item has the same of compatible type - */ -static int check_comment_type(snd_config_t *conf, int type) -{ - snd_config_t *n = search_comment_item(conf, "type"); - int ctype; - - if (!n) - return 0; /* not defined */ - ctype = get_comment_type(n); - if (ctype == type) - return 0; - if ((ctype == SND_CTL_ELEM_TYPE_BOOLEAN || - ctype == SND_CTL_ELEM_TYPE_INTEGER || - ctype == SND_CTL_ELEM_TYPE_INTEGER64 || - ctype == SND_CTL_ELEM_TYPE_ENUMERATED) && - (type == SND_CTL_ELEM_TYPE_BOOLEAN || - type == SND_CTL_ELEM_TYPE_INTEGER || - type == SND_CTL_ELEM_TYPE_INTEGER64 || - type == SND_CTL_ELEM_TYPE_ENUMERATED)) - return 0; /* OK, compatible */ - return -EINVAL; -} - -/* - * convert from an old value to a new value with the same dB level - */ -static int convert_to_new_db(snd_config_t *value, long omin, long omax, - long nmin, long nmax, - long odbmin, long odbmax, - long ndbmin, long ndbmax, - int doit) -{ - long val; - if (config_integer(value, &val, doit) < 0) - return -EINVAL; - if (val < omin || val > omax) - return -EINVAL; - val = ((val - omin) * (odbmax - odbmin)) / (omax - omin) + odbmin; - if (val < ndbmin) - val = ndbmin; - else if (val > ndbmax) - val = ndbmax; - val = ((val - ndbmin) * (nmax - nmin)) / (ndbmax - ndbmin) + nmin; - return snd_config_set_integer(value, val); -} - -/* - * compare the current value range with the old range in comments. - * also, if dB information is available, try to compare them. - * if any change occurs, try to keep the same dB level. - */ -static int check_comment_range(snd_ctl_t *handle, snd_config_t *conf, - snd_ctl_elem_info_t *info, snd_config_t *value, - int doit) -{ - snd_config_t *n; - long omin, omax, ostep; - long nmin, nmax; - long odbmin, odbmax; - long ndbmin, ndbmax; - snd_ctl_elem_id_t *id; - - n = search_comment_item(conf, "range"); - if (!n) - return 0; - if (get_comment_range(n, SND_CTL_ELEM_TYPE_INTEGER, - &omin, &omax, &ostep) < 0) - return 0; - nmin = snd_ctl_elem_info_get_min(info); - nmax = snd_ctl_elem_info_get_max(info); - if (omin != nmin && omax != nmax) { - /* Hey, the range mismatches */ - if (!force_restore || !doit) - return -EINVAL; - } - if (omin >= omax || nmin >= nmax) - return 0; /* invalid values */ - - n = search_comment_item(conf, "dbmin"); - if (!n) - return 0; - if (config_integer(n, &odbmin, doit) < 0) - return 0; - n = search_comment_item(conf, "dbmax"); - if (!n) - return 0; - if (config_integer(n, &odbmax, doit) < 0) - return 0; - if (odbmin >= odbmax) - return 0; /* invalid values */ - snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_info_get_id(info, id); - if (snd_ctl_get_dB_range(handle, id, &ndbmin, &ndbmax) < 0) - return 0; - if (ndbmin >= ndbmax) - return 0; /* invalid values */ - if (omin == nmin && omax == nmax && - odbmin == ndbmin && odbmax == ndbmax) - return 0; /* OK, identical one */ - - /* Let's guess the current value from dB range */ - if (snd_config_get_type(value) == SND_CONFIG_TYPE_COMPOUND) { - snd_config_iterator_t i, next; - snd_config_for_each(i, next, value) { - snd_config_t *n = snd_config_iterator_entry(i); - convert_to_new_db(n, omin, omax, nmin, nmax, - odbmin, odbmax, ndbmin, ndbmax, doit); - } - } else - convert_to_new_db(value, omin, omax, nmin, nmax, - odbmin, odbmax, ndbmin, ndbmax, doit); - return 0; -} - -static int restore_config_value(snd_ctl_t *handle, snd_ctl_elem_info_t *info, - snd_ctl_elem_iface_t type, - snd_config_t *value, - snd_ctl_elem_value_t *ctl, int idx, - int doit) -{ - long val; - long long lval; - int err; - - switch (type) { - case SND_CTL_ELEM_TYPE_BOOLEAN: - val = config_bool(value, doit); - if (val >= 0) { - snd_ctl_elem_value_set_boolean(ctl, idx, val); - return 1; - } - break; - case SND_CTL_ELEM_TYPE_INTEGER: - err = config_integer(value, &val, doit); - if (err == 0) { - snd_ctl_elem_value_set_integer(ctl, idx, val); - return 1; - } - break; - case SND_CTL_ELEM_TYPE_INTEGER64: - err = config_integer64(value, &lval, doit); - if (err == 0) { - snd_ctl_elem_value_set_integer64(ctl, idx, lval); - return 1; - } - break; - case SND_CTL_ELEM_TYPE_ENUMERATED: - val = config_enumerated(value, handle, info, doit); - if (val >= 0) { - snd_ctl_elem_value_set_enumerated(ctl, idx, val); - return 1; - } - break; - case SND_CTL_ELEM_TYPE_BYTES: - case SND_CTL_ELEM_TYPE_IEC958: - break; - default: - cerror(doit, "Unknow control type: %d", type); - return -EINVAL; - } - return 0; -} - -static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info, - snd_ctl_elem_iface_t type, - snd_config_t *value, - snd_ctl_elem_value_t *ctl, int idx, - unsigned int numid, int doit) -{ - int err = restore_config_value(handle, info, type, value, ctl, idx, doit); - long val; - - if (err != 0) - return err; - switch (type) { - case SND_CTL_ELEM_TYPE_BYTES: - case SND_CTL_ELEM_TYPE_IEC958: - err = snd_config_get_integer(value, &val); - if (err < 0 || val < 0 || val > 255) { - cerror(doit, "bad control.%d.value.%d content", numid, idx); - return force_restore && doit ? 0 : -EINVAL; - } - snd_ctl_elem_value_set_byte(ctl, idx, val); - return 1; - break; - default: - break; - } - return 0; -} - -static int set_control(snd_ctl_t *handle, snd_config_t *control, - int *maxnumid, int doit) -{ - 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; - 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) { - cerror(doit, "control is not a compound"); - return -EINVAL; - } - err = snd_config_get_id(control, &id); - if (err < 0) { - cerror(doit, "unable to get id"); - return -EINVAL; - } - numid = atoi(id); - if ((int)numid > *maxnumid) - *maxnumid = numid; - 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) { - cerror(doit, "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); - continue; - } - if (strcmp(fld, "device") == 0) { - if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { - cerror(doit, "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) { - cerror(doit, "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) { - cerror(doit, "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) { - cerror(doit, "control.%d.%s is invalid", numid, fld); - return -EINVAL; - } - snd_config_get_integer(n, &index); - continue; - } - if (strcmp(fld, "value") == 0) { - value = n; - continue; - } - cerror(doit, "unknown control.%d.%s field", numid, fld); - } - if (!value) { - cerror(doit, "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 && 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) { - cerror(doit, "failed to add user control #%d (%s)", - numid, snd_strerror(err)); - return err; - } - } - } - if (err < 0) { - cerror(doit, "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) - cerror(doit, "warning: numid mismatch (%d/%d) for control #%d", - numid, numid1, numid); - if (err |= iface != iface1) - cerror(doit, "warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid); - if (err |= device != device1) - cerror(doit, "warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid); - if (err |= subdevice != subdevice1) - cerror(doit, "warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid); - if (err |= strcmp(name, name1)) - cerror(doit, "warning: name mismatch (%s/%s) for control #%d", name, name1, numid); - if (err |= index != index1) - cerror(doit, "warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid); - if (err < 0) { - cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err)); - return -ENOENT; - } - - if (comment) { - if (check_comment_type(comment, type) < 0) - cerror(doit, "incompatible field type for control #%d", numid); - if (type == SND_CTL_ELEM_TYPE_INTEGER) { - if (check_comment_range(handle, comment, info, value, doit) < 0) { - cerror(doit, "value range mismatch for control #%d", - numid); - return -EINVAL; - } - } - } - - if (snd_ctl_elem_info_is_inactive(info) || - !snd_ctl_elem_info_is_writable(info)) - return 0; - snd_ctl_elem_value_set_numid(ctl, numid1); - - if (count == 1) { - err = restore_config_value(handle, info, type, value, ctl, 0, doit); - if (err < 0) - return err; - if (err > 0) - goto _ok; - } - 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) { - cerror(doit, "bad control.%d.value contents\n", numid); - return -EINVAL; - } - while (*buf) { - int c = *buf++; - if ((c = hextodigit(c)) < 0) { - cerror(doit, "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) { - if (!force_restore || !doit) { - cerror(doit, "bad control.%d.value type", numid); - return -EINVAL; - } - for (idx = 0; idx < count; ++idx) { - err = restore_config_value2(handle, info, type, value, - ctl, idx, numid, doit); - if (err < 0) - return err; - } - goto _ok; - } - - 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 >= count || set[idx]) { - cerror(doit, "bad control.%d.value index", numid); - if (!force_restore || !doit) - return -EINVAL; - continue; - } - err = restore_config_value2(handle, info, type, n, - ctl, idx, numid, doit); - if (err < 0) - return err; - if (err > 0) - set[idx] = 1; - } - for (idx = 0; idx < count; ++idx) { - if (!set[idx]) { - cerror(doit, "control.%d.value.%d is not specified", numid, idx); - if (!force_restore || !doit) - return -EINVAL; - } - } - - _ok: - err = doit ? snd_ctl_elem_write(handle, ctl) : 0; - 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, int doit) -{ - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_config_t *control; - snd_config_iterator_t i, next; - int err, maxnumid = -1; - 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) { - 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) { - cerror(doit, "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, &maxnumid, doit); - if (err < 0 && (!force_restore || !doit)) - goto _close; - } - - /* check if we have additional controls in driver */ - /* in this case we should go through init procedure */ - if (!doit && maxnumid >= 0) { - snd_ctl_elem_id_t *id; - snd_ctl_elem_info_t *info; - snd_ctl_elem_id_alloca(&id); - snd_ctl_elem_info_alloca(&info); - snd_ctl_elem_info_set_numid(info, maxnumid+1); - if (snd_ctl_elem_info(handle, info) == 0) { - /* not very informative */ - /* but value is used for check only */ - err = -EAGAIN; - 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) { - if (ignore_nocards) { - return 0; - } else { - 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: %s", file, snd_strerror(err)); - 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 *initfile, const char *cardname, - int do_init) -{ - int err, finalerr = 0; - 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; - } - } else { - int card, first = 1; - char cardname1[16]; - - error("Cannot open %s for reading: %s", file, snd_strerror(err)); - finalerr = err; - card = -1; - /* find each installed soundcards */ - while (1) { - if (snd_card_next(&card) < 0) - break; - if (card < 0) - break; - first = 0; - if (!do_init) - break; - sprintf(cardname1, "%i", card); - err = init(initfile, cardname1); - if (err < 0) { - finalerr = err; - initfailed(card, "init"); - } - initfailed(card, "restore"); - } - if (first) - finalerr = 0; /* no cards, no error code */ - return finalerr; - } - - if (!cardname) { - int card, first = 1; - char cardname1[16]; - - card = -1; - /* find each installed soundcards */ - while (1) { - if (snd_card_next(&card) < 0) - break; - if (card < 0) { - if (first) { - if (ignore_nocards) { - return 0; - } else { - error("No soundcards found..."); - return -ENODEV; - } - } - break; - } - first = 0; - /* do a check if controls matches state file */ - if (do_init && set_controls(card, config, 0)) { - sprintf(cardname1, "%i", card); - err = init(initfile, cardname1); - if (err < 0) { - initfailed(card, "init"); - finalerr = err; - } - } - if ((err = set_controls(card, config, 1))) { - if (!force_restore) - finalerr = err; - initfailed(card, "restore"); - } - } - } else { - int cardno; - - cardno = snd_card_get_index(cardname); - if (cardno < 0) { - error("Cannot find soundcard '%s'...", cardname); - return -ENODEV; - } - /* do a check if controls matches state file */ - if (do_init && set_controls(cardno, config, 0)) { - err = init(initfile, cardname); - if (err < 0) { - initfailed(cardno, "init"); - return err; - } - } - if ((err = set_controls(cardno, config, 1))) { - initfailed(cardno, "restore"); - if (!force_restore) - return err; - } - } - return finalerr; -} |