1c72fcc34Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 2c72fcc34Sopenharmony_ci// 3c72fcc34Sopenharmony_ci// subcmd-list.c - operations for list sub command. 4c72fcc34Sopenharmony_ci// 5c72fcc34Sopenharmony_ci// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp> 6c72fcc34Sopenharmony_ci// 7c72fcc34Sopenharmony_ci// Licensed under the terms of the GNU General Public License, version 2. 8c72fcc34Sopenharmony_ci 9c72fcc34Sopenharmony_ci#include "subcmd.h" 10c72fcc34Sopenharmony_ci#include "misc.h" 11c72fcc34Sopenharmony_ci 12c72fcc34Sopenharmony_ci#include <getopt.h> 13c72fcc34Sopenharmony_ci#include <stdbool.h> 14c72fcc34Sopenharmony_ci 15c72fcc34Sopenharmony_cienum list_op { 16c72fcc34Sopenharmony_ci LIST_OP_DEVICE = 0, 17c72fcc34Sopenharmony_ci LIST_OP_PCM, 18c72fcc34Sopenharmony_ci LIST_OP_HELP, 19c72fcc34Sopenharmony_ci}; 20c72fcc34Sopenharmony_ci 21c72fcc34Sopenharmony_cistatic int dump_device(snd_ctl_t *handle, const char *id, const char *name, 22c72fcc34Sopenharmony_ci snd_pcm_stream_t stream ATTRIBUTE_UNUSED, snd_pcm_info_t *info) 23c72fcc34Sopenharmony_ci{ 24c72fcc34Sopenharmony_ci unsigned int i, count; 25c72fcc34Sopenharmony_ci int err; 26c72fcc34Sopenharmony_ci 27c72fcc34Sopenharmony_ci printf("card %i: %s [%s], device %i: %s [%s]\n", 28c72fcc34Sopenharmony_ci snd_pcm_info_get_card(info), id, name, 29c72fcc34Sopenharmony_ci snd_pcm_info_get_device(info), snd_pcm_info_get_id(info), 30c72fcc34Sopenharmony_ci snd_pcm_info_get_name(info)); 31c72fcc34Sopenharmony_ci 32c72fcc34Sopenharmony_ci count = snd_pcm_info_get_subdevices_count(info); 33c72fcc34Sopenharmony_ci printf(" Subdevices: %i/%u\n", 34c72fcc34Sopenharmony_ci snd_pcm_info_get_subdevices_avail(info), count); 35c72fcc34Sopenharmony_ci 36c72fcc34Sopenharmony_ci for (i = 0; i < count; ++i) { 37c72fcc34Sopenharmony_ci snd_pcm_info_set_subdevice(info, i); 38c72fcc34Sopenharmony_ci 39c72fcc34Sopenharmony_ci err = snd_ctl_pcm_info(handle, info); 40c72fcc34Sopenharmony_ci if (err < 0) { 41c72fcc34Sopenharmony_ci printf("control digital audio playback info (%i): %s", 42c72fcc34Sopenharmony_ci snd_pcm_info_get_card(info), snd_strerror(err)); 43c72fcc34Sopenharmony_ci continue; 44c72fcc34Sopenharmony_ci } 45c72fcc34Sopenharmony_ci 46c72fcc34Sopenharmony_ci printf(" Subdevice #%u: %s\n", 47c72fcc34Sopenharmony_ci i, snd_pcm_info_get_subdevice_name(info)); 48c72fcc34Sopenharmony_ci } 49c72fcc34Sopenharmony_ci 50c72fcc34Sopenharmony_ci return 0; 51c72fcc34Sopenharmony_ci} 52c72fcc34Sopenharmony_ci 53c72fcc34Sopenharmony_cistatic int dump_devices(snd_ctl_t *handle, const char *id, const char *name, 54c72fcc34Sopenharmony_ci snd_pcm_stream_t direction) 55c72fcc34Sopenharmony_ci{ 56c72fcc34Sopenharmony_ci snd_pcm_info_t *info; 57c72fcc34Sopenharmony_ci int device = -1; 58c72fcc34Sopenharmony_ci int err; 59c72fcc34Sopenharmony_ci 60c72fcc34Sopenharmony_ci err = snd_pcm_info_malloc(&info); 61c72fcc34Sopenharmony_ci if (err < 0) 62c72fcc34Sopenharmony_ci return err; 63c72fcc34Sopenharmony_ci 64c72fcc34Sopenharmony_ci while (1) { 65c72fcc34Sopenharmony_ci err = snd_ctl_pcm_next_device(handle, &device); 66c72fcc34Sopenharmony_ci if (err < 0) 67c72fcc34Sopenharmony_ci break; 68c72fcc34Sopenharmony_ci if (device < 0) 69c72fcc34Sopenharmony_ci break; 70c72fcc34Sopenharmony_ci 71c72fcc34Sopenharmony_ci snd_pcm_info_set_device(info, device); 72c72fcc34Sopenharmony_ci snd_pcm_info_set_subdevice(info, 0); 73c72fcc34Sopenharmony_ci snd_pcm_info_set_stream(info, direction); 74c72fcc34Sopenharmony_ci err = snd_ctl_pcm_info(handle, info); 75c72fcc34Sopenharmony_ci if (err < 0) 76c72fcc34Sopenharmony_ci continue; 77c72fcc34Sopenharmony_ci 78c72fcc34Sopenharmony_ci err = dump_device(handle, id, name, direction, info); 79c72fcc34Sopenharmony_ci if (err < 0) 80c72fcc34Sopenharmony_ci break; 81c72fcc34Sopenharmony_ci } 82c72fcc34Sopenharmony_ci 83c72fcc34Sopenharmony_ci free(info); 84c72fcc34Sopenharmony_ci return err; 85c72fcc34Sopenharmony_ci} 86c72fcc34Sopenharmony_ci 87c72fcc34Sopenharmony_cistatic int list_devices(snd_pcm_stream_t direction) 88c72fcc34Sopenharmony_ci{ 89c72fcc34Sopenharmony_ci int card = -1; 90c72fcc34Sopenharmony_ci char name[32]; 91c72fcc34Sopenharmony_ci snd_ctl_t *handle; 92c72fcc34Sopenharmony_ci snd_ctl_card_info_t *info; 93c72fcc34Sopenharmony_ci int err; 94c72fcc34Sopenharmony_ci 95c72fcc34Sopenharmony_ci err = snd_ctl_card_info_malloc(&info); 96c72fcc34Sopenharmony_ci if (err < 0) 97c72fcc34Sopenharmony_ci return err; 98c72fcc34Sopenharmony_ci 99c72fcc34Sopenharmony_ci // Not found. 100c72fcc34Sopenharmony_ci if (snd_card_next(&card) < 0 || card < 0) 101c72fcc34Sopenharmony_ci goto end; 102c72fcc34Sopenharmony_ci 103c72fcc34Sopenharmony_ci printf("**** List of %s Hardware Devices ****\n", 104c72fcc34Sopenharmony_ci snd_pcm_stream_name(direction)); 105c72fcc34Sopenharmony_ci 106c72fcc34Sopenharmony_ci while (card >= 0) { 107c72fcc34Sopenharmony_ci sprintf(name, "hw:%d", card); 108c72fcc34Sopenharmony_ci err = snd_ctl_open(&handle, name, 0); 109c72fcc34Sopenharmony_ci if (err < 0) { 110c72fcc34Sopenharmony_ci printf("control open (%i): %s", 111c72fcc34Sopenharmony_ci card, snd_strerror(err)); 112c72fcc34Sopenharmony_ci } else { 113c72fcc34Sopenharmony_ci err = snd_ctl_card_info(handle, info); 114c72fcc34Sopenharmony_ci if (err < 0) { 115c72fcc34Sopenharmony_ci printf("control hardware info (%i): %s", 116c72fcc34Sopenharmony_ci card, snd_strerror(err)); 117c72fcc34Sopenharmony_ci } else { 118c72fcc34Sopenharmony_ci err = dump_devices(handle, 119c72fcc34Sopenharmony_ci snd_ctl_card_info_get_id(info), 120c72fcc34Sopenharmony_ci snd_ctl_card_info_get_name(info), 121c72fcc34Sopenharmony_ci direction); 122c72fcc34Sopenharmony_ci } 123c72fcc34Sopenharmony_ci snd_ctl_close(handle); 124c72fcc34Sopenharmony_ci } 125c72fcc34Sopenharmony_ci 126c72fcc34Sopenharmony_ci if (err < 0) 127c72fcc34Sopenharmony_ci break; 128c72fcc34Sopenharmony_ci 129c72fcc34Sopenharmony_ci // Go to next. 130c72fcc34Sopenharmony_ci if (snd_card_next(&card) < 0) { 131c72fcc34Sopenharmony_ci printf("snd_card_next"); 132c72fcc34Sopenharmony_ci break; 133c72fcc34Sopenharmony_ci } 134c72fcc34Sopenharmony_ci } 135c72fcc34Sopenharmony_ciend: 136c72fcc34Sopenharmony_ci free(info); 137c72fcc34Sopenharmony_ci return err; 138c72fcc34Sopenharmony_ci} 139c72fcc34Sopenharmony_ci 140c72fcc34Sopenharmony_cistatic int list_pcms(snd_pcm_stream_t direction) 141c72fcc34Sopenharmony_ci{ 142c72fcc34Sopenharmony_ci static const char *const filters[] = { 143c72fcc34Sopenharmony_ci [SND_PCM_STREAM_CAPTURE] = "Input", 144c72fcc34Sopenharmony_ci [SND_PCM_STREAM_PLAYBACK] = "Output", 145c72fcc34Sopenharmony_ci }; 146c72fcc34Sopenharmony_ci const char *filter; 147c72fcc34Sopenharmony_ci void **hints; 148c72fcc34Sopenharmony_ci void **n; 149c72fcc34Sopenharmony_ci char *io; 150c72fcc34Sopenharmony_ci char *name; 151c72fcc34Sopenharmony_ci char *desc; 152c72fcc34Sopenharmony_ci 153c72fcc34Sopenharmony_ci if (snd_device_name_hint(-1, "pcm", &hints) < 0) 154c72fcc34Sopenharmony_ci return -EINVAL; 155c72fcc34Sopenharmony_ci 156c72fcc34Sopenharmony_ci filter = filters[direction]; 157c72fcc34Sopenharmony_ci 158c72fcc34Sopenharmony_ci for (n = hints; *n != NULL; ++n) { 159c72fcc34Sopenharmony_ci io = snd_device_name_get_hint(*n, "IOID"); 160c72fcc34Sopenharmony_ci if (io != NULL && strcmp(io, filter) != 0) { 161c72fcc34Sopenharmony_ci free(io); 162c72fcc34Sopenharmony_ci continue; 163c72fcc34Sopenharmony_ci } 164c72fcc34Sopenharmony_ci 165c72fcc34Sopenharmony_ci name = snd_device_name_get_hint(*n, "NAME"); 166c72fcc34Sopenharmony_ci desc = snd_device_name_get_hint(*n, "DESC"); 167c72fcc34Sopenharmony_ci 168c72fcc34Sopenharmony_ci printf("%s\n", name); 169c72fcc34Sopenharmony_ci if (desc == NULL) { 170c72fcc34Sopenharmony_ci free(name); 171c72fcc34Sopenharmony_ci free(desc); 172c72fcc34Sopenharmony_ci continue; 173c72fcc34Sopenharmony_ci } 174c72fcc34Sopenharmony_ci 175c72fcc34Sopenharmony_ci 176c72fcc34Sopenharmony_ci printf(" "); 177c72fcc34Sopenharmony_ci while (*desc) { 178c72fcc34Sopenharmony_ci if (*desc == '\n') 179c72fcc34Sopenharmony_ci printf("\n "); 180c72fcc34Sopenharmony_ci else 181c72fcc34Sopenharmony_ci putchar(*desc); 182c72fcc34Sopenharmony_ci desc++; 183c72fcc34Sopenharmony_ci } 184c72fcc34Sopenharmony_ci putchar('\n'); 185c72fcc34Sopenharmony_ci } 186c72fcc34Sopenharmony_ci 187c72fcc34Sopenharmony_ci snd_device_name_free_hint(hints); 188c72fcc34Sopenharmony_ci 189c72fcc34Sopenharmony_ci return 0; 190c72fcc34Sopenharmony_ci} 191c72fcc34Sopenharmony_ci 192c72fcc34Sopenharmony_cistatic void print_help(void) 193c72fcc34Sopenharmony_ci{ 194c72fcc34Sopenharmony_ci printf( 195c72fcc34Sopenharmony_ci"Usage:\n" 196c72fcc34Sopenharmony_ci" axfer list DIRECTION TARGET\n" 197c72fcc34Sopenharmony_ci"\n" 198c72fcc34Sopenharmony_ci" where:\n" 199c72fcc34Sopenharmony_ci" DIRECTION = capture | playback\n" 200c72fcc34Sopenharmony_ci" TARGET = device | pcm\n" 201c72fcc34Sopenharmony_ci ); 202c72fcc34Sopenharmony_ci} 203c72fcc34Sopenharmony_ci 204c72fcc34Sopenharmony_ci// Backward compatibility to aplay(1). 205c72fcc34Sopenharmony_cistatic bool decide_operation(int argc, char *const *argv, enum list_op *op) 206c72fcc34Sopenharmony_ci{ 207c72fcc34Sopenharmony_ci static const char *s_opts = "hlL"; 208c72fcc34Sopenharmony_ci static const struct option l_opts[] = { 209c72fcc34Sopenharmony_ci {"list-devices", 0, NULL, 'l'}, 210c72fcc34Sopenharmony_ci {"list-pcms", 0, NULL, 'L'}, 211c72fcc34Sopenharmony_ci {NULL, 0, NULL, 0} 212c72fcc34Sopenharmony_ci }; 213c72fcc34Sopenharmony_ci 214c72fcc34Sopenharmony_ci optind = 0; 215c72fcc34Sopenharmony_ci opterr = 0; 216c72fcc34Sopenharmony_ci while (1) { 217c72fcc34Sopenharmony_ci int c = getopt_long(argc, argv, s_opts, l_opts, NULL); 218c72fcc34Sopenharmony_ci if (c < 0) 219c72fcc34Sopenharmony_ci break; 220c72fcc34Sopenharmony_ci if (c == 'l') { 221c72fcc34Sopenharmony_ci *op = LIST_OP_DEVICE; 222c72fcc34Sopenharmony_ci return true; 223c72fcc34Sopenharmony_ci } 224c72fcc34Sopenharmony_ci if (c == 'L') { 225c72fcc34Sopenharmony_ci *op = LIST_OP_PCM; 226c72fcc34Sopenharmony_ci return true; 227c72fcc34Sopenharmony_ci } 228c72fcc34Sopenharmony_ci } 229c72fcc34Sopenharmony_ci 230c72fcc34Sopenharmony_ci return false; 231c72fcc34Sopenharmony_ci} 232c72fcc34Sopenharmony_ci 233c72fcc34Sopenharmony_cistatic int detect_operation(int argc, char *const *argv, enum list_op *op) 234c72fcc34Sopenharmony_ci{ 235c72fcc34Sopenharmony_ci static const char *const ops[] = { 236c72fcc34Sopenharmony_ci [LIST_OP_DEVICE] = "device", 237c72fcc34Sopenharmony_ci [LIST_OP_PCM] = "pcm", 238c72fcc34Sopenharmony_ci }; 239c72fcc34Sopenharmony_ci int i; 240c72fcc34Sopenharmony_ci 241c72fcc34Sopenharmony_ci if (argc < 2) 242c72fcc34Sopenharmony_ci return false; 243c72fcc34Sopenharmony_ci 244c72fcc34Sopenharmony_ci for (i = 0; i < (int)ARRAY_SIZE(ops); ++i) { 245c72fcc34Sopenharmony_ci if (!strcmp(argv[1], ops[i])) { 246c72fcc34Sopenharmony_ci *op = i; 247c72fcc34Sopenharmony_ci return true; 248c72fcc34Sopenharmony_ci } 249c72fcc34Sopenharmony_ci } 250c72fcc34Sopenharmony_ci 251c72fcc34Sopenharmony_ci return false; 252c72fcc34Sopenharmony_ci} 253c72fcc34Sopenharmony_ci 254c72fcc34Sopenharmony_ciint subcmd_list(int argc, char *const *argv, snd_pcm_stream_t direction) 255c72fcc34Sopenharmony_ci{ 256c72fcc34Sopenharmony_ci enum list_op op = LIST_OP_HELP; 257c72fcc34Sopenharmony_ci int err = 0; 258c72fcc34Sopenharmony_ci 259c72fcc34Sopenharmony_ci // Renewed command system. 260c72fcc34Sopenharmony_ci if (!detect_operation(argc, argv, &op) && 261c72fcc34Sopenharmony_ci !decide_operation(argc, argv, &op)) 262c72fcc34Sopenharmony_ci err = -EINVAL; 263c72fcc34Sopenharmony_ci 264c72fcc34Sopenharmony_ci if (op == LIST_OP_DEVICE) 265c72fcc34Sopenharmony_ci err = list_devices(direction); 266c72fcc34Sopenharmony_ci else if (op == LIST_OP_PCM) 267c72fcc34Sopenharmony_ci err = list_pcms(direction); 268c72fcc34Sopenharmony_ci else 269c72fcc34Sopenharmony_ci print_help(); 270c72fcc34Sopenharmony_ci 271c72fcc34Sopenharmony_ci return err; 272c72fcc34Sopenharmony_ci} 273