// SPDX-License-Identifier: GPL-2.0 // // subcmd-list.c - operations for list sub command. // // Copyright (c) 2018 Takashi Sakamoto // // Licensed under the terms of the GNU General Public License, version 2. #include "subcmd.h" #include "misc.h" #include static int dump_device(snd_ctl_t *handle, const char *id, const char *name, snd_pcm_stream_t direction, snd_pcm_info_t *info) { unsigned int count; int i; int err; printf("card %i: %s [%s], device %i: %s [%s]\n", snd_pcm_info_get_card(info), id, name, snd_pcm_info_get_device(info), snd_pcm_info_get_id(info), snd_pcm_info_get_name(info)); count = snd_pcm_info_get_subdevices_count(info); printf(" Subdevices: %i/%i\n", snd_pcm_info_get_subdevices_avail(info), count); for (i = 0; i < count; ++i) { snd_pcm_info_set_subdevice(info, i); err = snd_ctl_pcm_info(handle, info); if (err < 0) { printf("control digital audio playback info (%i): %s", snd_pcm_info_get_card(info), snd_strerror(err)); continue; } printf(" Subdevice #%i: %s\n", i, snd_pcm_info_get_subdevice_name(info)); } return 0; } static int dump_devices(snd_ctl_t *handle, const char *id, const char *name, snd_pcm_stream_t direction) { snd_pcm_info_t *info; int device = -1; int err; err = snd_pcm_info_malloc(&info); if (err < 0) return err; while (1) { err = snd_ctl_pcm_next_device(handle, &device); if (err < 0) break; if (device < 0) break; snd_pcm_info_set_device(info, device); snd_pcm_info_set_subdevice(info, 0); snd_pcm_info_set_stream(info, direction); err = snd_ctl_pcm_info(handle, info); if (err < 0) continue; err = dump_device(handle, id, name, direction, info); if (err < 0) break; } free(info); return err; } static int list_devices(snd_pcm_stream_t direction) { int card = -1; char name[32]; snd_ctl_t *handle; snd_ctl_card_info_t *info; int err; err = snd_ctl_card_info_malloc(&info); if (err < 0) return err; // Not found. if (snd_card_next(&card) < 0 || card < 0) goto end; printf("**** List of %s Hardware Devices ****\n", snd_pcm_stream_name(direction)); while (card >= 0) { sprintf(name, "hw:%d", card); err = snd_ctl_open(&handle, name, 0); if (err < 0) { printf("control open (%i): %s", card, snd_strerror(err)); } else { err = snd_ctl_card_info(handle, info); if (err < 0) { printf("control hardware info (%i): %s", card, snd_strerror(err)); } else { err = dump_devices(handle, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info), direction); } snd_ctl_close(handle); } if (err < 0) break; // Go to next. if (snd_card_next(&card) < 0) { printf("snd_card_next"); break; } } end: free(info); return err; } static int list_pcms(snd_pcm_stream_t direction) { static const char *const filters[] = { [SND_PCM_STREAM_CAPTURE] = "Input", [SND_PCM_STREAM_PLAYBACK] = "Output", }; const char *filter; void **hints; void **n; char *io; char *name; char *desc; if (snd_device_name_hint(-1, "pcm", &hints) < 0) return -EINVAL; filter = filters[direction]; n = hints; for (n = hints; *n != NULL; ++n) { io = snd_device_name_get_hint(*n, "IOID"); if (io != NULL && strcmp(io, filter) != 0) { free(io); continue; } name = snd_device_name_get_hint(*n, "NAME"); desc = snd_device_name_get_hint(*n, "DESC"); printf("%s\n", name); if (desc == NULL) { free(name); free(desc); continue; } printf(" "); while (*desc) { if (*desc == '\n') printf("\n "); else putchar(*desc); desc++; } putchar('\n'); } snd_device_name_free_hint(hints); return 0; } static void print_help(void) { printf("help for list sub-command.\n"); } int subcmd_list(int argc, char *const *argv, snd_pcm_stream_t direction) { static const struct { const char *const category; int (*func)(snd_pcm_stream_t direction); } ops[] = { {"device", list_devices}, {"pcm", list_pcms}, }; int i; static const char *s_opts = "hlL"; static const struct option l_opts[] = { {"list-devices", 0, NULL, 'l'}, {"list-pcms", 0, NULL, 'L'}, {NULL, 0, NULL, 0} }; int c; // Renewed command system. if (argc > 2 && !strcmp(argv[1], "list")) { for (i = 0; i < ARRAY_SIZE(ops); ++i) { if (!strcmp(ops[i].category, argv[2])) return ops[i].func(direction); } } // Original command system. optind = 0; opterr = 0; while (1) { c = getopt_long(argc, argv, s_opts, l_opts, NULL); if (c < 0) break; if (c == 'l') return list_devices(direction); if (c == 'L') return list_pcms(direction); } print_help(); return 0; }