diff options
author | Abramo Bagnara <abramo@alsa-project.org> | 2000-08-25 14:34:26 +0000 |
---|---|---|
committer | Abramo Bagnara <abramo@alsa-project.org> | 2000-08-25 14:34:26 +0000 |
commit | 8c4e1aa76980d0248a0e90cc6510dc0836ab2843 (patch) | |
tree | 1c75ad5b8f12cc1d9164158c3727a8181855938d /alsactl | |
parent | 6add1cdfb51f283f1e58ba3fe8c8805d49384686 (diff) | |
download | alsa-utils-8c4e1aa76980d0248a0e90cc6510dc0836ab2843.tar.gz |
Rewritten alsactl
Diffstat (limited to 'alsactl')
-rw-r--r-- | alsactl/Makefile.am | 8 | ||||
-rw-r--r-- | alsactl/alsactl.c | 997 | ||||
-rw-r--r-- | alsactl/alsactl.h | 70 | ||||
-rw-r--r-- | alsactl/alsactl_lexer.l | 114 | ||||
-rw-r--r-- | alsactl/alsactl_parser.y | 319 | ||||
-rw-r--r-- | alsactl/merge.c | 170 | ||||
-rw-r--r-- | alsactl/setup.c | 530 |
7 files changed, 928 insertions, 1280 deletions
diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am index 3df5067..b0d2fa5 100644 --- a/alsactl/Makefile.am +++ b/alsactl/Makefile.am @@ -1,10 +1,4 @@ sbin_PROGRAMS=alsactl -noinst_HEADERS=alsactl.h man_MANS=alsactl.1 -alsactl_SOURCES=alsactl.c alsactl_parser.y setup.c merge.c alsactl_lexer.l -YFLAGS=-d - -# lexer / parser debug -#CFLAGS=-pipe -g -DYYDEBUG -#LFLAGS=-d +alsactl_SOURCES=alsactl.c diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c index cdd81f7..2f8b69e 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c @@ -1,6 +1,7 @@ /* * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1997 by Perex, APS, University of South Bohemia + * 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 @@ -19,54 +20,887 @@ * */ -#include "alsactl.h" #include "aconfig.h" #include "version.h" #include <getopt.h> #include <stdarg.h> +#include <stdio.h> +#include <assert.h> +#include <errno.h> +#include <sys/asoundlib.h> -#define HELPID_HELP 1000 -#define HELPID_FILE 1001 -#define HELPID_DEBUG 1002 -#define HELPID_VERSION 1003 - -extern int yyparse(void); -extern int linecount; -extern FILE *yyin; -extern int yydebug; +#define SYS_ASOUNDRC "/etc/asound.conf" int debugflag = 0; -char cfgfile[512] = ALSACTL_FILE; +char *command; -void error(const char *fmt,...) -{ - va_list va; - - va_start(va, fmt); - fprintf(stderr, "alsactl: "); - vfprintf(stderr, fmt, va); - fprintf(stderr, "\n"); - va_end(va); -} +#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 " ALSACTL_FILE ")\n"); + printf(" -f,--file # configuration file (default " SYS_ASOUNDRC ")\n"); printf(" -d,--debug debug mode\n"); printf(" -v,--version print version of this program\n"); printf("\nAvailable commands:\n"); - printf(" store <card #> store current driver setup for one or each soundcards\n"); + printf(" store <card #> save current driver setup for one or each soundcards\n"); printf(" to configuration file\n"); - printf(" restore <card #> restore current driver setup for one or each soundcards\n"); - printf(" from configuration file\n"); + printf(" restore<card #> load current driver setup for one or each soundcards\n"); + printf(" from configuration file\n"); +} + +char *id_str(snd_control_id_t *id) +{ + static char str[128]; + assert(id); + sprintf(str, "%i,%i,%i,%s,%i", id->iface, id->device, id->subdevice, id->name, id->index); + 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_integer_make(&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_integer_set(leaf, integer); + if (err < 0) { + snd_config_delete(leaf); + return err; + } + return 0; +} + +static int snd_config_string_add(snd_config_t *father, char *id, char *string) +{ + int err; + snd_config_t *leaf; + err = snd_config_string_make(&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_string_set(leaf, string); + if (err < 0) { + snd_config_delete(leaf); + return err; + } + return 0; +} + +static int snd_config_compound_add(snd_config_t *father, char *id, int join, + snd_config_t **node) +{ + int err; + snd_config_t *leaf; + err = snd_config_compound_make(&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_control_id_t *id, snd_config_t *top) +{ + snd_control_t ctl; + snd_control_info_t info; + snd_config_t *control, *comment, *item, *value; + char *s; + char buf[256]; + int idx, err; + + memset(&info, 0, sizeof(info)); + info.id = *id; + err = snd_ctl_cinfo(handle, &info); + if (err < 0) { + error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err)); + return err; + } + + if (!(info.access & SND_CONTROL_ACCESS_READ)) + return 0; + memset(&ctl, 0, sizeof(ctl)); + ctl.id = info.id; + err = snd_ctl_cread(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(info.id.numid), 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 (info.access & SND_CONTROL_ACCESS_READ) + strcat(buf, " read"); + if (info.access & SND_CONTROL_ACCESS_WRITE) + strcat(buf, " write"); + if (info.access & SND_CONTROL_ACCESS_INACTIVE) + strcat(buf, " inactive"); + err = snd_config_string_add(comment, "access", buf + 1); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + + switch (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + s = "bool"; + break; + case SND_CONTROL_TYPE_INTEGER: + s = "integer"; + break; + case SND_CONTROL_TYPE_ENUMERATED: + s = "enumerated"; + break; + case SND_CONTROL_TYPE_BYTES: + s = "bytes"; + break; + default: + s = "unknown"; + break; + } + err = snd_config_string_add(comment, "type", s); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + + switch (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + if (info.value.integer.min != 0 || info.value.integer.max != 1 || + info.value.integer.step != 0) + error("Bad boolean control '%s'", id_str(id)); + + break; + case SND_CONTROL_TYPE_INTEGER: + sprintf(buf, "%li - %li (step %li)", info.value.integer.min, info.value.integer.max, info.value.integer.step); + 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_CONTROL_TYPE_ENUMERATED: + err = snd_config_compound_add(comment, "item", 1, &item); + if (err < 0) { + error("snd_config_compound_add: %s", snd_strerror(err)); + return err; + } + for (idx = 0; idx < info.value.enumerated.items; idx++) { + info.value.enumerated.item = idx; + err = snd_ctl_cinfo(handle, &info); + if (err < 0) { + error("snd_ctl_info: %s", snd_strerror(err)); + return err; + } + err = snd_config_string_add(item, num_str(idx), info.value.enumerated.name); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + } + break; + default: + break; + } + switch (info.id.iface) { + case SND_CONTROL_IFACE_CARD: + s = "global"; + break; + case SND_CONTROL_IFACE_HWDEP: + s = "hwdep"; + break; + case SND_CONTROL_IFACE_MIXER: + s = "mixer"; + break; + case SND_CONTROL_IFACE_PCM: + s = "pcm"; + break; + case SND_CONTROL_IFACE_RAWMIDI: + s = "rawmidi"; + break; + case SND_CONTROL_IFACE_TIMER: + s = "timer"; + break; + case SND_CONTROL_IFACE_SEQUENCER: + s = "sequencer"; + break; + default: + s = num_str(info.id.iface); + break; + } + err = snd_config_string_add(control, "iface", s); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + if (info.id.device != 0) { + err = snd_config_integer_add(control, "device", info.id.device); + if (err < 0) { + error("snd_config_integer_add: %s", snd_strerror(err)); + return err; + } + } + if (info.id.subdevice != 0) { + err = snd_config_integer_add(control, "subdevice", info.id.subdevice); + if (err < 0) { + error("snd_config_integer_add: %s", snd_strerror(err)); + return err; + } + } + err = snd_config_string_add(control, "name", info.id.name); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + if (info.id.index != 0) { + err = snd_config_integer_add(control, "index", info.id.index); + if (err < 0) { + error("snd_config_integer_add: %s", snd_strerror(err)); + return err; + } + } + + if (info.type == SND_CONTROL_TYPE_BYTES) { + char buf[info.values_count * 2 + 1]; + char *p = buf; + char *hex = "0123456789abcdef"; + for (idx = 0; idx < info.values_count; idx++) { + int v = ctl.value.bytes.data[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; + } + + if (info.values_count == 1) { + switch (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + err = snd_config_string_add(control, "value", ctl.value.integer.value[0] ? "true" : "false"); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + return 0; + case SND_CONTROL_TYPE_INTEGER: + err = snd_config_integer_add(control, "value", ctl.value.integer.value[0]); + if (err < 0) { + error("snd_config_integer_add: %s", snd_strerror(err)); + return err; + } + return 0; + case SND_CONTROL_TYPE_ENUMERATED: + { + unsigned int v = ctl.value.integer.value[0]; + snd_config_t *c; + err = snd_config_search(item, num_str(v), &c); + if (err == 0) { + err = snd_config_string_get(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", info.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 (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + for (idx = 0; idx < info.values_count; idx++) { + err = snd_config_string_add(value, num_str(idx), ctl.value.integer.value[idx] ? "true" : "false"); + if (err < 0) { + error("snd_config_string_add: %s", snd_strerror(err)); + return err; + } + } + break; + case SND_CONTROL_TYPE_INTEGER: + for (idx = 0; idx < info.values_count; idx++) { + err = snd_config_integer_add(value, num_str(idx), ctl.value.integer.value[idx]); + if (err < 0) { + error("snd_config_integer_add: %s", snd_strerror(err)); + return err; + } + } + break; + case SND_CONTROL_TYPE_ENUMERATED: + for (idx = 0; idx < info.values_count; idx++) { + unsigned int v = ctl.value.integer.value[idx]; + snd_config_t *c; + err = snd_config_search(item, num_str(v), &c); + if (err == 0) { + err = snd_config_string_get(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", info.type); + return -EINVAL; + } + + return 0; +} + +static int get_controls(int cardno, snd_config_t *top) +{ + snd_ctl_t *handle; + snd_ctl_hw_info_t info; + snd_config_t *state, *card, *control; + snd_control_list_t list; + int idx, err; + + err = snd_ctl_open(&handle, cardno); + if (err < 0) { + error("snd_ctl_open error: %s", snd_strerror(err)); + return err; + } + err = snd_ctl_hw_info(handle, &info); + if (err < 0) { + error("snd_ctl_hw_info error: %s", snd_strerror(err)); + goto _close; + } + err = snd_config_search(top, "state", &state); + if (err == 0 && + snd_config_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, info.id, &card); + if (err == 0 && + snd_config_type(state) != SND_CONFIG_TYPE_COMPOUND) { + error("config state.%s node is not a compound", info.id); + err = -EINVAL; + goto _close; + } + if (err < 0) { + err = snd_config_compound_add(state, info.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_config_compound_add(card, "control", 1, &control); + if (err < 0) { + error("snd_config_compound_add: %s", snd_strerror(err)); + goto _close; + } + memset(&list, 0, sizeof(list)); + err = snd_ctl_clist(handle, &list); + if (err < 0) { + error("Cannot determine controls: %s", snd_strerror(err)); + goto _close; + } + if (list.controls <= 0) { + err = 0; + goto _close; + } + list.controls_request = list.controls; + list.controls_offset = list.controls_count = 0; + list.pids = malloc(sizeof(snd_control_id_t) * list.controls_request); + if (!list.pids) { + error("No enough memory..."); + goto _close; + } + if ((err = snd_ctl_clist(handle, &list)) < 0) { + error("Cannot determine controls (2): %s", snd_strerror(err)); + goto _free; + } + for (idx = 0; idx < list.controls_count; ++idx) { + err = get_control(handle, &list.pids[idx], control); + if (err < 0) + goto _free; + } + + err = 0; + _free: + free(list.pids); + _close: + snd_ctl_close(handle); + return err; +} + + +static int config_iface(snd_config_t *n) +{ + static struct { + int val; + char *str; + } v[] = { + { SND_CONTROL_IFACE_CARD, "card" }, + { SND_CONTROL_IFACE_HWDEP, "hwdep" }, + { SND_CONTROL_IFACE_MIXER, "mixer" }, + { SND_CONTROL_IFACE_PCM, "pcm" }, + { SND_CONTROL_IFACE_RAWMIDI, "rawmidi" }, + { SND_CONTROL_IFACE_TIMER, "timer" }, + { SND_CONTROL_IFACE_SEQUENCER, "sequencer" } + }; + long idx; + char *str; + switch (snd_config_type(n)) { + case SND_CONFIG_TYPE_INTEGER: + snd_config_integer_get(n, &idx); + return idx; + case SND_CONFIG_TYPE_STRING: + snd_config_string_get(n, &str); + break; + default: + return -1; + } + for (idx = 0; idx < sizeof(v) / sizeof(v[0]); ++idx) { + if (strcmp(v[idx].str, str) == 0) + return idx; + } + return -1; +} + +static int config_bool(snd_config_t *n) +{ + char *str; + long val; + switch (snd_config_type(n)) { + case SND_CONFIG_TYPE_INTEGER: + snd_config_integer_get(n, &val); + if (val < 0 || val > 1) + return -1; + return val; + case SND_CONFIG_TYPE_STRING: + snd_config_string_get(n, &str); + break; + default: + return -1; + } + if (strcmp(str, "on") || strcmp(str, "true")) + return 1; + if (strcmp(str, "off") || strcmp(str, "false")) + return 0; + return -1; +} + +static int config_enumerated(snd_config_t *n, snd_ctl_t *handle, + snd_control_info_t *info) +{ + char *str; + long val; + int idx; + switch (snd_config_type(n)) { + case SND_CONFIG_TYPE_INTEGER: + snd_config_integer_get(n, &val); + return val; + case SND_CONFIG_TYPE_STRING: + snd_config_string_get(n, &str); + break; + default: + return -1; + } + for (idx = 0; idx < info->value.enumerated.items; idx++) { + int err; + info->value.enumerated.item = idx; + err = snd_ctl_cinfo(handle, info); + if (err < 0) { + error("snd_ctl_info: %s", snd_strerror(err)); + return err; + } + if (strcmp(str, info->value.enumerated.name) == 0) + return idx; + } + return -1; +} + +static int set_control(snd_ctl_t *handle, snd_config_t *control) +{ + snd_control_t ctl; + snd_control_info_t info; + snd_config_iterator_t i; + int numid; + long iface = -1; + long device = -1; + long subdevice = -1; + char *name = NULL; + long index = -1; + snd_config_t *value = NULL; + long val; + int idx, err; + char *set; + if (snd_config_type(control) != SND_CONFIG_TYPE_COMPOUND) { + error("control is not a compound"); + return -EINVAL; + } + numid = atoi(snd_config_id(control)); + snd_config_foreach(i, control) { + snd_config_t *n = snd_config_entry(i); + char *fld = snd_config_id(n); + if (strcmp(fld, "comment") == 0) + continue; + if (strcmp(fld, "iface") == 0) { + iface = 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_type(n) != SND_CONFIG_TYPE_INTEGER) { + error("control.%d.%s is invalid", numid, fld); + return -EINVAL; + } + snd_config_integer_get(n, &device); + continue; + } + if (strcmp(fld, "subdevice") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_INTEGER) { + error("control.%d.%s is invalid", numid, fld); + return -EINVAL; + } + snd_config_integer_get(n, &subdevice); + continue; + } + if (strcmp(fld, "name") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_STRING) { + error("control.%d.%s is invalid", numid, fld); + return -EINVAL; + } + snd_config_string_get(n, &name); + continue; + } + if (strcmp(fld, "index") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_INTEGER) { + error("control.%d.%s is invalid", numid, fld); + return -EINVAL; + } + snd_config_integer_get(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; + memset(&info, 0, sizeof(info)); + info.id.numid = numid; + err = snd_ctl_cinfo(handle, &info); + if (err < 0) { + if (iface >= 0 && name) { + info.id.numid = 0; + info.id.iface = iface; + info.id.device = device; + info.id.subdevice = subdevice; + strncmp(info.id.name, name, sizeof(info.id.name)); + info.id.index = index; + err = snd_ctl_cinfo(handle, &info); + } + } + if (err < 0) { + error("failed to obtain info for control #%d (%s)", numid, snd_strerror(err)); + return -ENOENT; + } + if (info.id.numid != numid) + error("warning: numid mismatch (%d/%d) for control #%d", numid, info.id.numid, numid); + if (info.id.iface != iface) + error("warning: iface mismatch (%ld/%d) for control #%d", iface, info.id.iface, numid); + if (info.id.device != device) + error("warning: device mismatch (%ld/%d) for control #%d", device, info.id.device, numid); + if (info.id.subdevice != subdevice) + error("warning: subdevice mismatch (%ld/%d) for control #%d", subdevice, info.id.subdevice, numid); + if (strcmp(info.id.name, name)) + error("warning: name mismatch (%s/%s) for control #%d", name, info.id.name, numid); + if (info.id.index != index) + error("warning: index mismatch (%ld/%d) for control #%d", index, info.id.index, numid); + + if (!(info.access & SND_CONTROL_ACCESS_WRITE)) + return 0; + + memset(&ctl, 0, sizeof(ctl)); + ctl.id = info.id; + + if (info.values_count == 1) { + switch (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + val = config_bool(value); + if (val >= 0) { + ctl.value.integer.value[0] = val; + goto _ok; + } + break; + case SND_CONTROL_TYPE_INTEGER: + err = snd_config_integer_get(value, &val); + if (err == 0) { + ctl.value.integer.value[0] = val; + goto _ok; + } + break; + case SND_CONTROL_TYPE_ENUMERATED: + val = config_enumerated(value, handle, &info); + if (val >= 0) { + ctl.value.enumerated.item[0] = val; + goto _ok; + } + break; + default: + error("Unknow control type: %d", info.type); + return -EINVAL; + } + } + if (info.type == SND_CONTROL_TYPE_BYTES) { + char *buf; + err = snd_config_string_get(value, &buf); + if (err > 0) { + int c1 = 0; + int len = strlen(buf); + int idx = 0; + if (info.values_count * 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; + } + idx++; + if (idx % 2 == 0) + ctl.value.bytes.data[idx / 2] = c1 << 4 | c; + else + c1 = c; + } + goto _ok; + } + } + if (snd_config_type(value) != SND_CONFIG_TYPE_COMPOUND) { + error("bad control.%d.value type", numid); + return -EINVAL; + } + + set = alloca(info.values_count); + memset(set, 0, info.values_count); + snd_config_foreach(i, value) { + snd_config_t *n = snd_config_entry(i); + idx = atoi(snd_config_id(n)); + if (idx < 0 || idx >= info.values_count || + set[idx]) { + error("bad control.%d.value index", numid); + return -EINVAL; + } + switch (info.type) { + case SND_CONTROL_TYPE_BOOLEAN: + val = config_bool(n); + if (val < 0) { + error("bad control.%d.value.%d content", numid, idx); + return -EINVAL; + } + ctl.value.integer.value[idx] = val; + break; + case SND_CONTROL_TYPE_INTEGER: + err = snd_config_integer_get(n, &val); + if (err < 0) { + error("bad control.%d.value.%d content", numid, idx); + return -EINVAL; + } + ctl.value.integer.value[idx] = val; + break; + case SND_CONTROL_TYPE_ENUMERATED: + val = config_enumerated(n, handle, &info); + if (val < 0) { + error("bad control.%d.value.%d content", numid, idx); + return -EINVAL; + } + ctl.value.enumerated.item[idx] = val; + break; + case SND_CONTROL_TYPE_BYTES: + err = snd_config_integer_get(n, &val); + if (err < 0 || val < 0 || val > 255) { + error("bad control.%d.value.%d content", numid, idx); + return -EINVAL; + } + ctl.value.integer.value[idx] = val; + break; + default: + break; + } + set[idx] = 1; + } + for (idx = 0; idx < info.values_count; ++idx) { + if (!set[idx]) { + error("control.%d.value.%d is not specified", numid, idx); + return -EINVAL; + } + } + + _ok: + err = snd_ctl_cwrite(handle, &ctl); + if (err < 0) { + error("Cannot write control '%s': %s", id_str(&ctl.id), snd_strerror(err)); + return err; + } + return 0; +} + +static int set_controls(int card, snd_config_t *top) +{ + snd_ctl_t *handle; + snd_ctl_hw_info_t info; + snd_config_t *control; + snd_config_iterator_t i; + int err; + + err = snd_ctl_open(&handle, card); + if (err < 0) { + error("snd_ctl_open error: %s", snd_strerror(err)); + return err; + } + err = snd_ctl_hw_info(handle, &info); + if (err < 0) { + error("snd_ctl_hw_info error: %s", snd_strerror(err)); + goto _close; + } + err = snd_config_searchv(top, &control, "state", info.id, "control", 0); + if (err < 0) { + err = 0; + fprintf(stderr, "No state is present for card %s\n", info.id); + goto _close; + } + if (snd_config_type(control) != SND_CONFIG_TYPE_COMPOUND) { + error("state.%s.control is not a compound\n", info.id); + return -EINVAL; + } + snd_config_foreach(i, control) { + snd_config_t *n = snd_config_entry(i); + err = set_control(handle, n); + if (err < 0) + goto _close; + } + + _close: + snd_ctl_close(handle); + return err; } -static int store_setup(const char *cardname) + +static int save_state(char *file, const char *cardname) { int err; + snd_config_t *config; + FILE *fp; + + err = snd_config_top(&config); + if (err < 0) { + error("snd_config_top error: %s", snd_strerror(err)); + return err; + } + fp = fopen(file, "r"); + if (fp) { + err = snd_config_load(config, fp); + fclose(fp); + if (err < 0) { + error("snd_config_load error: %s", snd_strerror(err)); + return err; + } + } if (!cardname) { unsigned int card_mask, idx; @@ -76,62 +910,88 @@ static int store_setup(const char *cardname) error("No soundcards found..."); return 1; } - soundcard_setup_init(); for (idx = 0; idx < 32; idx++) { if (card_mask & (1 << idx)) { /* find each installed soundcards */ - if ((err = soundcard_setup_collect_controls(idx))) { - soundcard_setup_done(); + if ((err = get_controls(idx, config))) { return err; } } } - err = soundcard_setup_write(cfgfile, -1); - soundcard_setup_done(); } else { int cardno; - cardno = snd_card_name(cardname); + cardno = snd_card_get_index(cardname); if (cardno < 0) { error("Cannot find soundcard '%s'...", cardname); return 1; } - if ((err = soundcard_setup_collect_controls(cardno))) { - soundcard_setup_done(); + if ((err = get_controls(cardno, config))) { return err; } - err = soundcard_setup_write(cfgfile, cardno); - soundcard_setup_done(); } - return err; + + fp = fopen(file, "w"); + if (!fp) { + error("Cannot open %s for writing", file); + return -errno; + } + err = snd_config_save(config, fp); + fclose(fp); + if (err < 0) + error("snd_config_save: %s", snd_strerror(err)); + return 0; } -static int restore_setup(const char *cardname) + +static int load_state(char *file, const char *cardname) { - int err, cardno = -1; + int err; + snd_config_t *config; + FILE *fp; - if (cardname) { - cardno = snd_card_name(cardname); + err = snd_config_top(&config); + if (err < 0) { + error("snd_config_top error: %s", snd_strerror(err)); + return err; + } + fp = fopen(file, "r"); + if (fp) { + err = snd_config_load(config, fp); + fclose(fp); + if (err < 0) { + error("snd_config_load error: %s", snd_strerror(err)); + return err; + } + } + + if (!cardname) { + unsigned int card_mask, idx; + + card_mask = snd_cards_mask(); + if (!card_mask) { + error("No soundcards found..."); + return 1; + } + for (idx = 0; idx < 32; idx++) { + if (card_mask & (1 << idx)) { /* find each installed soundcards */ + if ((err = set_controls(idx, config))) { + return err; + } + } + } + } else { + int cardno; + + cardno = snd_card_get_index(cardname); if (cardno < 0) { error("Cannot find soundcard '%s'...", cardname); return 1; } + if ((err = set_controls(cardno, config))) { + return err; + } } - if ((err = soundcard_setup_load(cfgfile, 0))) - return err; - if ((err = soundcard_setup_collect_controls(cardno))) { - soundcard_setup_done(); - return err; - } - if ((err = soundcard_setup_merge_controls(cardno))) { - soundcard_setup_done(); - return err; - } - if ((err = soundcard_setup_process_controls(cardno))) { - soundcard_setup_done(); - return err; - } - soundcard_setup_done(); - return err; + return 0; } int main(int argc, char *argv[]) @@ -139,13 +999,15 @@ int main(int argc, char *argv[]) int morehelp; struct option long_option[] = { - {"help", 0, NULL, HELPID_HELP}, - {"file", 1, NULL, HELPID_FILE}, - {"debug", 0, NULL, HELPID_DEBUG}, - {"version", 0, NULL, HELPID_VERSION}, + {"help", 0, NULL, 'h'}, + {"file", 1, NULL, 'f'}, + {"debug", 0, NULL, 'd'}, + {"version", 0, NULL, 'v'}, {NULL, 0, NULL, 0}, }; + char *cfgfile = SYS_ASOUNDRC; + command = argv[0]; morehelp = 0; while (1) { int c; @@ -154,20 +1016,15 @@ int main(int argc, char *argv[]) break; switch (c) { case 'h': - case HELPID_HELP: morehelp++; break; case 'f': - case HELPID_FILE: - strncpy(cfgfile, optarg, sizeof(cfgfile) - 1); - cfgfile[sizeof(cfgfile) - 1] = 0; + cfgfile = optarg; break; case 'd': - case HELPID_DEBUG: debugflag = 1; break; case 'v': - case HELPID_VERSION: printf("alsactl version " SND_UTIL_VERSION_STR "\n"); return 1; default: @@ -184,10 +1041,10 @@ int main(int argc, char *argv[]) return 0; } if (!strcmp(argv[optind], "store")) { - return store_setup(argc - optind > 1 ? argv[optind + 1] : NULL) ? + return save_state(cfgfile, argc - optind > 1 ? argv[optind + 1] : NULL) ? 1 : 0; } else if (!strcmp(argv[optind], "restore")) { - return restore_setup(argc - optind > 1 ? argv[optind + 1] : NULL) ? + return load_state(cfgfile, argc - optind > 1 ? argv[optind + 1] : NULL) ? 1 : 0; } else { fprintf(stderr, "alsactl: Unknown command '%s'...\n", argv[optind]); diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h deleted file mode 100644 index 89df00a..0000000 --- a/alsactl/alsactl.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1997 by Perex, APS, University of South Bohemia - * - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <sys/asoundlib.h> - -#define ALSACTL_FILE "/etc/asound.conf" - -#define LEFT 1 -#define RIGHT 2 - -#define OUTPUT 0 -#define INPUT 1 - -extern int debugflag; - -extern void error(const char *fmt,...); - -struct ctl_control { - int change; - snd_control_type_t type; - snd_control_info_t info; - snd_control_t c; - struct ctl_control *next; -}; - -struct ctl { - snd_ctl_hw_info_t hwinfo; - struct ctl_control *controls; -}; - -struct soundcard { - int no; /* card number */ - struct ctl control; - struct soundcard *next; -}; - -extern struct soundcard *soundcards; -extern struct soundcard *rsoundcards; /* read soundcards */ - -void soundcard_setup_init(void); -void soundcard_setup_done(void); -int soundcard_setup_load(const char *filename, int skip); -int soundcard_setup_write(const char *filename, int cardno); -int soundcard_setup_collect_controls(int cardno); -int soundcard_setup_merge_controls(int cardno); -int soundcard_setup_process_controls(int cardno); - -char *control_id(snd_control_id_t *id); diff --git a/alsactl/alsactl_lexer.l b/alsactl/alsactl_lexer.l deleted file mode 100644 index 7253c16..0000000 --- a/alsactl/alsactl_lexer.l +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1998 by Perex, APS, University of South Bohemia - * - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -%{ - -#include "alsactl.h" - -struct bytearray { - unsigned char *data; - size_t datalen; -}; - -#include "alsactl_parser.h" - -#define YY_NO_UNPUT -#undef YY_CDECL -#define YY_CDECL int YY_PROTO(yylex( void )); - -int linecount; - -%} - -%% - - /* special characters */ - -"{"|"}" return yytext[0]; -"("|")" return yytext[0]; -"["|"]" return yytext[0]; -")"[ \t]*"{" return L_DOUBLE1; -"," return yytext[0]; -"=" return yytext[0]; - - /* tokens */ - -soundcard return L_SOUNDCARD; -control return L_CONTROL; - -global return L_GLOBAL; -hwdep return L_HWDEP; -mixer return L_MIXER; -pcm return L_PCM; -rawmidi return L_RAWMIDI; -timer return L_TIMER; -sequencer return L_SEQUENCER; - -ident return L_IDENT; -iface return L_IFACE; -name return L_NAME; -device return L_DEVICE; -subdevice return L_SUBDEVICE; -index return L_INDEX; - -bool return L_BOOL; -int return L_INT; -enum return L_ENUM; -byte return L_BYTE; - - /* boolean */ - -false|off|no return L_FALSE; -true|on|yes return L_TRUE; - - /* integers */ - -[0-9]+ { yylval.i_value = strtol(yytext, (char **)NULL, 10); return L_INTEGER; } -0x[0-9a-f]+ { yylval.i_value = strtol(yytext, (char **)NULL, 0); return L_INTEGER; } - - /* strings */ - -\"[^\"]*\" { yytext[strlen(yytext) - 1] = 0; - yylval.s_value = strdup(&yytext[1]); - return L_STRING; } -\'[^\']*\' { yytext[strlen(yytext) - 1] = 0; - yylval.s_value = strdup(&yytext[1]); - return L_STRING; } -[a-z0-9/\~@-Za-z_]+ { yylval.s_value = strdup(yytext); - return L_STRING; } -$[a-z0-9/\~@-Za-z_]+ { yylval.s_value = strdup(getenv(&yytext[1])); - return L_STRING; } - - /* comments & whitespaces */ - -[#\;][^\n]*\n { linecount++; } -[ \t]+ ; -\n { linecount++; } -. fprintf( stderr, "alsactl: discarding char '%c' - line %i\n", yytext[0], linecount + 1 ); - -%% - -#ifndef yywrap -int yywrap(void) /* do this avoid to do -lfl */ -{ - return 1; -} -#endif diff --git a/alsactl/alsactl_parser.y b/alsactl/alsactl_parser.y deleted file mode 100644 index 8eef8ff..0000000 --- a/alsactl/alsactl_parser.y +++ /dev/null @@ -1,319 +0,0 @@ -%{ - -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1998 by Perex, APS, University of South Bohemia - * - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include "alsactl.h" -#include <stdarg.h> - - /* insgus_lexer.c */ - -int yylex( void ); - -extern char cfgfile[]; -extern int linecount; -extern FILE *yyin; - - /* structure for byte arrays */ - -struct bytearray { - unsigned char *data; - size_t datalen; -}; - - /* local functions */ - -static void yyerror(char *, ...); - -static void build_soundcard(char *name); -static void build_control_begin(void); -static void build_control_end(void); -static void set_control_iface(int iface); -static void set_control_device(int dev); -static void set_control_subdevice(int subdev); -static void set_control_name(char *name); -static void set_control_index(int idx); -static void set_control_type(snd_control_type_t type); -static void set_control_boolean(int val); -static void set_control_integer(long val); - - /* local variables */ - -static struct soundcard *Xsoundcard = NULL; -static struct ctl_control *Xcontrol = NULL; -static int Xposition = 0; -static snd_control_type_t Xtype = SND_CONTROL_TYPE_NONE; - -%} - -%start lines - -%union { - int b_value; - long i_value; - char *s_value; - struct bytearray a_value; - }; - -%token <b_value> L_TRUE L_FALSE -%token <i_value> L_INTEGER -%token <s_value> L_STRING -%token <a_value> L_BYTEARRAY - - /* types */ -%token L_INTEGER L_STRING - /* boolean */ -%token L_FALSE L_TRUE - /* misc */ -%token L_DOUBLE1 - /* other keywords */ -%token L_SOUNDCARD L_CONTROL L_RAWDATA -%token L_GLOBAL L_HWDEP L_MIXER L_PCM L_RAWMIDI L_TIMER L_SEQUENCER -%token L_IDENT L_IFACE L_NAME L_DEVICE L_SUBDEVICE L_INDEX -%token L_BOOL L_INT L_ENUM L_BYTE - -%type <b_value> boolean -%type <i_value> integer iface -%type <s_value> string -%type <a_value> rawdata - -%% - -lines : line - | lines line - ; - -line : L_SOUNDCARD '(' string { build_soundcard($3); } - L_DOUBLE1 soundcards '}' { build_soundcard(NULL); } - | error { yyerror("unknown keyword in top level"); } - ; - -soundcards : - | soundcards soundcard - ; - -soundcard : L_CONTROL '(' L_IDENT '=' { build_control_begin(); } - '{' ctlids '}' ',' controls ')' { build_control_end(); } - | error { yyerror("an unknown keyword in the soundcard{} level"); } - ; - -ctlids : ctlid - | ctlids ',' ctlid - ; - -ctlid : L_IFACE '=' iface { set_control_iface($3); } - | L_DEVICE '=' integer { set_control_device($3); } - | L_SUBDEVICE '=' integer { set_control_subdevice($3); } - | L_NAME '=' string { set_control_name($3); } - | L_INDEX '=' integer { set_control_index($3); } - | error { yyerror("an unknown keyword in the control ID level"); } - ; - -controls : control - ; - -control : L_BOOL '=' { set_control_type(SND_CONTROL_TYPE_BOOLEAN); } '{' datas '}' - | L_INT '=' { set_control_type(SND_CONTROL_TYPE_INTEGER); } '{' datas '}' - | L_ENUM '=' { set_control_type(SND_CONTROL_TYPE_ENUMERATED); } '{' datas '}' - | L_BYTE '=' { set_control_type(SND_CONTROL_TYPE_BYTES); } '{' datas '}' - | error { yyerror( "an unknown keyword in the control() data parameter" ); } - ; - -datas : data - | datas ',' data - ; - -data : boolean { set_control_boolean($1); } - | integer { set_control_integer($1); } - | error { yyerror( "an unknown keyword in the control() data argument" ); } - ; - -iface : L_INTEGER { $$ = $1; } - | L_GLOBAL { $$ = SND_CONTROL_IFACE_CARD; } - | L_HWDEP { $$ = SND_CONTROL_IFACE_HWDEP; } - | L_MIXER { $$ = SND_CONTROL_IFACE_MIXER; } - | L_PCM { $$ = SND_CONTROL_IFACE_PCM; } - | L_RAWMIDI { $$ = SND_CONTROL_IFACE_RAWMIDI; } - | L_TIMER { $$ = SND_CONTROL_IFACE_TIMER; } - | L_SEQUENCER { $$ = SND_CONTROL_IFACE_SEQUENCER; } - | error { yyerror( "an unknown keyword in the interface field"); } - ; - -boolean : L_TRUE { $$ = 1; } - | L_FALSE { $$ = 0; } - ; - -integer : L_INTEGER { $$ = $1; } - ; - -string : L_STRING { $$ = $1; } - ; - -rawdata : L_RAWDATA '(' L_BYTEARRAY ')' { $$ = $3; } - | L_RAWDATA error { yyerror( "malformed rawdata value" ); } - ; - -%% - -static void yyerror(char *string,...) -{ - char errstr[1024]; - - va_list vars; - va_start(vars, string); - vsprintf(errstr, string, vars); - va_end(vars); - error("Error in configuration file '%s' (line %i): %s", cfgfile, linecount + 1, errstr); - - exit(1); -} - -static void error_nomem(void) -{ - yyerror("No enough memory...\n"); -} - -static void build_soundcard(char *name) -{ - struct soundcard *soundcard; - - if (!name) { - Xsoundcard = NULL; - return; - } - Xsoundcard = (struct soundcard *)malloc(sizeof(struct soundcard)); - if (!Xsoundcard) { - free(name); - error_nomem(); - return; - } - bzero(Xsoundcard, sizeof(*Xsoundcard)); - for (soundcard = rsoundcards; soundcard && soundcard->next; soundcard = soundcard->next); - if (soundcard) { - soundcard->next = Xsoundcard; - } else { - rsoundcards = Xsoundcard; - } - strncpy(Xsoundcard->control.hwinfo.id, name, sizeof(Xsoundcard->control.hwinfo.id)); - free(name); -} - -static void build_control_begin(void) -{ - struct ctl_control **first; - struct ctl_control *ctl; - - first = &Xsoundcard->control.controls; - Xcontrol = (struct ctl_control *)malloc(sizeof(struct ctl_control)); - if (!Xcontrol) { - error_nomem(); - return; - } - Xposition = 0; - Xtype = SND_CONTROL_TYPE_NONE; - bzero(Xcontrol, sizeof(*Xcontrol)); - for (ctl = *first; ctl && ctl->next; ctl = ctl->next); - if (ctl) { - ctl->next = Xcontrol; - } else { - *first = Xcontrol; - } -} - -static void build_control_end(void) -{ - Xcontrol = NULL; -} - -static void set_control_iface(int iface) -{ - Xcontrol->c.id.iface = iface; -} - -static void set_control_device(int dev) -{ - Xcontrol->c.id.device = dev; -} - -static void set_control_subdevice(int subdev) -{ - Xcontrol->c.id.subdevice = subdev; -} - -static void set_control_name(char *name) -{ - if (name == NULL) - return; - strncpy(Xcontrol->c.id.name, name, sizeof(Xcontrol->c.id.name)); - free(name); -} - -static void set_control_index(int idx) -{ - Xcontrol->c.id.index = idx; -} - -static void set_control_type(snd_control_type_t type) -{ - Xcontrol->type = Xtype = type; -} - -static void set_control_boolean(int val) -{ - if (Xposition >= 512) - yyerror("Array overflow."); - switch (Xtype) { - case SND_CONTROL_TYPE_BOOLEAN: - Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0; - break; - case SND_CONTROL_TYPE_INTEGER: - Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0; - break; - case SND_CONTROL_TYPE_ENUMERATED: - Xcontrol->c.value.enumerated.item[Xposition++] = val ? 1 : 0; - break; - case SND_CONTROL_TYPE_BYTES: - Xcontrol->c.value.bytes.data[Xposition++] = val ? 1 : 0; - break; - default: break; - } -} - -static void set_control_integer(long val) -{ - if (Xposition >= 512) - yyerror("Array overflow."); - switch (Xtype) { - case SND_CONTROL_TYPE_BOOLEAN: - Xcontrol->c.value.integer.value[Xposition++] = val ? 1 : 0; - break; - case SND_CONTROL_TYPE_INTEGER: - Xcontrol->c.value.integer.value[Xposition++] = val; - break; - case SND_CONTROL_TYPE_ENUMERATED: - Xcontrol->c.value.enumerated.item[Xposition++] = (unsigned int)val; - break; - case SND_CONTROL_TYPE_BYTES: - Xcontrol->c.value.bytes.data[Xposition++] = (unsigned char)val; - break; - default: break; - } -} diff --git a/alsactl/merge.c b/alsactl/merge.c deleted file mode 100644 index 46d87f1..0000000 --- a/alsactl/merge.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1999 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include "alsactl.h" - -static int merge_one_control(struct ctl_control *cctl, struct ctl_control *uctl, int cardno) -{ - int idx; - - if (!(cctl->info.access & SND_CONTROL_ACCESS_WRITE)) - return 0; - switch (cctl->info.type) { - case SND_CONTROL_TYPE_BOOLEAN: - if (uctl->type != SND_CONTROL_TYPE_BOOLEAN) { - error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id)); - return 1; - } - for (idx = 0; idx < cctl->info.values_count; idx++) { - if (cctl->c.value.integer.value[idx] != uctl->c.value.integer.value[idx]) { - cctl->change = 1; - cctl->c.value.integer.value[idx] = uctl->c.value.integer.value[idx]; - } - } - break; - case SND_CONTROL_TYPE_INTEGER: - if (uctl->type != SND_CONTROL_TYPE_INTEGER) { - error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id)); - return 1; - } - for (idx = 0; idx < cctl->info.values_count; idx++) { - if (cctl->info.value.integer.min > uctl->c.value.integer.value[idx] || - cctl->info.value.integer.max < uctl->c.value.integer.value[idx]) { - error("The value %li for the control '%s' is out of range %i-%i.", uctl->c.value.integer.value[idx], control_id(&uctl->c.id), cctl->info.value.integer.min, cctl->info.value.integer.max); - return 1; - } - if (cctl->c.value.integer.value[idx] != uctl->c.value.integer.value[idx]) { - cctl->change = 1; - cctl->c.value.integer.value[idx] = uctl->c.value.integer.value[idx]; - } - } - break; - case SND_CONTROL_TYPE_ENUMERATED: - if (uctl->type != SND_CONTROL_TYPE_ENUMERATED) { - error("A wrong type %i for the control '%s'. The type integer is expected. Skipping...", uctl->type, control_id(&uctl->c.id)); - return 1; - } - for (idx = 0; idx < cctl->info.values_count; idx++) { - if (cctl->info.value.enumerated.items <= uctl->c.value.enumerated.item[idx]) { - error("The value %u for the control '%s' is out of range 0-%i.", uctl->c.value.integer.value[idx], control_id(&uctl->c.id), cctl->info.value.enumerated.items-1); - return 1; - } - if (cctl->c.value.enumerated.item[idx] != uctl->c.value.enumerated.item[idx]) { - cctl->change = 1; - cctl->c.value.enumerated.item[idx] = uctl->c.value.enumerated.item[idx]; - } - } - break; - case SND_CONTROL_TYPE_BYTES: - if (uctl->type != SND_CONTROL_TYPE_BYTES) { - error("A wrong type %i for the control %s. The type 'bytes' is expected. Skipping...", uctl->type, control_id(&uctl->c.id)); - return 1; - } - if (memcmp(cctl->c.value.bytes.data, uctl->c.value.bytes.data, uctl->info.values_count)) { - cctl->change = 1; - memcpy(cctl->c.value.bytes.data, uctl->c.value.bytes.data, uctl->info.values_count); - } - break; - default: - error("The control type %i is not known.", cctl->type); - } - return 0; -} - -static int soundcard_setup_merge_control(struct ctl_control *cctl, struct ctl_control *uctl, int cardno) -{ - struct ctl_control *cctl1; - - for ( ; uctl; uctl = uctl->next) { - for (cctl1 = cctl; cctl1; cctl1 = cctl1->next) { - if (cctl1->c.id.iface == uctl->c.id.iface && - cctl1->c.id.device == uctl->c.id.device && - cctl1->c.id.subdevice == uctl->c.id.subdevice && - !strncmp(cctl1->c.id.name, uctl->c.id.name, sizeof(cctl1->c.id.name)) && - cctl1->c.id.index == uctl->c.id.index) { - merge_one_control(cctl1, uctl, cardno); - break; - } - } - if (!cctl1) { - error("Cannot find the control %s...", control_id(&uctl->c.id)); - } - } - return 0; -} - -int soundcard_setup_merge_controls(int cardno) -{ - struct soundcard *soundcard, *rsoundcard; - - for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) { - for (soundcard = soundcards; soundcard; soundcard = soundcard->next) { - if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id))) - break; - } - if (!soundcard) { - error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id); - continue; - } - if (cardno >= 0 && soundcard->no != cardno) - continue; - soundcard_setup_merge_control(soundcard->control.controls, rsoundcard->control.controls, soundcard->no); - } - return 0; -} - -static int soundcard_open_ctl(snd_ctl_t **ctlhandle, struct soundcard *soundcard) -{ - int err; - - if (*ctlhandle) - return 0; - if ((err = snd_ctl_open(ctlhandle, soundcard->no)) < 0) { - error("Cannot open control interface for soundcard #%i.", soundcard->no + 1); - return 1; - } - return 0; -} - -int soundcard_setup_process_controls(int cardno) -{ - int err; - snd_ctl_t *ctlhandle = NULL; - struct soundcard *soundcard; - struct ctl_control *ctl; - - for (soundcard = soundcards; soundcard; soundcard = soundcard->next) { - if (cardno >= 0 && soundcard->no != cardno) - continue; - for (ctl = soundcard->control.controls; ctl; ctl = ctl->next) { - if (ctl->change) - if (!soundcard_open_ctl(&ctlhandle, soundcard)) { - if ((err = snd_ctl_cwrite(ctlhandle, &ctl->c)) < 0) - error("Control '%s' write error: %s", control_id(&ctl->c.id), snd_strerror(err)); - } - } - if (ctlhandle) { - snd_ctl_close(ctlhandle); - ctlhandle = NULL; - } - } - return 0; -} diff --git a/alsactl/setup.c b/alsactl/setup.c deleted file mode 100644 index 8385268..0000000 --- a/alsactl/setup.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Advanced Linux Sound Architecture Control Program - * Copyright (c) 1997 by Perex, APS, University of South Bohemia - * - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include "alsactl.h" - -#define SND_INTERFACE_CONTROL 0 -#define SND_INTERFACE_MIXER 1 -#define SND_INTERFACE_PCM 2 -#define SND_INTERFACE_RAWMIDI 3 - -extern int yyparse(void); -extern int linecount; -extern FILE *yyin; -extern int yydebug; -struct soundcard *soundcards = NULL; -struct soundcard *rsoundcards = NULL; - -/* - * misc functions - */ - -char *control_id(snd_control_id_t *id) -{ - static char str[128]; - - if (!id) - return "???"; - sprintf(str, "%i,%i,%i,%s,%i", id->iface, id->device, id->subdevice, id->name, id->index); - return str; -} - -/* - * free functions - */ - -static void soundcard_ctl_control_free(struct ctl_control *first) -{ - struct ctl_control *next; - - while (first) { - next = first->next; - free(first); - first = next; - } -} - -static void soundcard_free1(struct soundcard *soundcard) -{ - if (!soundcard) - return; - soundcard_ctl_control_free(soundcard->control.controls); - free(soundcard); -} - -static void soundcard_free(struct soundcard *first) -{ - struct soundcard *next; - - while (first) { - next = first->next; - soundcard_free1(first); - first = next; - } -} - -static int soundcard_remove(int cardno) -{ - struct soundcard *first, *prev = NULL, *next; - - first = soundcards; - while (first) { - next = first->next; - if (first->no == cardno) { - soundcard_free1(first); - if (!prev) - soundcards = next; - else - prev->next = next; - return 0; - } - prev = first; - first = first->next; - } - return -1; -} - -/* - * exported functions - */ - -void soundcard_setup_init(void) -{ - soundcards = NULL; -} - -void soundcard_setup_done(void) -{ - soundcard_free(soundcards); - soundcard_free(rsoundcards); - soundcards = NULL; -} - -static int determine_controls(void *handle, struct ctl_control **cctl) -{ - int err, idx; - snd_control_list_t list; - snd_control_id_t *item; - snd_control_t ctl; - struct ctl_control *prev_control; - struct ctl_control *new_control; - - *cctl = NULL; - bzero(&list, sizeof(list)); - if ((err = snd_ctl_clist(handle, &list)) < 0) { - error("Cannot determine controls: %s", snd_strerror(err)); - return 1; - } - if (list.controls <= 0) - return 0; - list.controls_request = list.controls + 16; - list.controls_offset = list.controls_count = 0; - list.pids = malloc(sizeof(snd_control_id_t) * list.controls_request); - if (!list.pids) { - error("No enough memory..."); - return 1; - } - if ((err = snd_ctl_clist(handle, &list)) < 0) { - error("Cannot determine controls (2): %s", snd_strerror(err)); - return 1; - } - for (idx = 0, prev_control = NULL; idx < list.controls_count; idx++) { - item = &list.pids[idx]; - bzero(&ctl, sizeof(ctl)); - ctl.id = *item; - if ((err = snd_ctl_cread(handle, &ctl)) < 0) { - error("Cannot read control '%s': %s", control_id(item), snd_strerror(err)); - free(list.pids); - return 1; - } - new_control = malloc(sizeof(*new_control)); - if (!new_control) { - error("No enough memory..."); - free(list.pids); - return 1; - } - bzero(new_control, sizeof(*new_control)); - memcpy(&new_control->c, &ctl, sizeof(new_control->c)); - new_control->info.id = ctl.id; - if ((err = snd_ctl_cinfo(handle, &new_control->info)) < 0) { - error("Cannot read control info '%s': %s", control_id(item), snd_strerror(err)); - free(new_control); - free(list.pids); - return 1; - } - if (*cctl) { - prev_control->next = new_control; - prev_control = new_control; - } else { - *cctl = prev_control = new_control; - } - } - free(list.pids); - return 0; -} - -static int soundcard_setup_collect_controls1(int cardno) -{ - snd_ctl_t *handle; - struct soundcard *card, *first, *prev; - int err; - - soundcard_remove(cardno); - if ((err = snd_ctl_open(&handle, cardno)) < 0) { - error("SND CTL open error: %s", snd_strerror(err)); - return 1; - } - /* --- */ - card = (struct soundcard *) malloc(sizeof(struct soundcard)); - if (!card) { - snd_ctl_close(handle); - error("malloc error"); - return 1; - } - bzero(card, sizeof(struct soundcard)); - card->no = cardno; - for (first = soundcards, prev = NULL; first; first = first->next) { - if (first->no > cardno) { - if (!prev) { - soundcards = card; - } else { - prev->next = card; - } - card->next = first; - break; - } - prev = first; - } - if (!first) { - if (!soundcards) { - soundcards = card; - } else { - prev->next = card; - } - } - if ((err = snd_ctl_hw_info(handle, &card->control.hwinfo)) < 0) { - snd_ctl_close(handle); - error("SND CTL HW INFO error: %s", snd_strerror(err)); - return 1; - } - /* --- */ - if (determine_controls(handle, &card->control.controls)) { - snd_ctl_close(handle); - return 1; - } - /* --- */ - snd_ctl_close(handle); - return 0; -} - -int soundcard_setup_collect_controls(int cardno) -{ - int err; - unsigned int mask; - - if (cardno >= 0) { - return soundcard_setup_collect_controls1(cardno); - } else { - mask = snd_cards_mask(); - for (cardno = 0; cardno < SND_CARDS; cardno++) { - if (!(mask & (1 << cardno))) - continue; - err = soundcard_setup_collect_controls1(cardno); - if (err) - return err; - } - return 0; - } -} - -int soundcard_setup_load(const char *cfgfile, int skip) -{ - extern int yyparse(void); - extern int linecount; - extern FILE *yyin; -#ifdef YYDEBUG - extern int yydebug; -#endif - int xtry; - -#ifdef YYDEBUG - yydebug = 1; -#endif - if (debugflag) - printf("cfgfile = '%s'\n", cfgfile); - if (skip && access(cfgfile, R_OK)) - return 0; - if ((yyin = fopen(cfgfile, "r")) == NULL) { - error("Cannot open configuration file '%s'...", cfgfile); - return 1; - } - linecount = 0; - xtry = yyparse(); - fclose(yyin); - if (debugflag) - printf("Config ok..\n"); - if (xtry) - error("Ignored error in configuration file '%s'...", cfgfile); - return 0; -} - -static void soundcard_setup_write_control(FILE * out, const char *space, int card, struct ctl_control *control) -{ - char *s, v[16]; - int err, idx; - snd_ctl_t *handle; - snd_control_info_t info; - - memcpy(&info, &control->info, sizeof(info)); - v[0] = '\0'; - switch (info.type) { - case SND_CONTROL_TYPE_BOOLEAN: s = "bool"; break; - case SND_CONTROL_TYPE_INTEGER: s = "integer"; break; - case SND_CONTROL_TYPE_ENUMERATED: s = "enumerated"; break; - case SND_CONTROL_TYPE_BYTES: s = "bytes"; break; - default: s = "unknown"; - } - fprintf(out, "\n%s; The type is '%s'. Access:", space, s); - if (info.access & SND_CONTROL_ACCESS_READ) - fprintf(out, " read"); - if (info.access & SND_CONTROL_ACCESS_WRITE) - fprintf(out, " write"); - if (info.access & SND_CONTROL_ACCESS_INACTIVE) - fprintf(out, " inactive"); - fprintf(out, ". Count is %i.\n", info.values_count); - switch (info.type) { - case SND_CONTROL_TYPE_BOOLEAN: - if (info.value.integer.min != 0 || info.value.integer.max != 1 || - info.value.integer.step != 0) - error("Wrong control '%s' (boolean)\n", control_id(&info.id)); - break; - case SND_CONTROL_TYPE_INTEGER: - fprintf(out, "%s; The range is %li-%li (step %li)\n", space, info.value.integer.min, info.value.integer.max, info.value.integer.step); - break; - case SND_CONTROL_TYPE_ENUMERATED: - if ((err = snd_ctl_open(&handle, card)) >= 0) { - for (idx = 0; idx < info.value.enumerated.items; idx++) { - info.value.enumerated.item = idx; - if (snd_ctl_cinfo(handle, &info) >= 0) - fprintf(out, "%s; Item #%i - %s\n", space, idx, info.value.enumerated.name); - } - snd_ctl_close(handle); - } - break; - default: - break; - } - switch (info.id.iface) { - case SND_CONTROL_IFACE_CARD: s = "global"; break; - case SND_CONTROL_IFACE_HWDEP: s = "hwdep"; break; - case SND_CONTROL_IFACE_MIXER: s = "mixer"; break; - case SND_CONTROL_IFACE_PCM: s = "pcm"; break; - case SND_CONTROL_IFACE_RAWMIDI: s = "rawmidi"; break; - case SND_CONTROL_IFACE_TIMER: s = "timer"; break; - case SND_CONTROL_IFACE_SEQUENCER: s = "sequencer"; break; - default: sprintf(v, "%i", info.id.iface); s = v; break; - } - fprintf(out, "%scontrol(ident={iface=%s", space, s); - if (info.id.device > 0) - fprintf(out, ", device=%i", info.id.device); - if (info.id.subdevice > 0) - fprintf(out, ", subdevice=%i", info.id.subdevice); - fprintf(out, ", name='%s'", info.id.name); - if (info.id.index > 0) - fprintf(out, ", index=%i", info.id.index); - fprintf(out, "}, "); - switch (info.type) { - case SND_CONTROL_TYPE_BOOLEAN: fprintf(out, "bool={"); break; - case SND_CONTROL_TYPE_INTEGER: fprintf(out, "int={"); break; - case SND_CONTROL_TYPE_ENUMERATED: fprintf(out, "enum={"); break; - case SND_CONTROL_TYPE_BYTES: fprintf(out, "byte={"); break; - default: break; - } - for (idx = 0; idx < info.values_count; idx++) { - if (idx > 0) - fprintf(out, ","); - switch (info.type) { - case SND_CONTROL_TYPE_BOOLEAN: - fprintf(out, "%s", control->c.value.integer.value[idx] ? "true" : "false"); - break; - case SND_CONTROL_TYPE_INTEGER: - fprintf(out, "%li", control->c.value.integer.value[idx]); - break; - case SND_CONTROL_TYPE_ENUMERATED: - fprintf(out, "%u", control->c.value.enumerated.item[idx]); - break; - case SND_CONTROL_TYPE_BYTES: - fprintf(out, "%02x", control->c.value.bytes.data[idx]); - break; - default: - break; - } - } - fprintf(out, "})\n"); -} - -static void soundcard_setup_write_controls(FILE *out, const char *space, int card, struct ctl_control **controls) -{ - struct ctl_control *ctl; - - if (!(*controls)) - return; - for (ctl = *controls; ctl; ctl = ctl->next) - soundcard_setup_write_control(out, space, card, ctl); -} - -#define MAX_LINE (32 * 1024) - -int soundcard_setup_write(const char *cfgfile, int cardno) -{ - FILE *out, *out1, *out2, *in; - char *tmpfile1, *tmpfile2; - struct soundcard *first, *sel = NULL; - char *line, cardname[sizeof(first->control.hwinfo.name)+16], *ptr1; - int mark, size, ok; - - tmpfile1 = (char *)malloc(strlen(cfgfile) + 16); - tmpfile2 = (char *)malloc(strlen(cfgfile) + 16); - if (!tmpfile1 || !tmpfile2) { - error("No enough memory...\n"); - if (tmpfile1) - free(tmpfile1); - if (tmpfile2) - free(tmpfile2); - return 1; - } - strcpy(tmpfile1, cfgfile); - strcat(tmpfile1, ".new"); - strcpy(tmpfile2, cfgfile); - strcat(tmpfile2, ".insert"); - - if (cardno >= 0) { - line = (char *)malloc(MAX_LINE); - if (!line) { - error("No enough memory...\n"); - return 1; - } - if ((in = fopen(cfgfile, "r")) == NULL) - cardno = -1; - } else { - line = NULL; - in = NULL; - } - if ((out = out1 = fopen(tmpfile1, "w+")) == NULL) { - error("Cannot open file '%s' for writing...\n", tmpfile1); - return 1; - } - fprintf(out, "# ALSA driver configuration\n"); - fprintf(out, "# This configuration is generated with the alsactl program.\n"); - fprintf(out, "\n"); - if (cardno >= 0) { - if ((out = out2 = fopen(tmpfile2, "w+")) == NULL) { - error("Cannot open file '%s' for writing...\n", tmpfile2); - return 1; - } - } else { - out2 = NULL; - } - for (first = soundcards; first; first = first->next) { - if (cardno >= 0 && first->no != cardno) - continue; - sel = first; - fprintf(out, "soundcard(\"%s\") {\n", first->control.hwinfo.id); - if (first->control.controls) { - soundcard_setup_write_controls(out, " ", first->no, &first->control.controls); - } - fprintf(out, "}\n%s", cardno < 0 && first->next ? "\n" : ""); - } - /* merge the old and new text */ - if (cardno >= 0) { - fseek(out2, 0, SEEK_SET); - mark = ok = 0; - __1: - while (fgets(line, MAX_LINE - 1, in)) { - line[MAX_LINE - 1] = '\0'; - if (!strncmp(line, "soundcard(", 10)) - break; - } - while (!feof(in)) { - ptr1 = line + 10; - while (*ptr1 && *ptr1 != '"') - ptr1++; - if (*ptr1) - ptr1++; - strncpy(cardname, sel->control.hwinfo.id, sizeof(sel->control.hwinfo.id)); - cardname[sizeof(sel->control.hwinfo.id)] = '\0'; - strcat(cardname, "\""); - if (!strncmp(ptr1, cardname, strlen(cardname))) { - if (mark) - fprintf(out1, "\n"); - do { - size = fread(line, 1, MAX_LINE, out2); - if (size > 0) - fwrite(line, 1, size, out1); - } while (size > 0); - mark = ok = 1; - goto __1; - } else { - if (mark) - fprintf(out1, "\n"); - fprintf(out1, line); - while (fgets(line, MAX_LINE - 1, in)) { - line[MAX_LINE - 1] = '\0'; - fprintf(out1, line); - if (line[0] == '}') { - mark = 1; - goto __1; - } - } - } - } - if (!ok) { - if (mark) - fprintf(out1, "\n"); - do { - size = fread(line, 1, MAX_LINE, out2); - printf("size = %i\n", size); - if (size > 0) - fwrite(line, 1, size, out1); - } while (size > 0); - } - } - if (in) - fclose(in); - if (out2) - fclose(out2); - if (!access(cfgfile, F_OK) && remove(cfgfile)) - error("Cannot remove file '%s'...", cfgfile); - if (rename(tmpfile1, cfgfile) < 0) - error("Cannot rename file '%s' to '%s'...", tmpfile1, cfgfile); - fclose(out1); - if (line) - free(line); - if (tmpfile2) { - remove(tmpfile2); - free(tmpfile2); - } - if (tmpfile1) { - remove(tmpfile1); - free(tmpfile1); - } - return 0; -} |