]> git.alsa-project.org Git - alsa-utils.git/commitdiff
alsactl: implement 'info' command
authorJaroslav Kysela <perex@perex.cz>
Mon, 9 May 2022 07:34:22 +0000 (09:34 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 10 May 2022 07:08:26 +0000 (09:08 +0200)
It may be useful to collect the basic sound card information.
The output is in the YAML structured syntax (human and machine readable).

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

index 3d2f6e82b9e261eb41268bb5ca75694043fcada3..80dba69d07a6dae6f96359a2f9fc093d166df52a 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 clean.c
+               daemon.c monitor.c clean.c info.c
 
 alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \
                -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \
index 8296663a7c2dca62b86f335af1833d81ab3e6dd2..2a31a2fbebff2defc4bfe440343857dfad5effb6 100644 (file)
@@ -8,6 +8,8 @@ alsactl \- advanced controls for ALSA soundcard driver
 
 \fBalsactl\fP \fImonitor\fP <card # or id>
 
+\fBalsactl\fP \fIinfo\fP <card # or id>
+
 \fBalsactl\fP [\fIclean\fP] <card # or id or device> [[control identifiers]]
 
 .SH DESCRIPTION
@@ -62,6 +64,11 @@ rescan, save_and_quit).
 This command is for monitoring the events received from the given
 control device.
 
+.SS info <card>
+
+This command shows the general information in the YAML format
+collected from the given control device (sound card).
+
 .SS clean <card> [filter]
 
 This command cleans the controls created by applications.
index 9719b83845f139aedac6522e138796988c64255a..ae73715e6a695e66f052bedb50227a8dedb218ad 100644 (file)
@@ -119,6 +119,7 @@ static struct arg args[] = {
 { CARDCMD, "rdaemon", "like daemon but do the state restore at first" },
 { KILLCMD, "kill", "notify daemon to quit, rescan or save_and_quit" },
 { CARDCMD, "monitor", "monitor control events" },
+{ CARDCMD, "info", "general information" },
 { CARDCMD, "clean", "clean application controls" },
 { EMPCMD, "dump-state", "dump the state (for all cards)" },
 { EMPCMD, "dump-cfg", "dump the configuration (expanded, for all cards)" },
@@ -465,6 +466,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, "info")) {
+               res = general_info(cardname);
        } else if (!strcmp(cmd, "clean")) {
                res = clean(cardname, extra_args);
        } else if (!strcmp(cmd, "dump-state")) {
index 2f925700baacf6dfda00cdcf1a63e09bc4b74f4b..6ae45e3853069d22c1d38db7df614b3f46cf71d2 100644 (file)
@@ -62,6 +62,7 @@ int load_state(const char *cfgdir, const char *file,
               const char *cardname, int do_init);
 int power(const char *argv[], int argc);
 int monitor(const char *name);
+int general_info(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);
diff --git a/alsactl/info.c b/alsactl/info.c
new file mode 100644 (file)
index 0000000..253539d
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ *  Advanced Linux Sound Architecture Control Program - General info
+ *  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 "alsactl.h"
+
+static int pcm_device_list(snd_ctl_t *ctl, snd_pcm_stream_t stream, bool *first)
+{
+#ifdef __ALSA_PCM_H
+       int err, dev, idx;
+       unsigned int count;
+       snd_pcm_info_t *pcminfo;
+       snd_pcm_info_alloca(&pcminfo);
+       bool streamfirst, subfirst;
+
+       dev = -1;
+       streamfirst = true;
+       while (1) {
+               if ((err = snd_ctl_pcm_next_device(ctl, &dev)) < 0) {
+                       error("snd_ctl_pcm_next_device");
+                       return err;
+               }
+               if (dev < 0)
+                       break;
+               snd_pcm_info_set_device(pcminfo, dev);
+               snd_pcm_info_set_subdevice(pcminfo, 0);
+               snd_pcm_info_set_stream(pcminfo, stream);
+               if ((err = snd_ctl_pcm_info(ctl, pcminfo)) < 0) {
+                       if (err != -ENOENT)
+                               return err;
+                       continue;
+               }
+               if (*first) {
+                       printf("  pcm:\n");
+                       *first = false;
+               }
+               if (streamfirst) {
+                       printf("    - stream: %s\n      devices:\n", snd_pcm_stream_name(stream));
+                       streamfirst = false;
+               }
+               printf("        - device: %d\n          id: %s\n          name: %s\n",
+                               dev,
+                               snd_pcm_info_get_id(pcminfo),
+                               snd_pcm_info_get_name(pcminfo));
+               count = snd_pcm_info_get_subdevices_count(pcminfo);
+               subfirst = true;
+               for (idx = 0; idx < (int)count; idx++) {
+                       snd_pcm_info_set_subdevice(pcminfo, idx);
+                       if ((err = snd_ctl_pcm_info(ctl, pcminfo)) < 0) {
+                               error("control digital audio playback info (%s): %s", snd_ctl_name(ctl), snd_strerror(err));
+                               return err;
+                       }
+                       if (subfirst) {
+                               printf("          subdevices:\n");
+                               subfirst = false;
+                       }
+                       printf("            - subdevice: %d\n              name: %s\n",
+                                               idx, snd_pcm_info_get_subdevice_name(pcminfo));
+               }
+       }
+#endif
+       return 0;
+}
+
+static const char *snd_rawmidi_stream_name(snd_rawmidi_stream_t stream)
+{
+       if (stream == SND_RAWMIDI_STREAM_INPUT)
+               return "INPUT";
+       if (stream == SND_RAWMIDI_STREAM_OUTPUT)
+               return "OUTPUT";
+       return "???";
+}
+
+static int rawmidi_device_list(snd_ctl_t *ctl, snd_rawmidi_stream_t stream, bool *first)
+{
+#ifdef __ALSA_RAWMIDI_H
+       int err, dev, idx;
+       unsigned int count;
+       snd_rawmidi_info_t *info;
+       snd_rawmidi_info_alloca(&info);
+       bool streamfirst, subfirst;
+
+       dev = -1;
+       streamfirst = true;
+       while (1) {
+               if ((err = snd_ctl_rawmidi_next_device(ctl, &dev)) < 0) {
+                       error("snd_ctl_rawmidi_next_device");
+                       return err;
+               }
+               if (dev < 0)
+                       break;
+               snd_rawmidi_info_set_device(info, dev);
+               snd_rawmidi_info_set_subdevice(info, 0);
+               snd_rawmidi_info_set_stream(info, stream);
+               if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0) {
+                       if (err != -ENOENT)
+                               return err;
+                       continue;
+               }
+               if (*first) {
+                       printf("  rawmidi:\n");
+                       *first = false;
+               }
+               if (streamfirst) {
+                       printf("    - stream: %s\n      devices:\n", snd_rawmidi_stream_name(stream));
+                       streamfirst = false;
+               }
+               printf("        - device: %d\n          id: %s\n          name: %s\n",
+                               dev,
+                               snd_rawmidi_info_get_id(info),
+                               snd_rawmidi_info_get_name(info));
+               count = snd_rawmidi_info_get_subdevices_count(info);
+               subfirst = true;
+               for (idx = 0; idx < (int)count; idx++) {
+                       snd_rawmidi_info_set_subdevice(info, idx);
+                       if ((err = snd_ctl_rawmidi_info(ctl, info)) < 0) {
+                               error("control digital audio playback info (%s): %s", snd_ctl_name(ctl), snd_strerror(err));
+                               return err;
+                       }
+                       if (subfirst) {
+                               printf("          subdevices:\n");
+                               subfirst = false;
+                       }
+                       printf("            - subdevice: %d\n              name: %s\n",
+                                               idx, snd_rawmidi_info_get_subdevice_name(info));
+               }
+       }
+#endif
+       return 0;
+}
+
+static int hwdep_device_list(snd_ctl_t *ctl)
+{
+#ifdef __ALSA_HWDEP_H
+       int err, dev;
+       snd_hwdep_info_t *info;
+       snd_hwdep_info_alloca(&info);
+       bool first;
+
+       dev = -1;
+       first = true;
+       while (1) {
+               if ((err = snd_ctl_hwdep_next_device(ctl, &dev)) < 0) {
+                       error("snd_ctl_pcm_next_device");
+                       return err;
+               }
+               if (dev < 0)
+                       break;
+               snd_hwdep_info_set_device(info, dev);
+               if ((err = snd_ctl_hwdep_info(ctl, info)) < 0) {
+                       if (err != -ENOENT)
+                               return err;
+                       continue;
+               }
+               if (first) {
+                       printf("  hwdep:\n");
+                       first = false;
+               }
+               printf("    - device: %d\n      id: %s\n      name: %s\n      iface: %d\n",
+                               dev,
+                               snd_hwdep_info_get_id(info),
+                               snd_hwdep_info_get_name(info),
+                               snd_hwdep_info_get_iface(info));
+       }
+#endif
+       return 0;
+}
+
+static int card_info(snd_ctl_t *ctl)
+{
+       snd_ctl_card_info_t *info;
+       snd_ctl_elem_list_t *clist;
+       int err;
+
+       snd_ctl_card_info_alloca(&info);
+       snd_ctl_elem_list_alloca(&clist);
+
+       if ((err = snd_ctl_card_info(ctl, info)) < 0) {
+               error("Control device %s hw info error: %s", snd_ctl_name(ctl), snd_strerror(err));
+               return err;
+       }
+       printf("#\n# Sound card\n#\n");
+       printf("- card: %i\n  id: %s\n  name: %s\n  longname: %s\n",
+               snd_ctl_card_info_get_card(info),
+               snd_ctl_card_info_get_id(info),
+               snd_ctl_card_info_get_name(info),
+               snd_ctl_card_info_get_longname(info));
+       printf("  mixer_name: %s\n", snd_ctl_card_info_get_mixername(info));
+       printf("  components: %s\n", snd_ctl_card_info_get_components(info));
+       if ((err = snd_ctl_elem_list(ctl, clist)) < 0) {
+               error("snd_ctl_elem_list failure: %s", snd_strerror(err));
+       } else {
+               printf("  controls_count: %i\n", snd_ctl_elem_list_get_count(clist));
+       }
+       return err;
+}
+
+int general_card_info(int cardno)
+{
+       snd_ctl_t *ctl;
+       char dev[16];
+       bool first;
+       int err;
+
+       snprintf(dev, sizeof(dev), "hw:%i", cardno);
+       if ((err = snd_ctl_open(&ctl, dev, 0)) < 0) {
+               error("Control device %s open error: %s", dev, snd_strerror(err));
+               return err;
+       }
+       err = card_info(ctl);
+
+       first = true;
+       if (err >= 0)
+               err = pcm_device_list(ctl, SND_PCM_STREAM_PLAYBACK, &first);
+       if (err >= 0)
+               err = pcm_device_list(ctl, SND_PCM_STREAM_CAPTURE, &first);
+
+       first = true;
+       if (err >= 0)
+               err = rawmidi_device_list(ctl, SND_PCM_STREAM_PLAYBACK, &first);
+       if (err >= 0)
+               err = rawmidi_device_list(ctl, SND_PCM_STREAM_CAPTURE, &first);
+
+       if (err >= 0)
+               err = hwdep_device_list(ctl);
+       snd_ctl_close(ctl);
+       return err;
+}
+
+int general_info(const char *cardname)
+{
+       struct snd_card_iterator iter;
+       int err;
+
+       err = snd_card_iterator_sinit(&iter, cardname);
+       if (err < 0)
+               return err;
+       while (snd_card_iterator_next(&iter)) {
+               if ((err = general_card_info(iter.card)))
+                       return err;
+       }
+       return snd_card_iterator_error(&iter);
+}