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
is_long(const char * str)126 int 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
parse_line(struct context * context,char * line)140 static 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
my_exit(struct context * context,int exitcode)186 static 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 }
do_initial_open(struct context * context)200 static 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
do_one(struct context * context,struct cmd * cmd,char ** argv)239 static 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
do_commands(struct context * context)402 static 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
434 enum {
435 OPT_VERSION = 1,
436 };
437
main(int argc,char * argv[])438 int 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