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