• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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