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 stream ATTRIBUTE_UNUSED, 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 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
dump_devices(snd_ctl_t *handle, const char *id, const char *name, snd_pcm_stream_t direction)53 static 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
list_devices(snd_pcm_stream_t direction)87 static 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 }
135 end:
136 free(info);
137 return err;
138 }
139
list_pcms(snd_pcm_stream_t direction)140 static 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
print_help(void)192 static 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).
decide_operation(int argc, char *const *argv, enum list_op *op)205 static 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
detect_operation(int argc, char *const *argv, enum list_op *op)233 static 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
subcmd_list(int argc, char *const *argv, snd_pcm_stream_t direction)254 int 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