From 17b4129e6c89d1a96d4d86dabea38389927e3cf4 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 26 Feb 2021 19:28:03 +0100 Subject: [PATCH] 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 --- alsactl/Makefile.am | 2 +- alsactl/alsactl.1 | 2 + alsactl/alsactl.c | 5 + alsactl/alsactl.h | 1 + alsactl/clean.c | 228 ++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 2 +- 6 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 alsactl/clean.c diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am index c1031ac..deff2cd 100644 --- a/alsactl/Makefile.am +++ b/alsactl/Makefile.am @@ -10,7 +10,7 @@ EXTRA_DIST=alsactl.1 alsactl_init.xml AM_CFLAGS = -D_GNU_SOURCE alsactl_SOURCES=alsactl.c state.c lock.c utils.c init_parse.c init_ucm.c \ - daemon.c monitor.c + daemon.c monitor.c clean.c alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \ -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \ diff --git a/alsactl/alsactl.1 b/alsactl/alsactl.1 index 615491a..6fdc099 100644 --- a/alsactl/alsactl.1 +++ b/alsactl/alsactl.1 @@ -39,6 +39,8 @@ rescan, save_and_quit). \fImonitor\fP is for monitoring the events received from the given control device. +\fIclean\fP clean the controls created by applications. + If no soundcards are specified, setup for all cards will be saved, loaded or monitored. diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c index 20ebac1..06ea707 100644 --- a/alsactl/alsactl.c +++ b/alsactl/alsactl.c @@ -187,6 +187,7 @@ int main(int argc, char *argv[]) char *pidfile = SYS_PIDFILE; char *cardname, ncardname[16]; char *cmd; + char *const *extra_args; const char *const *tmp; int removestate = 0; int init_fallback = 1; /* new default behavior */ @@ -346,6 +347,8 @@ int main(int argc, char *argv[]) } } + extra_args = argc - optind > 2 ? argv + optind + 2 : NULL; + /* the global system file should be always locked */ if (strcmp(cfgfile, SYS_ASOUNDRC) == 0 && do_lock >= 0) do_lock = 1; @@ -391,6 +394,8 @@ int main(int argc, char *argv[]) res = state_daemon_kill(pidfile, cardname); } else if (!strcmp(cmd, "monitor")) { res = monitor(cardname); + } else if (!strcmp(cmd, "clean")) { + res = clean(cardname, extra_args); } else { fprintf(stderr, "alsactl: Unknown command '%s'...\n", cmd); res = -ENODEV; diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h index c478695..74079de 100644 --- a/alsactl/alsactl.h +++ b/alsactl/alsactl.h @@ -40,6 +40,7 @@ int monitor(const char *name); int state_daemon(const char *file, const char *cardname, int period, const char *pidfile); int state_daemon_kill(const char *pidfile, const char *cmd); +int clean(const char *cardname, char *const *extra_args); /* utils */ diff --git a/alsactl/clean.c b/alsactl/clean.c new file mode 100644 index 0000000..b74714f --- /dev/null +++ b/alsactl/clean.c @@ -0,0 +1,228 @@ +/* + * Advanced Linux Sound Architecture Control Program + * Copyright (c) by Jaroslav Kysela + * + * + * 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 +#include +#include +#include +#include +#include +#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; +} diff --git a/configure.ac b/configure.ac index 1f119c5..7005ccc 100644 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ AC_PROG_MKDIR_P AC_PROG_LN_S AC_PROG_SED PKG_PROG_PKG_CONFIG -AM_PATH_ALSA(1.0.27) +AM_PATH_ALSA(1.2.5) if test "x$enable_alsatest" = "xyes"; then AC_CHECK_FUNC([snd_ctl_elem_add_enumerated], , [AC_ERROR([No user enum control support in alsa-lib])]) -- 2.47.1