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 }