alsa-utils/alsactl/alsactl.c

1530 lines
37 KiB
C
Raw Permalink Normal View History

1998-10-29 23:45:59 +01:00
/*
* Advanced Linux Sound Architecture Control Program
2000-08-25 16:34:26 +02:00
* Copyright (c) by Abramo Bagnara <abramo@alsa-project.org>
* Jaroslav Kysela <perex@suse.cz>
1998-10-29 23:45:59 +01:00
*
*
* 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
2001-12-30 10:32:53 +01:00
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1998-10-29 23:45:59 +01:00
*
*/
1998-10-31 20:50:16 +01:00
#include "aconfig.h"
1999-01-30 20:12:34 +01:00
#include "version.h"
1998-10-29 23:45:59 +01:00
#include <getopt.h>
#include <stdarg.h>
2000-08-25 16:34:26 +02:00
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <alsa/asoundlib.h>
1998-10-29 23:45:59 +01:00
2001-05-15 14:10:22 +02:00
#define SYS_ASOUNDRC "/etc/asound.state"
1998-10-29 23:45:59 +01:00
int debugflag = 0;
int force_restore = 0;
2000-08-25 16:34:26 +02:00
char *command;
1998-10-29 23:45:59 +01:00
2000-08-25 16:34:26 +02:00
#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
1998-10-29 23:45:59 +01:00
1998-11-27 16:13:57 +01:00
static void help(void)
1998-10-29 23:45:59 +01:00
{
1998-11-27 16:13:57 +01:00
printf("Usage: alsactl <options> command\n");
printf("\nAvailable options:\n");
printf(" -h,--help this help\n");
2000-08-25 16:34:26 +02:00
printf(" -f,--file # configuration file (default " SYS_ASOUNDRC ")\n");
printf(" -F,--force try to restore the matching controls as much as possible\n");
1998-11-27 16:13:57 +01:00
printf(" -d,--debug debug mode\n");
printf(" -v,--version print version of this program\n");
printf("\nAvailable commands:\n");
2000-08-25 16:34:26 +02:00
printf(" store <card #> save current driver setup for one or each soundcards\n");
1998-11-27 16:13:57 +01:00
printf(" to configuration file\n");
2000-08-25 16:34:26 +02:00
printf(" restore<card #> load current driver setup for one or each soundcards\n");
printf(" from configuration file\n");
2001-09-26 18:05:03 +02:00
printf(" power [card #] [state]\n");
printf(" get/set power state for one or each soundcards\n");
2000-08-25 16:34:26 +02:00
}
2001-02-09 12:20:32 +01:00
char *id_str(snd_ctl_elem_id_t *id)
2000-08-25 16:34:26 +02:00
{
static char str[128];
assert(id);
sprintf(str, "%i,%i,%i,%s,%i",
snd_ctl_elem_id_get_interface(id),
2001-02-09 12:20:32 +01:00
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));
2000-08-25 16:34:26 +02:00
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;
2001-02-07 12:34:42 +01:00
err = snd_config_make_integer(&leaf, id);
2000-08-25 16:34:26 +02:00
if (err < 0)
return err;
err = snd_config_add(father, leaf);
if (err < 0) {
snd_config_delete(leaf);
return err;
}
2001-02-07 12:34:42 +01:00
err = snd_config_set_integer(leaf, integer);
2000-08-25 16:34:26 +02:00
if (err < 0) {
snd_config_delete(leaf);
return err;
}
return 0;
}
2002-05-13 11:54:24 +02:00
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)
2000-08-25 16:34:26 +02:00
{
int err;
snd_config_t *leaf;
2001-02-07 12:34:42 +01:00
err = snd_config_make_string(&leaf, id);
2000-08-25 16:34:26 +02:00
if (err < 0)
return err;
err = snd_config_add(father, leaf);
if (err < 0) {
snd_config_delete(leaf);
return err;
}
2001-02-07 12:34:42 +01:00
err = snd_config_set_string(leaf, string);
2000-08-25 16:34:26 +02:00
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,
2000-08-25 16:34:26 +02:00
snd_config_t **node)
{
int err;
snd_config_t *leaf;
2001-02-07 12:34:42 +01:00
err = snd_config_make_compound(&leaf, id, join);
2000-08-25 16:34:26 +02:00
if (err < 0)
return err;
err = snd_config_add(father, leaf);
if (err < 0) {
snd_config_delete(leaf);
return err;
}
*node = leaf;
return 0;
}
2001-02-09 12:20:32 +01:00
static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top)
2000-08-25 16:34:26 +02:00
{
snd_ctl_elem_value_t *ctl;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_t *info;
2000-08-25 16:34:26 +02:00
snd_config_t *control, *comment, *item, *value;
const char *s;
2000-08-25 16:34:26 +02:00
char buf[256];
unsigned int idx;
int err;
unsigned int device, subdevice, index;
const char *name;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_type_t type;
unsigned int count;
snd_ctl_elem_value_alloca(&ctl);
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_info_set_id(info, id);
err = snd_ctl_elem_info(handle, info);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err));
return err;
}
2001-02-09 12:20:32 +01:00
if (!snd_ctl_elem_info_is_readable(info))
2000-08-25 16:34:26 +02:00
return 0;
snd_ctl_elem_value_set_id(ctl, id);
2001-02-09 12:20:32 +01:00
err = snd_ctl_elem_read(handle, ctl);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("Cannot read control '%s': %s", id_str(id), snd_strerror(err));
return err;
}
2001-02-09 12:20:32 +01:00
err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control);
2000-08-25 16:34:26 +02:00
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';
2001-02-09 12:20:32 +01:00
if (snd_ctl_elem_info_is_readable(info))
2000-08-25 16:34:26 +02:00
strcat(buf, " read");
2001-02-09 12:20:32 +01:00
if (snd_ctl_elem_info_is_writable(info))
2000-08-25 16:34:26 +02:00
strcat(buf, " write");
2001-02-09 12:20:32 +01:00
if (snd_ctl_elem_info_is_inactive(info))
2000-08-25 16:34:26 +02:00
strcat(buf, " inactive");
2001-02-09 12:20:32 +01:00
if (snd_ctl_elem_info_is_volatile(info))
strcat(buf, " volatile");
2001-02-09 12:20:32 +01:00
if (snd_ctl_elem_info_is_locked(info))
strcat(buf, " locked");
if (snd_ctl_elem_info_is_user(info))
strcat(buf, " user");
2000-08-25 16:34:26 +02:00
err = snd_config_string_add(comment, "access", buf + 1);
if (err < 0) {
error("snd_config_string_add: %s", snd_strerror(err));
return err;
}
2001-02-09 12:20:32 +01:00
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);
2000-08-25 16:34:26 +02:00
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;
}
2000-08-25 16:34:26 +02:00
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BOOLEAN:
2000-08-25 16:34:26 +02:00
break;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER:
{
2001-02-09 12:20:32 +01:00
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);
2000-08-25 16:34:26 +02:00
err = snd_config_string_add(comment, "range", buf);
if (err < 0) {
error("snd_config_string_add: %s", snd_strerror(err));
return err;
}
break;
}
2002-05-13 11:54:24 +02:00
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;
}
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED:
{
unsigned int items;
2000-08-25 16:34:26 +02:00
err = snd_config_compound_add(comment, "item", 1, &item);
if (err < 0) {
error("snd_config_compound_add: %s", snd_strerror(err));
return err;
}
2001-02-09 12:20:32 +01:00
items = snd_ctl_elem_info_get_items(info);
for (idx = 0; idx < items; idx++) {
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_set_item(info, idx);
err = snd_ctl_elem_info(handle, info);
2000-08-25 16:34:26 +02:00
if (err < 0) {
2001-02-07 16:13:17 +01:00
error("snd_ctl_card_info: %s", snd_strerror(err));
2000-08-25 16:34:26 +02:00
return err;
}
2001-02-09 12:20:32 +01:00
err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info));
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_string_add: %s", snd_strerror(err));
return err;
}
}
break;
}
default:
break;
}
2001-02-09 12:20:32 +01:00
s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info));
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_integer_add: %s", snd_strerror(err));
return err;
}
}
err = snd_config_string_add(control, "name", name);
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_integer_add: %s", snd_strerror(err));
return err;
}
}
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BYTES:
case SND_CTL_ELEM_TYPE_IEC958:
{
2001-02-09 12:20:32 +01:00
size_t size = type == SND_CTL_ELEM_TYPE_BYTES ?
count : sizeof(snd_aes_iec958_t);
2001-02-07 18:09:00 +01:00
char buf[size * 2 + 1];
char *p = buf;
char *hex = "0123456789abcdef";
2002-12-04 15:41:32 +01:00
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;
2000-08-25 16:34:26 +02:00
}
return 0;
}
2000-12-21 22:53:07 +01:00
default:
break;
2000-08-25 16:34:26 +02:00
}
if (count == 1) {
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BOOLEAN:
err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false");
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_string_add: %s", snd_strerror(err));
return err;
}
return 0;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER:
err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0));
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_integer_add: %s", snd_strerror(err));
return err;
}
return 0;
2002-05-13 11:54:24 +02:00
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;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED:
2000-08-25 16:34:26 +02:00
{
unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0);
2000-08-25 16:34:26 +02:00
snd_config_t *c;
err = snd_config_search(item, num_str(v), &c);
if (err == 0) {
2001-02-07 12:34:42 +01:00
err = snd_config_get_string(c, &s);
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
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) {
2001-02-09 12:20:32 +01:00
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");
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_string_add: %s", snd_strerror(err));
return err;
}
}
break;
2001-02-09 12:20:32 +01:00
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));
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_integer_add: %s", snd_strerror(err));
return err;
}
}
break;
2002-05-13 11:54:24 +02:00
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;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED:
for (idx = 0; idx < count; idx++) {
unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx);
2000-08-25 16:34:26 +02:00
snd_config_t *c;
err = snd_config_search(item, num_str(v), &c);
if (err == 0) {
2001-02-07 12:34:42 +01:00
err = snd_config_get_string(c, &s);
2000-08-25 16:34:26 +02:00
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) {
2000-08-25 16:34:26 +02:00
error("snd_config add: %s", snd_strerror(err));
return err;
}
2000-08-25 16:34:26 +02:00
}
break;
default:
error("Unknown control type: %d\n", type);
2000-08-25 16:34:26 +02:00
return -EINVAL;
}
return 0;
}
static int get_controls(int cardno, snd_config_t *top)
{
snd_ctl_t *handle;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_t *info;
2000-08-25 16:34:26 +02:00
snd_config_t *state, *card, *control;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_t *list;
unsigned int idx;
int err;
2000-11-20 21:11:13 +01:00
char name[32];
unsigned int count;
const char *id;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_alloca(&info);
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_alloca(&list);
2000-08-25 16:34:26 +02:00
2000-11-20 21:11:13 +01:00
sprintf(name, "hw:%d", cardno);
2002-12-04 15:41:32 +01:00
err = snd_ctl_open(&handle, name, SND_CTL_READONLY);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_ctl_open error: %s", snd_strerror(err));
return err;
}
2001-02-07 16:13:17 +01:00
err = snd_ctl_card_info(handle, info);
2000-08-25 16:34:26 +02:00
if (err < 0) {
2001-02-07 16:13:17 +01:00
error("snd_ctl_card_info error: %s", snd_strerror(err));
2000-08-25 16:34:26 +02:00
goto _close;
}
2001-02-07 16:13:17 +01:00
id = snd_ctl_card_info_get_id(info);
2000-08-25 16:34:26 +02:00
err = snd_config_search(top, "state", &state);
if (err == 0 &&
2001-02-07 12:34:42 +01:00
snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) {
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
if (err == 0 &&
snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) {
error("config state.%s node is not a compound", id);
2000-08-25 16:34:26 +02:00
err = -EINVAL;
goto _close;
}
if (err < 0) {
err = snd_config_compound_add(state, id, 0, &card);
2000-08-25 16:34:26 +02:00
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;
}
}
2001-02-09 12:20:32 +01:00
err = snd_ctl_elem_list(handle, list);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("Cannot determine controls: %s", snd_strerror(err));
goto _close;
}
2001-02-09 12:20:32 +01:00
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) {
2000-08-25 16:34:26 +02:00
err = 0;
goto _close;
}
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_set_offset(list, 0);
if (snd_ctl_elem_list_alloc_space(list, count) < 0) {
2000-08-25 16:34:26 +02:00
error("No enough memory...");
goto _close;
}
2001-02-09 12:20:32 +01:00
if ((err = snd_ctl_elem_list(handle, list)) < 0) {
2000-08-25 16:34:26 +02:00
error("Cannot determine controls (2): %s", snd_strerror(err));
goto _free;
}
for (idx = 0; idx < count; ++idx) {
2001-02-09 12:20:32 +01:00
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);
2000-08-25 16:34:26 +02:00
if (err < 0)
goto _free;
}
err = 0;
_free:
2001-02-09 12:20:32 +01:00
snd_ctl_elem_list_free_space(list);
2000-08-25 16:34:26 +02:00
_close:
snd_ctl_close(handle);
return err;
}
2002-12-04 15:41:32 +01:00
static long config_iface(snd_config_t *n)
2000-08-25 16:34:26 +02:00
{
2002-12-04 15:41:32 +01:00
long i;
long long li;
snd_ctl_elem_iface_t idx;
const char *str;
switch (snd_config_get_type(n)) {
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_INTEGER:
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &i);
return i;
2002-05-13 11:54:24 +02:00
case SND_CONFIG_TYPE_INTEGER64:
snd_config_get_integer64(n, &li);
2002-12-04 15:41:32 +01:00
return li;
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_STRING:
2001-02-07 12:34:42 +01:00
snd_config_get_string(n, &str);
2000-08-25 16:34:26 +02:00
break;
default:
return -1;
}
for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) {
2001-02-09 12:20:32 +01:00
if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0)
return idx;
2000-08-25 16:34:26 +02:00
}
return -1;
}
static int config_bool(snd_config_t *n)
{
const char *str;
2000-08-25 16:34:26 +02:00
long val;
2002-05-13 11:54:24 +02:00
long long lval;
switch (snd_config_get_type(n)) {
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_INTEGER:
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &val);
2000-08-25 16:34:26 +02:00
if (val < 0 || val > 1)
return -1;
return val;
2002-05-13 11:54:24 +02:00
case SND_CONFIG_TYPE_INTEGER64:
snd_config_get_integer64(n, &lval);
if (lval < 0 || lval > 1)
return -1;
return (int) lval;
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_STRING:
2001-02-07 12:34:42 +01:00
snd_config_get_string(n, &str);
2000-08-25 16:34:26 +02:00
break;
default:
return -1;
}
if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0)
2000-08-25 16:34:26 +02:00
return 1;
if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0)
2000-08-25 16:34:26 +02:00
return 0;
return -1;
}
static int config_enumerated(snd_config_t *n, snd_ctl_t *handle,
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_t *info)
2000-08-25 16:34:26 +02:00
{
const char *str;
2000-08-25 16:34:26 +02:00
long val;
2002-05-13 11:54:24 +02:00
long long lval;
unsigned int idx, items;
switch (snd_config_get_type(n)) {
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_INTEGER:
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &val);
2000-08-25 16:34:26 +02:00
return val;
2002-05-13 11:54:24 +02:00
case SND_CONFIG_TYPE_INTEGER64:
snd_config_get_integer64(n, &lval);
return (int) lval;
2000-08-25 16:34:26 +02:00
case SND_CONFIG_TYPE_STRING:
2001-02-07 12:34:42 +01:00
snd_config_get_string(n, &str);
2000-08-25 16:34:26 +02:00
break;
default:
return -1;
}
2001-02-09 12:20:32 +01:00
items = snd_ctl_elem_info_get_items(info);
for (idx = 0; idx < items; idx++) {
2000-08-25 16:34:26 +02:00
int err;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_set_item(info, idx);
err = snd_ctl_elem_info(handle, info);
2000-08-25 16:34:26 +02:00
if (err < 0) {
2001-06-06 16:24:10 +02:00
error("snd_ctl_elem_info: %s", snd_strerror(err));
2000-08-25 16:34:26 +02:00
return err;
}
2001-02-09 12:20:32 +01:00
if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0)
2000-08-25 16:34:26 +02:00
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);
}
2000-08-25 16:34:26 +02:00
static int set_control(snd_ctl_t *handle, snd_config_t *control)
{
snd_ctl_elem_value_t *ctl;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_t *info;
snd_config_iterator_t i, next;
2002-12-04 15:41:32 +01:00
unsigned int numid1;
snd_ctl_elem_iface_t iface = -1;
int iface1;
const char *name1;
unsigned int numid;
2001-02-09 12:20:32 +01:00
snd_ctl_elem_type_t type;
unsigned int count;
2000-08-25 16:34:26 +02:00
long device = -1;
2002-12-04 15:41:32 +01:00
long device1;
2000-08-25 16:34:26 +02:00
long subdevice = -1;
2002-12-04 15:41:32 +01:00
long subdevice1;
const char *name = NULL;
2002-12-04 15:41:32 +01:00
long index1;
2000-08-25 16:34:26 +02:00
long index = -1;
snd_config_t *value = NULL;
snd_config_t *comment = NULL;
2000-08-25 16:34:26 +02:00
long val;
2002-05-13 11:54:24 +02:00
long long lval;
unsigned int idx;
int err;
2000-08-25 16:34:26 +02:00
char *set;
2001-11-19 13:00:18 +01:00
const char *id;
snd_ctl_elem_value_alloca(&ctl);
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_alloca(&info);
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
2000-08-25 16:34:26 +02:00
error("control is not a compound");
return -EINVAL;
}
2001-11-19 13:00:18 +01:00
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) {
2001-02-07 12:34:42 +01:00
snd_config_t *n = snd_config_iterator_entry(i);
2001-11-19 13:00:18 +01:00
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;
2000-08-25 16:34:26 +02:00
continue;
}
2000-08-25 16:34:26 +02:00
if (strcmp(fld, "iface") == 0) {
2002-12-04 15:41:32 +01:00
iface = (snd_ctl_elem_iface_t)config_iface(n);
2000-08-25 16:34:26 +02:00
if (iface < 0) {
error("control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
continue;
}
if (strcmp(fld, "device") == 0) {
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
2000-08-25 16:34:26 +02:00
error("control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &device);
2000-08-25 16:34:26 +02:00
continue;
}
if (strcmp(fld, "subdevice") == 0) {
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
2000-08-25 16:34:26 +02:00
error("control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &subdevice);
2000-08-25 16:34:26 +02:00
continue;
}
if (strcmp(fld, "name") == 0) {
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
2000-08-25 16:34:26 +02:00
error("control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
2001-02-07 12:34:42 +01:00
snd_config_get_string(n, &name);
2000-08-25 16:34:26 +02:00
continue;
}
if (strcmp(fld, "index") == 0) {
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) {
2000-08-25 16:34:26 +02:00
error("control.%d.%s is invalid", numid, fld);
return -EINVAL;
}
2001-02-07 12:34:42 +01:00
snd_config_get_integer(n, &index);
2000-08-25 16:34:26 +02:00
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);
}
2000-08-25 16:34:26 +02:00
if (err < 0) {
if (iface >= 0 && name) {
2001-02-09 12:20:32 +01:00
snd_ctl_elem_info_set_numid(info, 0);
snd_ctl_elem_info_set_interface(info, iface);
2001-02-09 12:20:32 +01:00
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;
}
}
2000-08-25 16:34:26 +02:00
}
}
if (err < 0) {
error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err));
return -ENOENT;
}
2001-02-09 12:20:32 +01:00
numid1 = snd_ctl_elem_info_get_numid(info);
iface1 = snd_ctl_elem_info_get_interface(info);
2001-02-09 12:20:32 +01:00
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)
2002-12-04 15:41:32 +01:00
error("warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid);
if (err |= device != device1)
2002-12-04 15:41:32 +01:00
error("warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid);
if (err |= subdevice != subdevice1)
2002-12-04 15:41:32 +01:00
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)
2002-12-04 15:41:32 +01:00
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
2001-02-09 12:20:32 +01:00
if (!snd_ctl_elem_info_is_writable(info))
2000-08-25 16:34:26 +02:00
return 0;
snd_ctl_elem_value_set_numid(ctl, numid1);
2000-08-25 16:34:26 +02:00
if (count == 1) {
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BOOLEAN:
2000-08-25 16:34:26 +02:00
val = config_bool(value);
if (val >= 0) {
snd_ctl_elem_value_set_boolean(ctl, 0, val);
2000-08-25 16:34:26 +02:00
goto _ok;
}
break;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER:
2001-02-07 12:34:42 +01:00
err = snd_config_get_integer(value, &val);
2000-08-25 16:34:26 +02:00
if (err == 0) {
snd_ctl_elem_value_set_integer(ctl, 0, val);
2000-08-25 16:34:26 +02:00
goto _ok;
}
break;
2002-05-13 11:54:24 +02:00
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;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED:
val = config_enumerated(value, handle, info);
2000-08-25 16:34:26 +02:00
if (val >= 0) {
snd_ctl_elem_value_set_enumerated(ctl, 0, val);
2000-08-25 16:34:26 +02:00
goto _ok;
}
break;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BYTES:
case SND_CTL_ELEM_TYPE_IEC958:
2000-12-22 12:25:11 +01:00
break;
2000-08-25 16:34:26 +02:00
default:
error("Unknow control type: %d", type);
2000-08-25 16:34:26 +02:00
return -EINVAL;
}
}
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BYTES:
case SND_CTL_ELEM_TYPE_IEC958:
{
const char *buf;
2001-02-07 12:34:42 +01:00
err = snd_config_get_string(value, &buf);
if (err >= 0) {
int c1 = 0;
int len = strlen(buf);
unsigned int idx = 0;
2001-02-09 12:20:32 +01:00
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';
2001-02-13 19:36:36 +01:00
else if (c >= 'a' && c <= 'f')
c = c - 'a' + 10;
2001-02-13 19:36:36 +01:00
else if (c >= 'A' && c <= 'F')
c = c - 'A' + 10;
else {
2000-08-25 16:34:26 +02:00
error("bad control.%d.value contents\n", numid);
return -EINVAL;
}
2001-02-13 21:20:52 +01:00
if (idx % 2 == 1)
snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c);
else
c1 = c;
2001-02-13 21:20:52 +01:00
idx++;
2000-08-25 16:34:26 +02:00
}
goto _ok;
2000-08-25 16:34:26 +02:00
}
}
2000-12-22 12:25:11 +01:00
default:
break;
2000-08-25 16:34:26 +02:00
}
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) {
2000-08-25 16:34:26 +02:00
error("bad control.%d.value type", numid);
return -EINVAL;
}
2002-12-04 15:41:32 +01:00
set = (char*) alloca(count);
memset(set, 0, count);
snd_config_for_each(i, next, value) {
2001-02-07 12:34:42 +01:00
snd_config_t *n = snd_config_iterator_entry(i);
2001-11-19 13:00:18 +01:00
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
idx = atoi(id);
if (idx < 0 || idx >= count ||
2000-08-25 16:34:26 +02:00
set[idx]) {
error("bad control.%d.value index", numid);
return -EINVAL;
}
switch (type) {
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BOOLEAN:
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
break;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_INTEGER:
2001-02-07 12:34:42 +01:00
err = snd_config_get_integer(n, &val);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("bad control.%d.value.%d content", numid, idx);
return -EINVAL;
}
snd_ctl_elem_value_set_integer(ctl, idx, val);
2000-08-25 16:34:26 +02:00
break;
2002-05-13 11:54:24 +02:00
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;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_ENUMERATED:
val = config_enumerated(n, handle, info);
2000-08-25 16:34:26 +02:00
if (val < 0) {
error("bad control.%d.value.%d content", numid, idx);
return -EINVAL;
}
snd_ctl_elem_value_set_enumerated(ctl, idx, val);
2000-08-25 16:34:26 +02:00
break;
2001-02-09 12:20:32 +01:00
case SND_CTL_ELEM_TYPE_BYTES:
case SND_CTL_ELEM_TYPE_IEC958:
2001-02-07 12:34:42 +01:00
err = snd_config_get_integer(n, &val);
2000-08-25 16:34:26 +02:00
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);
2000-08-25 16:34:26 +02:00
break;
default:
break;
}
set[idx] = 1;
}
for (idx = 0; idx < count; ++idx) {
2000-08-25 16:34:26 +02:00
if (!set[idx]) {
error("control.%d.value.%d is not specified", numid, idx);
return -EINVAL;
}
}
_ok:
2001-02-09 12:20:32 +01:00
err = snd_ctl_elem_write(handle, ctl);
2000-08-25 16:34:26 +02:00
if (err < 0) {
2002-12-04 15:41:32 +01:00
error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err));
2000-08-25 16:34:26 +02:00
return err;
}
return 0;
}
static int set_controls(int card, snd_config_t *top)
{
snd_ctl_t *handle;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_t *info;
2000-08-25 16:34:26 +02:00
snd_config_t *control;
snd_config_iterator_t i, next;
2000-08-25 16:34:26 +02:00
int err;
char name[32], tmpid[16];
const char *id;
2001-02-07 16:13:17 +01:00
snd_ctl_card_info_alloca(&info);
2000-08-25 16:34:26 +02:00
2000-11-20 21:11:13 +01:00
sprintf(name, "hw:%d", card);
err = snd_ctl_open(&handle, name, 0);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_ctl_open error: %s", snd_strerror(err));
return err;
}
2001-02-07 16:13:17 +01:00
err = snd_ctl_card_info(handle, info);
2000-08-25 16:34:26 +02:00
if (err < 0) {
2001-02-07 16:13:17 +01:00
error("snd_ctl_card_info error: %s", snd_strerror(err));
2000-08-25 16:34:26 +02:00
goto _close;
}
2001-02-07 16:13:17 +01:00
id = snd_ctl_card_info_get_id(info);
err = snd_config_searchv(top, &control, "state", id, "control", 0);
2000-08-25 16:34:26 +02:00
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;
2000-08-25 16:34:26 +02:00
}
2001-02-07 12:34:42 +01:00
if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) {
error("state.%s.control is not a compound\n", id);
2000-08-25 16:34:26 +02:00
return -EINVAL;
}
snd_config_for_each(i, next, control) {
2001-02-07 12:34:42 +01:00
snd_config_t *n = snd_config_iterator_entry(i);
2000-08-25 16:34:26 +02:00
err = set_control(handle, n);
if (err < 0 && ! force_restore)
2000-08-25 16:34:26 +02:00
goto _close;
}
_close:
snd_ctl_close(handle);
return err;
1998-10-29 23:45:59 +01:00
}
2000-08-25 16:34:26 +02:00
static int save_state(char *file, const char *cardname)
1998-10-29 23:45:59 +01:00
{
1998-11-27 16:13:57 +01:00
int err;
2000-08-25 16:34:26 +02:00
snd_config_t *config;
2001-01-17 12:00:34 +01:00
snd_input_t *in;
snd_output_t *out;
int stdio;
2000-08-25 16:34:26 +02:00
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) {
2001-01-17 12:00:34 +01:00
err = snd_config_load(config, in);
snd_input_close(in);
#if 0
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_load error: %s", snd_strerror(err));
return err;
}
#endif
2000-08-25 16:34:26 +02:00
}
1998-11-27 16:13:57 +01:00
if (!cardname) {
2000-12-04 17:48:30 +01:00
int card, first = 1;
1998-11-27 16:13:57 +01:00
card = -1;
/* find each installed soundcards */
while (1) {
if (snd_card_next(&card) < 0)
break;
if (card < 0) {
if (first) {
error("No soundcards found...");
2002-12-04 15:41:32 +01:00
return -ENODEV;
1998-11-27 16:13:57 +01:00
}
break;
1998-11-27 16:13:57 +01:00
}
first = 0;
if ((err = get_controls(card, config)))
return err;
1998-11-27 16:13:57 +01:00
}
} else {
int cardno;
2000-08-25 16:34:26 +02:00
cardno = snd_card_get_index(cardname);
if (cardno < 0) {
1998-11-27 16:13:57 +01:00
error("Cannot find soundcard '%s'...", cardname);
2002-12-04 15:41:32 +01:00
return cardno;
1998-11-27 16:13:57 +01:00
}
2000-08-25 16:34:26 +02:00
if ((err = get_controls(cardno, config))) {
1998-11-27 16:13:57 +01:00
return err;
}
}
2000-08-25 16:34:26 +02:00
2001-01-17 12:00:34 +01:00
if (stdio)
err = snd_output_stdio_attach(&out, stdout, 0);
else
err = snd_output_stdio_open(&out, file, "w");
2001-01-17 12:00:34 +01:00
if (err < 0) {
2000-08-25 16:34:26 +02:00
error("Cannot open %s for writing", file);
return -errno;
}
2001-01-17 12:00:34 +01:00
err = snd_config_save(config, out);
snd_output_close(out);
2000-08-25 16:34:26 +02:00
if (err < 0)
error("snd_config_save: %s", snd_strerror(err));
return 0;
1998-10-29 23:45:59 +01:00
}
2002-12-04 15:41:32 +01:00
int load_state(const char *file, const char *cardname)
1998-10-29 23:45:59 +01:00
{
2000-08-25 16:34:26 +02:00
int err;
snd_config_t *config;
2001-01-17 12:00:34 +01:00
snd_input_t *in;
int stdio;
1998-11-27 16:13:57 +01:00
2000-08-25 16:34:26 +02:00
err = snd_config_top(&config);
if (err < 0) {
error("snd_config_top error: %s", snd_strerror(err));
return err;
}
stdio = !strcmp(file, "-");
if (stdio)
2001-01-17 12:00:34 +01:00
err = snd_input_stdio_attach(&in, stdin, 0);
else
err = snd_input_stdio_open(&in, file, "r");
2001-01-17 12:00:34 +01:00
if (err >= 0) {
err = snd_config_load(config, in);
snd_input_close(in);
2000-08-25 16:34:26 +02:00
if (err < 0) {
error("snd_config_load error: %s", snd_strerror(err));
return err;
}
}
if (!cardname) {
2000-12-04 17:48:30 +01:00
int card, first = 1;
2000-08-25 16:34:26 +02:00
card = -1;
/* find each installed soundcards */
while (1) {
if (snd_card_next(&card) < 0)
break;
if (card < 0) {
if (first) {
error("No soundcards found...");
2002-12-04 15:41:32 +01:00
return -ENODEV;
2000-08-25 16:34:26 +02:00
}
break;
2000-08-25 16:34:26 +02:00
}
first = 0;
if ((err = set_controls(card, config)) && ! force_restore)
return err;
2000-08-25 16:34:26 +02:00
}
} else {
int cardno;
cardno = snd_card_get_index(cardname);
1998-11-27 16:13:57 +01:00
if (cardno < 0) {
error("Cannot find soundcard '%s'...", cardname);
2002-12-04 15:41:32 +01:00
return -ENODEV;
1998-11-27 16:13:57 +01:00
}
if ((err = set_controls(cardno, config)) && ! force_restore) {
2000-08-25 16:34:26 +02:00
return err;
}
1998-11-27 16:13:57 +01:00
}
2000-08-25 16:34:26 +02:00
return 0;
1998-10-29 23:45:59 +01:00
}
2001-09-26 18:05:03 +02:00
static int get_int_state(const char *str)
{
2001-10-04 17:03:39 +02:00
if (!strcasecmp(str, "on"))
return SND_CTL_POWER_D0;
if (!strcasecmp(str, "off"))
return SND_CTL_POWER_D3hot;
2001-09-26 18:05:03 +02:00
if (*str == 'D' || *str == 'd') {
str++;
2001-10-04 17:03:39 +02:00
if (!strcmp(str, "0"))
2001-09-26 18:05:03 +02:00
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;
2001-10-04 17:03:39 +02:00
if (!strcmp(str, "3hot"))
2001-09-26 18:05:03 +02:00
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];
2002-12-04 15:41:32 +01:00
unsigned int power_state;
int err;
2001-09-26 18:05:03 +02:00
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;
}
2002-12-04 15:41:32 +01:00
static int set_power(int cardno, unsigned int power_state)
2001-09-26 18:05:03 +02:00
{
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...");
2002-12-04 15:41:32 +01:00
return -ENODEV;
2001-09-26 18:05:03 +02:00
}
break;
}
first = 0;
if ((err = show_power(card)) < 0)
return err;
}
return 0;
}
power_state = get_int_state(argv[0]);
2001-10-04 17:03:39 +02:00
if (power_state >= 0) {
2001-09-26 18:05:03 +02:00
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...");
2002-12-04 15:41:32 +01:00
return -ENODEV;
2001-09-26 18:05:03 +02:00
}
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]);
2002-12-04 15:41:32 +01:00
return -ENODEV;
2001-09-26 18:05:03 +02:00
}
if (argc > 1) {
power_state = get_int_state(argv[1]);
if (power_state < 0) {
error("Invalid power state '%s'...", argv[1]);
2002-12-04 15:41:32 +01:00
return -EINVAL;
2001-09-26 18:05:03 +02:00
}
if ((err = set_power(cardno, power_state)) < 0)
return err;
} else {
if ((err = show_power(cardno)) < 0)
return err;
}
}
2002-12-04 15:41:32 +01:00
return 0;
2001-09-26 18:05:03 +02:00
}
1998-11-27 16:13:57 +01:00
int main(int argc, char *argv[])
1998-10-29 23:45:59 +01:00
{
1998-11-27 16:13:57 +01:00
struct option long_option[] =
{
2000-08-25 16:34:26 +02:00
{"help", 0, NULL, 'h'},
{"file", 1, NULL, 'f'},
{"force", 1, NULL, 'F'},
2000-08-25 16:34:26 +02:00
{"debug", 0, NULL, 'd'},
{"version", 0, NULL, 'v'},
1998-11-27 16:13:57 +01:00
{NULL, 0, NULL, 0},
};
2000-08-25 16:34:26 +02:00
char *cfgfile = SYS_ASOUNDRC;
2002-12-04 15:41:32 +01:00
int res;
1998-11-27 16:13:57 +01:00
2000-08-25 16:34:26 +02:00
command = argv[0];
1998-11-27 16:13:57 +01:00
while (1) {
int c;
2003-01-07 14:44:08 +01:00
if ((c = getopt_long(argc, argv, "hf:Fdv", long_option, NULL)) < 0)
1998-11-27 16:13:57 +01:00
break;
switch (c) {
case 'h':
help();
return EXIT_SUCCESS;
1998-11-27 16:13:57 +01:00
case 'f':
2000-08-25 16:34:26 +02:00
cfgfile = optarg;
1998-11-27 16:13:57 +01:00
break;
case 'F':
force_restore = 1;
break;
1998-11-27 16:13:57 +01:00
case 'd':
debugflag = 1;
break;
case 'v':
1999-01-30 20:12:34 +01:00
printf("alsactl version " SND_UTIL_VERSION_STR "\n");
2001-09-26 18:05:03 +02:00
return EXIT_SUCCESS;
2002-12-04 15:41:32 +01:00
case '?': // error msg already printed
help();
return EXIT_FAILURE;
2002-12-04 15:41:32 +01:00
break;
default: // should never happen
fprintf(stderr,
"Invalid option '%c' (%d) not handled??\n", c, c);
1998-11-27 16:13:57 +01:00
}
}
if (argc - optind <= 0) {
fprintf(stderr, "alsactl: Specify command...\n");
return 0;
}
2002-12-04 15:41:32 +01:00
1998-11-27 16:13:57 +01:00
if (!strcmp(argv[optind], "store")) {
2002-12-04 15:41:32 +01:00
res = save_state(cfgfile,
argc - optind > 1 ? argv[optind + 1] : NULL);
1998-11-27 16:13:57 +01:00
} else if (!strcmp(argv[optind], "restore")) {
2002-12-04 15:41:32 +01:00
res = load_state(cfgfile,
argc - optind > 1 ? argv[optind + 1] : NULL);
2001-09-26 18:05:03 +02:00
} else if (!strcmp(argv[optind], "power")) {
2002-12-04 15:41:32 +01:00
res = power((const char **)argv + optind + 1, argc - optind - 1);
1998-11-27 16:13:57 +01:00
} else {
2002-12-04 15:41:32 +01:00
fprintf(stderr, "alsactl: Unknown command '%s'...\n",
argv[optind]);
res = -ENODEV;
1998-11-27 16:13:57 +01:00
}
2002-12-04 15:41:32 +01:00
return res < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1998-10-29 23:45:59 +01:00
}