diff --git a/Makefile.am b/Makefile.am index 48a4780..21d69ab 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ INCLUDES=-I$(top_srcdir)/include -SUBDIRS = include alsactl utils m4 po +SUBDIRS = include alsactl alsaucm utils m4 po if ALSAMIXER SUBDIRS += alsamixer endif diff --git a/alsaucm/Makefile.am b/alsaucm/Makefile.am new file mode 100644 index 0000000..4eba677 --- /dev/null +++ b/alsaucm/Makefile.am @@ -0,0 +1,13 @@ +bin_PROGRAMS = \ + alsaucm + +alsaucm_SOURCES = usecase.c + +INCLUDES = \ + -Wall -I$(top_srcdir)/include + +alsaucm_LDADD = -lasound + +# local build +INCLUDES += -I$(top_srcdir)/../alsa-lib/include +alsaucm_LDADD += -L$(top_srcdir)/../alsa-lib/src/.libs diff --git a/alsaucm/go.sh b/alsaucm/go.sh new file mode 100755 index 0000000..2aadd1c --- /dev/null +++ b/alsaucm/go.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +#GDB="gdb --args" + +ALSA_CONFIG_UCM="$HOME/alsa/alsa-lib/test/ucm" \ +LD_PRELOAD="$HOME/alsa/alsa-lib/src/.libs/libasound.so" \ +$GDB ./alsaucm "$@" diff --git a/alsaucm/usecase.c b/alsaucm/usecase.c new file mode 100644 index 0000000..bb894c6 --- /dev/null +++ b/alsaucm/usecase.c @@ -0,0 +1,435 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Support for the verb/device/modifier core logic and API, + * command line tool and file parser was kindly sponsored by + * Texas Instruments Inc. + * Support for multiple active modifiers and devices, + * transition sequences, multiple client access and user defined use + * cases was kindly sponsored by Wolfson Microelectronics PLC. + * + * Copyright (C) 2008-2010 SlimLogic Ltd + * Copyright (C) 2010 Wolfson Microelectronics PLC + * Copyright (C) 2010 Texas Instruments Inc. + * Copyright (C) 2010 Red Hat Inc. + * Authors: Liam Girdwood + * Stefan Schmidt + * Justin Xu + * Jaroslav Kysela + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "aconfig.h" +#include "version.h" + +#define MAX_BUF 256 + +struct context { + snd_use_case_mgr_t *uc_mgr; + const char *command; + char *card; + char **argv; + int argc; + unsigned int interactive:1; + unsigned int no_open:1; + unsigned int do_exit:1; +}; + +enum uc_cmd { + /* management */ + OM_UNKNOWN = 0, + OM_OPEN, + OM_RESET, + OM_RELOAD, + OM_LISTCARDS, + OM_LIST, + + /* set/get */ + OM_SET, + OM_GET, + OM_GETI, + + /* misc */ + OM_HELP, + OM_QUIT, +}; + +struct cmd { + int code; + int args; + unsigned int opencard:1; + const char *id; +}; + +static struct cmd cmds[] = { + { OM_OPEN, 1, 0, "open" }, + { OM_RESET, 0, 1, "reset" }, + { OM_RELOAD, 0, 1, "reload" }, + { OM_LISTCARDS, 0, 0, "listcards" }, + { OM_LIST, 0, 1, "list" }, + { OM_SET, 2, 1, "set" }, + { OM_GET, 1, 1, "get" }, + { OM_GETI, 1, 1, "geti" }, + { OM_HELP, 0, 0, "help" }, + { OM_QUIT, 0, 0, "quit" }, + { OM_HELP, 0, 0, "h" }, + { OM_QUIT, 0, 0, "q" }, + { OM_UNKNOWN, 0, 0, NULL } +}; + +static void dump_help(struct context *context) +{ + if (context->command) + printf("Usage: %s [command]\n", context->command); + printf( +"\nAvailable options:\n" +" -h,--help this help\n" +" -c,--card NAME open card NAME\n" +" -i,--interactive interactive mode\n" +" -n,--no-open do not open first card found\n" +"\nAvailable commands:\n" +" open NAME open card NAME\n" +" reset reset sound card to default state\n" +" reload reload configuration\n" +" listcards list available cards\n" +" list IDENTIFIER list command\n" +" get IDENTIFIER get string value\n" +" geti IDENTIFIER get integer value\n" +" set IDENTIFIER VALUE set string value\n" +" h,help help\n" +" q,quit quit\n" +); +} + +static int parse_line(struct context *context, char *line) +{ + char *start; + int c; + + context->argc = 0; + while (*line) { + while (*line && (*line == ' ' || *line == '\t' || + *line == '\n')) + line++; + c = *line; + if (c == '\"' || c == '\'') { + start = ++line; + while (*line && *line != c) + line++; + if (*line) { + *line = '\0'; + line++; + } + } else { + start = line; + while (*line && *line != ' ' && *line != '\t' && + *line != '\n') + line++; + if (*line) { + *line = '\0'; + line++; + } + } + context->argv[context->argc++] = start; + } + return 0; +} + +static int do_one(struct context *context, struct cmd *cmd, char **argv) +{ + const char **list, *str; + long lval; + int err, i; + + if (cmd->opencard && context->uc_mgr == NULL) { + fprintf(stderr, "%s: command '%s' requires an open card\n", + context->command, cmd->id); + return 0; + } + switch (cmd->code) { + case OM_OPEN: + if (context->uc_mgr) + snd_use_case_mgr_close(context->uc_mgr); + context->uc_mgr = NULL; + free(context->card); + context->card = strdup(argv[0]); + err = snd_use_case_mgr_open(&context->uc_mgr, context->card); + if (err < 0) { + fprintf(stderr, + "%s: error failed to open sound card %s: %s\n", + context->command, context->card, + snd_strerror(err)); + return err; + } + break; + case OM_RESET: + err = snd_use_case_mgr_reset(context->uc_mgr); + if (err < 0) { + fprintf(stderr, + "%s: error failed to reset sound card %s: %s\n", + context->command, context->card, + snd_strerror(err)); + return err; + } + break; + case OM_RELOAD: + err = snd_use_case_mgr_reload(context->uc_mgr); + if (err < 0) { + fprintf(stderr, + "%s: error failed to reload manager %s: %s\n", + context->command, context->card, + snd_strerror(err)); + return err; + } + break; + case OM_LISTCARDS: + err = snd_use_case_card_list(&list); + if (err < 0) { + fprintf(stderr, + "%s: error failed to get card list: %s\n", + context->command, + snd_strerror(err)); + return err; + } + if (err == 0) + printf(" list is empty\n"); + for (i = 0; i < err / 2; i++) { + printf(" %i: %s\n", i, list[i*2]); + if (list[i*2+1]) + printf(" %s\n", list[i*2+1]); + } + snd_use_case_free_list(list, err); + break; + case OM_LIST: + err = snd_use_case_get_list(context->uc_mgr, + argv[0], + &list); + if (err < 0) { + fprintf(stderr, + "%s: error failed to get list %s: %s\n", + context->command, argv[0], + snd_strerror(err)); + return err; + } + if (err == 0) + printf(" list is empty\n"); + for (i = 0; i < err; i++) + printf(" %i: %s\n", i, list[i]); + snd_use_case_free_list(list, err); + break; + case OM_SET: + err = snd_use_case_set(context->uc_mgr, argv[0], argv[1]); + if (err < 0) { + fprintf(stderr, + "%s: error failed to set %s=%s: %s\n", + context->command, argv[0], argv[1], + snd_strerror(err)); + return err; + } + break; + case OM_GET: + err = snd_use_case_get(context->uc_mgr, argv[0], &str); + if (err < 0) { + fprintf(stderr, + "%s: error failed to get %s: %s\n", + context->command, argv[0], + snd_strerror(err)); + return err; + } + printf(" %s=%s\n", argv[0], str); + free((void *)str); + break; + case OM_GETI: + err = snd_use_case_geti(context->uc_mgr, argv[0], &lval); + if (err < 0) { + fprintf(stderr, + "%s: error failed to get integer %s: %s\n", + context->command, argv[0], + snd_strerror(err)); + return lval; + } + printf(" %s=%li\n", argv[0], lval); + break; + case OM_QUIT: + context->do_exit = 1; + break; + case OM_HELP: + dump_help(context); + break; + default: + fprintf(stderr, "%s: unimplemented command '%s'\n", + context->command, cmd->id); + return -EINVAL; + } + return 0; +} + +static int do_commands(struct context *context) +{ + char *command, **argv; + struct cmd *cmd; + int i, acnt, err; + + for (i = 0; i < context->argc && !context->do_exit; i++) { + command = context->argv[i]; + for (cmd = cmds; cmd->id != NULL; cmd++) { + if (strcmp(cmd->id, command) == 0) + break; + } + if (cmd->id == NULL) { + fprintf(stderr, "%s: unknown command '%s'\n", + context->command, command); + return -EINVAL; + } + acnt = context->argc - (i + 1); + if (acnt < cmd->args) { + fprintf(stderr, "%s: expected %i arguments (got %i)\n", + context->command, cmd->args, acnt); + return -EINVAL; + } + argv = context->argv + i + 1; + err = do_one(context, cmd, argv); + if (err < 0) + return err; + i += cmd->args; + } + return 0; +} + +static void my_exit(struct context *context, int exitcode) +{ + if (context->uc_mgr) + snd_use_case_mgr_close(context->uc_mgr); + free(context); + exit(exitcode); +} + +enum { + OPT_VERSION = 1, +}; + +int main(int argc, char *argv[]) +{ + static const char short_options[] = "hc:in"; + static const struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"version", 0, 0, OPT_VERSION}, + {"card", 1, 0, 'c'}, + {"interactive", 0, 0, 'i'}, + {"no-open", 0, 0, 'n'}, + {0, 0, 0, 0} + }; + struct context *context; + const char *command = argv[0]; + const char **list; + int c, err, option_index; + char cmd[MAX_BUF]; + + context = calloc(1, sizeof(*context)); + if (context == NULL) + return EXIT_FAILURE; + context->command = command; + while ((c = getopt_long(argc, argv, short_options, + long_options, &option_index)) != -1) { + switch (c) { + case 'h': + dump_help(context); + break; + case OPT_VERSION: + printf("%s: version " SND_UTIL_VERSION_STR "\n", command); + break; + case 'c': + if (context->card) + free(context->card); + context->card = strdup(optarg); + break; + case 'i': + context->interactive = 1; + break; + case 'n': + context->no_open = 1; + break; + default: + fprintf(stderr, "Try '%s --help' for more information.\n", command); + my_exit(context, EXIT_FAILURE); + } + } + + if (!context->no_open && context->card == NULL) { + err = snd_use_case_card_list(&list); + if (err < 0) { + fprintf(stderr, "%s: unable to obtain card list: %s\n", command, snd_strerror(err)); + my_exit(context, EXIT_FAILURE); + } + if (err == 0) { + printf("No card found\n"); + my_exit(context, EXIT_SUCCESS); + } + context->card = strdup(list[0]); + snd_use_case_free_list(list, err); + } + + /* open library */ + if (!context->no_open) { + err = snd_use_case_mgr_open(&context->uc_mgr, + context->card); + if (err < 0) { + fprintf(stderr, + "%s: error failed to open sound card %s: %s\n", + command, context->card, snd_strerror(err)); + my_exit(context, EXIT_FAILURE); + } + } + + /* parse and execute any command line commands */ + if (argc > optind) { + context->argv = argv + optind; + context->argc = argc - optind; + err = do_commands(context); + if (err < 0) + my_exit(context, EXIT_FAILURE); + } + + if (!context->interactive) + my_exit(context, EXIT_SUCCESS); + + printf("%s: Interacive mode - 'q' to quit\n", command); + /* run the interactive command parser and handler */ + while (!context->do_exit) { + printf("%s>> ", argv[0]); + fflush(stdin); + if (fgets(cmd, MAX_BUF, stdin) == NULL) + break; + err = parse_line(context, cmd); + if (err < 0) { + fprintf(stderr, "%s: unable to parse line\n", + command); + my_exit(context, EXIT_FAILURE); + } + err = do_commands(context); + if (err < 0) + my_exit(context, EXIT_FAILURE); + } + + my_exit(context, EXIT_SUCCESS); + return EXIT_SUCCESS; +} diff --git a/configure.in b/configure.in index 561ca46..f784363 100644 --- a/configure.in +++ b/configure.in @@ -304,6 +304,7 @@ AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \ m4/Makefile po/Makefile.in \ alsaconf/alsaconf alsaconf/Makefile \ alsaconf/po/Makefile \ + alsaucm/Makefile \ aplay/Makefile include/Makefile iecset/Makefile utils/Makefile \ utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \ seq/aplaymidi/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \