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