• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
47 enum 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 
70 struct cmd {
71 	int code;
72 	int args;
73 	unsigned int opencard:1;
74 	const char *id;
75 };
76 
77 static 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 
dump_help(struct context * context)99 static 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 
parse_line(struct context * context,char * line)126 static int parse_line(struct context *context, char *line)
127 {
128 	char *start, **nargv;
129 	int c;
130 
131 	context->argc = 0;
132 	while (*line) {
133 		while (*line && (*line == ' ' || *line == '\t' ||
134 							*line == '\n'))
135 			line++;
136 		c = *line;
137 		if (c == '\0')
138 			return 0;
139 		if (c == '\"' || c == '\'') {
140 			start = ++line;
141 			while (*line && *line != c)
142 				line++;
143 			if (*line) {
144 				*line = '\0';
145 				line++;
146 			}
147 		} else {
148 			start = line;
149 			while (*line && *line != ' ' && *line != '\t' &&
150 			       *line != '\n')
151 				line++;
152 			if (*line) {
153 				*line = '\0';
154 				line++;
155 			}
156 		}
157 		if (start[0] == '\0' && context->argc == 0)
158 			return 0;
159 		if (context->argc + 1 >= context->arga) {
160 			context->arga += 4;
161 			nargv = realloc(context->argv,
162 					context->arga * sizeof(char *));
163 			if (nargv == NULL)
164 				return -ENOMEM;
165 			context->argv = nargv;
166 		}
167 		context->argv[context->argc++] = start;
168 	}
169 	return 0;
170 }
171 
my_exit(struct context * context,int exitcode)172 static void my_exit(struct context *context, int exitcode)
173 {
174 	if (context->uc_mgr)
175 		snd_use_case_mgr_close(context->uc_mgr);
176 	if (context->arga > 0)
177 		free(context->argv);
178 	if (context->card)
179 		free(context->card);
180 	if (context->batch)
181 		free(context->batch);
182 	free(context);
183 	snd_config_update_free_global();
184 	exit(exitcode);
185 }
do_initial_open(struct context * context)186 static void do_initial_open(struct context *context)
187 {
188 	int card, err;
189 	char name[16];
190 
191 	if (!context->no_open && context->card == NULL) {
192 		card = -1;
193 		err = snd_card_next(&card);
194 		if (err < 0) {
195 			fprintf(stderr, "%s: no sound card found: %s\n",
196 					context->command, snd_strerror(err));
197 			my_exit(context, EXIT_FAILURE);
198 		}
199 		snprintf(name, sizeof(name), "hw:%d", card);
200 		context->card = strdup(name);
201 	}
202 
203 	/* open library */
204 	if (!context->no_open) {
205 		err = snd_use_case_mgr_open(&context->uc_mgr,
206 					    context->card);
207 		if (err < 0) {
208 			fprintf(stderr,
209 				"%s: error failed to open sound card %s: %s\n",
210 				context->command, context->card, snd_strerror(err));
211 			my_exit(context, EXIT_FAILURE);
212 		}
213 	}
214 }
215 
do_one(struct context * context,struct cmd * cmd,char ** argv)216 static int do_one(struct context *context, struct cmd *cmd, char **argv)
217 {
218 	const char **list, *str;
219 	long lval;
220 	int err, i, j, entries;
221 
222 	if (cmd->opencard && context->uc_mgr == NULL) {
223 		if (!context->no_open) {
224 			do_initial_open(context);
225 			context->no_open = 1;
226 		} else {
227 			fprintf(stderr, "%s: command '%s' requires an open card\n",
228 					context->command, cmd->id);
229 			return 0;
230 		}
231 	}
232 	switch (cmd->code) {
233 	case OM_OPEN:
234 		if (context->uc_mgr)
235 			snd_use_case_mgr_close(context->uc_mgr);
236 		context->uc_mgr = NULL;
237 		free(context->card);
238 		context->card = strdup(argv[0]);
239 		err = snd_use_case_mgr_open(&context->uc_mgr, context->card);
240 		if (err < 0) {
241 			fprintf(stderr,
242 				"%s: error failed to open sound card %s: %s\n",
243 				context->command, context->card,
244 				snd_strerror(err));
245 			return err;
246 		}
247 		break;
248 	case OM_RESET:
249 		err = snd_use_case_mgr_reset(context->uc_mgr);
250 		if (err < 0) {
251 			fprintf(stderr,
252 				"%s: error failed to reset sound card %s: %s\n",
253 				context->command, context->card,
254 				snd_strerror(err));
255 			return err;
256 		}
257 		break;
258 	case OM_RELOAD:
259 		err = snd_use_case_mgr_reload(context->uc_mgr);
260 		if (err < 0) {
261 			fprintf(stderr,
262 				"%s: error failed to reload manager %s: %s\n",
263 				context->command, context->card,
264 				snd_strerror(err));
265 			return err;
266 		}
267 		break;
268 	case OM_LISTCARDS:
269 		err = snd_use_case_card_list(&list);
270 		if (err < 0) {
271 			fprintf(stderr,
272 				"%s: error failed to get card list: %s\n",
273 				context->command,
274 				snd_strerror(err));
275 			return err;
276 		}
277 		if (err == 0) {
278 			printf("  list is empty\n");
279 			return 0;
280 		}
281 		for (i = 0; i < err / 2; i++) {
282 			printf("  %i: %s\n", i, list[i*2]);
283 			if (list[i*2+1])
284 				printf("    %s\n", list[i*2+1]);
285 		}
286 		snd_use_case_free_list(list, err);
287 		break;
288 	case OM_DUMP:
289 		dump(context, argv[0]);
290 		break;
291 	case OM_LIST1:
292 	case OM_LIST2:
293 		switch (cmd->code) {
294 		case OM_LIST1:
295 		    entries = 1;
296 		    break;
297 		case OM_LIST2:
298 		    entries = 2;
299 		    break;
300 		}
301 
302 		err = snd_use_case_get_list(context->uc_mgr,
303 					    argv[0],
304 					    &list);
305 		if (err < 0) {
306 			fprintf(stderr,
307 				"%s: error failed to get list %s: %s\n",
308 				context->command, argv[0],
309 				snd_strerror(err));
310 			return err;
311 		}
312 		if (err == 0) {
313 			printf("  list is empty\n");
314 			return 0;
315 		}
316 		for (i = 0; i < err / entries; i++) {
317 			printf("  %i: %s\n", i, list[i*entries]);
318 			for (j = 0; j < entries - 1; j++)
319 				if (list[i*entries+j+1])
320 					printf("    %s\n", list[i*entries+j+1]);
321 		}
322 		snd_use_case_free_list(list, err);
323 		break;
324 	case OM_SET:
325 		err = snd_use_case_set(context->uc_mgr, argv[0], argv[1]);
326 		if (err < 0) {
327 			fprintf(stderr,
328 				"%s: error failed to set %s=%s: %s\n",
329 				context->command, argv[0], argv[1],
330 				snd_strerror(err));
331 			return err;
332 		}
333 		break;
334 	case OM_GET:
335 	case OM_GET_VAL:
336 		err = snd_use_case_get(context->uc_mgr, argv[0], &str);
337 		if (err < 0) {
338 			fprintf(stderr,
339 				"%s: error failed to get %s: %s\n",
340 				context->command, argv[0],
341 				snd_strerror(err));
342 			return err;
343 		}
344 		if (cmd->code == OM_GET)
345 			printf("  %s=%s\n", argv[0], str);
346 		else
347 			printf("%s\n", str);
348 		free((void *)str);
349 		break;
350 	case OM_GETI:
351 	case OM_GETI_VAL:
352 		err = snd_use_case_geti(context->uc_mgr, argv[0], &lval);
353 		if (err < 0) {
354 			fprintf(stderr,
355 				"%s: error failed to get integer %s: %s\n",
356 				context->command, argv[0],
357 				snd_strerror(err));
358 			return lval;
359 		}
360 		if (cmd->code == OM_GETI)
361 			printf("  %s=%li\n", argv[0], lval);
362 		else
363 			printf("%li\n", lval);
364 		break;
365 	case OM_QUIT:
366 		context->do_exit = 1;
367 		break;
368 	case OM_HELP:
369 		dump_help(context);
370 		break;
371 	default:
372 		fprintf(stderr, "%s: unimplemented command '%s'\n",
373 				context->command, cmd->id);
374 		return -EINVAL;
375 	}
376 	return 0;
377 }
378 
do_commands(struct context * context)379 static int do_commands(struct context *context)
380 {
381 	char *command, **argv;
382 	struct cmd *cmd;
383 	int i, acnt, err;
384 
385 	for (i = 0; i < context->argc && !context->do_exit; i++) {
386 		command = context->argv[i];
387 		for (cmd = cmds; cmd->id != NULL; cmd++) {
388 			if (strcmp(cmd->id, command) == 0)
389 				break;
390 		}
391 		if (cmd->id == NULL) {
392 			fprintf(stderr, "%s: unknown command '%s'\n",
393 						context->command, command);
394 			return -EINVAL;
395 		}
396 		acnt = context->argc - (i + 1);
397 		if (acnt < cmd->args) {
398 			fprintf(stderr, "%s: expected %i arguments (got %i)\n",
399 					context->command, cmd->args, acnt);
400 			return -EINVAL;
401 		}
402 		argv = context->argv + i + 1;
403 		err = do_one(context, cmd, argv);
404 		if (err < 0)
405 			return err;
406 		i += cmd->args;
407 	}
408 	return 0;
409 }
410 
411 enum {
412 	OPT_VERSION = 1,
413 };
414 
main(int argc,char * argv[])415 int main(int argc, char *argv[])
416 {
417 	static const char short_options[] = "hb:c:in";
418 	static const struct option long_options[] = {
419 		{"help", 0, 0, 'h'},
420 		{"version", 0, 0, OPT_VERSION},
421 		{"card", 1, 0, 'c'},
422 		{"interactive", 0, 0, 'i'},
423 		{"batch", 1, 0, 'b'},
424 		{"no-open", 0, 0, 'n'},
425 		{0, 0, 0, 0}
426 	};
427 	struct context *context;
428 	const char *command = argv[0];
429 	int c, err, option_index;
430 	char cmd[MAX_BUF];
431 	FILE *in;
432 
433 	context = calloc(1, sizeof(*context));
434 	if (context == NULL)
435 		return EXIT_FAILURE;
436 	context->command = command;
437 	while ((c = getopt_long(argc, argv, short_options,
438 				 long_options, &option_index)) != -1) {
439 		switch (c) {
440 		case 'h':
441 			dump_help(context);
442 			break;
443 		case OPT_VERSION:
444 			printf("%s: version " SND_UTIL_VERSION_STR "\n", command);
445 			break;
446 		case 'c':
447 			if (context->card)
448 				free(context->card);
449 			context->card = strdup(optarg);
450 			break;
451 		case 'i':
452 			context->interactive = 1;
453 			context->batch = NULL;
454 			break;
455 		case 'b':
456 			context->batch = strdup(optarg);
457 			context->interactive = 0;
458 			break;
459 		case 'n':
460 			context->no_open = 1;
461 			break;
462 		default:
463 			fprintf(stderr, "Try '%s --help' for more information.\n", command);
464 			my_exit(context, EXIT_FAILURE);
465 		}
466 	}
467 
468 	/* parse and execute any command line commands */
469 	if (argc > optind) {
470 		context->argv = argv + optind;
471 		context->argc = argc - optind;
472 		err = do_commands(context);
473 		if (err < 0)
474 			my_exit(context, EXIT_FAILURE);
475 	}
476 
477 	if (!context->interactive && !context->batch)
478 		my_exit(context, EXIT_SUCCESS);
479 
480 	if (context->interactive) {
481 		printf("%s: Interacive mode - 'q' to quit\n", command);
482 		in = stdin;
483 	} else {
484 		if (strcmp(context->batch, "-") == 0) {
485 			in = stdin;
486 		} else {
487 			in = fopen(context->batch, "r");
488 			if (in == NULL) {
489 				fprintf(stderr, "%s: error failed to open file '%s': %s\n",
490 					command, context->batch, strerror(-errno));
491 				my_exit(context, EXIT_FAILURE);
492 			}
493 		}
494 	}
495 
496 	/* run the interactive command parser and handler */
497 	while (!context->do_exit && !feof(in)) {
498 		if (context->interactive)
499 			printf("%s>> ", argv[0]);
500 		fflush(stdin);
501 		if (fgets(cmd, MAX_BUF, in) == NULL)
502 			break;
503 		err = parse_line(context, cmd);
504 		if (err < 0) {
505 			fprintf(stderr, "%s: unable to parse line\n",
506 				command);
507 			my_exit(context, EXIT_FAILURE);
508 		}
509 		err = do_commands(context);
510 		if (err < 0) {
511 			if (context->interactive)
512 				printf("^^^ error, try again\n");
513 			else
514 				my_exit(context, EXIT_FAILURE);
515 		}
516 	}
517 
518 	if (in != stdin)
519 		fclose(in);
520 
521 	my_exit(context, EXIT_SUCCESS);
522 	return EXIT_SUCCESS;
523 }
524