]> git.alsa-project.org Git - alsa-utils.git/commitdiff
alsactl: add 'clean' command to remove the application controls
authorJaroslav Kysela <perex@perex.cz>
Fri, 26 Feb 2021 18:28:03 +0000 (19:28 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 4 Mar 2021 20:15:56 +0000 (21:15 +0100)
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>
alsactl/Makefile.am
alsactl/alsactl.1
alsactl/alsactl.c
alsactl/alsactl.h
alsactl/clean.c [new file with mode: 0644]
configure.ac

index c1031ac818aa05bafda542d3850ca6946e2de108..deff2cd1e8b7f68dae1f037f65c7cbfaf6ce2cca 100644 (file)
@@ -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\" \
index 615491a74f86b0fb18d151f3bc6fffd3acdfdaa6..6fdc0999da8a6dc5fb6ace02edb5e56638406de5 100644 (file)
@@ -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.
 
index 20ebac1567080dfd35160d564d7470f5c340bd4e..06ea7073061c8802237470981674c72ea1b18632 100644 (file)
@@ -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;
index c47869570373bf4cf5924591bdcbe8b73d740116..74079ded1c73cf1e55597264ac907ab2dd13d52b 100644 (file)
@@ -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 (file)
index 0000000..b74714f
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ *  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;
+}
index 1f119c5baeeb46125ac433ae85ecce6acc2d83e6..7005ccce5347c9f45178e79da95c252e9d4af8a1 100644 (file)
@@ -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])])