OSDN Git Service

axfer: apply refactoring in list subcommand for new command system
[android-x86/external-alsa-utils.git] / axfer / subcmd-list.c
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
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/%i\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
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
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
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         n = hints;
160         for (n = hints; *n != NULL; ++n) {
161                 io = snd_device_name_get_hint(*n, "IOID");
162                 if (io != NULL && strcmp(io, filter) != 0) {
163                         free(io);
164                         continue;
165                 }
166
167                 name = snd_device_name_get_hint(*n, "NAME");
168                 desc = snd_device_name_get_hint(*n, "DESC");
169
170                 printf("%s\n", name);
171                 if (desc == NULL) {
172                         free(name);
173                         free(desc);
174                         continue;
175                 }
176
177
178                 printf("    ");
179                 while (*desc) {
180                         if (*desc == '\n')
181                                 printf("\n    ");
182                         else
183                                 putchar(*desc);
184                         desc++;
185                 }
186                 putchar('\n');
187         }
188
189         snd_device_name_free_hint(hints);
190
191         return 0;
192 }
193
194 static void print_help(void)
195 {
196         printf("help for list sub-command.\n");
197 }
198
199 // Backward compatibility to aplay(1).
200 static bool decide_operation(int argc, char *const *argv, enum list_op *op)
201 {
202         static const char *s_opts = "hlL";
203         static const struct option l_opts[] = {
204                 {"list-devices",        0, NULL, 'l'},
205                 {"list-pcms",           0, NULL, 'L'},
206                 {NULL,                  0, NULL, 0}
207         };
208
209         optind = 0;
210         opterr = 0;
211         while (1) {
212                 int c = getopt_long(argc, argv, s_opts, l_opts, NULL);
213                 if (c < 0)
214                         break;
215                 if (c == 'l') {
216                         *op = LIST_OP_DEVICE;
217                         return true;
218                 }
219                 if (c == 'L') {
220                         *op = LIST_OP_PCM;
221                         return true;
222                 }
223         }
224
225         return false;
226 }
227
228 static int detect_operation(int argc, char *const *argv, enum list_op *op)
229 {
230         static const char *const ops[] = {
231                 [LIST_OP_DEVICE] = "device",
232                 [LIST_OP_PCM] = "pcm",
233         };
234         int i;
235
236         if (strcmp(argv[1], "list") || argc < 3)
237                 return false;
238
239         for (i = 0; i < ARRAY_SIZE(ops); ++i) {
240                 if (!strcmp(argv[2], ops[i])) {
241                         *op = i;
242                         return true;
243                 }
244         }
245
246         return false;
247 }
248
249 int subcmd_list(int argc, char *const *argv, snd_pcm_stream_t direction)
250 {
251         enum list_op op = LIST_OP_HELP;
252         int err = 0;
253
254         // Renewed command system.
255         if (argc > 1) {
256                 if (!detect_operation(argc, argv, &op) &&
257                     !decide_operation(argc, argv, &op))
258                                 err = -EINVAL;
259         }
260
261         if (op == LIST_OP_DEVICE)
262                 err = list_devices(direction);
263         else if (op == LIST_OP_PCM)
264                 err = list_pcms(direction);
265         else
266                 print_help();
267
268         return err;
269 }