[alsa-cvslog] alsa-lib: ALSA library repository branch, ucm now at v1.0.23-25-g8440637
Brought to you by:
perex
From: <nor...@al...> - 2010-09-09 15:11:12
|
Hello, This is an automated email from the git hooks/update script, it was generated because a ref change was pushed to the repository. Updating branch, ucm, via 84406371eb64763f6bd1778125519312eedb1021 (commit) based on fcc9adb26058667656277ba92032ba7e0b00261c from 7adef4f92688b17105a30e4df586b41c9695dc19 (commit) This ref update crossed a branch point; i.e. the old rev is not a strict subset of the new rev. This occurs, when you --force push a change in a situation like this: * -- * -- B -- O -- O -- O (7adef4f92688b17105a30e4df586b41c9695dc19) \ N -- N -- N (84406371eb64763f6bd1778125519312eedb1021) Therefore, we assume that you've already had alert emails for all of the O revisions, and now give you all the revisions in the N branch from the common base, B (fcc9adb26058667656277ba92032ba7e0b00261c), up to the new revision. - Log ----------------------------------------------------------------- commit 84406371eb64763f6bd1778125519312eedb1021 Author: Jaroslav Kysela <pe...@pe...> AuthorDate: Tue Sep 7 15:35:14 2010 +0200 Commit: Jaroslav Kysela <pe...@pe...> CommitDate: Thu Sep 9 17:10:03 2010 +0200 ucm: Moved ucm to src/ucm subdirectory - separate code to more files - use standard lists to represent structures - use alsa-lib configuration parser Signed-off-by: Jaroslav Kysela <pe...@pe...> ----------------------------------------------------------------------- Diffstat: configure.in | 6 +- doc/doxygen.cfg.in | 6 +- include/use-case.h | 13 +- src/Makefile.am | 6 +- src/ucm/Makefile.am | 10 + src/ucm/main.c | 1924 ++++++++++++++++++++++++++++ src/ucm/parser.c | 955 ++++++++++++++ src/ucm/ucm_local.h | 207 +++ src/ucm/utils.c | 201 +++ src/use-case.c | 3515 --------------------------------------------------- 10 files changed, 3321 insertions(+), 3522 deletions(-) Patch -------------- (max 1500 lines) diff --git a/configure.in b/configure.in index abc4687..0a65cc1 100644 --- a/configure.in +++ b/configure.in @@ -373,6 +373,9 @@ AC_ARG_ENABLE(hwdep, AC_ARG_ENABLE(seq, AS_HELP_STRING([--disable-seq], [disable the sequencer component]), [build_seq="$enableval"], [build_seq="yes"]) +AC_ARG_ENABLE(ucm, + AS_HELP_STRING([--disable-ucm], [disable the use-case-manager component]), + [build_ucm="$enableval"], [build_ucm="yes"]) AC_ARG_ENABLE(alisp, AS_HELP_STRING([--disable-alisp], [disable the alisp component]), [build_alisp="$enableval"], [build_alisp="yes"]) @@ -414,6 +417,7 @@ AM_CONDITIONAL(BUILD_PCM, test x$build_pcm = xyes) AM_CONDITIONAL(BUILD_RAWMIDI, test x$build_rawmidi = xyes) AM_CONDITIONAL(BUILD_HWDEP, test x$build_hwdep = xyes) AM_CONDITIONAL(BUILD_SEQ, test x$build_seq = xyes) +AM_CONDITIONAL(BUILD_UCM, test x$build_ucm = xyes) AM_CONDITIONAL(BUILD_ALISP, test x$build_alisp = xyes) AM_CONDITIONAL(BUILD_PYTHON, test x$build_python = xyes) @@ -598,7 +602,7 @@ AC_OUTPUT(Makefile doc/Makefile doc/pictures/Makefile doc/doxygen.cfg \ src/control/Makefile src/mixer/Makefile \ src/pcm/Makefile src/pcm/scopes/Makefile \ src/rawmidi/Makefile src/timer/Makefile \ - src/hwdep/Makefile src/seq/Makefile \ + src/hwdep/Makefile src/seq/Makefile src/ucm/Makefile \ src/compat/Makefile src/alisp/Makefile src/conf/Makefile \ src/conf/cards/Makefile \ src/conf/pcm/Makefile \ diff --git a/doc/doxygen.cfg.in b/doc/doxygen.cfg.in index 3929772..f4499d6 100644 --- a/doc/doxygen.cfg.in +++ b/doc/doxygen.cfg.in @@ -77,7 +77,8 @@ INPUT = @top_srcdir@/doc/index.doxygen \ @top_srcdir@/src/rawmidi \ @top_srcdir@/src/timer \ @top_srcdir@/src/hwdep \ - @top_srcdir@/src/seq + @top_srcdir@/src/seq \ + @top_srcdir@/src/ucm EXCLUDE = @top_srcdir@/src/control/control_local.h \ @top_srcdir@/src/pcm/atomic.h \ @top_srcdir@/src/pcm/interval.h \ @@ -92,7 +93,8 @@ EXCLUDE = @top_srcdir@/src/control/control_local.h \ @top_srcdir@/src/hwdep/hwdep_local.h \ @top_srcdir@/src/mixer/mixer_local.h \ @top_srcdir@/src/rawmidi/rawmidi_local.h \ - @top_srcdir@/src/seq/seq_local.h + @top_srcdir@/src/seq/seq_local.h \ + @top_srcdir@/src/seq/ucm_local.h RECURSIVE = YES FILE_PATTERNS = *.c *.h EXAMPLE_PATH = @top_srcdir@/test diff --git a/include/use-case.h b/include/use-case.h index 33d480f..abe96c4 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -177,10 +177,17 @@ typedef struct snd_use_case_mgr snd_use_case_mgr_t; char *snd_use_case_identifier(const char *fmt, ...); /** + * \brief Free a string list + * \param list The string list to free + * \return Zero if success, otherwise a negative error code + */ +int snd_use_case_free_list(const char *list[]); + +/** * \brief Obtain a list of entries * \param uc_mgr Use case manager * \param identifier (may be NULL) - * \param list Returned list + * \param list Returned allocated list * \return Number of list entries if success, otherwise a negative error code * * Defined identifiers: @@ -215,6 +222,8 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, * Known identifiers: * NULL - return current card * _verb - return current verb + * _tq - return current Tone Quality + * _tq/<modifier> - return Tone Quality for given modifier * _pcm_/_pdevice[/<modifier>] - full PCM playback device name * _pcm_/_cdevice[/<modifier>] - full PCM capture device name * _ctl_/_pctl_[/<modifier>] - playback control device name @@ -241,8 +250,6 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, * Known identifiers: * _devstatus/<device> - return status for given device * _modstatus/<modifier> - return status for given modifier - * _tq - return current Tone Quality - * _tq/<modifier> - return Tone Quality for given modifier */ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, const char *identifier); diff --git a/src/Makefile.am b/src/Makefile.am index 7206dbd..9a00dca 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,7 @@ SYMFUNCS = endif lib_LTLIBRARIES = libasound.la -libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c use-case.c +libasound_la_SOURCES = conf.c confmisc.c input.c output.c async.c error.c dlmisc.c socket.c shmarea.c userfile.c names.c SUBDIRS=control libasound_la_LIBADD = control/libcontrol.la @@ -38,6 +38,10 @@ if BUILD_SEQ SUBDIRS += seq libasound_la_LIBADD += seq/libseq.la endif +if BUILD_UCM +SUBDIRS += ucm +libasound_la_LIBADD += ucm/libucm.la +endif if BUILD_ALISP SUBDIRS += alisp libasound_la_LIBADD += alisp/libalisp.la diff --git a/src/ucm/Makefile.am b/src/ucm/Makefile.am new file mode 100644 index 0000000..7435d90 --- /dev/null +++ b/src/ucm/Makefile.am @@ -0,0 +1,10 @@ +EXTRA_LTLIBRARIES = libucm.la + +libucm_la_SOURCES = utils.c parser.c main.c + +noinst_HEADERS = ucm_local.h + +all: libucm.la + + +INCLUDES=-I$(top_srcdir)/include diff --git a/src/ucm/main.c b/src/ucm/main.c new file mode 100644 index 0000000..ab9cc81 --- /dev/null +++ b/src/ucm/main.c @@ -0,0 +1,1924 @@ +/* + * 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 <lr...@sl...> + * Stefan Schmidt <st...@sl...> + * Justin Xu <ju...@sl...> + * Jaroslav Kysela <pe...@pe...> + */ + +#include "ucm_local.h" + +#if 0 +static int set_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, + snd_use_case_mgr_t *uc_mgr, unsigned short value[]); + +static inline void set_value(struct control_settings *control, + int count, unsigned short val) +{ + uc_dbg("value %d, count %d", val, count); + control->value[count] = val; +} + +static inline unsigned short get_value(struct control_settings *control, + int count) +{ + return control->value[count]; +} + +static inline void set_device_status(struct snd_use_case_mgr *uc_mgr, + int device_id, int status) +{ + struct use_case_verb *verb; + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + uc_mgr->card.current_device[device_id] = status; +} + +static inline void set_modifier_status(struct snd_use_case_mgr *uc_mgr, + int modifier_id, int status) +{ + struct use_case_verb *verb; + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + uc_mgr->card.current_modifier[modifier_id] = status; +} + +static inline int get_device_status(struct snd_use_case_mgr *uc_mgr, + int device_id) +{ + struct use_case_verb *verb; + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + return uc_mgr->card.current_device[device_id]; +} + +static inline int get_modifier_status(struct snd_use_case_mgr *uc_mgr, + int modifier_id) +{ + struct use_case_verb *verb; + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + return uc_mgr->card.current_modifier[modifier_id]; +} + +static int dump_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id) +{ + int err, count, i; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + snd_ctl_elem_value_t *control; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_info_set_id(info, id); + err = snd_ctl_elem_info(handle, info); + if (err < 0) { + uc_error("error: failed to get ctl info: %s\n", + snd_strerror(err)); + return err; + } + + snd_ctl_elem_value_set_id(control, id); + snd_ctl_elem_read(handle, control); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + if (count == 0) + return 0; + + uc_mgr_stdout("'%s':%d:", + snd_ctl_elem_id_get_name(id), count); + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + for (i = 0; i < count - 1; i++) + uc_mgr_stdout("%d,", + snd_ctl_elem_value_get_boolean(control, i)); + uc_mgr_stdout("%d", + snd_ctl_elem_value_get_boolean(control, i)); + break; + case SND_CTL_ELEM_TYPE_INTEGER: + for (i = 0; i < count - 1; i++) + uc_mgr_stdout("%d,", + snd_ctl_elem_value_get_integer(control, i)); + uc_mgr_stdout("%d", + snd_ctl_elem_value_get_integer(control, i)); + break; + case SND_CTL_ELEM_TYPE_INTEGER64: + for (i = 0; i < count - 1; i++) + uc_mgr_stdout("%ld,", + snd_ctl_elem_value_get_integer64(control, i)); + uc_mgr_stdout("%ld", + snd_ctl_elem_value_get_integer64(control, i)); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + for (i = 0; i < count - 1; i++) + uc_mgr_stdout("%d,", + snd_ctl_elem_value_get_enumerated(control, i)); + uc_mgr_stdout("%d", + snd_ctl_elem_value_get_enumerated(control, i)); + break; + case SND_CTL_ELEM_TYPE_BYTES: + for (i = 0; i < count - 1; i++) + uc_mgr_stdout("%2.2x,", + snd_ctl_elem_value_get_byte(control, i)); + uc_mgr_stdout("%2.2x", + snd_ctl_elem_value_get_byte(control, i)); + break; + default: + break; + } + uc_mgr_stdout("\n"); + return 0; +} + +/* + * Add new kcontrol from sound card into memory database. + */ +static int add_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, + struct control_settings *control_settings) +{ + int err; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *control; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_info_set_id(info, id); + err = snd_ctl_elem_info(handle, info); + if (err < 0) { + uc_error("error: failed to get ctl info: %s\n", + snd_strerror(err)); + return err; + } + + snd_ctl_elem_value_set_id(control, id); + snd_ctl_elem_read(handle, control); + + strncpy(control_settings->name, snd_ctl_elem_id_get_name(id), + MAX_NAME); + + control_settings->name[MAX_NAME - 1] = 0; + control_settings->count = snd_ctl_elem_info_get_count(info); + control_settings->type = snd_ctl_elem_info_get_type(info); + control_settings->id = snd_ctl_elem_id_get_numid(id); + uc_dbg("control name %s", control_settings->name); + uc_dbg("control count %d", control_settings->count); + uc_dbg("control type %d", control_settings->type); + uc_dbg("control id %d", control_settings->id); + return 0; +} + +static int set_control_default(snd_use_case_mgr_t *uc_mgr, + struct control_settings *control) +{ + snd_ctl_elem_id_t *id; + int i, ret = -ENODEV; + unsigned int numid; + + snd_ctl_elem_id_alloca(&id); + + /* Where is id lookup from numid if you need it? */ + for (i = 0; i < uc_mgr->count; ++i) { + snd_ctl_elem_list_get_id(uc_mgr->list, i, id); + numid = snd_ctl_elem_id_get_numid(id); + if (numid == control->id) { + ret = set_control(uc_mgr->handle, id, uc_mgr, + control->value); + goto out; + } + } + uc_error("error: could not find control ID %s : %d", + control->name, control->id); +out: + return ret; +} + +static int get_control_id(snd_use_case_mgr_t *uc_mgr, + struct control_settings *control) +{ + int i = 0; + struct control_settings *card_control; + + uc_dbg("name %s count %d", + control->name, control->count); + + for (i = 0; i < uc_mgr->count; i++) { + card_control = &uc_mgr->control[i]; + if (!strcmp(card_control->name, control->name)) { + control->id = uc_mgr->control[i].id; + uc_dbg("Get id %d", control->id); + return 0; + } + } + + uc_error("error: control name %s is not available", control->name); + + return -EINVAL; +} + +static void free_sequence(struct sequence_element *sequence) +{ + struct sequence_element *element = sequence; + + while (element) { + struct sequence_element *pre_element; + + if (element->control) { + free(element->control->value); + free(element->control); + } + pre_element = element; + element = element->next; + free(pre_element); + } +} + +static void free_transition_sequence_element(struct transition_sequence *trans) +{ + free(trans->name); + free_sequence(trans->transition); + free(trans); +} + +static void free_transition_sequence(struct transition_sequence *transition_list) +{ + struct transition_sequence *last, *trans_sequence = transition_list; + + while (trans_sequence) { + last = trans_sequence; + trans_sequence = trans_sequence->next; + free_transition_sequence_element(last); + } +} + +static void free_modifier(struct use_case_modifier *modifier) +{ + if (!modifier) + return; + + free(modifier->name); + free(modifier->comment); + free(modifier->playback_volume_id); + free(modifier->playback_switch_id); + free(modifier->capture_volume_id); + free(modifier->capture_switch_id); + + free_sequence(modifier->enable); + free_sequence(modifier->disable); + free_transition_sequence(modifier->transition_list); +} + +static void free_device(struct use_case_device *device) +{ + if (!device) + return; + + free(device->name); + free(device->comment); + free(device->playback_volume_id); + free(device->playback_switch_id); + free(device->capture_volume_id); + free(device->capture_switch_id); + + free_sequence(device->enable); + free_sequence(device->disable); + free_transition_sequence(device->transition_list); +} + +static void free_devices(struct use_case_device *devices, int num_devices) +{ + int i; + + if (!devices) + return; + + for (i = 0; i< num_devices; i++) + free_device(devices + i); + + free(devices); +} + +static void free_modifiers(struct use_case_modifier *modifiers, + int num_modifiers) +{ + int i; + + if (!modifiers) + return; + + for (i = 0; i < num_modifiers; i++) + free_modifier(modifiers + i); + + free(modifiers); +} + +static void free_verb(struct use_case_verb *verb) +{ + if (!verb) + return; + + free(verb->name); + free(verb->comment); + + free_sequence(verb->enable); + free_sequence(verb->disable); + free_transition_sequence(verb->transition_list); + + free_devices(verb->device, verb->num_devices); + free_modifiers(verb->modifier, verb->num_modifiers); +} + +static void free_verbs(struct use_case_verb *verbs, int num_verbs) +{ + int i; + + if (!verbs) + return; + + for (i = 0; i < num_verbs; i++) + free_verb(verbs + i); + + free(verbs); +} + +static void free_controls(struct control_settings *controls, int num_controls) +{ + int i = 0; + + if (!controls) + return; + + for(i = 0; i < num_controls; i++){ + struct control_settings *control = controls + i; + free(control->value); + } + free(controls); +} + +/* + * Free all use case manager resources. + * Callers holds locks. + */ +static void free_uc_mgr(snd_use_case_mgr_t *uc_mgr) +{ + if (uc_mgr == NULL) + return; + + if (uc_mgr->info) + snd_ctl_card_info_free(uc_mgr->info); + if (uc_mgr->list) + snd_ctl_elem_list_free(uc_mgr->list); + if (uc_mgr->id) + snd_ctl_elem_id_free(uc_mgr->id); + + if (uc_mgr->handle) + snd_ctl_close(uc_mgr->handle); + + free(uc_mgr->card_name); + free(uc_mgr->ctl_name); + + free_verbs(uc_mgr->verb, uc_mgr->num_verbs); + + free_controls(uc_mgr->control, uc_mgr->count); + + pthread_mutex_destroy(&uc_mgr->mutex); + + free(uc_mgr); +} + + /** + * \brief Init sound card use case manager. + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +snd_use_case_mgr_t *snd_use_case_mgr_open(const char *card_name) +{ + snd_use_case_mgr_t *uc_mgr; + char ctl_name[8]; + int err, idx; + + idx = snd_card_get_index(card_name); + if (idx < 0) { + uc_error("error: can't get sound card %s: %s", + card_name, snd_strerror(idx)); + return NULL; + } + + /* create a new UCM */ + uc_mgr = calloc(1, sizeof(snd_use_case_mgr_t)); + if (uc_mgr == NULL) + return NULL; + + uc_mgr->card_name = strdup(card_name); + if (uc_mgr->card_name == NULL) { + free(uc_mgr); + return NULL; + } + sprintf(ctl_name, "hw:%d", idx); + uc_mgr->ctl_name = strdup(ctl_name); + if (uc_mgr->ctl_name == NULL) { + free_uc_mgr(uc_mgr); + return NULL; + } + if (snd_ctl_card_info_malloc(&uc_mgr->info) < 0) { + free_uc_mgr(uc_mgr); + return NULL; + } + if (snd_ctl_elem_list_malloc(&uc_mgr->list) < 0) { + free_uc_mgr(uc_mgr); + return NULL; + } + if (snd_ctl_elem_id_malloc(&uc_mgr->id) < 0) { + free_uc_mgr(uc_mgr); + return NULL; + } + + /* open and init CTLs */ + err = snd_ctl_open(&uc_mgr->handle, uc_mgr->ctl_name, 0); + if (err) { + uc_error("error: can't open sound card %s: %s", + uc_mgr->card_name, snd_strerror(err)); + goto err; + } + + err = snd_ctl_card_info(uc_mgr->handle, uc_mgr->info); + if (err < 0) { + uc_error("error: can't get sound card %s control info: %s", + uc_mgr->card_name, snd_strerror(err)); + goto err; + } + + err = snd_ctl_elem_list(uc_mgr->handle, uc_mgr->list); + if (err < 0) { + uc_error("error: can't get sound card %s elements: %s", + uc_mgr->card_name, snd_strerror(err)); + goto err; + } + + uc_mgr->count = snd_ctl_elem_list_get_count(uc_mgr->list); + if (uc_mgr->count < 0) { + uc_error("error: can't get sound card %s controls %s:", + uc_mgr->card_name, snd_strerror(err)); + goto err; + } + + snd_ctl_elem_list_set_offset(uc_mgr->list, 0); + if (snd_ctl_elem_list_alloc_space(uc_mgr->list, uc_mgr->count) < 0) { + uc_error("error: could not allocate elements for %s", + uc_mgr->card_name); + goto err; + } + + err = snd_ctl_elem_list(uc_mgr->handle, uc_mgr->list); + if (err < 0) { + uc_error("error: could not get elements for %s : %s", + uc_mgr->card_name, snd_strerror(err)); + goto err; + } + + /* get info about sound card */ + err = parse_card_controls(uc_mgr); + if (err < 0) { + uc_error("error: failed to parse sound device %s controls %d", + card_name, err); + goto err; + } + + /* get info on use_cases and verify against card */ + err = import_use_case_files(uc_mgr); + if (err < 0) { + uc_error("error: failed to import %s use case configuration %d", + card_name, err); + goto err; + } + + pthread_mutex_init(&uc_mgr->mutex, NULL); + uc_mgr->card.current_verb = VERB_NOT_INITIALISED; + + return uc_mgr; + +err: + free_uc_mgr(uc_mgr); + return NULL; +} + + /** + * \brief Reload and reparse all use case files. + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr) +{ + int ret; + + pthread_mutex_lock(&uc_mgr->mutex); + + free_verbs(uc_mgr->verb, uc_mgr->num_verbs); + free_controls(uc_mgr->control, uc_mgr->count); + + /* reload all sound card controls */ + ret = parse_card_controls(uc_mgr); + if (ret <= 0) { + uc_error("error: failed to reload sound card controls %d\n", + ret); + free_uc_mgr(uc_mgr); + return -EINVAL; + } + + /* reload all use cases */ + uc_mgr->num_verbs = import_use_case_files(uc_mgr); + if (uc_mgr->num_verbs <= 0) { + uc_error("error: failed to reload use cases\n"); + return -EINVAL; + } + + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + + /** + * \brief Close use case manager. + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr) +{ + free_uc_mgr(uc_mgr); + + return 0; +} + + /** + * \brief Reset sound card controls to default values. + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr) +{ + int ret = 0, i; + struct control_settings *control; + + pthread_mutex_lock(&uc_mgr->mutex); + + for (i = 0; i < uc_mgr->count; i++) { + control = &uc_mgr->control[i]; + + /* Only set default value specified in master config file */ + if (control->value == NULL) + continue; + + ret = set_control_default(uc_mgr, control); + if (ret < 0) + goto out; + } + uc_mgr->card.current_verb = VERB_NOT_INITIALISED; +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + + +/* + * Change a sound card control to a new value. + */ +static int set_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, + snd_use_case_mgr_t *uc_mgr, unsigned short value[]) +{ + struct control_settings *setting; + int ret, count, i; + unsigned int idnum; + snd_ctl_elem_info_t *info; + snd_ctl_elem_type_t type; + snd_ctl_elem_value_t *control; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + + snd_ctl_elem_info_set_id(info, id); + ret = snd_ctl_elem_info(handle, info); + if (ret < 0) { + uc_error("error: failed to get ctl elem info %d: ", + snd_strerror(ret)); + return ret; + } + + snd_ctl_elem_value_set_id(control, id); + snd_ctl_elem_read(handle, control); + + idnum = snd_ctl_elem_id_get_numid(id); + for (i = 0; i < uc_mgr->count; i++) { + setting = &uc_mgr->control[i]; + if (setting->id == idnum) + goto set_val; + } + uc_error("error: failed to find control at id %d", idnum); + return 0; + +set_val: + uc_dbg("set control %s id %d count %d", setting->name, setting->id, + setting->count); + + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + if (count == 0) + return 0; + + uc_dbg("type %d count %d", type, count); + + switch (type) { + case SND_CTL_ELEM_TYPE_BOOLEAN: + for (i = 0; i < count; i++) { + uc_dbg("count %d value %u", i, *(value + i)); + snd_ctl_elem_value_set_boolean(control, i, value[i]); + } + break; + case SND_CTL_ELEM_TYPE_INTEGER: + uc_dbg("int"); + for (i = 0; i < count; i++) { + uc_dbg("count %d value %u", i, value[i]); + snd_ctl_elem_value_set_integer(control, i, value[i]); + } + break; + case SND_CTL_ELEM_TYPE_INTEGER64: + uc_dbg("int64"); + for (i = 0; i < count; i++) { + uc_dbg("count %d value %u", i, value[i]); + snd_ctl_elem_value_set_integer64(control, i, value[i]); + } + + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + uc_dbg("enumerated"); + for (i = 0; i < count; i++) { + uc_dbg("count %d value %u", i, value[i]); + snd_ctl_elem_value_set_enumerated(control, i, value[i]); + } + break; + case SND_CTL_ELEM_TYPE_BYTES: + uc_dbg("bytes"); + for (i = 0; i < count; i++) { + uc_dbg("count %d value %u", i, value[i]); + snd_ctl_elem_value_set_byte(control, i, value[i]); + } + break; + default: + break; + } + + ret = snd_ctl_elem_write(handle, control); + if (ret < 0) { + uc_error("error: failed to set control %s: %s", + setting->name, snd_strerror(ret)); + uc_error("error: count %d type: %d", + count, type); + for (i = 0; i < count; i++) + fprintf(stderr, "%d ", get_value(setting, i)); + return ret; + } + return 0; +} + +/* + * Execute a sequence of control writes. + */ +static int exec_sequence(struct sequence_element *seq, snd_use_case_mgr_t + *uc_mgr, snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + int count = snd_ctl_elem_list_get_count(list); + int ret, i; + + uc_dbg(""); + + /* keep going until end of sequence */ + while (seq) { + /* do we need to sleep */ + if (seq->sleep) { + uc_dbg("msleep %d", seq->sleep); + usleep(seq->sleep); + } else { + uc_dbg("control name %s, id %d, count %d, vale[1] %u", + seq->control->name, seq->control->id, + seq->control->count, seq->control->value[0]); + + /* control write */ + snd_ctl_elem_id_t *id; + snd_ctl_elem_id_alloca(&id); + unsigned int numid; + + /* Where is id lookup from numid if you need it? */ + for (i = 0; i < count; ++i) { + + snd_ctl_elem_list_get_id(list, i, id); + numid = snd_ctl_elem_id_get_numid(id); + + if (numid == seq->control->id) { + ret = set_control(handle, id, uc_mgr, seq->control->value); + if (ret < 0) { + uc_error("error: failed to set control %s", + __func__, uc_mgr->card_name); + return ret; + } + break; + } + } + } + seq = seq->next; + } + return 0; +} + +static int enable_use_case_verb(snd_use_case_mgr_t *uc_mgr, int verb_id, + snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb; + int ret; + + if (verb_id >= uc_mgr->num_verbs) { + uc_error("error: invalid verb id %d", verb_id); + return -EINVAL; + } + verb = &uc_mgr->verb[verb_id]; + + uc_dbg("verb %s", verb->name); + ret = exec_sequence(verb->enable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not enable verb %s", verb->name); + return ret; + } + uc_mgr->card.current_verb = verb_id; + + return 0; +} + +static int disable_use_case_verb(snd_use_case_mgr_t *uc_mgr, int verb_id, + snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb; + int ret; + + if (verb_id >= uc_mgr->num_verbs) { + uc_error("error: invalid verb id %d", verb_id); + return -EINVAL; + } + verb = &uc_mgr->verb[verb_id]; + + /* we set the invalid verb at open() but we should still + * check that this succeeded */ + if (verb == NULL) + return 0; + + uc_dbg("verb %s", verb->name); + ret = exec_sequence(verb->disable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not disable verb %s", verb->name); + return ret; + } + + return 0; +} + +static int enable_use_case_device(snd_use_case_mgr_t *uc_mgr, + int device_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + struct use_case_device *device = &verb->device[device_id]; + int ret; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return -EINVAL; + + uc_dbg("device %s", device->name); + ret = exec_sequence(device->enable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not enable device %s", device->name); + return ret; + } + + set_device_status(uc_mgr, device_id, 1); + return 0; +} + +static int disable_use_case_device(snd_use_case_mgr_t *uc_mgr, + int device_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + struct use_case_device *device = &verb->device[device_id]; + int ret; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return -EINVAL; + + uc_dbg("device %s", device->name); + ret = exec_sequence(device->disable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not disable device %s", device->name); + return ret; + } + + set_device_status(uc_mgr, device_id, 0); + return 0; +} + +static int enable_use_case_modifier(snd_use_case_mgr_t *uc_mgr, + int modifier_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + struct use_case_modifier *modifier = &verb->modifier[modifier_id]; + int ret; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return -EINVAL; + + uc_dbg("modifier %s", modifier->name); + ret = exec_sequence(modifier->enable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not enable modifier %s", modifier->name); + return ret; + } + + set_modifier_status(uc_mgr, modifier_id, 1); + return 0; +} + +static int disable_use_case_modifier(snd_use_case_mgr_t *uc_mgr, + int modifier_id, snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + struct use_case_modifier *modifier = &verb->modifier[modifier_id]; + int ret; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return -EINVAL; + + uc_dbg("modifier %s", modifier->name); + ret = exec_sequence(modifier->disable, uc_mgr, list, handle); + if (ret < 0) { + uc_error("error: could not disable modifier %s", modifier->name); + return ret; + } + + set_modifier_status(uc_mgr, modifier_id, 0); + return 0; +} + +/* + * Tear down current use case verb, device and modifier. + */ +static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr, + snd_ctl_elem_list_t *list, snd_ctl_t *handle) +{ + struct use_case_verb *verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + int ret, i; + + /* No active verb */ + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return 0; + + /* disable all modifiers that are active */ + for (i = 0; i < verb->num_modifiers; i++) { + if (get_modifier_status(uc_mgr,i)) { + ret = disable_use_case_modifier(uc_mgr, i, list, handle); + if (ret < 0) + return ret; + } + } + + /* disable all devices that are active */ + for (i = 0; i < verb->num_devices; i++) { + if (get_device_status(uc_mgr,i)) { + ret = disable_use_case_device(uc_mgr, i, list, handle); + if (ret < 0) + return ret; + } + } + + /* disable verb */ + ret = disable_use_case_verb(uc_mgr, uc_mgr->card.current_verb, list, handle); + if (ret < 0) + return ret; + + return 0; +} + + /** + * \brief Dump sound card controls in format required for sequencer. + * \param card_name The name of the sound card to be dumped + * \return zero on success, otherwise a negative error code + */ +int snd_use_case_dump(const char *card_name) +{ + snd_ctl_t *handle; + snd_ctl_card_info_t *info; + snd_ctl_elem_list_t *list; + int ret, i, count, idx; + char ctl_name[8]; + + snd_ctl_card_info_alloca(&info); + snd_ctl_elem_list_alloca(&list); + + idx = snd_card_get_index(card_name); + if (idx < 0) + return idx; + sprintf(ctl_name, "hw:%d", idx); + + /* open and load snd card */ + ret = snd_ctl_open(&handle, ctl_name, SND_CTL_READONLY); + if (ret < 0) { + uc_error("error: could not open controls for %s: %s", + card_name, snd_strerror(ret)); + return ret; + } + + ret = snd_ctl_card_info(handle, info); + if (ret < 0) { + uc_error("error: could not get control info for %s:%s", + card_name, snd_strerror(ret)); + goto close; + } + + ret = snd_ctl_elem_list(handle, list); + if (ret < 0) { + uc_error("error: cannot determine controls for %s: %s", + card_name, snd_strerror(ret)); + goto close; + } + + count = snd_ctl_elem_list_get_count(list); + if (count < 0) { + ret = 0; + goto close; + } + + snd_ctl_elem_list_set_offset(list, 0); + if (snd_ctl_elem_list_alloc_space(list, count) < 0) { + uc_error("error: not enough memory for control elements"); + ret = -ENOMEM; + goto close; + } + if ((ret = snd_ctl_elem_list(handle, list)) < 0) { + uc_error("error: cannot determine controls: %s", + snd_strerror(ret)); + goto free; + } + + /* iterate through each kcontrol and add to use + * case manager control list */ + for (i = 0; i < count; ++i) { + snd_ctl_elem_id_t *id; + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_list_get_id(list, i, id); + + /* dump to stdout in friendly format */ + ret = dump_control(handle, id); + if (ret < 0) { + uc_error("error: control dump failed: %s", + snd_strerror(ret)); + goto free; + } + } +free: + snd_ctl_elem_list_free_space(list); +close: + snd_ctl_close(handle); + return ret; +} + +/** + * \brief List supported use case verbs for given soundcard + * \param uc_mgr use case manager + * \param verb returned list of supported use case verb id and names + * \return number of use case verbs if success, otherwise a negative error code + */ +int snd_use_case_get_verb_list(snd_use_case_mgr_t *uc_mgr, + const char **verb[]) +{ + int ret; + + pthread_mutex_lock(&uc_mgr->mutex); + + *verb = uc_mgr->verb_list; + ret = uc_mgr->num_verbs; + + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +/** + * \brief List supported use case devices for given verb + * \param uc_mgr use case manager + * \param verb verb id. + * \param device returned list of supported use case device id and names + * \return number of use case devices if success, otherwise a negative error code + */ +int snd_use_case_get_device_list(snd_use_case_mgr_t *uc_mgr, + const char *verb_name, const char **device[]) +{ + struct use_case_verb *verb = NULL; + int i, ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + /* find verb name */ + for (i = 0; i < uc_mgr->num_verbs; i++) { + verb = &uc_mgr->verb[i]; + if (!strcmp(uc_mgr->verb[i].name, verb_name)) + goto found; + } + + uc_error("error: use case verb %s not found", verb_name); + goto out; + +found: + *device = verb->device_list; + ret = verb->num_devices; +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +/** + * \brief List supported use case verb modifiers for given verb + * \param uc_mgr use case manager + * \param verb verb id. + * \param mod returned list of supported use case modifier id and names + * \return number of use case modifiers if success, otherwise a negative error code + */ +int snd_use_case_get_mod_list(snd_use_case_mgr_t *uc_mgr, + const char *verb_name, const char **mod[]) +{ + struct use_case_verb *verb = NULL; + int i, ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + /* find verb name */ + for (i = 0; i <uc_mgr->num_verbs; i++) { + verb = &uc_mgr->verb[i]; + if (!strcmp(uc_mgr->verb[i].name, verb_name)) + goto found; + } + + uc_error("error: use case verb %s not found", verb_name); + goto out; + +found: + *mod = verb->modifier_list; + ret = verb->num_modifiers; +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +static struct sequence_element *get_transition_sequence( + struct transition_sequence *trans_list, const char *name) +{ + struct transition_sequence *trans = trans_list; + + while (trans) { + if (trans->name && !strcmp(trans->name, name)) + return trans->transition; + + trans = trans->next; + } + + return NULL; +} + +static int exec_transition_sequence(snd_use_case_mgr_t *uc_mgr, + struct sequence_element *trans_sequence) +{ + int ret; + + ret = exec_sequence(trans_sequence, uc_mgr, uc_mgr->list, + uc_mgr->handle); + if (ret < 0) + uc_error("error: could not exec transition sequence"); + + return ret; +} + +static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr, + int new_verb_id) +{ + struct use_case_verb *old_verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + struct use_case_verb *new_verb; + static struct sequence_element *trans_sequence; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return -EINVAL; + + if (new_verb_id >= uc_mgr->num_verbs) { + uc_error("error: invalid new_verb id %d", new_verb_id); + return -EINVAL; + } + + new_verb = &uc_mgr->verb[new_verb_id]; + + uc_dbg("new verb %s", new_verb->name); + + trans_sequence = get_transition_sequence(old_verb->transition_list, + new_verb->name); + if (trans_sequence != NULL) { + int ret, i; + + uc_dbg("find transition sequence %s->%s", + old_verb->name, new_verb->name); + + /* disable all modifiers that are active */ + for (i = 0; i < old_verb->num_modifiers; i++) { + if (get_modifier_status(uc_mgr,i)) { + ret = disable_use_case_modifier(uc_mgr, i, + uc_mgr->list, uc_mgr->handle); + if (ret < 0) + return ret; + } + } + + /* disable all devices that are active */ + for (i = 0; i < old_verb->num_devices; i++) { + if (get_device_status(uc_mgr,i)) { + ret = disable_use_case_device(uc_mgr, i, + uc_mgr->list, uc_mgr->handle); + if (ret < 0) + return ret; + } + } + + ret = exec_transition_sequence(uc_mgr, trans_sequence); + if (ret) + return ret; + + uc_mgr->card.current_verb = new_verb_id; + + return 0; + } + + return-EINVAL; +} + +/** + * \brief Set new use case verb for sound card + * \param uc_mgr use case manager + * \param verb verb id + * \return zero if success, otherwise a negative error code + */ +int snd_use_case_set_verb(snd_use_case_mgr_t *uc_mgr, + const char *verb_name) +{ + int i = 0, ret = -EINVAL, inactive = 0; + + pthread_mutex_lock(&uc_mgr->mutex); + + uc_dbg("uc_mgr %p, verb_name %s", uc_mgr, verb_name); + + /* check for "Inactive" */ + if (!strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE)) { + inactive = 1; + goto found; + } + + /* find verb name */ + for (i = 0; i <uc_mgr->num_verbs; i++) { + if (!strcmp(uc_mgr->verb[i].name, verb_name)) + goto found; + } + + uc_error("error: use case verb %s not found", verb_name); + goto out; +found: + /* use case verb found - check that we actually changing the verb */ + if (i == uc_mgr->card.current_verb) { + uc_dbg("current verb ID %d", i); + ret = 0; + goto out; + } + + if (handle_transition_verb(uc_mgr, i) == 0) + goto out; + + /* + * Dismantle the old use cases by running it's verb, device and modifier + * disable sequences + */ + ret = dismantle_use_case(uc_mgr, uc_mgr->list, uc_mgr->handle); + if (ret < 0) { + uc_error("error: failed to dismantle current use case: %s", + uc_mgr->verb[i].name); + goto out; + } + + /* we don't need to initialise new verb if inactive */ + if (inactive) + goto out; + + /* Initialise the new use case verb */ + ret = enable_use_case_verb(uc_mgr, i, uc_mgr->list, uc_mgr->handle); + if (ret < 0) + uc_error("error: failed to initialise new use case: %s", + verb_name); +out: + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +static int config_use_case_device(snd_use_case_mgr_t *uc_mgr, + const char *device_name, int enable) +{ + struct use_case_verb *verb; + int ret, i; + + pthread_mutex_lock(&uc_mgr->mutex); + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) { + uc_error("error: no valid use case verb set\n"); + ret = -EINVAL; + goto out; + } + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + + uc_dbg("current verb %s", verb->name); + uc_dbg("uc_mgr %p device_name %s", uc_mgr, device_name); + + /* find device name and index */ + for (i = 0; i <verb->num_devices; i++) { + uc_dbg("verb->num_devices %s", verb->device[i].name); + if (!strcmp(verb->device[i].name, device_name)) + goto found; + } + + uc_error("error: use case device %s not found", device_name); + ret = -EINVAL; + goto out; + +found: + if (enable) { + /* Initialise the new use case device */ + ret = enable_use_case_device(uc_mgr, i, uc_mgr->list, + uc_mgr->handle); + if (ret < 0) + goto out; + } else { + /* disable the old device */ + ret = disable_use_case_device(uc_mgr, i, uc_mgr->list, + uc_mgr->handle); + if (ret < 0) + goto out; + } + +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +/** + * \brief Enable use case device + * \param uc_mgr Use case manager + * \param device the device to be enabled + * \return 0 = successful negative = error + */ +int snd_use_case_enable_device(snd_use_case_mgr_t *uc_mgr, + const char *device) +{ + return config_use_case_device(uc_mgr, device, 1); +} + +/** + * \brief Disable use case device + * \param uc_mgr Use case manager + * \param device the device to be disabled + * \return 0 = successful negative = error + */ +int snd_use_case_disable_device(snd_use_case_mgr_t *uc_mgr, + const char *device) +{ + return config_use_case_device(uc_mgr, device, 0); +} + +static struct use_case_device *get_device(snd_use_case_mgr_t *uc_mgr, + const char *name, int *id) +{ + struct use_case_verb *verb; + int i; + + if (uc_mgr->card.current_verb == VERB_NOT_INITIALISED) + return NULL; + + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + + uc_dbg("current verb %s", verb->name); + + for (i = 0; i < verb->num_devices; i++) { + uc_dbg("device %s", verb->device[i].name); + + if (!strcmp(verb->device[i].name, name)) { + if (id) + *id = i; + return &verb->device[i]; + } + } + + return NULL; +} + +/** + * \brief Disable old_device and then enable new_device. + * If from_device is not enabled just return. + * Check transition sequence firstly. + * \param uc_mgr Use case manager + * \param old the device to be closed + * \param new the device to be opened ------- End of patch hooks/update --- Git Source Code Management System hooks/update refs/heads/ucm \ 7adef4f92688b17105a30e4df586b41c9695dc19 \ 84406371eb64763f6bd1778125519312eedb1021 |