alsa-utils/alsactl/clean.c
Jaroslav Kysela 17b4129e6c alsactl: add 'clean' command to remove the application controls
It is handy to remove all card controls created by applications.

This change allows to remove those controls for all cards, selected
card or selected card with a control id filter list like:

   alsactl clean 0 "name='PCM'" "name='Mic Phantom'"

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
2021-03-04 21:15:56 +01:00

229 lines
5.2 KiB
C

/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "aconfig.h"
#include "version.h"
#include <getopt.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <alsa/asoundlib.h>
#include "alsactl.h"
static int clean_one_control(snd_ctl_t *handle, snd_ctl_elem_id_t *elem_id,
snd_ctl_elem_id_t **filter)
{
snd_ctl_elem_info_t *info;
char *s;
int err;
snd_ctl_elem_info_alloca(&info);
snd_ctl_elem_info_set_id(info, elem_id);
err = snd_ctl_elem_info(handle, info);
if (err < 0) {
s = snd_ctl_ascii_elem_id_get(elem_id);
error("Cannot read control info '%s': %s", s, snd_strerror(err));
free(s);
return err;
}
if (!snd_ctl_elem_info_is_user(info))
return 0;
s = snd_ctl_ascii_elem_id_get(elem_id);
dbg("Application control \"%s\" found.", s);
if (filter) {
for (; *filter; filter++) {
if (snd_ctl_elem_id_compare(elem_id, *filter) == 0)
break;
}
if (*filter == NULL) {
free(s);
return 0;
}
}
err = snd_ctl_elem_remove(handle, elem_id);
if (err < 0) {
error("Cannot remove control '%s': %s", s, snd_strerror(err));
free(s);
return err;
}
dbg("Application control \"%s\" removed.", s);
free(s);
return 0;
}
static void filter_controls_free(snd_ctl_elem_id_t **_filter)
{
snd_ctl_elem_id_t **filter;
for (filter = _filter; filter; filter++)
free(*filter);
free(_filter);
}
static int filter_controls_parse(char *const *controls, snd_ctl_elem_id_t ***_filter)
{
snd_ctl_elem_id_t **filter = NULL;
char *const *c;
char *s;
unsigned int count, idx;
int err;
if (!controls)
goto fin;
for (count = 0, c = controls; *c; c++, count++);
if (count == 0)
goto fin;
filter = calloc(count + 1, sizeof(snd_ctl_elem_id_t *));
if (filter == NULL) {
nomem:
error("No enough memory...");
return -ENOMEM;
}
filter[count] = NULL;
for (idx = 0; idx < count; idx++) {
err = snd_ctl_elem_id_malloc(&filter[idx]);
if (err < 0) {
filter_controls_free(filter);
goto nomem;
}
err = snd_ctl_ascii_elem_id_parse(filter[idx], controls[idx]);
if (err < 0) {
error("Cannot parse id '%s': %s", controls[idx], snd_strerror(err));
filter_controls_free(filter);
return err;
}
s = snd_ctl_ascii_elem_id_get(filter[idx]);
dbg("Add to filter: \"%s\"", s);
free(s);
}
fin:
*_filter = filter;
return 0;
}
static int clean_controls(int cardno, char *const *controls)
{
snd_ctl_t *handle;
snd_ctl_elem_list_t *list;
snd_ctl_elem_id_t *elem_id;
snd_ctl_elem_id_t **filter;
char name[32];
unsigned int idx, count;
int err;
snd_ctl_elem_id_alloca(&elem_id);
snd_ctl_elem_list_alloca(&list);
err = filter_controls_parse(controls, &filter);
if (err < 0)
return 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));
filter_controls_free(filter);
return err;
}
dbg("Control device '%s' opened.", name);
err = snd_ctl_elem_list(handle, list);
if (err < 0) {
error("Cannot determine controls: %s", snd_strerror(err));
goto fin_err;
}
count = snd_ctl_elem_list_get_count(list);
if (count == 0)
goto fin_ok;
snd_ctl_elem_list_set_offset(list, 0);
if ((err = snd_ctl_elem_list_alloc_space(list, count)) < 0) {
error("No enough memory...");
goto fin_err;
}
if ((err = snd_ctl_elem_list(handle, list)) < 0) {
error("Cannot determine controls (2): %s", snd_strerror(err));
goto fin_err;
}
for (idx = 0; idx < count; idx++) {
snd_ctl_elem_list_get_id(list, idx, elem_id);
err = clean_one_control(handle, elem_id, filter);
if (err < 0)
goto fin_err;
}
fin_ok:
filter_controls_free(filter);
snd_ctl_close(handle);
return 0;
fin_err:
filter_controls_free(filter);
snd_ctl_close(handle);
return err;
}
int clean(const char *cardname, char *const *extra_args)
{
int err;
if (!cardname) {
int card, first = 1;
card = -1;
/* find each installed soundcards */
while (1) {
if (snd_card_next(&card) < 0)
break;
if (card < 0) {
if (first) {
if (ignore_nocards) {
err = 0;
goto out;
} else {
error("No soundcards found...");
err = -ENODEV;
goto out;
}
}
break;
}
first = 0;
if ((err = clean_controls(card, extra_args)))
goto out;
}
} else {
int cardno;
cardno = snd_card_get_index(cardname);
if (cardno < 0) {
error("Cannot find soundcard '%s'...", cardname);
err = cardno;
goto out;
}
if ((err = clean_controls(cardno, extra_args))) {
goto out;
}
}
out:
return err;
}