tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

usecase.c (12383B)


      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 "aconfig.h"
     42 #include "version.h"
     43 
     44 #define MAX_BUF 256
     45 
     46 struct context {
     47 	snd_use_case_mgr_t *uc_mgr;
     48 	const char *command;
     49 	char *card;
     50 	char **argv;
     51 	int argc;
     52 	int arga;
     53 	char *batch;
     54 	unsigned int interactive:1;
     55 	unsigned int no_open:1;
     56 	unsigned int do_exit:1;
     57 };
     58 
     59 enum uc_cmd {
     60 	/* management */
     61 	OM_UNKNOWN = 0,
     62 	OM_OPEN,
     63 	OM_RESET,
     64 	OM_RELOAD,
     65 	OM_LISTCARDS,
     66 	OM_LIST2,
     67 	OM_LIST1,
     68 
     69 	/* set/get */
     70 	OM_SET,
     71 	OM_GET,
     72 	OM_GETI,
     73 
     74 	/* misc */
     75 	OM_HELP,
     76 	OM_QUIT,
     77 };
     78 
     79 struct cmd {
     80 	int code;
     81 	int args;
     82 	unsigned int opencard:1;
     83 	const char *id;
     84 };
     85 
     86 static struct cmd cmds[] = {
     87 	{ OM_OPEN, 1, 0, "open" },
     88 	{ OM_RESET, 0, 1, "reset" },
     89 	{ OM_RELOAD, 0, 1, "reload" },
     90 	{ OM_LISTCARDS, 0, 0, "listcards" },
     91 	{ OM_LIST1, 1, 1, "list1" },
     92 	{ OM_LIST2, 1, 1, "list" },
     93 	{ OM_SET, 2, 1, "set" },
     94 	{ OM_GET, 1, 1, "get" },
     95 	{ OM_GETI, 1, 1, "geti" },
     96 	{ OM_HELP, 0, 0, "help" },
     97 	{ OM_QUIT, 0, 0, "quit" },
     98 	{ OM_HELP, 0, 0, "h" },
     99 	{ OM_HELP, 0, 0, "?" },
    100 	{ OM_QUIT, 0, 0, "q" },
    101 	{ OM_UNKNOWN, 0, 0, NULL }
    102 };
    103 
    104 static void dump_help(struct context *context)
    105 {
    106 	if (context->command)
    107 		printf("Usage: %s <options> [command]\n", context->command);
    108 	printf(
    109 "\nAvailable options:\n"
    110 "  -h,--help                  this help\n"
    111 "  -c,--card NAME             open card NAME\n"
    112 "  -i,--interactive           interactive mode\n"
    113 "  -b,--batch FILE            batch mode (use '-' for the stdin input)\n"
    114 "  -n,--no-open               do not open first card found\n"
    115 "\nAvailable commands:\n"
    116 "  open NAME                  open card NAME\n"
    117 "  reset                      reset sound card to default state\n"
    118 "  reload                     reload configuration\n"
    119 "  listcards                  list available cards\n"
    120 "  list IDENTIFIER            list command, for items with value + comment\n"
    121 "  list1 IDENTIFIER           list command, for items without comments\n"
    122 "  get IDENTIFIER             get string value\n"
    123 "  geti IDENTIFIER            get integer value\n"
    124 "  set IDENTIFIER VALUE       set string value\n"
    125 "  h,help                     help\n"
    126 "  q,quit                     quit\n"
    127 );
    128 }
    129 
    130 static int parse_line(struct context *context, char *line)
    131 {
    132 	char *start, **nargv;
    133 	int c;
    134 
    135 	context->argc = 0;
    136 	while (*line) {
    137 		while (*line && (*line == ' ' || *line == '\t' ||
    138 							*line == '\n'))
    139 			line++;
    140 		c = *line;
    141 		if (c == '\"' || c == '\'') {
    142 			start = ++line;
    143 			while (*line && *line != c)
    144 				line++;
    145 			if (*line) {
    146 				*line = '\0';
    147 				line++;
    148 			}
    149 		} else {
    150 			start = line;
    151 			while (*line && *line != ' ' && *line != '\t' &&
    152 			       *line != '\n')
    153 				line++;
    154 			if (*line) {
    155 				*line = '\0';
    156 				line++;
    157 			}
    158 		}
    159 		if (start[0] == '\0' && context->argc == 0)
    160 			return 0;
    161 		if (context->argc + 1 >= context->arga) {
    162 			context->arga += 4;
    163 			nargv = realloc(context->argv,
    164 					context->arga * sizeof(char *));
    165 			if (nargv == NULL)
    166 				return -ENOMEM;
    167 			context->argv = nargv;
    168 		}
    169 		context->argv[context->argc++] = start;
    170 	}
    171 	return 0;
    172 }
    173 
    174 static int do_one(struct context *context, struct cmd *cmd, char **argv)
    175 {
    176 	const char **list, *str;
    177 	long lval;
    178 	int err, i, j, entries;
    179 
    180 	if (cmd->opencard && context->uc_mgr == NULL) {
    181 		fprintf(stderr, "%s: command '%s' requires an open card\n",
    182 				context->command, cmd->id);
    183 		return 0;
    184 	}
    185 	switch (cmd->code) {
    186 	case OM_OPEN:
    187 		if (context->uc_mgr)
    188 			snd_use_case_mgr_close(context->uc_mgr);
    189 		context->uc_mgr = NULL;
    190 		free(context->card);
    191 		context->card = strdup(argv[0]);
    192 		err = snd_use_case_mgr_open(&context->uc_mgr, context->card);
    193 		if (err < 0) {
    194 			fprintf(stderr,
    195 				"%s: error failed to open sound card %s: %s\n",
    196 				context->command, context->card,
    197 				snd_strerror(err));
    198 			return err;
    199 		}
    200 		break;
    201 	case OM_RESET:
    202 		err = snd_use_case_mgr_reset(context->uc_mgr);
    203 		if (err < 0) {
    204 			fprintf(stderr,
    205 				"%s: error failed to reset sound card %s: %s\n",
    206 				context->command, context->card,
    207 				snd_strerror(err));
    208 			return err;
    209 		}
    210 		break;
    211 	case OM_RELOAD:
    212 		err = snd_use_case_mgr_reload(context->uc_mgr);
    213 		if (err < 0) {
    214 			fprintf(stderr,
    215 				"%s: error failed to reload manager %s: %s\n",
    216 				context->command, context->card,
    217 				snd_strerror(err));
    218 			return err;
    219 		}
    220 		break;
    221 	case OM_LISTCARDS:
    222 		err = snd_use_case_card_list(&list);
    223 		if (err < 0) {
    224 			fprintf(stderr,
    225 				"%s: error failed to get card list: %s\n",
    226 				context->command,
    227 				snd_strerror(err));
    228 			return err;
    229 		}
    230 		if (err == 0) {
    231 			printf("  list is empty\n");
    232 			return 0;
    233 		}
    234 		for (i = 0; i < err / 2; i++) {
    235 			printf("  %i: %s\n", i, list[i*2]);
    236 			if (list[i*2+1])
    237 				printf("    %s\n", list[i*2+1]);
    238 		}
    239 		snd_use_case_free_list(list, err);
    240 		break;
    241 	case OM_LIST1:
    242 	case OM_LIST2:
    243 		switch (cmd->code) {
    244 		case OM_LIST1:
    245 		    entries = 1;
    246 		    break;
    247 		case OM_LIST2:
    248 		    entries = 2;
    249 		    break;
    250 		}
    251 
    252 		err = snd_use_case_get_list(context->uc_mgr,
    253 					    argv[0],
    254 					    &list);
    255 		if (err < 0) {
    256 			fprintf(stderr,
    257 				"%s: error failed to get list %s: %s\n",
    258 				context->command, argv[0],
    259 				snd_strerror(err));
    260 			return err;
    261 		}
    262 		if (err == 0) {
    263 			printf("  list is empty\n");
    264 			return 0;
    265 		}
    266 		for (i = 0; i < err / entries; i++) {
    267 			printf("  %i: %s\n", i, list[i*entries]);
    268 			for (j = 0; j < entries - 1; j++)
    269 				if (list[i*entries+j+1])
    270 					printf("    %s\n", list[i*entries+j+1]);
    271 		}
    272 		snd_use_case_free_list(list, err);
    273 		break;
    274 	case OM_SET:
    275 		err = snd_use_case_set(context->uc_mgr, argv[0], argv[1]);
    276 		if (err < 0) {
    277 			fprintf(stderr,
    278 				"%s: error failed to set %s=%s: %s\n",
    279 				context->command, argv[0], argv[1],
    280 				snd_strerror(err));
    281 			return err;
    282 		}
    283 		break;
    284 	case OM_GET:
    285 		err = snd_use_case_get(context->uc_mgr, argv[0], &str);
    286 		if (err < 0) {
    287 			fprintf(stderr,
    288 				"%s: error failed to get %s: %s\n",
    289 				context->command, argv[0],
    290 				snd_strerror(err));
    291 			return err;
    292 		}
    293 		printf("  %s=%s\n", argv[0], str);
    294 		free((void *)str);
    295 		break;
    296 	case OM_GETI:
    297 		err = snd_use_case_geti(context->uc_mgr, argv[0], &lval);
    298 		if (err < 0) {
    299 			fprintf(stderr,
    300 				"%s: error failed to get integer %s: %s\n",
    301 				context->command, argv[0],
    302 				snd_strerror(err));
    303 			return lval;
    304 		}
    305 		printf("  %s=%li\n", argv[0], lval);
    306 		break;
    307 	case OM_QUIT:
    308 		context->do_exit = 1;
    309 		break;
    310 	case OM_HELP:
    311 		dump_help(context);
    312 		break;
    313 	default:
    314 		fprintf(stderr, "%s: unimplemented command '%s'\n",
    315 				context->command, cmd->id);
    316 		return -EINVAL;
    317 	}
    318 	return 0;
    319 }
    320 
    321 static int do_commands(struct context *context)
    322 {
    323 	char *command, **argv;
    324 	struct cmd *cmd;
    325 	int i, acnt, err;
    326 
    327 	for (i = 0; i < context->argc && !context->do_exit; i++) {
    328 		command = context->argv[i];
    329 		for (cmd = cmds; cmd->id != NULL; cmd++) {
    330 			if (strcmp(cmd->id, command) == 0)
    331 				break;
    332 		}
    333 		if (cmd->id == NULL) {
    334 			fprintf(stderr, "%s: unknown command '%s'\n",
    335 						context->command, command);
    336 			return -EINVAL;
    337 		}
    338 		acnt = context->argc - (i + 1);
    339 		if (acnt < cmd->args) {
    340 			fprintf(stderr, "%s: expected %i arguments (got %i)\n",
    341 					context->command, cmd->args, acnt);
    342 			return -EINVAL;
    343 		}
    344 		argv = context->argv + i + 1;
    345 		err = do_one(context, cmd, argv);
    346 		if (err < 0)
    347 			return err;
    348 		i += cmd->args;
    349 	}
    350 	return 0;
    351 }
    352 
    353 static void my_exit(struct context *context, int exitcode)
    354 {
    355 	if (context->uc_mgr)
    356 		snd_use_case_mgr_close(context->uc_mgr);
    357 	if (context->arga > 0)
    358 		free(context->argv);
    359 	if (context->card)
    360 		free(context->card);
    361 	if (context->batch)
    362 		free(context->batch);
    363 	free(context);
    364 	exit(exitcode);
    365 }
    366 
    367 enum {
    368 	OPT_VERSION = 1,
    369 };
    370 
    371 int main(int argc, char *argv[])
    372 {
    373 	static const char short_options[] = "hb:c:in";
    374 	static const struct option long_options[] = {
    375 		{"help", 0, 0, 'h'},
    376 		{"version", 0, 0, OPT_VERSION},
    377 		{"card", 1, 0, 'c'},
    378 		{"interactive", 0, 0, 'i'},
    379 		{"batch", 1, 0, 'b'},
    380 		{"no-open", 0, 0, 'n'},
    381 		{0, 0, 0, 0}
    382 	};
    383 	struct context *context;
    384 	const char *command = argv[0];
    385 	const char **list;
    386 	int c, err, option_index;
    387 	char cmd[MAX_BUF];
    388 	FILE *in;
    389 
    390 	context = calloc(1, sizeof(*context));
    391 	if (context == NULL)
    392 		return EXIT_FAILURE;
    393 	context->command = command;
    394 	while ((c = getopt_long(argc, argv, short_options,
    395 				 long_options, &option_index)) != -1) {
    396 		switch (c) {
    397 		case 'h':
    398 			dump_help(context);
    399 			break;
    400 		case OPT_VERSION:
    401 			printf("%s: version " SND_UTIL_VERSION_STR "\n", command);
    402 			break;
    403 		case 'c':
    404 			if (context->card)
    405 				free(context->card);
    406 			context->card = strdup(optarg);
    407 			break;
    408 		case 'i':
    409 			context->interactive = 1;
    410 			context->batch = NULL;
    411 			break;
    412 		case 'b':
    413 			context->batch = strdup(optarg);
    414 			context->interactive = 0;
    415 			break;
    416 		case 'n':
    417 			context->no_open = 1;
    418 			break;
    419 		default:
    420 			fprintf(stderr, "Try '%s --help' for more information.\n", command);
    421 			my_exit(context, EXIT_FAILURE);
    422 		}
    423 	}
    424 
    425 	if (!context->no_open && context->card == NULL) {
    426 		err = snd_use_case_card_list(&list);
    427 		if (err < 0) {
    428 			fprintf(stderr, "%s: unable to obtain card list: %s\n", command, snd_strerror(err));
    429 			my_exit(context, EXIT_FAILURE);
    430 		}
    431 		if (err == 0) {
    432 			printf("No card found\n");
    433 			my_exit(context, EXIT_SUCCESS);
    434 		}
    435 		context->card = strdup(list[0]);
    436 		snd_use_case_free_list(list, err);
    437 	}
    438 
    439 	/* open library */
    440 	if (!context->no_open) {
    441 		err = snd_use_case_mgr_open(&context->uc_mgr,
    442 					    context->card);
    443 		if (err < 0) {
    444 			fprintf(stderr,
    445 				"%s: error failed to open sound card %s: %s\n",
    446 				command, context->card, snd_strerror(err));
    447 			my_exit(context, EXIT_FAILURE);
    448 		}
    449 	}
    450 
    451 	/* parse and execute any command line commands */
    452 	if (argc > optind) {
    453 		context->argv = argv + optind;
    454 		context->argc = argc - optind;
    455 		err = do_commands(context);
    456 		if (err < 0)
    457 			my_exit(context, EXIT_FAILURE);
    458 	}
    459 
    460 	if (!context->interactive && !context->batch)
    461 		my_exit(context, EXIT_SUCCESS);
    462 
    463 	if (context->interactive) {
    464 		printf("%s: Interacive mode - 'q' to quit\n", command);
    465 		in = stdin;
    466 	} else {
    467 		if (strcmp(context->batch, "-") == 0) {
    468 			in = stdin;
    469 		} else {
    470 			in = fopen(context->batch, "r");
    471 			if (in == NULL) {
    472 				fprintf(stderr, "%s: error failed to open file '%s': %s\n",
    473 					command, context->batch, strerror(-errno));
    474 				my_exit(context, EXIT_FAILURE);
    475 			}
    476 		}
    477 	}
    478 
    479 	/* run the interactive command parser and handler */
    480 	while (!context->do_exit && !feof(in)) {
    481 		if (context->interactive)
    482 			printf("%s>> ", argv[0]);
    483 		fflush(stdin);
    484 		if (fgets(cmd, MAX_BUF, in) == NULL)
    485 			break;
    486 		err = parse_line(context, cmd);
    487 		if (err < 0) {
    488 			fprintf(stderr, "%s: unable to parse line\n",
    489 				command);
    490 			my_exit(context, EXIT_FAILURE);
    491 		}
    492 		err = do_commands(context);
    493 		if (err < 0) {
    494 			if (context->interactive)
    495 				printf("^^^ error, try again\n");
    496 			else
    497 				my_exit(context, EXIT_FAILURE);
    498 		}
    499 	}
    500 	
    501 	if (in != stdin)
    502 		fclose(in);
    503 
    504 	my_exit(context, EXIT_SUCCESS);
    505 	return EXIT_SUCCESS;
    506 }