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