alsactl - remove 'names' command, fix alsactl man page

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2008-08-18 16:47:33 +02:00
parent a227a9ad52
commit ef8bfcecda
4 changed files with 39 additions and 576 deletions

View file

@ -4,7 +4,7 @@ sbin_PROGRAMS=alsactl
man_MANS=alsactl.1 alsactl_init.7
EXTRA_DIST=alsactl.1 alsactl_init.xml
alsactl_SOURCES=alsactl.c state.c names.c utils.c init_parse.c
alsactl_SOURCES=alsactl.c state.c utils.c init_parse.c
noinst_HEADERS=alsactl.h
%.7: %.xml

View file

@ -4,7 +4,7 @@ alsactl \- advanced controls for ALSA soundcard driver
.SH SYNOPSIS
\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fInames\fP] <card # or id>
\fBalsactl\fP [\fIoptions\fP] [\fIstore\fP|\fIrestore\fP|\fIinit\fP] <card # or id or device>
.SH DESCRIPTION
\fBalsactl\fP is used to control advanced settings for the ALSA
@ -18,11 +18,11 @@ you have come to the right place.
to the configuration file.
\fIrestore\fP loads driver state for the selected soundcard from the
configuration file.
configuration file. If restoring fails (eventually partly), the init
action is called.
\fInames\fP generates list of available device names for applications.
The card number or id is ignored for this command. The list is always
generated for all available cards.
\fIinit\fP tries to initialize all devices to a default state. If device
is not known, error code 99 is returned.
If no soundcards are specified, setup for all cards will be saved or
loaded.
@ -33,10 +33,17 @@ loaded.
\fI\-h, \-\-help\fP
Help: show available flags and commands.
.TP
\fI\-d, \-\-debug\fP
Use debug mode: a bit more verbose.
.TP
\fI\-v, \-\-version\fP
Print alsactl version number.
.TP
\fI\-f, \-\-file\fP
Select the configuration file to use. The default is /etc/asound.state or
/etc/asound.names (for the \fInames\fP command).
Select the configuration file to use. The default is /etc/asound.state.
.TP
\fI\-F, \-\-force\fP
@ -49,12 +56,22 @@ Used with restore command. Don't restore mismatching control elements.
This option was the old default behavior.
.TP
\fI\-d, \-\-debug\fP
Use debug mode: a bit more verbose.
\fI\-r, \-\-runstate\fP
Save restore and init state to this file. The file will contain only errors.
Errors are appended with the soundcard id to the end of file.
.TP
\fI\-v, \-\-version\fP
Print alsactl version number.
\fI\-R, \-\-remove\fP
Remove runstate file at first.
.TP
\fI\-E, \-\-env\fP #=#
Set environment variable (useful for init action).
.TP
\fI\-i, \-\-initfile\fP #=#
The configuration file for init. By default, PREFIX/share/alsa/init/00main
is used.
.SH FILES
\fI/etc/asound.state\fP (or whatever file you specify with the
@ -70,13 +87,6 @@ necessary for some soundcard features (e.g. enabling/disabling
automatic mic gain, digital output, joystick/game ports, some future MIDI
routing options, etc).
\fI/etc/asound.names\fP (or whatever file you specify with the
\fB\-f\fP flag) is used to store the list of device names available
in your system. The list does not contain all virtual names, because
the name space is infinite, but it detects present hardware and
generates list of common names. The user / system administrator / another
configuration tool might modify the file to add virtual names as well.
.SH SEE ALSO
\fB
amixer(1),

View file

@ -31,7 +31,6 @@
#include "alsactl.h"
#define SYS_ASOUNDRC "/etc/asound.state"
#define SYS_ASOUNDNAMES "/etc/asound.names"
int debugflag = 0;
int force_restore = 1;
@ -46,12 +45,13 @@ static void help(void)
printf(" -d,--debug debug mode\n");
printf(" -v,--version print version of this program\n");
printf("\nAvailable state options:\n");
printf(" -f,--file # configuration file (default " SYS_ASOUNDRC " or " SYS_ASOUNDNAMES ")\n");
printf(" -f,--file # configuration file (default " SYS_ASOUNDRC ")\n");
printf(" -F,--force try to restore the matching controls as much as possible\n");
printf(" (default mode)\n");
printf(" -P,--pedantic don't restore mismatching controls (old default)\n");
printf(" -r,--runstate # save restore and init state to this file (only errors)\n");
printf(" default settings is 'no file set'\n");
printf(" -R,--remove remove runstate file at first, otherwise append errors\n");
printf("\nAvailable init options:\n");
printf(" -E,--env #=# set environment variable for init phase (NAME=VALUE)\n");
printf(" -i,--initfile # main configuation file for init phase (default " DATADIR "/init/00main)\n");
@ -77,6 +77,7 @@ int main(int argc, char *argv[])
{"force", 0, NULL, 'F'},
{"pedantic", 0, NULL, 'P'},
{"runstate", 0, NULL, 'r'},
{"remove", 0, NULL, 'R'},
{"debug", 0, NULL, 'd'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0},
@ -84,13 +85,14 @@ int main(int argc, char *argv[])
char *cfgfile = SYS_ASOUNDRC;
char *initfile = DATADIR "/init/00main";
char *cardname;
int removestate = 0;
int res;
command = argv[0];
while (1) {
int c;
if ((c = getopt_long(argc, argv, "hdvf:FE:i:Pr:", long_option, NULL)) < 0)
if ((c = getopt_long(argc, argv, "hdvf:FE:i:Pr:R", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
@ -114,6 +116,9 @@ int main(int argc, char *argv[])
case 'r':
statefile = optarg;
break;
case 'R':
removestate = 1;
break;
case 'P':
force_restore = 0;
break;
@ -143,12 +148,9 @@ int main(int argc, char *argv[])
} else if (!strcmp(argv[optind], "store")) {
res = save_state(cfgfile, cardname);
} else if (!strcmp(argv[optind], "restore")) {
remove(statefile);
if (removestate)
remove(statefile);
res = load_state(cfgfile, initfile, cardname);
} else if (!strcmp(argv[optind], "names")) {
if (!strcmp(cfgfile, SYS_ASOUNDRC))
cfgfile = SYS_ASOUNDNAMES;
res = generate_names(cfgfile);
} else {
fprintf(stderr, "alsactl: Unknown command '%s'...\n",
argv[optind]);

View file

@ -1,549 +0,0 @@
/*
* Advanced Linux Sound Architecture Control Program - ALSA Device Names
* Copyright (c) 2005 by Jaroslav Kysela <perex@perex.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "aconfig.h"
#include "version.h"
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <alsa/asoundlib.h>
#include "alsactl.h"
typedef int (probe_single)(int card, snd_ctl_t *ctl, snd_config_t *config);
static int globidx;
static void dummy_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
{
}
static int for_each_card(probe_single *fcn, snd_config_t *config)
{
int card = -1, first = 1, err;
snd_ctl_t *ctl;
char ctlname[16];
while (1) {
if (snd_card_next(&card) < 0)
break;
if (card < 0) {
if (first) {
error("No soundcards found...");
return -ENODEV;
}
break;
}
first = 0;
sprintf(ctlname, "hw:%i", card);
err = snd_ctl_open(&ctl, ctlname, 0);
if (err < 0)
return err;
err = (fcn)(card, ctl, config);
snd_ctl_close(ctl);
if (err < 0)
return err;
}
return 0;
}
static int add_entry(snd_config_t *cfg, const char *name,
const char *cprefix, const char *flag,
const char *comment)
{
int err;
snd_config_t *c, *d;
char id[16];
char xcomment[256];
char *flag0 = " (", *flag1 = ")";
if (cprefix == NULL)
cprefix = "";
if (flag == NULL) {
flag0 = "";
flag = "";
flag1 = "";
}
sprintf(xcomment, "%s - %s%s%s%s", cprefix, comment, flag0, flag, flag1);
sprintf(id, "alsactl%i", globidx++);
err = snd_config_make_compound(&c, id, 0);
if (err < 0)
return err;
err = snd_config_add(cfg, c);
if (err < 0)
return err;
err = snd_config_make_string(&d, "name");
if (err < 0)
return err;
err = snd_config_set_string(d, name);
if (err < 0)
return err;
err = snd_config_add(c, d);
if (err < 0)
return err;
err = snd_config_make_string(&d, "comment");
if (err < 0)
return err;
err = snd_config_set_string(d, xcomment);
if (err < 0)
return err;
err = snd_config_add(c, d);
if (err < 0)
return err;
return 0;
}
static int probe_ctl_card(int card, snd_ctl_t *ctl, snd_config_t *config)
{
int err;
snd_ctl_card_info_t * info;
char name[16];
const char *dname;
snd_ctl_card_info_alloca(&info);
err = snd_ctl_card_info(ctl, info);
if (err < 0) {
error("Unable to get info for card %i: %s\n", card, snd_strerror(err));
return err;
}
sprintf(name, "hw:%i", card);
dname = snd_ctl_card_info_get_longname(info);
err = add_entry(config, name, "Physical Device", NULL, dname);
if (err < 0)
return err;
return 0;
}
static int probe_ctl(snd_config_t *config)
{
int err;
snd_config_t *c;
err = snd_config_make_compound(&c, "ctl", 0);
if (err < 0)
return err;
err = snd_config_add(config, c);
if (err < 0)
return err;
err = for_each_card(probe_ctl_card, c);
if (err < 0)
return err;
return 0;
}
static int probe_pcm_virtual(int card, snd_ctl_t *ctl, snd_config_t *config,
const char *name, const char *comment)
{
snd_pcm_t *pcm1, *pcm2;
int err1, err2, playback, capture, err;
char name1[32], name2[32], *flag;
if (!debugflag)
snd_lib_error_set_handler(dummy_error_handler);
sprintf(name1, name, card);
sprintf(name2, "plug:%s", name1);
err1 = snd_pcm_open(&pcm1, name1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
err2 = snd_pcm_open(&pcm2, name1, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
snd_lib_error_set_handler(NULL);
if (err1 >= 0)
snd_pcm_close(pcm1);
if (err2 >= 0)
snd_pcm_close(pcm2);
playback = (err1 == 0 || err1 == -EBUSY);
capture = (err2 == 0 || err2 == -EBUSY);
if (playback && capture)
flag = "Duplex";
else if (playback)
flag = "Playback";
else if (capture)
flag = "Capture";
else
return 0;
err = add_entry(config, name1, "Abstract Device", flag, comment);
if (err >= 0)
err = add_entry(config, name2, "Abstract Device With Conversions", flag, comment);
return err;
}
static int probe_pcm_card(int card, snd_ctl_t *ctl, snd_config_t *config)
{
int dev = -1, err, err1, err2;
snd_pcm_info_t * info1, * info2;
snd_pcm_class_t class;
char name[16];
const char *dname;
char *flag;
int first = 1, idx;
static const char *vnames1[] = {
"default:%i", "Default Device",
"front:%i", "Front Speakers",
"rear:%i", "Rear Speakers",
NULL
};
static const char *vnames2[] = {
"surround40:%i", "Front and Rear Speakers",
"surround51:%i", "Front, Rear, Center and Woofer",
"surround71:%i", "Front, Rear, Side, Center and Woofer",
"spdif:%i", "S/PDIF (IEC958) Optical or Coaxial Wire",
"phoneline:%i", "Phone Line Interface",
"modem:%i", "Soft Modem",
NULL
};
snd_pcm_info_alloca(&info1);
snd_pcm_info_alloca(&info2);
while (1) {
err = snd_ctl_pcm_next_device(ctl, &dev);
if (err < 0)
return err;
if (dev < 0)
break;
memset(info1, 0, snd_pcm_info_sizeof());
snd_pcm_info_set_device(info1, dev);
snd_pcm_info_set_stream(info1, SND_PCM_STREAM_PLAYBACK);
err1 = snd_ctl_pcm_info(ctl, info1);
memset(info2, 0, snd_pcm_info_sizeof());
snd_pcm_info_set_device(info2, dev);
snd_pcm_info_set_stream(info2, SND_PCM_STREAM_CAPTURE);
err2 = snd_ctl_pcm_info(ctl, info2);
if (err1 < 0 && err2 < 0) {
error("Unable to get info for pcm device %i:%i: %s\n", card, dev, snd_strerror(err1));
continue;
}
dname = snd_pcm_info_get_name(info1);
class = snd_pcm_info_get_class(info1);
if (err1 == 0 && err2 == 0)
flag = "Duplex";
else if (err1 == 0)
flag = "Playback";
else {
flag = "Capture";
dname = snd_pcm_info_get_name(info2);
class = snd_pcm_info_get_class(info2);
}
if (class != SND_PCM_CLASS_GENERIC &&
class != SND_PCM_CLASS_MULTI &&
class != SND_PCM_CLASS_MODEM ) /* skip this */
continue;
if (first) {
for (idx = 0; vnames1[idx] != NULL; idx += 2)
probe_pcm_virtual(card, ctl, config, vnames1[idx], vnames1[idx+1]);
}
first = 0;
sprintf(name, "hw:%i,%i", card, dev);
err = add_entry(config, name, "Physical Device", flag, dname);
if (err < 0)
return err;
sprintf(name, "plughw:%i,%i", card, dev);
err = add_entry(config, name, "Physical Device With Conversions", flag, dname);
if (err < 0)
return err;
}
if (!first) {
for (idx = 0; vnames2[idx] != NULL; idx += 2)
probe_pcm_virtual(card, ctl, config, vnames2[idx], vnames2[idx+1]);
}
return 0;
}
static int probe_pcm(snd_config_t *config)
{
int err;
snd_config_t *c;
err = snd_config_make_compound(&c, "pcm", 0);
if (err < 0)
return err;
err = snd_config_add(config, c);
if (err < 0)
return err;
err = for_each_card(probe_pcm_card, c);
if (err < 0)
return err;
return 0;
}
static int probe_rawmidi_virtual(snd_config_t *config,
const char *name, const char *comment)
{
snd_rawmidi_t *rawmidi1, *rawmidi2;
int err1, err2, playback, capture, err;
char *flag;
if (!debugflag)
snd_lib_error_set_handler(dummy_error_handler);
err1 = snd_rawmidi_open(NULL, &rawmidi1, name, SND_RAWMIDI_NONBLOCK);
err2 = snd_rawmidi_open(&rawmidi2, NULL, name, SND_RAWMIDI_NONBLOCK);
snd_lib_error_set_handler(NULL);
if (err1 >= 0)
snd_rawmidi_close(rawmidi1);
if (err2 >= 0)
snd_rawmidi_close(rawmidi2);
playback = (err1 == 0 || err1 == -EBUSY);
capture = (err2 == 0 || err2 == -EBUSY);
if (playback && capture)
flag = "Duplex";
else if (playback)
flag = "Playback";
else if (capture)
flag = "Capture";
else
return 0;
err = add_entry(config, name, "Abstract Device", flag, comment);
return err;
}
static int probe_rawmidi_card(int card, snd_ctl_t *ctl, snd_config_t *config)
{
int dev = -1, err, err1, err2;
snd_rawmidi_info_t * info1, * info2;
char name[16];
const char *dname;
const char *subname;
char *flag;
int subdev;
snd_rawmidi_info_alloca(&info1);
snd_rawmidi_info_alloca(&info2);
while (1) {
err = snd_ctl_rawmidi_next_device(ctl, &dev);
if (err < 0)
return err;
if (dev < 0)
break;
memset(info1, 0, snd_rawmidi_info_sizeof());
snd_rawmidi_info_set_device(info1, dev);
snd_rawmidi_info_set_stream(info1, SND_RAWMIDI_STREAM_OUTPUT);
err1 = snd_ctl_rawmidi_info(ctl, info1);
memset(info2, 0, snd_rawmidi_info_sizeof());
snd_rawmidi_info_set_device(info2, dev);
snd_rawmidi_info_set_stream(info2, SND_RAWMIDI_STREAM_INPUT);
err2 = snd_ctl_rawmidi_info(ctl, info2);
if (err1 < 0 && err2 < 0) {
error("Unable to get info for rawmidi device %i:%i: %s\n", card, dev, snd_strerror(err1));
continue;
}
dname = snd_rawmidi_info_get_name(info1);
subname = snd_rawmidi_info_get_subdevice_name(info1);
if (err1 == 0 && err2 == 0)
flag = "Duplex";
else if (err1 == 0)
flag = "Output";
else {
flag = "Input";
dname = snd_rawmidi_info_get_name(info2);
subname = snd_rawmidi_info_get_subdevice_name(info2);
}
if (subname[0] == '\0') {
sprintf(name, "hw:%i,%i", card, dev);
err = add_entry(config, name, "Physical Device", flag, dname);
if (err < 0)
return err;
} else {
subdev = 0;
do {
sprintf(name, "hw:%i,%i,%i", card, dev, subdev);
if (err1 == 0)
subname = snd_rawmidi_info_get_subdevice_name(info1);
else
subname = snd_rawmidi_info_get_subdevice_name(info2);
if (err1 == 0 && err2 == 0)
flag = "Duplex";
else if (err1 == 0)
flag = "Output";
else
flag = "Input";
err = add_entry(config, name, "Physical Device", flag, subname);
if (err < 0)
return err;
++subdev;
snd_rawmidi_info_set_subdevice(info1, subdev);
snd_rawmidi_info_set_subdevice(info2, subdev);
err1 = snd_ctl_rawmidi_info(ctl, info1);
err2 = snd_ctl_rawmidi_info(ctl, info2);
} while (err1 == 0 || err2 == 0);
}
}
return 0;
}
static int probe_rawmidi(snd_config_t *config)
{
int err;
snd_config_t *c;
err = snd_config_make_compound(&c, "rawmidi", 0);
if (err < 0)
return err;
err = snd_config_add(config, c);
if (err < 0)
return err;
err = probe_rawmidi_virtual(c, "default", "Default Device");
if (err < 0)
return err;
err = for_each_card(probe_rawmidi_card, c);
if (err < 0)
return err;
err = add_entry(c, "virtual", "Virtual Device", "Duplex", "Sequencer");
if (err < 0)
return err;
err = add_entry(c, "virtual:MERGE=0", "Virtual Device", "Duplex", "Sequencer (No Merge)");
if (err < 0)
return err;
return 0;
}
static int probe_timers(snd_config_t *config)
{
int err;
snd_timer_query_t *handle;
snd_timer_id_t *id;
snd_timer_ginfo_t *info;
char name[64];
const char *dname;
err = snd_timer_query_open(&handle, "default", 0);
if (err < 0)
return err;
snd_timer_id_alloca(&id);
snd_timer_ginfo_alloca(&info);
snd_timer_id_set_class(id, SND_TIMER_CLASS_NONE);
while (1) {
err = snd_timer_query_next_device(handle, id);
if (err < 0)
goto _err;
if (snd_timer_id_get_class(id) < 0)
break;
if (snd_timer_id_get_class(id) == SND_TIMER_CLASS_PCM)
continue;
snd_timer_ginfo_set_tid(info, id);
err = snd_timer_query_info(handle, info);
if (err < 0)
continue;
sprintf(name, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i",
snd_timer_id_get_class(id),
snd_timer_id_get_sclass(id),
snd_timer_id_get_card(id),
snd_timer_id_get_device(id),
snd_timer_id_get_subdevice(id));
dname = snd_timer_ginfo_get_name(info);
err = add_entry(config, name, "Physical Device", NULL, dname);
if (err < 0)
goto _err;
}
err = 0;
_err:
snd_timer_query_close(handle);
return err;
}
static int probe_timer(snd_config_t *config)
{
int err;
snd_config_t *c;
err = snd_config_make_compound(&c, "timer", 0);
if (err < 0)
return err;
err = snd_config_add(config, c);
if (err < 0)
return err;
err = probe_timers(c);
if (err < 0)
return err;
return 0;
}
static int probe_seq(snd_config_t *config)
{
int err;
snd_config_t *c;
err = snd_config_make_compound(&c, "seq", 0);
if (err < 0)
return err;
err = snd_config_add(config, c);
if (err < 0)
return err;
err = add_entry(c, "default", "Default Device", "Duplex", "Sequencer");
if (err < 0)
return err;
err = add_entry(c, "hw", "Physical Device", "Duplex", "Sequencer");
if (err < 0)
return err;
return 0;
}
typedef int (probe_fcn)(snd_config_t *config);
static probe_fcn * probes[] = {
probe_ctl,
probe_pcm,
probe_rawmidi,
probe_timer,
probe_seq,
NULL
};
int generate_names(const char *cfgfile)
{
int err, idx;
snd_config_t *config;
snd_output_t *out;
int stdio, ok = 0;
err = snd_config_top(&config);
if (err < 0) {
error("snd_config_top error: %s", snd_strerror(err));
return err;
}
for (idx = 0; probes[idx]; idx++) {
globidx = 1;
err = (probes[idx])(config);
if (err < 0) {
/* ignore -ENOTTY indicating the non-existing component */
if (err != -ENOTTY)
error("probe %i failed: %s", idx, snd_strerror(err));
} else {
ok++;
}
}
if (ok > 0) {
stdio = !strcmp(cfgfile, "-");
if (stdio) {
err = snd_output_stdio_attach(&out, stdout, 0);
} else {
err = snd_output_stdio_open(&out, cfgfile, "w+");
}
if (err < 0) {
error("Cannot open %s for writing: %s", cfgfile, snd_strerror(err));
return -errno;
}
err = snd_config_save(config, out);
snd_output_close(out);
if (err < 0)
error("snd_config_save: %s", snd_strerror(err));
} else {
return -ENOENT;
}
return 0;
}