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