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