xref: /third_party/alsa-utils/alsaucm/usecase.c (revision c72fcc34)
1/*
2 *  This library is free software; you can redistribute it and/or
3 *  modify it under the terms of the GNU Lesser General Public
4 *  License as published by the Free Software Foundation; either
5 *  version 2 of the License, or (at your option) any later version.
6 *
7 *  This library is distributed in the hope that it will be useful,
8 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 *  Lesser General Public License for more details.
11 *
12 *  You should have received a copy of the GNU General Public License
13 *  along with this program; if not, write to the Free Software
14 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15 *
16 *  Support for the verb/device/modifier core logic and API,
17 *  command line tool and file parser was kindly sponsored by
18 *  Texas Instruments Inc.
19 *  Support for multiple active modifiers and devices,
20 *  transition sequences, multiple client access and user defined use
21 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
22 *
23 *  Copyright (C) 2008-2010 SlimLogic Ltd
24 *  Copyright (C) 2010 Wolfson Microelectronics PLC
25 *  Copyright (C) 2010 Texas Instruments Inc.
26 *  Copyright (C) 2010 Red Hat Inc.
27 *  Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 *           Stefan Schmidt <stefan@slimlogic.co.uk>
29 *           Justin Xu <justinx@slimlogic.co.uk>
30 *           Jaroslav Kysela <perex@perex.cz>
31 */
32
33#include <stdio.h>
34#include <string.h>
35#include <stdlib.h>
36#include <unistd.h>
37#include <signal.h>
38#include <getopt.h>
39#include <alsa/asoundlib.h>
40#include <alsa/use-case.h>
41#include "usecase.h"
42#include "aconfig.h"
43#include "version.h"
44
45#define MAX_BUF 256
46
47enum uc_cmd {
48	/* management */
49	OM_UNKNOWN = 0,
50	OM_OPEN,
51	OM_RESET,
52	OM_RELOAD,
53	OM_LISTCARDS,
54	OM_DUMP,
55	OM_LIST2,
56	OM_LIST1,
57
58	/* set/get */
59	OM_SET,
60	OM_GET,
61	OM_GET_VAL,
62	OM_GETI,
63	OM_GETI_VAL,
64
65	/* misc */
66	OM_HELP,
67	OM_QUIT,
68};
69
70struct cmd {
71	int code;
72	int args;
73	unsigned int opencard:1;
74	const char *id;
75};
76
77static struct cmd cmds[] = {
78	{ OM_OPEN, 1, 0, "open" },
79	{ OM_RESET, 0, 1, "reset" },
80	{ OM_RELOAD, 0, 1, "reload" },
81	{ OM_LISTCARDS, 0, 0, "listcards" },
82	{ OM_DUMP, 1, 1, "dump" },
83	{ OM_LIST1, 1, 1, "list1" },
84	{ OM_LIST2, 1, 1, "list" },
85	{ OM_SET, 2, 1, "set" },
86	{ OM_GET, 1, 1, "get" },
87	{ OM_GET_VAL, 1, 1, "getval" },
88	{ OM_GETI, 1, 1, "geti" },
89	{ OM_GETI_VAL, 1, 1, "getival" },
90	{ OM_DUMP, 1, 1, "dump" },
91	{ OM_HELP, 0, 0, "help" },
92	{ OM_QUIT, 0, 0, "quit" },
93	{ OM_HELP, 0, 0, "h" },
94	{ OM_HELP, 0, 0, "?" },
95	{ OM_QUIT, 0, 0, "q" },
96	{ OM_UNKNOWN, 0, 0, NULL }
97};
98
99static void dump_help(struct context *context)
100{
101	if (context->command)
102		printf("Usage: %s <options> [command]\n", context->command);
103	printf(
104"\nAvailable options:\n"
105"  -h,--help                  this help\n"
106"  -c,--card NAME             open card NAME\n"
107"  -i,--interactive           interactive mode\n"
108"  -b,--batch FILE            batch mode (use '-' for the stdin input)\n"
109"  -n,--no-open               do not open first card found\n"
110"\nAvailable commands:\n"
111"  open NAME                  open card NAME\n"
112"  reset                      reset sound card to default state\n"
113"  reload                     reload configuration\n"
114"  listcards                  list available cards\n"
115"  dump FORMAT                dump all config information (format: text,json)\n"
116"  list IDENTIFIER            list command, for items with value + comment\n"
117"  list1 IDENTIFIER           list command, for items without comments\n"
118"  get IDENTIFIER             get string value\n"
119"  geti IDENTIFIER            get integer value\n"
120"  set IDENTIFIER VALUE       set string value\n"
121"  h,help                     help\n"
122"  q,quit                     quit\n"
123);
124}
125
126int is_long(const char *str)
127{
128	char *end;
129	if (!*str)
130		return 0;
131	errno = 0;
132	strtol(str, &end, 10);
133	if (errno)
134		return 0;
135	if (*end)
136		return 0;
137	return 1;
138}
139
140static int parse_line(struct context *context, char *line)
141{
142	char *start, **nargv;
143	int c;
144
145	context->argc = 0;
146	while (*line) {
147		while (*line && (*line == ' ' || *line == '\t' ||
148							*line == '\n'))
149			line++;
150		c = *line;
151		if (c == '\0')
152			return 0;
153		if (c == '\"' || c == '\'') {
154			start = ++line;
155			while (*line && *line != c)
156				line++;
157			if (*line) {
158				*line = '\0';
159				line++;
160			}
161		} else {
162			start = line;
163			while (*line && *line != ' ' && *line != '\t' &&
164			       *line != '\n')
165				line++;
166			if (*line) {
167				*line = '\0';
168				line++;
169			}
170		}
171		if (start[0] == '\0' && context->argc == 0)
172			return 0;
173		if (context->argc + 1 >= context->arga) {
174			context->arga += 4;
175			nargv = realloc(context->argv,
176					context->arga * sizeof(char *));
177			if (nargv == NULL)
178				return -ENOMEM;
179			context->argv = nargv;
180		}
181		context->argv[context->argc++] = start;
182	}
183	return 0;
184}
185
186static void my_exit(struct context *context, int exitcode)
187{
188	if (context->uc_mgr)
189		snd_use_case_mgr_close(context->uc_mgr);
190	if (context->arga > 0)
191		free(context->argv);
192	if (context->card)
193		free(context->card);
194	if (context->batch)
195		free(context->batch);
196	free(context);
197	snd_config_update_free_global();
198	exit(exitcode);
199}
200static void do_initial_open(struct context *context)
201{
202	int card, err;
203	char name[16];
204
205	if (!context->no_open && context->card == NULL) {
206		card = -1;
207		err = snd_card_next(&card);
208		if (err < 0) {
209			fprintf(stderr, "%s: no sound card found: %s\n",
210					context->command, snd_strerror(err));
211			my_exit(context, EXIT_FAILURE);
212		}
213		snprintf(name, sizeof(name), "hw:%d", card);
214		context->card = strdup(name);
215	}
216
217	/* open library */
218	if (!context->no_open) {
219		if (is_long(context->card)) {
220			snprintf(name, sizeof(name), "hw:%s", context->card);
221			free(context->card);
222			context->card = strdup(name);
223		}
224		if (context->card == NULL) {
225			fprintf(stderr, "%s: empty card name\n", context->command);
226			my_exit(context, EXIT_FAILURE);
227		}
228		err = snd_use_case_mgr_open(&context->uc_mgr,
229					    context->card);
230		if (err < 0) {
231			fprintf(stderr,
232				"%s: error failed to open sound card %s: %s\n",
233				context->command, context->card, snd_strerror(err));
234			my_exit(context, EXIT_FAILURE);
235		}
236	}
237}
238
239static int do_one(struct context *context, struct cmd *cmd, char **argv)
240{
241	const char **list, *str;
242	long lval;
243	int err, i, j, entries;
244
245	if (cmd->opencard && context->uc_mgr == NULL) {
246		if (!context->no_open) {
247			do_initial_open(context);
248			context->no_open = 1;
249		} else {
250			fprintf(stderr, "%s: command '%s' requires an open card\n",
251					context->command, cmd->id);
252			return 0;
253		}
254	}
255	switch (cmd->code) {
256	case OM_OPEN:
257		if (context->uc_mgr)
258			snd_use_case_mgr_close(context->uc_mgr);
259		context->uc_mgr = NULL;
260		free(context->card);
261		context->card = strdup(argv[0]);
262		err = snd_use_case_mgr_open(&context->uc_mgr, context->card);
263		if (err < 0) {
264			fprintf(stderr,
265				"%s: error failed to open sound card %s: %s\n",
266				context->command, context->card,
267				snd_strerror(err));
268			return err;
269		}
270		break;
271	case OM_RESET:
272		err = snd_use_case_mgr_reset(context->uc_mgr);
273		if (err < 0) {
274			fprintf(stderr,
275				"%s: error failed to reset sound card %s: %s\n",
276				context->command, context->card,
277				snd_strerror(err));
278			return err;
279		}
280		break;
281	case OM_RELOAD:
282		err = snd_use_case_mgr_reload(context->uc_mgr);
283		if (err < 0) {
284			fprintf(stderr,
285				"%s: error failed to reload manager %s: %s\n",
286				context->command, context->card,
287				snd_strerror(err));
288			return err;
289		}
290		break;
291	case OM_LISTCARDS:
292		err = snd_use_case_card_list(&list);
293		if (err < 0) {
294			fprintf(stderr,
295				"%s: error failed to get card list: %s\n",
296				context->command,
297				snd_strerror(err));
298			return err;
299		}
300		if (err == 0) {
301			printf("  list is empty\n");
302			return 0;
303		}
304		for (i = 0; i < err / 2; i++) {
305			printf("  %i: %s\n", i, list[i*2]);
306			if (list[i*2+1])
307				printf("    %s\n", list[i*2+1]);
308		}
309		snd_use_case_free_list(list, err);
310		break;
311	case OM_DUMP:
312		dump(context, argv[0]);
313		break;
314	case OM_LIST1:
315	case OM_LIST2:
316		switch (cmd->code) {
317		case OM_LIST1:
318		    entries = 1;
319		    break;
320		case OM_LIST2:
321		    entries = 2;
322		    break;
323		}
324
325		err = snd_use_case_get_list(context->uc_mgr,
326					    argv[0],
327					    &list);
328		if (err < 0) {
329			fprintf(stderr,
330				"%s: error failed to get list %s: %s\n",
331				context->command, argv[0],
332				snd_strerror(err));
333			return err;
334		}
335		if (err == 0) {
336			printf("  list is empty\n");
337			return 0;
338		}
339		for (i = 0; i < err / entries; i++) {
340			printf("  %i: %s\n", i, list[i*entries]);
341			for (j = 0; j < entries - 1; j++)
342				if (list[i*entries+j+1])
343					printf("    %s\n", list[i*entries+j+1]);
344		}
345		snd_use_case_free_list(list, err);
346		break;
347	case OM_SET:
348		err = snd_use_case_set(context->uc_mgr, argv[0], argv[1]);
349		if (err < 0) {
350			fprintf(stderr,
351				"%s: error failed to set %s=%s: %s\n",
352				context->command, argv[0], argv[1],
353				snd_strerror(err));
354			return err;
355		}
356		break;
357	case OM_GET:
358	case OM_GET_VAL:
359		err = snd_use_case_get(context->uc_mgr, argv[0], &str);
360		if (err < 0) {
361			fprintf(stderr,
362				"%s: error failed to get %s: %s\n",
363				context->command, argv[0],
364				snd_strerror(err));
365			return err;
366		}
367		if (cmd->code == OM_GET)
368			printf("  %s=%s\n", argv[0], str);
369		else
370			printf("%s\n", str);
371		free((void *)str);
372		break;
373	case OM_GETI:
374	case OM_GETI_VAL:
375		err = snd_use_case_geti(context->uc_mgr, argv[0], &lval);
376		if (err < 0) {
377			fprintf(stderr,
378				"%s: error failed to get integer %s: %s\n",
379				context->command, argv[0],
380				snd_strerror(err));
381			return lval;
382		}
383		if (cmd->code == OM_GETI)
384			printf("  %s=%li\n", argv[0], lval);
385		else
386			printf("%li\n", lval);
387		break;
388	case OM_QUIT:
389		context->do_exit = 1;
390		break;
391	case OM_HELP:
392		dump_help(context);
393		break;
394	default:
395		fprintf(stderr, "%s: unimplemented command '%s'\n",
396				context->command, cmd->id);
397		return -EINVAL;
398	}
399	return 0;
400}
401
402static int do_commands(struct context *context)
403{
404	char *command, **argv;
405	struct cmd *cmd;
406	int i, acnt, err;
407
408	for (i = 0; i < context->argc && !context->do_exit; i++) {
409		command = context->argv[i];
410		for (cmd = cmds; cmd->id != NULL; cmd++) {
411			if (strcmp(cmd->id, command) == 0)
412				break;
413		}
414		if (cmd->id == NULL) {
415			fprintf(stderr, "%s: unknown command '%s'\n",
416						context->command, command);
417			return -EINVAL;
418		}
419		acnt = context->argc - (i + 1);
420		if (acnt < cmd->args) {
421			fprintf(stderr, "%s: expected %i arguments (got %i)\n",
422					context->command, cmd->args, acnt);
423			return -EINVAL;
424		}
425		argv = context->argv + i + 1;
426		err = do_one(context, cmd, argv);
427		if (err < 0)
428			return err;
429		i += cmd->args;
430	}
431	return 0;
432}
433
434enum {
435	OPT_VERSION = 1,
436};
437
438int main(int argc, char *argv[])
439{
440	static const char short_options[] = "hb:c:in";
441	static const struct option long_options[] = {
442		{"help", 0, 0, 'h'},
443		{"version", 0, 0, OPT_VERSION},
444		{"card", 1, 0, 'c'},
445		{"interactive", 0, 0, 'i'},
446		{"batch", 1, 0, 'b'},
447		{"no-open", 0, 0, 'n'},
448		{0, 0, 0, 0}
449	};
450	struct context *context;
451	const char *command = argv[0];
452	int c, err, option_index;
453	char cmd[MAX_BUF];
454	FILE *in;
455
456	context = calloc(1, sizeof(*context));
457	if (context == NULL)
458		return EXIT_FAILURE;
459	context->command = command;
460	while ((c = getopt_long(argc, argv, short_options,
461				 long_options, &option_index)) != -1) {
462		switch (c) {
463		case 'h':
464			dump_help(context);
465			break;
466		case OPT_VERSION:
467			printf("%s: version " SND_UTIL_VERSION_STR "\n", command);
468			break;
469		case 'c':
470			if (context->card)
471				free(context->card);
472			context->card = strdup(optarg);
473			break;
474		case 'i':
475			context->interactive = 1;
476			context->batch = NULL;
477			break;
478		case 'b':
479			context->batch = strdup(optarg);
480			context->interactive = 0;
481			break;
482		case 'n':
483			context->no_open = 1;
484			break;
485		default:
486			fprintf(stderr, "Try '%s --help' for more information.\n", command);
487			my_exit(context, EXIT_FAILURE);
488		}
489	}
490
491	/* parse and execute any command line commands */
492	if (argc > optind) {
493		context->argv = argv + optind;
494		context->argc = argc - optind;
495		err = do_commands(context);
496		if (err < 0)
497			my_exit(context, EXIT_FAILURE);
498	}
499
500	if (!context->interactive && !context->batch)
501		my_exit(context, EXIT_SUCCESS);
502
503	if (context->interactive) {
504		printf("%s: Interacive mode - 'q' to quit\n", command);
505		in = stdin;
506	} else {
507		if (strcmp(context->batch, "-") == 0) {
508			in = stdin;
509		} else {
510			in = fopen(context->batch, "r");
511			if (in == NULL) {
512				fprintf(stderr, "%s: error failed to open file '%s': %s\n",
513					command, context->batch, strerror(errno));
514				my_exit(context, EXIT_FAILURE);
515			}
516		}
517	}
518
519	/* run the interactive command parser and handler */
520	while (!context->do_exit && !feof(in)) {
521		if (context->interactive)
522			printf("%s>> ", argv[0]);
523		fflush(stdin);
524		if (fgets(cmd, MAX_BUF, in) == NULL)
525			break;
526		err = parse_line(context, cmd);
527		if (err < 0) {
528			fprintf(stderr, "%s: unable to parse line\n",
529				command);
530			my_exit(context, EXIT_FAILURE);
531		}
532		err = do_commands(context);
533		if (err < 0) {
534			if (context->interactive)
535				printf("^^^ error, try again\n");
536			else
537				my_exit(context, EXIT_FAILURE);
538		}
539	}
540
541	if (in != stdin)
542		fclose(in);
543
544	my_exit(context, EXIT_SUCCESS);
545	return EXIT_SUCCESS;
546}
547