alsa-utils/axfer/subcmd-list.c
Takashi Sakamoto ac3e1d904b axfer: apply refactoring to list subcommand for backward compatibility to aplay(1)
This commit splits option parser for backward compatibility to aplay(1)
in list subcommand. Supported operations are listed in local enumeration.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2018-12-05 15:21:47 +01:00

262 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
//
// subcmd-list.c - operations for list sub command.
//
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
//
// Licensed under the terms of the GNU General Public License, version 2.
#include "subcmd.h"
#include "misc.h"
#include <getopt.h>
#include <stdbool.h>
enum list_op {
LIST_OP_DEVICE = 0,
LIST_OP_PCM,
LIST_OP_HELP,
};
static int dump_device(snd_ctl_t *handle, const char *id, const char *name,
snd_pcm_stream_t direction, snd_pcm_info_t *info)
{
unsigned int count;
int i;
int err;
printf("card %i: %s [%s], device %i: %s [%s]\n",
snd_pcm_info_get_card(info), id, name,
snd_pcm_info_get_device(info), snd_pcm_info_get_id(info),
snd_pcm_info_get_name(info));
count = snd_pcm_info_get_subdevices_count(info);
printf(" Subdevices: %i/%i\n",
snd_pcm_info_get_subdevices_avail(info), count);
for (i = 0; i < count; ++i) {
snd_pcm_info_set_subdevice(info, i);
err = snd_ctl_pcm_info(handle, info);
if (err < 0) {
printf("control digital audio playback info (%i): %s",
snd_pcm_info_get_card(info), snd_strerror(err));
continue;
}
printf(" Subdevice #%i: %s\n",
i, snd_pcm_info_get_subdevice_name(info));
}
return 0;
}
static int dump_devices(snd_ctl_t *handle, const char *id, const char *name,
snd_pcm_stream_t direction)
{
snd_pcm_info_t *info;
int device = -1;
int err;
err = snd_pcm_info_malloc(&info);
if (err < 0)
return err;
while (1) {
err = snd_ctl_pcm_next_device(handle, &device);
if (err < 0)
break;
if (device < 0)
break;
snd_pcm_info_set_device(info, device);
snd_pcm_info_set_subdevice(info, 0);
snd_pcm_info_set_stream(info, direction);
err = snd_ctl_pcm_info(handle, info);
if (err < 0)
continue;
err = dump_device(handle, id, name, direction, info);
if (err < 0)
break;
}
free(info);
return err;
}
static int list_devices(snd_pcm_stream_t direction)
{
int card = -1;
char name[32];
snd_ctl_t *handle;
snd_ctl_card_info_t *info;
int err;
err = snd_ctl_card_info_malloc(&info);
if (err < 0)
return err;
// Not found.
if (snd_card_next(&card) < 0 || card < 0)
goto end;
printf("**** List of %s Hardware Devices ****\n",
snd_pcm_stream_name(direction));
while (card >= 0) {
sprintf(name, "hw:%d", card);
err = snd_ctl_open(&handle, name, 0);
if (err < 0) {
printf("control open (%i): %s",
card, snd_strerror(err));
} else {
err = snd_ctl_card_info(handle, info);
if (err < 0) {
printf("control hardware info (%i): %s",
card, snd_strerror(err));
} else {
err = dump_devices(handle,
snd_ctl_card_info_get_id(info),
snd_ctl_card_info_get_name(info),
direction);
}
snd_ctl_close(handle);
}
if (err < 0)
break;
// Go to next.
if (snd_card_next(&card) < 0) {
printf("snd_card_next");
break;
}
}
end:
free(info);
return err;
}
static int list_pcms(snd_pcm_stream_t direction)
{
static const char *const filters[] = {
[SND_PCM_STREAM_CAPTURE] = "Input",
[SND_PCM_STREAM_PLAYBACK] = "Output",
};
const char *filter;
void **hints;
void **n;
char *io;
char *name;
char *desc;
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
return -EINVAL;
filter = filters[direction];
n = hints;
for (n = hints; *n != NULL; ++n) {
io = snd_device_name_get_hint(*n, "IOID");
if (io != NULL && strcmp(io, filter) != 0) {
free(io);
continue;
}
name = snd_device_name_get_hint(*n, "NAME");
desc = snd_device_name_get_hint(*n, "DESC");
printf("%s\n", name);
if (desc == NULL) {
free(name);
free(desc);
continue;
}
printf(" ");
while (*desc) {
if (*desc == '\n')
printf("\n ");
else
putchar(*desc);
desc++;
}
putchar('\n');
}
snd_device_name_free_hint(hints);
return 0;
}
static void print_help(void)
{
printf("help for list sub-command.\n");
}
// Backward compatibility to aplay(1).
static bool decide_operation(int argc, char *const *argv, enum list_op *op)
{
static const char *s_opts = "hlL";
static const struct option l_opts[] = {
{"list-devices", 0, NULL, 'l'},
{"list-pcms", 0, NULL, 'L'},
{NULL, 0, NULL, 0}
};
optind = 0;
opterr = 0;
while (1) {
int c = getopt_long(argc, argv, s_opts, l_opts, NULL);
if (c < 0)
break;
if (c == 'l') {
*op = LIST_OP_DEVICE;
return true;
}
if (c == 'L') {
*op = LIST_OP_PCM;
return true;
}
}
return false;
}
int subcmd_list(int argc, char *const *argv, snd_pcm_stream_t direction)
{
static const struct {
const char *const category;
int (*func)(snd_pcm_stream_t direction);
} ops[] = {
{"device", list_devices},
{"pcm", list_pcms},
};
enum list_op op;
int i;
int err;
// Renewed command system.
if (argc > 2 && !strcmp(argv[1], "list")) {
for (i = 0; i < ARRAY_SIZE(ops); ++i) {
if (!strcmp(ops[i].category, argv[2]))
return ops[i].func(direction);
}
}
if (!decide_operation(argc, argv, &op)) {
err = -EINVAL;
op = LIST_OP_HELP;
}
if (op == LIST_OP_DEVICE)
err = list_devices(direction);
else if (op == LIST_OP_PCM)
err = list_pcms(direction);
else
print_help();
return err;
}