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])])