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