]> git.alsa-project.org Git - alsa-utils.git/commitdiff
alsactl: add -Y option to export card states as key=value pairs
authorJaroslav Kysela <perex@perex.cz>
Thu, 27 Nov 2025 16:04:46 +0000 (17:04 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 4 Dec 2025 14:08:14 +0000 (15:08 +0100)
Add export.c with support for exporting card states as key=value pairs.
Exports ALSA_CARD_NUMBER/ALSA_CARD_STATE for single cards, or
ALSA_CARD#_STATE for multiple cards. States: active, skip, waiting.

Add -Y (--export) option to restore command for state export.

This feature is designed for udev IMPORT{program} use.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
alsactl/Makefile.am
alsactl/alsactl.c
alsactl/alsactl.h
alsactl/export.c [new file with mode: 0644]
alsactl/state.c

index 1f6b712786ddc94013411f96eb7d8c21402c14e0..6725c9c50c325c1108ea676330e60c07d1ad2c5e 100644 (file)
@@ -13,7 +13,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include
 
 alsactl_SOURCES=alsactl.c state.c lock.c utils.c \
                init_parse.c init_ucm.c boot_params.c \
-               daemon.c monitor.c clean.c info.c
+               daemon.c monitor.c clean.c info.c export.c
 
 alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \
                -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \
index adde783637e7aab3498140c3ba051f0666207baa..8ea4a84cd00e27f177f7d2a59080ab560e95c42f 100644 (file)
@@ -53,6 +53,7 @@ int force_restore = 1;
 int ignore_nocards = 0;
 int do_lock = 0;
 int use_syslog = 0;
+int do_export = 0;
 char *command;
 char *statefile = NULL;
 char *groupfile = SYS_CARD_GROUP;
@@ -97,6 +98,7 @@ static struct arg args[] = {
 { FILEARG | 'r', "runstate", "save restore and init state to this file (only errors)" },
 { 0, NULL, "  default settings is 'no file set'" },
 { 'R', "remove", "remove runstate file at first, otherwise append errors" },
+{ 'Y', "export", "export card state as key=value pairs (restore command only)" },
 { INTARG | 'p', "period", "store period in seconds for the daemon command" },
 { FILEARG | 'e', "pid-file", "pathname for the process id (daemon mode)" },
 { HEADER, NULL, "Available init options:" },
@@ -362,6 +364,9 @@ int main(int argc, char *argv[])
                case 'R':
                        removestate = 1;
                        break;
+               case 'Y':
+                       do_export = 1;
+                       break;
                case 'P':
                        force_restore = 0;
                        break;
@@ -470,6 +475,8 @@ int main(int argc, char *argv[])
                if (removestate)
                        remove(statefile);
                res = load_state(cfgdir, cfgfile, initfile, initflags, cardname, init_fallback);
+               if (do_export && res >= 0)
+                       res = export_cards(cardname);
                if (!strcmp(cmd, "rdaemon")) {
                        do_nice(use_nice, sched_idle);
                        res = state_daemon(cfgfile, cardname, period, pidfile);
index 6f7b3a254214c3c77164aca3766a680657a0a6e8..aeee1143210c539e5cb38103f4725db640891d55 100644 (file)
@@ -8,6 +8,7 @@ extern int force_restore;
 extern int ignore_nocards;
 extern int do_lock;
 extern int use_syslog;
+extern int do_export;
 extern char *command;
 extern char *statefile;
 extern char *groupfile;
@@ -96,6 +97,11 @@ int state_daemon_kill(const char *pidfile, const char *cmd);
 int clean(const char *cardname, char *const *extra_args);
 int snd_card_clean_cfgdir(const char *cfgdir, int cardno);
 
+/* export */
+
+int export_card_state_set(int card, int state);
+int export_cards(const char *cardname);
+
 /* utils */
 
 int file_map(const char *filename, char **buf, size_t *bufsize);
diff --git a/alsactl/export.c b/alsactl/export.c
new file mode 100644 (file)
index 0000000..422a92c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program - Export
+ *  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
+ *
+ */
+
+/**
+ * Export variable syntax:
+ *
+ * For single card:
+ *
+ * ALSA_CARD_NUMBER=<number>
+ * ALSA_CARD_STATE=<state>
+ *
+ * For multiple cards:
+ *
+ * ALSA_CARD#_STATE=<state>            # where # is replaced with the card number
+ *
+ * State list:
+ *
+ * active                              # card was initialized and active
+ * skip                                        # card was skipped (other card in group)
+ * waiting                             # card is waiting for the initial configuration
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "alsactl.h"
+
+/* Global array to store card states (0 = active, 1 = waiting) */
+static int export_card_state[32];
+
+/**
+ * export_card_state_set - Set card state
+ * @card: Card number
+ * @state: Card state (0 = active, 1 = waiting)
+ *
+ * Returns 0 on success, negative error code on failure
+ */
+int export_card_state_set(int card, int state)
+{
+       /* Check bounds */
+       if (card < 0 || (unsigned long)card >= ARRAY_SIZE(export_card_state))
+               return -EINVAL;
+
+       export_card_state[card] = state;
+       return 0;
+}
+
+/**
+ * export_card_state_print - Export and print card state information
+ * @iter: Card iterator containing current card information
+ *
+ * Prints key=value pairs based on iterator's single flag and export_card_state array.
+ * Returns 0 on success, negative error code on failure
+ */
+static int export_card_state_print(struct snd_card_iterator *iter)
+{
+       const char *state;
+       int istate;
+
+       if (!iter)
+               return -EINVAL;
+
+       /* Check bounds */
+       if (iter->card < 0 || (unsigned long)iter->card >= ARRAY_SIZE(export_card_state))
+               return -EINVAL;
+
+       /* Determine state from export_card_state array */
+       istate = export_card_state[iter->card];
+       switch (istate) {
+       case CARD_STATE_WAIT: state = "waiting"; break;
+       case CARD_STATE_SKIP: state = "skip"; break;
+       default: state = "active"; break;
+       }
+
+       if (iter->single) {
+               /* Single card export format */
+               printf("ALSA_CARD_NUMBER=%d\n", iter->card);
+               printf("ALSA_CARD_STATE=%s\n", state);
+       } else {
+               /* Multiple cards export format */
+               printf("ALSA_CARD%d_STATE=%s\n", iter->card, state);
+       }
+
+       return 0;
+}
+
+/**
+ * export_cards - Export state for all cards
+ * @cardname: Card name or NULL for all cards
+ *
+ * Returns 0 on success, negative error code on failure
+ */
+int export_cards(const char *cardname)
+{
+       struct snd_card_iterator iter;
+       const char *name;
+       int ret;
+
+       ret = snd_card_iterator_sinit(&iter, cardname);
+       if (ret < 0)
+               return ret;
+
+       while ((name = snd_card_iterator_next(&iter)) != NULL) {
+               ret = export_card_state_print(&iter);
+               if (ret < 0)
+                       break;
+       }
+
+       if (ret == 0)
+               ret = snd_card_iterator_error(&iter);
+
+       return ret;
+}
index f9b926079892dc883e479f056250a2c3f0d65a7c..4d326a19eabab99ec95e5faea7daed71c827327d 100644 (file)
@@ -1729,6 +1729,8 @@ int load_state(const char *cfgdir, const char *file,
                        }
                        err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
                        card_unlock(lock_fd, iter.card);
+                       if (card_state_is_okay(err))
+                               export_card_state_set(iter.card, err);
                        if (err < 0) {
                                finalerr = err;
                                initfailed(iter.card, "init", err);
@@ -1754,8 +1756,10 @@ int load_state(const char *cfgdir, const char *file,
                /* error is ignored */
                err = init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
                /* return code 1 and 2 -> postpone initialization */
-               if (card_state_is_okay(err))
+               if (card_state_is_okay(err)) {
+                       export_card_state_set(iter.card, err);
                        goto unlock_card;
+               }
                /* do a check if controls matches state file */
                if (do_init && set_controls(iter.card, config, 0)) {
                        err = init(cfgdir, initfile, initflags | FLAG_UCM_BOOT, cardname1);