diff options
author | Jaroslav Kysela <perex@perex.cz> | 2010-09-07 15:35:14 +0200 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2010-09-15 08:09:31 +0200 |
commit | 1c79fad9692fb04cb8cbc7be8e2ec88595e62e46 (patch) | |
tree | 65e5d943e5d4f4d9c1e32b2d52bcca57116994b2 | |
parent | fcc9adb26058667656277ba92032ba7e0b00261c (diff) | |
download | alsa-lib-1c79fad9692fb04cb8cbc7be8e2ec88595e62e46.tar.gz |
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 <perex@perex.cz>
-rw-r--r-- | configure.in | 6 | ||||
-rw-r--r-- | doc/doxygen.cfg.in | 6 | ||||
-rw-r--r-- | include/list.h | 8 | ||||
-rw-r--r-- | include/use-case.h | 22 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/ucm/Makefile.am | 10 | ||||
-rw-r--r-- | src/ucm/main.c | 1448 | ||||
-rw-r--r-- | src/ucm/parser.c | 956 | ||||
-rw-r--r-- | src/ucm/ucm_local.h | 217 | ||||
-rw-r--r-- | src/ucm/utils.c | 220 | ||||
-rw-r--r-- | src/use-case.c | 3515 |
11 files changed, 2888 insertions, 3526 deletions
diff --git a/configure.in b/configure.in index abc4687e..0a65cc15 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 39297722..f4499d61 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/list.h b/include/list.h index 5b3f1bf0..4d9895fe 100644 --- a/include/list.h +++ b/include/list.h @@ -162,5 +162,13 @@ static __inline__ void list_splice(struct list_head *list, struct list_head *hea #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @offset: offset of entry inside a struct + */ +#define list_entry_offset(ptr, type, offset) \ + ((type *)((char *)(ptr)-(offset))) #endif /* _LIST_H */ diff --git a/include/use-case.h b/include/use-case.h index 33d480f6..6b9b6455 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 uc_mgr Use case manager (may be NULL - card list) + * \param identifier (may be NULL - card 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); @@ -275,10 +282,11 @@ int snd_use_case_set(snd_use_case_mgr_t *uc_mgr, /** * \brief Open and initialise use case core for sound card + * \param uc_mgr Returned use case manager pointer * \param card_name Sound card name. - * \return Use case handle if success, otherwise NULL + * \return zero if success, otherwise a negative error code */ -snd_use_case_mgr_t *snd_use_case_mgr_open(const char *card_name); +int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name); /** diff --git a/src/Makefile.am b/src/Makefile.am index 7206dbdb..9a00dca3 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 00000000..7435d903 --- /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 00000000..a8df44c2 --- /dev/null +++ b/src/ucm/main.c @@ -0,0 +1,1448 @@ +/* + * 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 <lrg@slimlogic.co.uk> + * Stefan Schmidt <stefan@slimlogic.co.uk> + * Justin Xu <justinx@slimlogic.co.uk> + * Jaroslav Kysela <perex@perex.cz> + */ + +#include "ucm_local.h" +#include <pthread.h> + +/** + * \brief Execute the sequence + * \param uc_mgr Use case manager + * \param seq Sequence + * \return zero on success, otherwise a negative error code + */ +static int execute_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct list_head *seq) +{ + struct list_head *pos; + struct sequence_element *s; + + list_for_each(pos, seq) { + s = list_entry(pos, struct sequence_element, list); + switch (s->type) { + case SEQUENCE_ELEMENT_TYPE_CSET: + uc_error("cset not yet implemented: '%s'", s->data.cset); + break; + case SEQUENCE_ELEMENT_TYPE_SLEEP: + usleep(s->data.sleep); + break; + case SEQUENCE_ELEMENT_TYPE_EXEC: + uc_error("exec not yet implemented: '%s'", s->data.exec); + break; + default: + uc_error("unknown sequence command %i", s->type); + break; + } + } + return 0; +} + +/** + * \brief Import master config and execute the default sequence + * \param uc_mgr Use case manager + * \return zero on success, otherwise a negative error code + */ +static int import_master_config(snd_use_case_mgr_t *uc_mgr) +{ + int err; + + err = uc_mgr_import_master_config(uc_mgr); + if (err < 0) + return err; + err = execute_sequence(uc_mgr, &uc_mgr->default_list); + if (err < 0) + uc_error("Unable to execute default sequence"); + return err; +} + +/** + * \brief Universal find - string in a list + * \param uc_mgr Use case manager + * \param list List of structures + * \param offset Offset of list structure + * \param soffset Offset of string structure + * \param match String to match + * \return structure on success, otherwise a NULL (not found) + */ +static void *find0(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct list_head *list, + unsigned long offset, + unsigned long soffset, + const char *match) +{ + struct list_head *pos; + char *ptr, *str; + + list_for_each(pos, list) { + ptr = list_entry_offset(pos, char, offset); + str = *((char **)(ptr + soffset)); + if (strcmp(str, match) == 0) + return ptr; + } + return NULL; +} + +#define find(uc_mgr, list, type, member, value, match) \ + find0(uc_mgr, list, \ + (unsigned long)(&((type *)0)->member), \ + (unsigned long)(&((type *)0)->value), match) + +/** + * \brief Find verb + * \param uc_mgr Use case manager + * \param verb_name verb to find + * \return structure on success, otherwise a NULL (not found) + */ +static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr, + const char *_name) +{ + return find(uc_mgr, &uc_mgr->verb_list, + struct use_case_verb, list, name, + _name); +} + +/** + * \brief Set verb + * \param uc_mgr Use case manager + * \param verb verb to set + * \return zero on success, otherwise a negative error code + */ +static int set_verb(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, + int enable) +{ + struct list_head *seq; + int err; + + if (enable) { + seq = &verb->enable_list; + } else { + seq = &verb->disable_list; + } + err = execute_sequence(uc_mgr, seq); + if (enable && err >= 0) + uc_mgr->active_verb = verb; + return err; +} + +/** + * \brief Init sound card use case manager. + * \param uc_mgr Returned use case manager pointer + * \param card_name name of card to open + * \return zero on success, otherwise a negative error code + */ +int snd_use_case_mgr_open(snd_use_case_mgr_t **mgr, + const char *card_name) +{ + snd_use_case_mgr_t *uc_mgr; + int err; + + /* create a new UCM */ + uc_mgr = calloc(1, sizeof(snd_use_case_mgr_t)); + if (uc_mgr == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&uc_mgr->verb_list); + INIT_LIST_HEAD(&uc_mgr->default_list); + pthread_mutex_init(&uc_mgr->mutex, NULL); + + uc_mgr->card_name = strdup(card_name); + if (uc_mgr->card_name == NULL) { + free(uc_mgr); + return -ENOMEM; + } + + /* get info on use_cases and verify against card */ + err = import_master_config(uc_mgr); + if (err < 0) { + uc_error("error: failed to import %s use case configuration %d", + card_name, err); + goto err; + } + + *mgr = uc_mgr; + return 0; + +err: + uc_mgr_free(uc_mgr); + return err; +} + +/** + * \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 err; + + pthread_mutex_lock(&uc_mgr->mutex); + + uc_mgr_free_verb(uc_mgr); + + /* reload all use cases */ + err = import_master_config(uc_mgr); + if (err < 0) { + uc_error("error: failed to reload use cases\n"); + pthread_mutex_unlock(&uc_mgr->mutex); + return -EINVAL; + } + + pthread_mutex_unlock(&uc_mgr->mutex); + return err; +} + +/** + * \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) +{ + uc_mgr_free(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) +{ + struct list_head *pos, *npos; + struct use_case_modifier *modifier; + struct use_case_device *device; + int err; + + pthread_mutex_lock(&uc_mgr->mutex); + + list_for_each_safe(pos, npos, &uc_mgr->active_modifiers) { + modifier = list_entry(pos, struct use_case_modifier, + active_list); + err = disable_modifier(uc_mgr, modifier); + if (err < 0) + uc_error("Unable to disable modifier %s", modifier->name); + } + INIT_LIST_HEAD(&uc_mgr->active_modifiers); + + list_for_each_safe(pos, npos, &uc_mgr->active_devices) { + device = list_entry(pos, struct use_case_device, + active_list); + err = disable_device(uc_mgr, device); + if (err < 0) + uc_error("Unable to disable device %s", device->name); + } + INIT_LIST_HEAD(&uc_mgr->active_devices); + + err = disable_verb(uc_mgr, uc_mgr->active_verb); + if (err < 0) { + uc_error("Unable to disable verb %s", uc_mgr->active_verb->name); + return err; + } + uc_mgr->active_verb = NULL; + + err = execute_sequence(uc_mgr, &uc_mgr->default_list); + + pthread_mutex_unlock(&uc_mgr->mutex); + return err; +} + +#if 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 + * \return 0 = successful negative = error + */ +int snd_use_case_switch_device(snd_use_case_mgr_t *uc_mgr, + const char *old, const char *new) +{ + static struct sequence_element *trans_sequence; + struct use_case_device *old_device; + struct use_case_device *new_device; + int ret = 0, old_id, new_id; + + uc_dbg("old %s, new %s", old, new); + + pthread_mutex_lock(&uc_mgr->mutex); + + old_device = get_device(uc_mgr, old, &old_id); + if (!old_device) { + uc_error("error: device %s not found", old); + ret = -EINVAL; + goto out; + } + + if (!get_device_status(uc_mgr, old_id)) { + uc_error("error: device %s not enabled", old); + goto out; + } + + new_device = get_device(uc_mgr, new, &new_id); + if (!new_device) { + uc_error("error: device %s not found", new); + ret = -EINVAL; + goto out; + } + + trans_sequence = get_transition_sequence(old_device->transition_list, new); + if (trans_sequence != NULL) { + + uc_dbg("find transition sequece %s->%s", old, new); + + ret = exec_transition_sequence(uc_mgr, trans_sequence); + if (ret) + goto out; + + set_device_status(uc_mgr, old_id, 0); + set_device_status(uc_mgr, new_id, 1); + } else { + /* use lock in config_use_case_device */ + pthread_mutex_unlock(&uc_mgr->mutex); + + config_use_case_device(uc_mgr, old, 0); + config_use_case_device(uc_mgr, new, 1); + + return 0; + } +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +/* + * Check to make sure that the modifier actually supports any of the + * active devices. + */ +static int is_modifier_valid(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, struct use_case_modifier *modifier) +{ + struct dev_list *dev_list; + int dev; + + /* check modifier list against each enabled device */ + for (dev = 0; dev < verb->num_devices; dev++) { + if (!get_device_status(uc_mgr, dev)) + continue; + + dev_list = modifier->dev_list; + uc_dbg("checking device %s for %s", verb->device[dev].name, + dev_list->name ? dev_list->name : ""); + + while (dev_list) { + uc_dbg("device supports %s", dev_list->name); + if (!strcmp(dev_list->name, verb->device[dev].name)) + return 1; + dev_list = dev_list->next; + } + } + return 0; +} + +static int config_use_case_mod(snd_use_case_mgr_t *uc_mgr, + const char *modifier_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) { + 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 modifier_name %s", uc_mgr, modifier_name); + + /* find modifier name */ + for (i = 0; i <verb->num_modifiers; i++) { + uc_dbg("verb->num_modifiers %d %s", i, verb->modifier[i].name); + if (!strcmp(verb->modifier[i].name, modifier_name) && + is_modifier_valid(uc_mgr, verb, &verb->modifier[i])) + goto found; + } + + uc_error("error: use case modifier %s not found or invalid", + modifier_name); + ret = -EINVAL; + goto out; + +found: + if (enable) { + /* Initialise the new use case device */ + ret = enable_use_case_modifier(uc_mgr, i, uc_mgr->list, + uc_mgr->handle); + if (ret < 0) + goto out; + } else { + /* disable the old device */ + ret = disable_use_case_modifier(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 modifier + * \param uc_mgr Use case manager + * \param modifier the modifier to be enabled + * \return 0 = successful negative = error + */ +int snd_use_case_enable_modifier(snd_use_case_mgr_t *uc_mgr, + const char *modifier) +{ + return config_use_case_mod(uc_mgr, modifier, 1); +} + +/** + * \brief Disable use case modifier + * \param uc_mgr Use case manager + * \param modifier the modifier to be disabled + * \return 0 = successful negative = error + */ +int snd_use_case_disable_modifier(snd_use_case_mgr_t *uc_mgr, + const char *modifier) +{ + return config_use_case_mod(uc_mgr, modifier, 0); +} + +static struct use_case_modifier *get_modifier(snd_use_case_mgr_t *uc_mgr, + const char *name, int *mod_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); + + uc_dbg("uc_mgr %p modifier_name %s", uc_mgr, name); + + for (i = 0; i < verb->num_modifiers; i++) { + uc_dbg("verb->num_devices %s", verb->modifier[i].name); + + if (!strcmp(verb->modifier[i].name, name)) { + if (mod_id) + *mod_id = i; + return &verb->modifier[i]; + } + } + + return NULL; +} + +/** + * \brief Disable old_modifier and then enable new_modifier. + * If old_modifier is not enabled just return. + * Check transition sequence firstly. + * \param uc_mgr Use case manager + * \param old the modifier to be closed + * \param new the modifier to be opened + * \return 0 = successful negative = error + */ +int snd_use_case_switch_modifier(snd_use_case_mgr_t *uc_mgr, + const char *old, const char *new) +{ + struct use_case_modifier *old_modifier; + struct use_case_modifier *new_modifier; + static struct sequence_element *trans_sequence; + int ret = 0, old_id, new_id + + uc_dbg("old %s, new %s", old, new); + + pthread_mutex_lock(&uc_mgr->mutex); + + old_modifier = get_modifier(uc_mgr, old, &old_id); + if (!old_modifier) { + uc_error("error: modifier %s not found", old); + ret = -EINVAL; + goto out; + } + + if (!get_modifier_status(uc_mgr, old_id)) { + uc_error("error: modifier %s not enabled", old); + ret = -EINVAL; + goto out; + } + + new_modifier = get_modifier(uc_mgr, new, &new_id); + if (!new_modifier) { + uc_error("error: modifier %s not found", new); + ret = -EINVAL; + goto out; + } + + trans_sequence = get_transition_sequence( + old_modifier->transition_list, new); + if (trans_sequence != NULL) { + uc_dbg("find transition sequence %s->%s", old, new); + + ret = exec_transition_sequence(uc_mgr, trans_sequence); + if (ret) + goto out; + + set_device_status(uc_mgr, old_id, 0); + set_device_status(uc_mgr, new_id, 1); + } else { + /* use lock in config_use_case_mod*/ + pthread_mutex_unlock(&uc_mgr->mutex); + + config_use_case_mod(uc_mgr, old, 0); + config_use_case_mod(uc_mgr, new, 1); + + return 0; + } +out: + pthread_mutex_unlock(&uc_mgr->mutex); + return ret; +} + +/** + * \brief Get current use case verb from sound card + * \param uc_mgr use case manager + * \return Verb Name if success, otherwise NULL + */ +const char *snd_use_case_get_verb(snd_use_case_mgr_t *uc_mgr) +{ + const char *ret = NULL; + + pthread_mutex_lock(&uc_mgr->mutex); + + if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) + ret = uc_mgr->verb_list[uc_mgr->card.current_verb]; + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get device status for current use case verb + * \param uc_mgr Use case manager + * \param device_name The device we are interested in. + * \return - 1 = enabled, 0 = disabled, negative = error + */ +int snd_use_case_get_device_status(snd_use_case_mgr_t *uc_mgr, + const char *device_name) +{ + struct use_case_device *device; + int ret = -EINVAL, dev_id; + + pthread_mutex_lock(&uc_mgr->mutex); + + device = get_device(uc_mgr, device_name, &dev_id); + if (device == NULL) { + uc_error("error: use case device %s not found", device_name); + goto out; + } + + ret = get_device_status(uc_mgr, dev_id); +out: + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get modifier status for current use case verb + * \param uc_mgr Use case manager + * \param device_name The device we are interested in. + * \return - 1 = enabled, 0 = disabled, negative = error + */ +int snd_use_case_get_modifier_status(snd_use_case_mgr_t *uc_mgr, + const char *modifier_name) +{ + struct use_case_modifier *modifier; + int ret = -EINVAL, mod_id; + + pthread_mutex_lock(&uc_mgr->mutex); + + modifier = get_modifier(uc_mgr, modifier_name, &mod_id); + if (modifier == NULL) { + uc_error("error: use case modifier %s not found", modifier_name); + goto out; + } + + ret = get_modifier_status(uc_mgr, mod_id); +out: + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case verb QoS + * \param uc_mgr use case manager + * \return QoS level + */ +enum snd_use_case_qos + snd_use_case_get_verb_qos(snd_use_case_mgr_t *uc_mgr) +{ + struct use_case_verb *verb; + enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; + + pthread_mutex_lock(&uc_mgr->mutex); + + if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + ret = verb->qos; + } + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case modifier QoS + * \param uc_mgr use case manager + * \return QoS level + */ +enum snd_use_case_qos + snd_use_case_get_mod_qos(snd_use_case_mgr_t *uc_mgr, + const char *modifier_name) +{ + struct use_case_modifier *modifier; + enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; + + pthread_mutex_lock(&uc_mgr->mutex); + + modifier = get_modifier(uc_mgr, modifier_name, NULL); + if (modifier != NULL) + ret = modifier->qos; + else + uc_error("error: use case modifier %s not found", modifier_name); + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case verb playback PCM + * \param uc_mgr use case manager + * \return PCM number if success, otherwise negative + */ +int snd_use_case_get_verb_playback_pcm(snd_use_case_mgr_t *uc_mgr) +{ + struct use_case_verb *verb; + int ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + ret = verb->playback_pcm; + } + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case verb playback PCM + * \param uc_mgr use case manager + * \return PCM number if success, otherwise negative + */ +int snd_use_case_get_verb_capture_pcm(snd_use_case_mgr_t *uc_mgr) +{ + struct use_case_verb *verb; + int ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { + verb = &uc_mgr->verb[uc_mgr->card.current_verb]; + ret = verb->capture_pcm; + } + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case modifier playback PCM + * \param uc_mgr use case manager + * \return PCM number if success, otherwise negative + */ +int snd_use_case_get_mod_playback_pcm(snd_use_case_mgr_t *uc_mgr, + const char *modifier_name) +{ + struct use_case_modifier *modifier; + int ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + modifier = get_modifier(uc_mgr, modifier_name, NULL); + if (modifier == NULL) + uc_error("error: use case modifier %s not found", + modifier_name); + else + ret = modifier->playback_pcm; + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get current use case modifier playback PCM + * \param uc_mgr use case manager + * \return PCM number if success, otherwise negative + */ +int snd_use_case_get_mod_capture_pcm(snd_use_case_mgr_t *uc_mgr, + const char *modifier_name) +{ + struct use_case_modifier *modifier; + int ret = -EINVAL; + + pthread_mutex_lock(&uc_mgr->mutex); + + modifier = get_modifier(uc_mgr, modifier_name, NULL); + if (modifier == NULL) + uc_error("error: use case modifier %s not found", + modifier_name); + else + ret = modifier->capture_pcm; + + pthread_mutex_unlock(&uc_mgr->mutex); + + return ret; +} + +/** + * \brief Get volume/mute control name depending on use case device. + * \param uc_mgr use case manager + * \param type the control type we are looking for + * \param device_name The use case device we are interested in. + * \return control name if success, otherwise NULL + * + * Get the control id for common volume and mute controls that are aliased + * in the named use case device. + */ +const char *snd_use_case_get_device_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, + enum snd_use_case_control_alias type, const char *device_name) +{ + struct use_case_device *device; + const char *kcontrol_name = NULL; + + pthread_mutex_lock(&uc_mgr->mutex); + + device = get_device(uc_mgr, device_name, NULL); + if (!device) { + uc_error("error: device %s not found", device_name); + goto out; + } + + switch (type) { + case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: + kcontrol_name = device->playback_volume_id; + break; + case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: + kcontrol_name = device->capture_volume_id; + break; + case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: + kcontrol_name = device->playback_switch_id; + break; + case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: + kcontrol_name = device->capture_switch_id; + break; + default: + uc_error("error: invalid control alias %d", type); + break; + } + +out: + pthread_mutex_unlock(&uc_mgr->mutex); + + return kcontrol_name; +} + +/** + * \brief Get volume/mute control IDs depending on use case modifier. + * \param uc_mgr use case manager + * \param type the control type we are looking for + * \param modifier_name The use case modifier we are interested in. + * \return ID if success, otherwise a negative error code + * + * Get the control id for common volume and mute controls that are aliased + * in the named use case device. + */ +const char *snd_use_case_get_modifier_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, + enum snd_use_case_control_alias type, const char *modifier_name) +{ + struct use_case_modifier *modifier; + const char *kcontrol_name = NULL; + + pthread_mutex_lock(&uc_mgr->mutex); + + modifier = get_modifier(uc_mgr, modifier_name, NULL); + if (!modifier) { + uc_error("error: modifier %s not found", modifier_name); + goto out; + } + + switch (type) { + case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: + kcontrol_name = modifier->playback_volume_id; + break; + case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: + kcontrol_name = modifier->capture_volume_id; + break; + case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: + kcontrol_name = modifier->playback_switch_id; + break; + case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: + kcontrol_name = modifier->capture_switch_id; + break; + default: + uc_error("error: invalid control alias %d", type); + break; + } + +out: + pthread_mutex_unlock(&uc_mgr->mutex); + + return kcontrol_name; +} +#endif diff --git a/src/ucm/parser.c b/src/ucm/parser.c new file mode 100644 index 00000000..bb047515 --- /dev/null +++ b/src/ucm/parser.c @@ -0,0 +1,956 @@ +/* + * 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 <lrg@slimlogic.co.uk> + * Stefan Schmidt <stefan@slimlogic.co.uk> + * Justin Xu <justinx@slimlogic.co.uk> + * Jaroslav Kysela <perex@perex.cz> + */ + +#include "ucm_local.h" + +static int parse_sequence(snd_use_case_mgr_t *uc_mgr, + struct list_head *base, + snd_config_t *cfg); + +/* + * Parse string + */ +int parse_string(snd_config_t *n, char **res) +{ + int err; + + err = snd_config_get_string(n, (const char **)res); + if (err < 0) + return err; + *res = strdup(*res); + if (*res == NULL) + return -ENOMEM; + return 0; +} + + +/* + * Parse transition + */ +static int parse_transition(snd_use_case_mgr_t *uc_mgr, + struct list_head *tlist, + snd_config_t *cfg) +{ + struct transition_sequence *tseq; + const char *id; + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + tseq = calloc(1, sizeof(*tseq)); + if (tseq == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&tseq->transition_list); + + tseq->name = strdup(id); + if (tseq->name == NULL) { + free(tseq); + return -ENOMEM; + } + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + err = -EINVAL; + goto __err; + } + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + err = -EINVAL; + goto __err; + } + + err = parse_sequence(uc_mgr, &tseq->transition_list, n); + if (err < 0) + return err; + } + + list_add(&tseq->list, tlist); + return 0; + __err: + free(tseq->name); + free(tseq); + return err; +} + +/* + * Parse compound + */ +static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, + int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *), + void *data1, void *data2) +{ + const char *id; + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + return -EINVAL; + } + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for %s", id); + return -EINVAL; + } + + err = fcn(uc_mgr, n, data1, data2); + if (err < 0) + return err; + } + + return 0; +} + +/* + * Parse sequences. + * + * Sequence controls elements are in the following form:- + * + * cset "element_id_syntax value_syntax" + * usleep time + * exec "any unix command with arguments" + * + * e.g. + * cset "name='Master Playback Switch' 0,0" + * cset "iface=PCM,name='Disable HDMI',index=1 0" + */ +static int parse_sequence(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct list_head *base, + snd_config_t *cfg) +{ + struct sequence_element *curr; + snd_config_iterator_t i, next, j, next2; + snd_config_t *n, *n2; + int err; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + snd_config_for_each(j, next2, n) { + const char *id; + n2 = snd_config_iterator_entry(i); + err = snd_config_get_id(n2, &id); + if (err < 0) + continue; + + /* alloc new sequence element */ + curr = calloc(1, sizeof(struct sequence_element)); + if (curr == NULL) + return -ENOMEM; + list_add_tail(&curr->list, base); + + if (strcmp(id, "cset") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_CSET; + err = parse_string(n2, &curr->data.cset); + if (err < 0) { + uc_error("error: cset requires a string!"); + return err; + } + continue; + } + + if (strcmp(id, "usleep") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP; + err = snd_config_get_integer(n2, &curr->data.sleep); + if (err < 0) { + uc_error("error: usleep requires integer!"); + return err; + } + continue; + } + + if (strcmp(id, "exec") == 0) { + curr->type = SEQUENCE_ELEMENT_TYPE_EXEC; + err = parse_string(n2, &curr->data.exec); + if (err < 0) { + uc_error("error: exec requires a string!"); + return err; + } + continue; + } + + list_del(&curr->list); + uc_mgr_free_sequence_element(curr); + } + } + + return 0; +} + +/* + * Parse Modifier Use cases + * + * # Each modifier is described in new section. N modifier are allowed + * SectionModifier."Capture Voice" { + * + * Comment "Record voice call" + * SupportedDevice [ + * "x" + * "y" + * ] + * + * EnableSequence [ + * .... + * ] + * + * DisableSequence [ + * ... + * ] + * + * # Optional TQ and ALSA PCMs + * TQ Voice + * CapturePCM "hw:1" + * MasterPlaybackVolume "name='Master Playback Volume',index=2" + * MasterPlaybackSwitch "name='Master Playback Switch',index=2" + * + * } + */ +static int parse_modifier(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, + void *data1, + void *data2 ATTRIBUTE_UNUSED) +{ + struct use_case_verb *verb = data1; + struct use_case_modifier *modifier; + const char *id; + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + /* allocate modifier */ + modifier = calloc(1, sizeof(*modifier)); + if (modifier == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&modifier->enable_list); + INIT_LIST_HEAD(&modifier->disable_list); + INIT_LIST_HEAD(&modifier->transition_list); + INIT_LIST_HEAD(&modifier->dev_list); + list_add_tail(&modifier->list, &verb->modifier_list); + err = snd_config_get_id(cfg, &id); + if (err < 0) + return err; + modifier->name = strdup(id); + if (modifier->name == NULL) + return -EINVAL; + + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "Comment") == 0) { + err = parse_string(n, &modifier->comment); + if (err < 0) { + uc_error("error: failed to get modifier comment"); + return err; + } + continue; + } + + if (strcmp(id, "SupportedDevice") == 0) { + struct dev_list *sdev; + + sdev = calloc(1, sizeof(struct dev_list)); + if (sdev == NULL) + return -ENOMEM; + err = parse_string(n, &sdev->name); + if (err < 0) { + free(sdev); + return err; + } + list_add(&sdev->list, &modifier->dev_list); + continue; + } + + if (strcmp(id, "EnableSequence") == 0) { + err = parse_sequence(uc_mgr, &modifier->enable_list, n); + if (err < 0) { + uc_error("error: failed to parse modifier" + " enable sequence"); + return err; + } + continue; + } + + if (strcmp(id, "DisableSequence") == 0) { + err = parse_sequence(uc_mgr, &modifier->disable_list, n); + if (err < 0) { + uc_error("error: failed to parse modifier" + " disable sequence"); + return err; + } + continue; + } + + if (strcmp(id, "TransitionModifier") == 0) { + err = parse_transition(uc_mgr, &modifier->transition_list, n); + if (err < 0) { + uc_error("error: failed to parse transition" + " modifier"); + return err; + } + continue; + } + + if (strcmp(id, "TQ") == 0) { + err = parse_string(n, &modifier->tq); + if (err < 0) { + uc_error("error: failed to parse TQ"); + return err; + } + continue; + } + + if (strcmp(id, "CapturePCM") == 0) { + err = parse_string(n, &modifier->capture_pcm); + if (err < 0) { + uc_error("error: failed to get Capture PCM ID"); + return err; + } + continue; + } + + if (strcmp(id, "PlaybackPCM") == 0) { + err = parse_string(n, &modifier->playback_pcm); + if (err < 0) { + uc_error("error: failed to get Playback PCM ID"); + return err; + } + continue; + } + + if (strcmp(id, "MasterPlaybackVolume") == 0) { + err = parse_string(n, &modifier->playback_volume_id); + if (err < 0) { + uc_error("error: failed to get MasterPlaybackVolume"); + return err; + } + continue; + } + + if (strcmp(id, "MasterPlaybackSwitch") == 0) { + err = parse_string(n, &modifier->playback_switch_id); + if (err < 0) { + uc_error("error: failed to get MasterPlaybackSwitch"); + return err; + } + continue; + } + + if (strcmp(id, "MasterCaptureVolume") == 0) { + err = parse_string(n, &modifier->capture_volume_id); + if (err < 0) { + uc_error("error: failed to get MasterCaptureVolume"); + return err; + } + continue; + } + + if (strcmp(id, "MasterCaptureSwitch") == 0) { + err = parse_string(n, &modifier->capture_switch_id); + if (err < 0) { + uc_error("error: failed to get MasterCaptureSwitch"); + return err; + } + continue; + } + } + + if (list_empty(&modifier->dev_list)) { + uc_error("error: %s: modifier missing supported device sequence"); + return -EINVAL; + } + + return 0; +} + +/* + * Parse Device Use Cases + * + *# Each device is described in new section. N devices are allowed + *SectionDevice."Headphones".0 { + * Comment "Headphones connected to 3.5mm jack" + * + * EnableSequence [ + * .... + * ] + * + * DisableSequence [ + * ... + * ] + * + * MasterPlaybackVolume "name='Master Playback Volume',index=2" + * MasterPlaybackSwitch "name='Master Playback Switch',index=2" + * + * } + */ +static int parse_device_index(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, + void *data1, + void *data2) +{ + struct use_case_verb *verb = data1; + char *name = data2; + struct use_case_device *device; + const char *id; + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + device = calloc(1, sizeof(*device)); + if (device == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&device->enable_list); + INIT_LIST_HEAD(&device->disable_list); + INIT_LIST_HEAD(&device->transition_list); + list_add_tail(&device->list, &verb->device_list); + device->name = strdup(name); + if (device->name == NULL) + return -ENOMEM; + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + err = safe_strtol(id, &device->idx); + if (err < 0) { + uc_error("Invalid device index '%s'", id); + return -EINVAL; + } + + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "Comment") == 0) { + err = parse_string(n, &device->comment); + if (err < 0) { + uc_error("error: failed to get device comment"); + return err; + } + continue; + } + + if (strcmp(id, "EnableSequence") == 0) { + uc_dbg("EnableSequence"); + err = parse_sequence(uc_mgr, &device->enable_list, n); + if (err < 0) { + uc_error("error: failed to parse device enable" + " sequence"); + return err; + } + continue; + } + + if (strcmp(id, "DisableSequence") == 0) { + uc_dbg("DisableSequence"); + err = parse_sequence(uc_mgr, &device->disable_list, n); + if (err < 0) { + uc_error("error: failed to parse device disable" + " sequence"); + return err; + } + continue; + } + + if (strcmp(id, "TransitionDevice") == 0) { + uc_dbg("TransitionDevice"); + err = parse_transition(uc_mgr, &device->transition_list, n); + if (err < 0) { + uc_error("error: failed to parse transition" + " device"); + return err; + } + continue; + } + + if (strcmp(id, "MasterPlaybackVolume") == 0) { + err = parse_string(n, &device->playback_volume_id); + if (err < 0) { + uc_error("error: failed to get MasterPlaybackVolume"); + return err; + } + continue; + } + + if (strcmp(id, "MasterPlaybackSwitch") == 0) { + err = parse_string(n, &device->playback_switch_id); + if (err < 0) { + uc_error("error: failed to get MasterPlaybackSwitch"); + return err; + } + continue; + } + + if (strcmp(id, "MasterCaptureVolume") == 0) { + err = parse_string(n, &device->capture_volume_id); + if (err < 0) { + uc_error("error: failed to get MasterCaptureVolume"); + return err; + } + continue; + } + + if (strcmp(id, "MasterCaptureSwitch") == 0) { + err = parse_string(n, &device->capture_switch_id); + if (err < 0) { + uc_error("error: failed to get MasterCaptureSwitch"); + return err; + } + continue; + } + } + return 0; +} + +static int parse_device_name(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, + void *data1, + void *data2 ATTRIBUTE_UNUSED) +{ + const char *id; + int err; + + err = snd_config_get_id(cfg, &id); + if (err < 0) + return err; + return parse_compound(uc_mgr, cfg, parse_device_index, + data1, (void *)id); +} + +static int parse_device(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, + snd_config_t *cfg) +{ + return parse_compound(uc_mgr, cfg, parse_device_name, verb, NULL); +} + +/* + * Parse Verb Section + * + * # Example Use case verb section for Voice call blah + * # By Joe Blogs <joe@blogs.com> + * + * SectionVerb { + * # enable and disable sequences are compulsory + * EnableSequence [ + * cset "name='Master Playback Switch',index=2 0,0" + * cset "name='Master Playback Volume':index=2 25,25" + * msleep 50 + * cset "name='Master Playback Switch',index=2 1,1" + * cset "name='Master Playback Volume',index=2 50,50" + * ] + * + * DisableSequence [ + * cset "name='Master Playback Switch',index=2 0,0" + * cset "name='Master Playback Volume',index=2 25,25" + * msleep 50 + * cset "name='Master Playback Switch',index=2 1,1" + * cset "name='Master Playback Volume',index=2 50,50" + * ] + * + * # Optional TQ and ALSA PCMs + * TQ HiFi + * CapturePCM 0 + * PlaybackPCM 0 + * + * } + */ +static int parse_verb(snd_use_case_mgr_t *uc_mgr, + struct use_case_verb *verb, + snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + /* parse verb section */ + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "EnableSequence") == 0) { + uc_dbg("Parse EnableSequence"); + err = parse_sequence(uc_mgr, &verb->enable_list, cfg); + if (err < 0) { + uc_error("error: failed to parse verb enable sequence"); + return err; + } + continue; + } + + if (strcmp(id, "DisableSequence") == 0) { + uc_dbg("Parse DisableSequence"); + err = parse_sequence(uc_mgr, &verb->disable_list, cfg); + if (err < 0) { + uc_error("error: failed to parse verb disable sequence"); + return err; + } + continue; + } + + if (strcmp(id, "TransitionVerb") == 0) { + uc_dbg("Parse TransitionVerb"); + err = parse_transition(uc_mgr, &verb->transition_list, n); + if (err < 0) { + uc_error("error: failed to parse transition verb"); + return err; + } + continue; + } + + if (strcmp(id, "TQ") == 0) { + uc_dbg("Parse TQ"); + err = parse_string(n, &verb->tq); + if (err < 0) + return err; + continue; + } + + if (strcmp(id, "CapturePCM") == 0) { + uc_dbg("Parse CapturePCM"); + err = parse_string(n, &verb->capture_pcm); + if (err < 0) + return err; + continue; + } + + if (strcmp(id, "PlaybackPCM") == 0) { + uc_dbg("Parse PlaybackPCM"); + err = parse_string(n, &verb->playback_pcm); + if (err < 0) + return err; + continue; + } + } + + return 0; +} + +/* + * Parse a Use case verb file. + * + * This file contains the following :- + * o Verb enable and disable sequences. + * o Supported Device enable and disable sequences for verb. + * o Supported Modifier enable and disable sequences for verb + * o Optional QoS for the verb and modifiers. + * o Optional PCM device ID for verb and modifiers + * o Alias kcontrols IDs for master and volumes and mutes. + */ +static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, + const char *use_case_name, + const char *comment, + const char *file) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + struct use_case_verb *verb; + snd_config_t *cfg; + char filename[MAX_FILE]; + int err; + + /* allocate verb */ + verb = calloc(1, sizeof(struct use_case_verb)); + if (verb == NULL) + return -ENOMEM; + INIT_LIST_HEAD(&verb->enable_list); + INIT_LIST_HEAD(&verb->disable_list); + INIT_LIST_HEAD(&verb->transition_list); + INIT_LIST_HEAD(&verb->device_list); + INIT_LIST_HEAD(&verb->modifier_list); + list_add_tail(&verb->list, &uc_mgr->verb_list); + verb->name = strdup(use_case_name); + if (verb->name == NULL) + return -ENOMEM; + verb->comment = strdup(comment); + if (verb->comment == NULL) + return -ENOMEM; + + /* open Verb file for reading */ + snprintf(filename, sizeof(filename), "%s/%s/%s", ALSA_USE_CASE_DIR, + uc_mgr->card_name, file); + filename[sizeof(filename)-1] = '\0'; + + err = uc_mgr_config_load(filename, &cfg); + if (err < 0) { + uc_error("error: failed to open verb file %s : %d", + filename, -errno); + return err; + } + + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* find verb section and parse it */ + if (strcmp(id, "SectionVerb") == 0) { + err = parse_verb(uc_mgr, verb, n); + if (err < 0) { + uc_error("error: %s failed to parse verb", + file); + return err; + } + continue; + } + + /* find device sections and parse them */ + if (strcmp(id, "SectionDevice") == 0) { + err = parse_device(uc_mgr, verb, n); + if (err < 0) { + uc_error("error: %s failed to parse device", + file); + return err; + } + continue; + } + + /* find modifier sections and parse them */ + if (strcmp(id, "SectionModifier") == 0) { + err = parse_compound(uc_mgr, n, + parse_modifier, verb, NULL); + if (err < 0) { + uc_error("error: %s failed to parse modifier", + file); + return err; + } + continue; + } + } + + /* use case verb must have at least 1 device */ + if (list_empty(&verb->device_list)) { + uc_error("error: no use case device defined", file); + return -EINVAL; + } + return 0; +} + +/* + * Parse master section for "Use Case" and "File" tags. + */ +static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, + void *data1 ATTRIBUTE_UNUSED, + void *data2 ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *use_case_name, *file = NULL, *comment = NULL; + int err; + + if (snd_config_get_id(cfg, &use_case_name) < 0) { + uc_error("unable to get name for use case section"); + return -EINVAL; + } + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for use case section"); + return -EINVAL; + } + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* get use case verb file name */ + if (strcmp(id, "File") == 0) { + err = snd_config_get_string(n, &file); + if (err < 0) { + uc_error("failed to get File"); + return err; + } + continue; + } + + /* get optional use case comment */ + if (strncmp(id, "Comment", 7) == 0) { + err = snd_config_get_string(n, &comment); + if (err < 0) { + uc_error("error: failed to get Comment"); + return err; + } + continue; + } + + uc_error("unknown field %s in master section"); + } + + uc_dbg("use_case_name %s file %s end %d", use_case_name, file, end); + + /* do we have both use case name and file ? */ + if (!file) { + uc_error("error: use case missing file"); + return -EINVAL; + } + + /* parse verb file */ + return parse_verb_file(uc_mgr, use_case_name, comment, file); +} + +/* + * parse and execute controls + */ +static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + struct list_head list; + int err; + + INIT_LIST_HEAD(&list); + err = parse_sequence(uc_mgr, &list, cfg); + if (err < 0) { + uc_error("Unable to parse SectionDefaults"); + return err; + } + printf("parse_controls - not yet implemented\n"); + return 0; +} + +/* + * Each sound card has a master sound card file that lists all the supported + * use case verbs for that sound card. i.e. + * + * #Example master file for blah sound card + * #By Joe Blogs <joe@bloggs.org> + * + * # The file is divided into Use case sections. One section per use case verb. + * + * SectionUseCase."Voice Call" { + * File "voice_call_blah" + * Comment "Make a voice phone call." + * } + * + * SectionUseCase."HiFi" { + * File "hifi_blah" + * Comment "Play and record HiFi quality Music." + * } + * + * # This file also stores the default sound card state. + * + * SectionDefaults [ + * cset "name='Master Playback Switch',index=2 1,1" + * cset "name='Master Playback Volume',index=2 25,25" + * cset "name='Master Mono Playback',index=1 0" + * cset "name='Master Mono Playback Volume',index=1 0" + * cset "name='PCM Switch',index=2 1,1" + * exec "some binary here" + * msleep 50 + * ........ + * ] + * + * # End of example file. + */ +static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int ret; + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + uc_error("compound type expected for master file"); + return -EINVAL; + } + + /* parse master config sections */ + snd_config_for_each(i, next, cfg) { + const char *id; + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* find use case section and parse it */ + if (strcmp(id, "SectionUseCase") == 0) { + ret = parse_compound(uc_mgr, n, + parse_master_section, + NULL, NULL); + if (ret < 0) + return ret; + continue; + } + + /* find default control values section and parse it */ + if (strcmp(id, "SectionDefaults") == 0) { + ret = parse_controls(uc_mgr, n); + if (ret < 0) + return ret; + continue; + } + uc_error("uknown master file field %s", id); + } + return 0; +} + +/* load master use case file for sound card */ +int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr) +{ + char filename[MAX_FILE]; + snd_config_t *cfg; + int err; + + snprintf(filename, sizeof(filename)-1, + "%s/%s/%s.conf", ALSA_USE_CASE_DIR, + uc_mgr->card_name, uc_mgr->card_name); + filename[MAX_FILE-1] = '\0'; + + err = uc_mgr_config_load(filename, &cfg); + if (err < 0) { + uc_error("error: could not parse configuration for card %s", + uc_mgr->card_name); + return err; + } + + err = parse_master_file(uc_mgr, cfg); + snd_config_delete(cfg); + if (err < 0) + uc_mgr_free_verb(uc_mgr); + + return err; +} diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h new file mode 100644 index 00000000..ffef8f00 --- /dev/null +++ b/src/ucm/ucm_local.h @@ -0,0 +1,217 @@ +/* + * 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 <lrg@slimlogic.co.uk> + * Stefan Schmidt <stefan@slimlogic.co.uk> + * Justin Xu <justinx@slimlogic.co.uk> + * Jaroslav Kysela <perex@perex.cz> + */ + + + +#if 0 +#define UC_MGR_DEBUG +#endif + +#include "local.h" +#include "use-case.h" + +#define PRE_SEQ 0 +#define POST_SEQ 1 +#define MAX_FILE 256 +#define ALSA_USE_CASE_DIR ALSA_CONFIG_DIR "/ucm" +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define VERB_NOT_INITIALISED -1 + +#define SEQUENCE_ELEMENT_TYPE_CSET 1 +#define SEQUENCE_ELEMENT_TYPE_SLEEP 2 +#define SEQUENCE_ELEMENT_TYPE_EXEC 3 + +struct sequence_element { + struct list_head list; + unsigned int type; + union { + long sleep; /* Sleep time in msecs if sleep element, else 0 */ + char *cset; + char *exec; + } data; +}; + +/* + * Transition sequences. i.e. transition between one verb, device, mod to another + */ +struct transition_sequence { + struct list_head list; + char *name; + struct list_head transition_list; +}; + +/* + * Modifier Supported Devices. + */ +struct dev_list { + struct list_head list; + char *name; +}; + + +/* + * Describes a Use Case Modifier and it's enable and disable sequences. + * A use case verb can have N modifiers. + */ +struct use_case_modifier { + struct list_head list; + struct list_head active_list; + + char *name; + char *comment; + + /* modifier enable and disable sequences */ + struct list_head enable_list; + struct list_head disable_list; + + /* modifier transition list */ + struct list_head transition_list; + + /* list of supported devices per modifier */ + struct list_head dev_list; + + /* ALSA PCM devices associated with any modifier PCM streams */ + char *capture_pcm; + char *playback_pcm; + + /* Any modifier stream TQ */ + char *tq; + + /* aliased controls */ + char *playback_ctl; + char *playback_volume_id; + char *playback_switch_id; + char *capture_ctl; + char *capture_volume_id; + char *capture_switch_id; +}; + +/* + * Describes a Use Case Device and it's enable and disable sequences. + * A use case verb can have N devices. + */ +struct use_case_device { + struct list_head list; + struct list_head active_list; + + unsigned int active: 1; + + char *name; + char *comment; + long idx; /* index for similar devices i.e. 2 headphone jacks */ + + /* device enable and disable sequences */ + struct list_head enable_list; + struct list_head disable_list; + + /* device transition list */ + struct list_head transition_list; + + /* aliased controls */ + char *playback_ctl; + char *playback_volume_id; + char *playback_switch_id; + char *capture_ctl; + char *capture_volume_id; + char *capture_switch_id; +}; + +/* + * Describes a Use Case Verb and it's enable and disable sequences. + * A use case verb can have N devices and N modifiers. + */ +struct use_case_verb { + struct list_head list; + + unsigned int active: 1; + + char *name; + char *comment; + + /* verb enable and disable sequences */ + struct list_head enable_list; + struct list_head disable_list; + + /* verb transition list */ + struct list_head transition_list; + + /* verb PCMs and TQ */ + char *tq; + char *capture_pcm; + char *playback_pcm; + + /* hardware devices that can be used with this use case */ + struct list_head device_list; + + /* modifiers that can be used with this use case */ + struct list_head modifier_list; +}; + +/* + * Manages a sound card and all its use cases. + */ +struct snd_use_case_mgr { + char *card_name; + char *comment; + + /* use case verb, devices and modifier configs parsed from files */ + struct list_head verb_list; + + /* default settings - sequence */ + struct list_head default_list; + + /* current status */ + struct use_case_verb *active_verb; + struct list_head active_devices; + struct list_head active_modifiers; + + /* locking */ + pthread_mutex_t mutex; +}; + +#define uc_error SNDERR + +#ifdef UC_MGR_DEBUG +#define uc_dbg SNDERR +#else +#define uc_dbg(fmt, arg...) +#endif + +void uc_mgr_error(const char *fmt, ...); +void uc_mgr_stdout(const char *fmt, ...); + +int uc_mgr_config_load(const char *file, snd_config_t **cfg); +int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr); + +void uc_mgr_free_sequence_element(struct sequence_element *seq); +void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr); +void uc_mgr_free(snd_use_case_mgr_t *uc_mgr); diff --git a/src/ucm/utils.c b/src/ucm/utils.c new file mode 100644 index 00000000..98e3cf65 --- /dev/null +++ b/src/ucm/utils.c @@ -0,0 +1,220 @@ +/* + * 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 <lrg@slimlogic.co.uk> + * Stefan Schmidt <stefan@slimlogic.co.uk> + * Justin Xu <justinx@slimlogic.co.uk> + * Jaroslav Kysela <perex@perex.cz> + */ + +#include "ucm_local.h" + +void uc_mgr_error(const char *fmt,...) +{ + va_list va; + va_start(va, fmt); + fprintf(stderr, "ucm: "); + vfprintf(stderr, fmt, va); + va_end(va); +} + +void uc_mgr_stdout(const char *fmt,...) +{ + va_list va; + va_start(va, fmt); + vfprintf(stdout, fmt, va); + va_end(va); +} + +int uc_mgr_config_load(const char *file, snd_config_t **cfg) +{ + FILE *fp; + snd_input_t *in; + snd_config_t *top; + int err; + + fp = fopen(file, "r"); + err = snd_input_stdio_attach(&in, fp, 1); + if (err < 0) { + uc_error("could not open configuration file %s", file); + return err; + } + err = snd_config_top(&top); + if (err < 0) + return err; + err = snd_config_load(top, in); + if (err < 0) { + uc_error("could not load configuration file %s", file); + snd_config_delete(top); + return err; + } + err = snd_input_close(in); + if (err < 0) { + snd_config_delete(top); + return err; + } + *cfg = top; + return 0; +} + +void uc_mgr_free_dev_list(struct list_head *base) +{ + struct list_head *pos, *npos; + struct dev_list *dlist; + + list_for_each_safe(pos, npos, base) { + dlist = list_entry(pos, struct dev_list, list); + free(dlist->name); + free(dlist); + list_del(pos); + } +} + +void uc_mgr_free_sequence_element(struct sequence_element *seq) +{ + if (seq == NULL) + return; + switch (seq->type) { + case SEQUENCE_ELEMENT_TYPE_CSET: + case SEQUENCE_ELEMENT_TYPE_EXEC: + free(seq->data.exec); + break; + default: + break; + } + free(seq); +} + +void uc_mgr_free_sequence(struct list_head *base) +{ + struct list_head *pos, *npos; + struct sequence_element *seq; + + list_for_each_safe(pos, npos, base) { + seq = list_entry(pos, struct sequence_element, list); + uc_mgr_free_sequence_element(seq); + list_del(pos); + } +} + +void uc_mgr_free_transition(struct list_head *base) +{ + struct list_head *pos, *npos; + struct transition_sequence *tseq; + + list_for_each_safe(pos, npos, base) { + tseq = list_entry(pos, struct transition_sequence, list); + free(tseq->name); + uc_mgr_free_sequence(&tseq->transition_list); + free(tseq); + list_del(pos); + } +} + +void uc_mgr_free_modifier(struct list_head *base) +{ + struct list_head *pos, *npos; + struct use_case_modifier *mod; + + list_for_each_safe(pos, npos, base) { + mod = list_entry(pos, struct use_case_modifier, list); + free(mod->name); + free(mod->comment); + uc_mgr_free_sequence(&mod->enable_list); + uc_mgr_free_sequence(&mod->disable_list); + uc_mgr_free_transition(&mod->transition_list); + uc_mgr_free_dev_list(&mod->dev_list); + free(mod->capture_pcm); + free(mod->playback_pcm); + free(mod->tq); + free(mod->playback_ctl); + free(mod->playback_volume_id); + free(mod->playback_switch_id); + free(mod->capture_ctl); + free(mod->capture_volume_id); + free(mod->capture_switch_id); + free(mod); + list_del(pos); + } +} + +void uc_mgr_free_device(struct list_head *base) +{ + struct list_head *pos, *npos; + struct use_case_device *dev; + + list_for_each_safe(pos, npos, base) { + dev = list_entry(pos, struct use_case_device, list); + free(dev->name); + free(dev->comment); + uc_mgr_free_sequence(&dev->enable_list); + uc_mgr_free_sequence(&dev->disable_list); + uc_mgr_free_transition(&dev->transition_list); + free(dev->playback_ctl); + free(dev->playback_volume_id); + free(dev->playback_switch_id); + free(dev->capture_ctl); + free(dev->capture_volume_id); + free(dev->capture_switch_id); + free(dev); + list_del(pos); + } +} + +void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) +{ + struct list_head *pos, *npos; + struct use_case_verb *verb; + + list_for_each_safe(pos, npos, &uc_mgr->verb_list) { + verb = list_entry(pos, struct use_case_verb, list); + free(verb->name); + free(verb->comment); + uc_mgr_free_sequence(&verb->enable_list); + uc_mgr_free_sequence(&verb->disable_list); + uc_mgr_free_transition(&verb->transition_list); + free(verb->tq); + free(verb->capture_pcm); + free(verb->playback_pcm); + uc_mgr_free_device(&verb->device_list); + uc_mgr_free_modifier(&verb->modifier_list); + free(verb); + list_del(pos); + } + uc_mgr_free_sequence(&uc_mgr->default_list); + free(uc_mgr->comment); + uc_mgr->comment = NULL; + uc_mgr->active_verb = NULL; + INIT_LIST_HEAD(&uc_mgr->active_devices); + INIT_LIST_HEAD(&uc_mgr->active_modifiers); +} + +void uc_mgr_free(snd_use_case_mgr_t *uc_mgr) +{ + uc_mgr_free_verb(uc_mgr); + free(uc_mgr->card_name); + free(uc_mgr); +} diff --git a/src/use-case.c b/src/use-case.c deleted file mode 100644 index b45fe1f1..00000000 --- a/src/use-case.c +++ /dev/null @@ -1,3515 +0,0 @@ -/* - * 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. - * Authors: Liam Girdwood <lrg@slimlogic.co.uk> - * Stefan Schmidt <stefan@slimlogic.co.uk> - * Justin Xu <justinx@slimlogic.co.uk> - */ - -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <ctype.h> -#include <alsa/asoundlib.h> -#include <pthread.h> - -#include "../include/use-case.h" -#include "../include/iatomic.h" - -#define PRE_SEQ 0 -#define POST_SEQ 1 -#define MAX_VERB 32 -#define MAX_DEVICE 64 -#define MAX_MODIFIER 64 -#define MAX_NAME 64 -#define MAX_FILE 256 -#define MAX_BUF 256 -#define ALSA_USE_CASE_DIR "/usr/share/alsa/ucm" -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) -#define VERB_NOT_INITIALISED -1 - -/* - * Stores all use case settings for 1 kcontrol. Hence we have a - * control_settings for each kcontrol in card. - */ -struct control_settings { - char name[MAX_NAME]; - unsigned int id; - snd_ctl_elem_type_t type; - short count; /* 1 = mono, 2 = stereo, etc */ - unsigned short *value; -}; - -/* - * If sleep is 0 the element contains the settings in control. Else sleep - * contains the sleep time in micro seconds. - */ -struct sequence_element { - unsigned int sleep; /* Sleep time in msecs if sleep element, else 0 */ - struct control_settings *control; - struct sequence_element *next; /* Pointer to next list element */ -}; - -/* - * Transition sequences. i.e. transition between one verb, device, mod to another - */ -struct transition_sequence { - char *name; - struct sequence_element *transition; - struct transition_sequence *next; -}; - -/* - * Modifier Supported Devicees. - */ -struct dev_list { - char *name; - struct dev_list *next; -}; - - -/* - * Describes a Use Case Modifier and it's enable and disable sequences. - * A use case verb can have N modifiers. - */ -struct use_case_modifier { - char *name; - char *comment; - - /* modifier enable and disable sequences */ - struct sequence_element *enable; - struct sequence_element *disable; - - /* modifier transition list */ - struct transition_sequence *transition_list; - - /* list of supported devices per modifier */ - struct dev_list *dev_list; - - /* ALSA PCM devices associated with any modifier PCM streams */ - int capture_pcm; - int playback_pcm; - - /* Any modifier stream QoS */ - enum snd_use_case_qos qos; - - /* aliased controls */ - char *playback_volume_id; - char *playback_switch_id; - char *capture_volume_id; - char *capture_switch_id; -}; - -/* - * Describes a Use Case Device and it's enable and disable sequences. - * A use case verb can have N devices. - */ -struct use_case_device { - char *name; - char *comment; - int idx; /* index for similar devices i.e. 2 headphone jacks */ - - /* device enable and disable sequences */ - struct sequence_element *enable; - struct sequence_element *disable; - - /* device transition list */ - struct transition_sequence *transition_list; - - /* aliased controls */ - char *playback_volume_id; - char *playback_switch_id; - char *capture_volume_id; - char *capture_switch_id; -}; - -/* - * Describes a Use Case Verb and it's enable and disable sequences. - * A use case verb can have N devices and N modifiers. - */ -struct use_case_verb { - char *name; - char *comment; - - /* verb enable and disable sequences */ - struct sequence_element *enable; - struct sequence_element *disable; - - /* verb transition list */ - struct transition_sequence *transition_list; - - /* verb PCMs and QoS */ - enum snd_use_case_qos qos; - int capture_pcm; - int playback_pcm; - - /* hardware devices that can be used with this use case */ - int num_devices; - struct use_case_device *device; - /* - * device_list[i] shares string with device[i].name, - * so device_list don't need not be freed - */ - const char *device_list[MAX_DEVICE]; - - /* modifiers that can be used with this use case */ - int num_modifiers; - struct use_case_modifier *modifier; - /* - * modifier_list[i] shares string with modifier[i].name, - * so modifier_list don't need not be freed - */ - const char *modifier_list[MAX_MODIFIER]; -}; - -struct ucm_card { - int current_verb; - int current_device[MAX_DEVICE]; - int current_modifier[MAX_MODIFIER]; -}; - -/* - * Manages a sound card and all its use cases. - */ -struct snd_use_case_mgr { - pthread_mutex_t mutex; - char *card_name; - char *ctl_name; - struct ucm_card card; - - /* use case verb, devices and modifier configs parsed from files */ - int num_verbs; /* number of supported use case verbs */ - struct use_case_verb *verb; /* var len array of use case info */ - /* - * verb_list[i] shares string with verb[i].name, - * so verb_list don't need not be freed - */ - const char *verb_list[MAX_VERB]; - - /* sound card ALSA kcontrol read from sound card device */ - struct control_settings *control; /* var len array of controls */ - - /* snd ctl */ - int count; - snd_ctl_t *handle; - snd_ctl_card_info_t *info; - snd_ctl_elem_list_t *list; - snd_ctl_elem_id_t *id; -}; - -static void uc_mgr_error(const char *fmt,...) -{ - va_list va; - va_start(va, fmt); - fprintf(stderr, "ucm: "); - vfprintf(stderr, fmt, va); - va_end(va); -} - -#define uc_error(fmt, arg...) do { \ - uc_mgr_error("%s() - " fmt "\n", __FUNCTION__ , ## arg); \ -} while (0) - -static void uc_mgr_stdout(const char *fmt,...) -{ - va_list va; - va_start(va, fmt); - vfprintf(stdout, fmt, va); - va_end(va); -} - -#undef UC_MGR_DEBUG - -#ifdef UC_MGR_DEBUG -#define uc_dbg(fmt, arg...) do { \ - uc_mgr_stdout("%s() - " fmt "\n", __FUNCTION__ , ## arg); \ -} while (0) -#else -#define uc_dbg(fmt, arg...) -#endif - -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 char *get_control_name(char *buf, int line, char *file) -{ - char name[MAX_NAME]; - char *name_start, *name_end, *tbuf = buf; - - /* get name start */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return NULL; - name_start = ++tbuf; - - /* get name end */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return NULL; - name_end = tbuf++; - - /* copy name */ - if ((name_end - name_start) > MAX_NAME) { - uc_error("error: %s:%d name too big at %d chars", - file, line, name_end - name_start); - return NULL; - } - strncpy(name, name_start, name_end - name_start); - name[name_end - name_start] = 0; - return strdup(name); -} - -/* - * Parse a single control from file. - * - * Controls are in the following form:- - * - * 'name':channels:value0,value1,...,valueN - * - * e.g. - * 'Master Playback Switch':2:0,0 - */ -static int parse_control(snd_use_case_mgr_t *uc_mgr, - struct control_settings *control, char *buf, - int line, char *file) -{ - char name[MAX_NAME]; - int count, i; - char *name_start, *name_end, *tbuf; - - uc_dbg("%s", buf); - - tbuf = buf; - - /* get name start */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - name_start = ++tbuf; - - /* get name end */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - name_end = tbuf++; - - /* copy name */ - if ((name_end - name_start) > MAX_NAME) { - uc_error("error: %s:%d name too big at %d chars", - file, line, name_end - name_start); - return -EINVAL; - } - strncpy(name, name_start, name_end - name_start); - name[name_end - name_start] = 0; - strncpy(control->name, name, name_end - name_start +1); - - /* get count */ - uc_dbg("%s", tbuf); - tbuf++; - count = atoi(tbuf); - if (count == 0) { - uc_error("error: %s:%d count == 0 on line %d", file, line); - return -EINVAL; - } - control->count = count; - - /* get vals */ - control->value = calloc(count, sizeof(unsigned short)); - if (control->value == NULL) - return -ENOMEM; - - while (*tbuf != 0 && *tbuf != ':') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - tbuf++; - - for (i = 0; i < count; i++) { - set_value(control, i, atoi(tbuf)); - while (*tbuf != 0 && *tbuf != ',') - tbuf++; - - if (*tbuf++ == 0 && i < (count - 1)) - return -EINVAL; - } - - return get_control_id(uc_mgr, control); -} - -static int parse_controls(snd_use_case_mgr_t *uc_mgr, FILE *f, int *line_, - char *file) -{ - struct control_settings *control = NULL; - char buf[MAX_BUF], name[MAX_NAME]; - int count, i, ret = 0, line = *line_; - char *name_start, *name_end, *tbuf; - - while (fgets(buf, MAX_BUF, f) != NULL) { - - uc_dbg("%s: get line %d\n%s", file, line, buf); - - tbuf = buf; - while (isblank(*tbuf)) - tbuf++; - line ++; - - /* end of section ?*/ - if (strncmp(tbuf, "EndSectionDefaults", 18) == 0) - return 0; - - /* get name start */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - name_start = ++tbuf; - - /* get name end */ - while (*tbuf != 0 && *tbuf != '\'') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - name_end = tbuf++; - - /* copy name */ - if ((name_end - name_start) > MAX_NAME) { - uc_error("error: %s:%d name too big at %d chars", - file, line, name_end - name_start); - return -EINVAL; - } - strncpy(name, name_start, name_end - name_start); - name[name_end - name_start] = 0; - - for (i = 0; i < uc_mgr->count; i++) { - struct control_settings *card_control = - &uc_mgr->control[i]; - if (!strcmp(card_control->name, name)) - control = &uc_mgr->control[i]; - } - - uc_dbg("control id %d", control->id); - - /* get count */ - tbuf++; - count = atoi(tbuf); - if (count == 0) { - uc_error("error: %s:%d count == 0", file, line); - return -EINVAL; - } - if (count != control->count) { - uc_error("error: %s:%d count %d does not match card count" - " %d", file, line, count, control->count); - return -EINVAL; - } - - /* get vals */ - control->value = malloc(control->count *uc_mgr->num_verbs * - sizeof(unsigned short)); - if (control->value == NULL) - return -ENOMEM; - - while (*tbuf != 0 && *tbuf != ':') - tbuf++; - if (*tbuf == 0) - return -EINVAL; - tbuf++; - - for (i = 0; i < count; i++) { - set_value(control, i, atoi(tbuf)); - while (*tbuf != 0 && *tbuf != ',') - tbuf++; - - if (*tbuf++ == 0 && i < (count - 1)) - return -EINVAL; - } - } - - *line_ = line; - return ret; -} - -static char *get_string (char *buf) -{ - char *str, *end; - - uc_dbg("%s", buf); - - while (isblank(*buf)) - buf++; - - /* find leading '"' */ - if (*buf == 0 || *buf != '"') { - uc_error("error: missing start '\"'"); - return NULL; - } - str = ++buf; - - /* get value */ - while (*buf != 0 && *buf != '"') - buf++; - end = buf; - - /* find '"' terminator */ - if (*buf == 0 || *buf != '"') { - uc_error("error: missing terminator '\"' %s", buf); - return NULL; - } - - *end = 0; - return strdup(str); -} - -static enum snd_use_case_qos get_enum (char *buf) -{ - while (isblank(*buf)) - buf++; - - if (!strncmp(buf, "Music", 5)) - return SND_USE_CASE_QOS_MUSIC; - if (!strncmp(buf, "Voice", 5)) - return SND_USE_CASE_QOS_VOICE; - if (!strncmp(buf, "Tones", 5)) - return SND_USE_CASE_QOS_TONES; - - return SND_USE_CASE_QOS_MUSIC; -} - -static void seq_list_append(struct sequence_element **base, - struct sequence_element *curr) -{ - struct sequence_element *last, *tmp; - - if (!*base) - *base = curr; - else { - tmp = *base; - while (tmp) { - last = tmp; - tmp = tmp->next; - } - last->next = curr; - curr->next = NULL; - } -} -/* - * Parse sequences. - * - * Sequence controls elements are in the following form:- - * - * 'name':value0,value1,...,valueN - * - * e.g. - * 'Master Playback Switch':0,0 - */ -static int parse_sequence(snd_use_case_mgr_t *uc_mgr, - struct sequence_element **base, FILE *f, - int *line_, char *file) -{ - char buf[MAX_BUF], *tbuf; - int ret, line = *line_; - struct sequence_element *curr; - - /* read line by line */ - while (fgets(buf, MAX_BUF, f) != NULL) { - - uc_dbg("%s", buf); - line++; - - /* Check for lines with comments and ignore */ - if (buf[0] == '#') - continue; - - /* Parse current line and skip blanks */ - tbuf = buf; - while (isblank(*tbuf)) - tbuf++; - - /* end of sequence ? */ - if (strncmp(tbuf, "EndSequence", 11) == 0) - goto out; - - if (strncmp(tbuf, "EndTransition", 13) == 0) - goto out; - - /* alloc new sequence element */ - curr = calloc(1, sizeof(struct sequence_element)); - if (curr == NULL) - return -ENOMEM; - - /* is element a sleep ? */ - if (strncmp(tbuf, "msleep", 6) == 0) { - curr->sleep = atoi(tbuf + 6); - uc_dbg("msleep %d", curr->sleep); - seq_list_append(base, curr); - continue; - } - - /* alloc new sequence control */ - curr->control = calloc(1, sizeof(struct control_settings)); - if (curr->control == NULL) - return -ENOMEM; - - ret = parse_control(uc_mgr, curr->control, tbuf, line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to get parse sequence" - " controls", file, line); - goto err; - } - - uc_dbg("name %s, id %d, count %d", curr->control->name, - curr->control->id, curr->control->count); - seq_list_append(base, curr); - } - -out: - *line_ = line; - return 0; - -err: - free(curr); - return ret; -} - -static void prepend_transition( - struct transition_sequence **transition_list, - struct transition_sequence *trans_seq) -{ - if (*transition_list == NULL) - *transition_list = trans_seq; - else { - trans_seq->next = *transition_list; - *transition_list = trans_seq; - } - -} - -static void prepend_dev(struct dev_list **dev_list, - struct dev_list *sdev) -{ - if (*dev_list == NULL) - *dev_list = sdev; - else { - sdev->next = *dev_list; - *dev_list = sdev; - } - -} - -/* - * Parse Modifier Use cases - * - * # Each modifier is described in new section. N modifier are allowed - * SectionModifier - * - * Name "Capture Voice" - * Comment "Record voice call" - * SupportedDevice "x" - * SupportedDevice "y" - * - * EnableSequence - * .... - * EndSequence - * - * DisableSequence - * ... - * EndSequence - * - * # Optional QoS and ALSA PCMs - * QoS Voice - * CapturePCM 1 - * MasterPlaybackVolume 'Device Master Playback Volume' - * MasterPlaybackSwitch 'Device Master Playback Switch' - * - * EndSection - */ -static int parse_modifier(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *verb, FILE *f, int *line_, char *file) -{ - struct use_case_modifier *modifier; - int line = *line_, end = 0, en_seq = 0, dis_seq = 0; - int id = 0, dev = 0, ret; - char buf[MAX_BUF], *tbuf; - char *name = NULL, *comment = NULL; - - /* allocate modifier */ - verb->modifier = realloc(verb->modifier, - (verb->num_modifiers + 1) * sizeof(struct use_case_modifier)); - if (verb->modifier == NULL) - return -ENOMEM; - modifier = verb->modifier + verb->num_modifiers; - bzero(modifier, sizeof(struct use_case_modifier)); - - /* read line by line */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* skip comments */ - if (buf[0] == '#') - continue; - - tbuf = buf; - /* skip spaces */ - while (isblank(*tbuf)) - tbuf++; - - /* get use case modifier name */ - if (strncmp(tbuf, "Name", 4) == 0) { - name = get_string(tbuf + 4); - if (name == NULL) { - uc_error("error: %s:%d failed to get modifier name", - file, line); - goto err; - } - modifier->name = name; - id = 1; - continue; - } - - if (strncmp(tbuf, "Comment", 8) == 0) { - name = get_string(tbuf + 8); - if (name == NULL) { - uc_error("error: %s:%d failed to get modifier comment", - file, line); - goto err; - } - modifier->comment = comment; - continue; - } - - if (strncmp(tbuf, "SupportedDevice", 15) == 0) { - struct dev_list *sdev; - name = get_string(tbuf + 15); - if (name == NULL) { - uc_error("error: %s:%d failed to get modifier" - " supported device", file, line); - goto err; - } - - sdev = calloc(1, sizeof(struct dev_list)); - if (sdev == NULL) - goto err; - - dev = 1; - sdev->name = name; - prepend_dev(&modifier->dev_list, sdev); - continue; - } - - if (strncmp(tbuf, "EnableSequence", 14) == 0) { - ret = parse_sequence(uc_mgr, &modifier->enable, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse modifier" - " enable sequence", file, line); - goto err; - } - en_seq = 1; - continue; - } - - if (strncmp(tbuf, "DisableSequence", 15) == 0) { - ret = parse_sequence(uc_mgr, &modifier->disable, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse modifier" - " disable sequence", file, line); - goto err; - } - dis_seq = 1; - continue; - } - - if (strncmp(tbuf, "TransitionModifier", 16) == 0) { - struct transition_sequence *trans_seq; - - name = get_string(tbuf + 16); - if (name == NULL) - continue; - - trans_seq = calloc(1, sizeof(struct transition_sequence)); - if (trans_seq == NULL) - return -ENOMEM; - - trans_seq->name = name; - - ret = parse_sequence(uc_mgr, &trans_seq->transition, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse transition" - " modifier", file, line); - goto err; - } - - prepend_transition(&modifier->transition_list, trans_seq); - continue; - } - - if (strncmp(tbuf, "QoS", 3) == 0) { - modifier->qos = get_enum(tbuf + 3); - continue; - } - - if (strncmp(tbuf, "CapturePCM", 3) == 0) { - modifier->capture_pcm = atoi(tbuf + 3); - if (modifier->capture_pcm < 0) { - uc_error("error: %s:%d failed to get Capture PCM ID", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "PlaybackPCM", 3) == 0) { - modifier->playback_pcm = atoi(tbuf + 3); - if (modifier->playback_pcm < 0) { - uc_error("error: %s:%d failed to get Playback PCM ID", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterPlaybackVolume", 20) == 0) { - modifier->playback_volume_id = - get_control_name(tbuf + 20, line, file); - if (modifier->playback_volume_id == NULL) { - uc_error("error: %s:%d failed to get MasterPlaybackVolume", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterPlaybackSwitch", 20) == 0) { - modifier->playback_switch_id = - get_control_name(tbuf + 20, line, file); - if (modifier->playback_switch_id == NULL) { - uc_error("error: %s:%d failed to get MasterPlaybackSwitch", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterCaptureVolume", 19) == 0) { - modifier->capture_volume_id = - get_control_name(tbuf + 19, line, file); - if (modifier->capture_volume_id == NULL) { - uc_error("error: %s:%d failed to get MasterCaptureVolume", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterCaptureSwitch", 19) == 0) { - modifier->capture_switch_id = - get_control_name(tbuf + 19, line, file); - if (modifier->capture_switch_id == NULL) { - uc_error("error: %s:%d failed to get MasterCaptureSwitch", - file, line); - goto err; - } - continue; - } - - /* end of section ?*/ - if (strncmp(tbuf, "EndSection", 10) == 0) { - end = 1; - break; - } - } - - /* do we have the modifier basics ? */ - if (!en_seq || !dis_seq || !end || !dev) { - uc_error("error: invalid modifier"); - if (!en_seq) - uc_error("error: %s: modifier missing enable sequence", - file); - if (!dis_seq) - uc_error("error: %s: modifier missing disable sequence", - file); - if (!dev) - uc_error("error: %s: modifier missing supported device" - " sequence", file); - if (!end) - uc_error("error: %s: modifier missing end", file); - return -EINVAL; - } - - verb->modifier_list[verb->num_modifiers++] = modifier->name; - *line_ = line; - return 0; - -err: - if (ferror(f)) { - uc_error("error: %s: failed to read modifier master section", - file); - return ferror(f); - } - return -ENOMEM; -} - -/* - * Parse Device Use Cases - * - *# Each device is described in new section. N devices are allowed - *SectionDevice - * - * Name "Headphones" - * Comment "Headphones connected to 3.5mm jack" - * Index 0 - * - * EnableSequence - * .... - * EndSequence - * - * DisableSequence - * ... - * EndSequence - * - * #Optional control aliases - * MasterPlaybackVolume 'Device Master Playback Volume' - * MasterPlaybackSwitch 'Device Master Playback Switch' - * - * EndSection - */ -static int parse_device(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *verb, FILE *f, int *line_, char *file) -{ - struct use_case_device *device; - int line = *line_, end = 0, en_seq = 0, dis_seq = 0, id = 0, ret; - char buf[MAX_BUF], *tbuf; - char *name = NULL, *comment; - - /* allocate device */ - verb->device = realloc(verb->device, - (verb->num_devices + 1) * sizeof(struct use_case_device)); - if (verb->device == NULL) - return -ENOMEM; - device = verb->device + verb->num_devices; - bzero(device, sizeof(struct use_case_device)); - - /* read line by line */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* skip comments */ - if (buf[0] == '#') - continue; - - tbuf = buf; - /* skip spaces */ - while (isblank(*tbuf)) - tbuf++; - - /* get use case device name */ - if (strncmp(tbuf, "Name", 4) == 0) { - name = get_string(tbuf + 4); - if (name == NULL) { - uc_error("error: %s:%d failed to get device name", - file, line); - goto err; - } - device->name = name; - id = 1; - continue; - } - - if (strncmp(tbuf, "Comment", 8) == 0) { - comment = get_string(tbuf + 8); - if (name == NULL) { - uc_error("error: %s: %d failed to get device comment", - file, line); - goto err; - } - - device->comment = comment; - continue; - } - - if (strncmp(tbuf, "Index", 5) == 0) { - device->idx = atoi(tbuf + 5); - if (device->idx < 0) { - uc_error("error: %s:%d failed to get device index", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "EnableSequence", 14) == 0) { - ret = parse_sequence(uc_mgr, &device->enable, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse device enable" - " sequence", file, line); - goto err; - } - en_seq = 1; - continue; - } - - if (strncmp(tbuf, "DisableSequence", 15) == 0) { - uc_dbg("DisableSequence"); - ret = parse_sequence(uc_mgr, &device->disable, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse device disable sequence", - file, line); - goto err; - } - dis_seq = 1; - continue; - } - - if (strncmp(tbuf, "TransitionDevice", 14) == 0) { - struct transition_sequence *trans_seq; - - name = get_string(tbuf + 14); - if (name == NULL) - continue; - - uc_dbg("TransitionDevice %s", name); - - trans_seq = calloc(1, sizeof(struct transition_sequence)); - if (trans_seq == NULL) - return -ENOMEM; - - trans_seq->name = name; - - ret = parse_sequence(uc_mgr, &trans_seq->transition, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse transition" - " device", file, line); - goto err; - } - - prepend_transition(&device->transition_list, trans_seq); - continue; - } - - if (strncmp(tbuf, "MasterPlaybackVolume", 20) == 0) { - device->playback_volume_id = - get_control_name(tbuf + 20, line, file); - if (device->playback_volume_id == NULL) { - uc_error("error: %s:%d failed to get MasterPlaybackVolume", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterPlaybackSwitch", 20) == 0) { - device->playback_switch_id = - get_control_name(tbuf + 20, line, file); - if (device->playback_switch_id == NULL) { - uc_error("error: %s:%d failed to get MasterPlaybackSwitch", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterCaptureVolume", 19) == 0) { - device->capture_volume_id = - get_control_name(tbuf + 19, line, file); - if (device->capture_volume_id == NULL) { - uc_error("error: %s:%d failed to get MasterCaptureVolume%d", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "MasterCaptureSwitch", 19) == 0) { - device->capture_switch_id = - get_control_name(tbuf + 19, line, file); - if (device->capture_switch_id == NULL) { - uc_error("error: %s:%d failed to get MasterCaptureSwitch", - file, line); - goto err; - } - continue; - } - - /* end of section ?*/ - if (strncmp(tbuf, "EndSection", 10) == 0) { - end = 1; - break; - } - } - - /* do we have the basics for this device ? */ - if (!en_seq || !dis_seq || !end || !id) { - uc_error("error: invalid device"); - if (!en_seq) - uc_error("error: %s: device missing enable sequence", file); - if (!dis_seq) - uc_error("error: %s: device missing disable sequence", file); - if (!end) - uc_error("error: %s: device missing end", file); - return -EINVAL; - } - - verb->device_list[verb->num_devices++] = device->name; - *line_ = line; - return 0; - -err: - if (ferror(f)) { - uc_error("%s: failed to read device section", file); - return ferror(f); - } - return -ENOMEM; -} - -/* - * Parse Verb Section - * - * # Example Use case verb section for Voice call blah - * # By Joe Blogs <joe@blogs.com> - * - * SectionVerb - * # enable and disable sequences are compulsory - * EnableSequence - * 'Master Playback Switch':2:0,0 - * 'Master Playback Volume':2:25,25 - * msleep 50 - * 'Master Playback Switch':2:1,1 - * 'Master Playback Volume':2:50,50 - * EndSequence - * - * DisableSequence - * 'Master Playback Switch':2:0,0 - * 'Master Playback Volume':2:25,25 - * msleep 50 - * 'Master Playback Switch':2:1,1 - * 'Master Playback Volume':2:50,50 - * EndSequence - * - * # Optional QoS and ALSA PCMs - * QoS HiFi - * CapturePCM 0 - * PlaybackPCM 0 - * - * EndSection - */ -static int parse_verb(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *verb, FILE *f, int *line_, char *file) -{ - int line = *line_, end = 0, en_seq = 0, dis_seq = 0, ret; - char buf[MAX_BUF], *tbuf; - - /* read line by line */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* skip comments */ - if (buf[0] == '#') - continue; - - tbuf = buf; - /* skip spaces */ - while (isblank(*tbuf)) - tbuf++; - - if (strncmp(tbuf, "EnableSequence", 14) == 0) { - uc_dbg("Parse EnableSequence"); - ret = parse_sequence(uc_mgr, &verb->enable, f, &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse verb enable sequence", - file, line); - goto err; - } - en_seq = 1; - continue; - } - - if (strncmp(tbuf, "DisableSequence", 15) == 0) { - uc_dbg("Parse DisableSequence"); - ret = parse_sequence(uc_mgr, &verb->disable, f, &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse verb disable sequence", - file, line); - goto err; - } - dis_seq = 1; - continue; - } - - if (strncmp(tbuf, "TransitionVerb", 12) == 0) { - struct transition_sequence *trans_seq; - char *name; - - name = get_string(tbuf + 12); - if (name == NULL) - continue; - - uc_dbg("TransitionVerb %s", name); - - trans_seq = calloc(1, sizeof(struct transition_sequence)); - if (trans_seq == NULL) - return -ENOMEM; - - trans_seq->name = name; - - ret = parse_sequence(uc_mgr, &trans_seq->transition, f, - &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse transition verb", - file, line); - goto err; - } - - prepend_transition(&verb->transition_list, trans_seq); - continue; - } - - if (strncmp(tbuf, "QoS", 3) == 0) { - uc_dbg("Parse Qos"); - verb->qos = get_enum(tbuf + 3); - continue; - } - - if (strncmp(tbuf, "CapturePCM", 3) == 0) { - uc_dbg("Parse CapTurePCM"); - verb->capture_pcm = atoi(tbuf + 3); - if (verb->capture_pcm < 0) { - uc_error("error: %s:%d failed to get Capture PCM ID", - file, line); - goto err; - } - continue; - } - - if (strncmp(tbuf, "PlaybackPCM", 3) == 0) { - uc_dbg("Parse PlaybackPCM"); - verb->playback_pcm = atoi(tbuf + 3); - if (verb->playback_pcm < 0) { - uc_error("error: %s: %d failed to get Playback PCM ID", - file, line); - goto err; - } - continue; - } - - /* end of section ?*/ - if (strncmp(tbuf, "EndSection", 10) == 0) { - end = 1; - break; - } - } - - /* do we have both use case name and file ? */ - if (!en_seq || !dis_seq || !end) { - uc_error("error: invalid verb"); - if (!en_seq) - uc_error("error: %s: verb missing enable sequence", file); - if (!dis_seq) - uc_error("error: %s: verb missing disable sequence", file); - if (!end) - uc_error("error: %s: verb missing end", file); - return -EINVAL; - } - - *line_ = line; - return 0; - -err: - if (ferror(f)) { - uc_error("error: failed to read verb master section"); - return ferror(f); - } - return -ENOMEM; -} - -/* - * Parse a Use case verb file. - * - * This file contains the following :- - * o Verb enable and disable sequences. - * o Supported Device enable and disable sequences for verb. - * o Supported Modifier enable and disable sequences for verb - * o Optional QoS for the verb and modifiers. - * o Optional PCM device ID for verb and modifiers - * o Alias kcontrols IDs for master and volumes and mutes. - */ -static int parse_verb_file(snd_use_case_mgr_t *uc_mgr, - char *use_case_name, char *file) -{ - struct use_case_verb *verb; - FILE *f; - int line = 0, ret = 0, dev_idx = 0, verb_idx = 0, mod_idx = 0; - char buf[MAX_BUF], *tbuf; - char filename[MAX_FILE]; - - /* allocate verb */ - uc_mgr->verb = realloc(uc_mgr->verb, - (uc_mgr->num_verbs + 1) * sizeof(struct use_case_verb)); - if (uc_mgr->verb == NULL) - return -ENOMEM; - verb = uc_mgr->verb +uc_mgr->num_verbs; - bzero(verb, sizeof(struct use_case_verb)); - - /* open Verb file for reading */ - sprintf(filename, "%s/%s/%s", ALSA_USE_CASE_DIR, - uc_mgr->card_name, file); - - f = fopen(filename, "r"); - if (f == NULL) { - uc_error("error: failed to open verb file %s : %d", - filename, -errno); - return -errno; - } - - /* read line by line */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* skip comments */ - if (buf[0] == '#') - continue; - - /* skip leading spaces */ - tbuf = buf; - while (isblank(*tbuf)) - tbuf++; - - /* find verb section and parse it */ - if (strncmp(tbuf, "SectionVerb", 11) == 0) { - ret = parse_verb(uc_mgr, verb, f, &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse verb %s", - file, line, use_case_name); - goto err; - } - verb_idx++; - continue; - } - - /* find device sections and parse them */ - if (strncmp(tbuf, "SectionDevice", 13) == 0) { - - if (verb->num_devices >= MAX_DEVICE) { - uc_error("error: %s:%d verb number of devices %d" - "exceeds max %d", file, line, - verb->num_devices, MAX_DEVICE); - goto err; - } - ret = parse_device(uc_mgr, verb, f, &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse device", - file, line); - goto err; - } - dev_idx++; - continue; - } - - /* find modifier sections and parse them */ - if (strncmp(tbuf, "SectionModifier", 15) == 0) { - if (verb->num_modifiers >= MAX_MODIFIER) { - uc_error("error: %s:%d verb number of modifiers %d" - " exceeds max %d", file, line, - verb->num_modifiers, MAX_MODIFIER); - goto err; - } - - ret = parse_modifier(uc_mgr, verb, f, &line, file); - if (ret < 0) { - uc_error("error: %s:%d failed to parse modifier", - file, line); - goto err; - } - mod_idx++; - continue; - } - } - - /* use case verb must have at least verb and 1 device */ - if (verb_idx && dev_idx) { - uc_mgr->verb_list[uc_mgr->num_verbs++] = use_case_name; - verb->name = use_case_name; - } - else { - uc_error("error: failed to parse use case %s", file); - if (verb_idx == 0) - uc_error("error: no use case verb defined", file); - if (dev_idx == 0) - uc_error("error: no use case device defined", file); - ret = -EINVAL; - } - -err: - fclose(f); - return ret; -} - -/* - * Parse master section for "Use Case" and "File" tags. - */ -static int parse_master_section(snd_use_case_mgr_t *uc_mgr, FILE *f, - int *line_) -{ - int line = *line_ - 1, end = 0; - char buf[MAX_BUF], *tbuf; - char *file = NULL, *use_case_name = NULL, *comment; - - /* read line by line */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* skip comments */ - if (buf[0] == '#') - continue; - - uc_dbg("%s", buf); - - /* skip leading spaces */ - tbuf = buf; - while (isblank(*tbuf)) - tbuf++; - - /* get use case name */ - if (strncmp(tbuf, "Use Case", 8) == 0) { - use_case_name = get_string(tbuf + 8); - if (use_case_name == NULL) { - uc_error("error: failed to get Use Case at line %d", - line); - goto err; - } - - if (uc_mgr->num_verbs >= MAX_VERB) { - uc_error("error: verb number exceed max %d", - uc_mgr->num_verbs, MAX_VERB); - goto err; - } - continue; - } - - /* get use case verb file name */ - if (strncmp(tbuf, "File", 4) == 0) { - file = get_string(tbuf + 4); - if (file == NULL) { - uc_error("error: failed to get File at line %d", line); - goto err; - } - continue; - } - - /* get optional use case comment */ - if (strncmp(tbuf, "Comment", 7) == 0) { - comment = get_string(tbuf + 7); - if (comment == NULL) { - uc_error("error: failed to get Comment at line %d", - line); - goto err; - } - continue; - } - - /* end of section ?*/ - if (strncmp(tbuf, "EndSectionUseCase", 10) == 0) { - end = 1; - break; - } - } - - uc_dbg("use_case_name %s file %s end %d", use_case_name, file, end); - - /* do we have both use case name and file ? */ - if (!use_case_name || !file || !end) { - uc_error("error: failed to find use case\n"); - if (!use_case_name) - uc_error("error: use case missing name"); - if (!file) - uc_error("error: use case missing file"); - if (!end) - uc_error("error: use case missing end"); - return -EINVAL; - } - - *line_ = line; - - /* parse verb file */ - return parse_verb_file(uc_mgr, use_case_name, file); - -err: - if (ferror(f)) { - uc_error("error: failed to read use case master section"); - return ferror(f); - } - return -ENOMEM; -} - -/* - * Each sound card has a master sound card file that lists all the supported - * use case verbs for that sound card. i.e. - * - * #Example master file for blah sound card - * #By Joe Blogs <joe@bloggs.org> - * - * # The file is divided into Use case sections. One section per use case verb. - * - * SectionUseCase - * Use Case "Voice Call" - * File "voice_call_blah" - * Comment "Make a voice phone call." - * EndSectionUseCase - * - * SectionUseCase - * Use Case "HiFi" - * File "hifi_blah" - * Comment "Play and record HiFi quality Music." - * EndSectionUseCase - * - * # This file also stores the default sound card state. - * - * SectionDefaults - * 'Master Playback Switch':2:1,1 - * 'Master Playback Volume':2:25,25 - * 'Master Mono Playback Switch':1:0 - * 'Master Mono Playback Volume':1:0 - * 'PCM Switch':2:1,1 - * ........ - * EndSectionDefaults - * - * # End of example file. - */ -static int parse_master_file(snd_use_case_mgr_t *uc_mgr, FILE *f, - char *file) -{ - int line = 0, ret = 0; - char buf[MAX_BUF], *tbuf; - - /* parse master config sections */ - while(fgets(buf, MAX_BUF, f) != NULL) { - - line++; - - /* ignore comments */ - if (buf[0] == '#') { - line++; - continue; - } - - /* find use case section and parse it */ - if (strncmp(buf, "SectionUseCase", 14) == 0) { - tbuf = buf + 14; - while (isblank(*tbuf)) - tbuf++; - ret = parse_master_section(uc_mgr, f, &line); - if (ret < 0) - goto err; - } - - /* find default control values section and parse it */ - if (strncmp(buf, "SectionDefaults", 15) == 0) { - tbuf = buf + 15; - while (isblank(*tbuf)) - tbuf++; - ret = parse_controls(uc_mgr, f, &line, file); - if (ret < 0) - goto err; - } - } - -err: - if (ferror(f)) { - uc_error("error: %s: failed to read master file", file); - return ferror(f); - } - return ret; -} - -/* load master use case file for sound card */ -static int import_master_config(snd_use_case_mgr_t *uc_mgr) -{ - int ret; - FILE *f; - char filename[MAX_FILE]; - - sprintf(filename, "%s/%s/%s.conf", ALSA_USE_CASE_DIR, - uc_mgr->card_name, uc_mgr->card_name); - - uc_dbg("master config file %s", filename); - - f = fopen(filename, "r"); - if (f == NULL) { - uc_error("error: couldn't open %s configuration file %s", - uc_mgr->card_name, filename); - return -errno; - } - - ret = parse_master_file(uc_mgr, f, uc_mgr->card_name); - fclose(f); - return ret; -} - -static int parse_card_controls(snd_use_case_mgr_t *uc_mgr) -{ - struct control_settings *control; - int i, ret = 0; - snd_ctl_elem_id_t *id; - - /* allocate memory for controls */ - uc_mgr->control = calloc(uc_mgr->count, - sizeof(struct control_settings)); - if (uc_mgr->control == NULL) { - uc_error("error: not enough memory to store controls.\n"); - return -ENOMEM; - } - control = uc_mgr->control; - snd_ctl_elem_id_alloca(&id); - - /* iterate through each kcontrol and add to manager */ - for (i = 0; i < uc_mgr->count; ++i) { - - snd_ctl_elem_list_get_id(uc_mgr->list, i, id); - ret = add_control(uc_mgr->handle, id, control++); - if (ret < 0) { - uc_error("error: failed to add control error %s\n", - __func__, snd_strerror(ret)); - break; - } - } - - return ret; -} - -static int import_use_case_files(snd_use_case_mgr_t *uc_mgr) -{ - int ret; - - /* import the master file and default kcontrol settings */ - ret = import_master_config(uc_mgr); - if (ret < 0) { - uc_error("error: failed to parse master use case config %s\n", - uc_mgr->card_name); - return ret; - } - - return 0; -} - - -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 - * \return 0 = successful negative = error - */ -int snd_use_case_switch_device(snd_use_case_mgr_t *uc_mgr, - const char *old, const char *new) -{ - static struct sequence_element *trans_sequence; - struct use_case_device *old_device; - struct use_case_device *new_device; - int ret = 0, old_id, new_id; - - uc_dbg("old %s, new %s", old, new); - - pthread_mutex_lock(&uc_mgr->mutex); - - old_device = get_device(uc_mgr, old, &old_id); - if (!old_device) { - uc_error("error: device %s not found", old); - ret = -EINVAL; - goto out; - } - - if (!get_device_status(uc_mgr, old_id)) { - uc_error("error: device %s not enabled", old); - goto out; - } - - new_device = get_device(uc_mgr, new, &new_id); - if (!new_device) { - uc_error("error: device %s not found", new); - ret = -EINVAL; - goto out; - } - - trans_sequence = get_transition_sequence(old_device->transition_list, new); - if (trans_sequence != NULL) { - - uc_dbg("find transition sequece %s->%s", old, new); - - ret = exec_transition_sequence(uc_mgr, trans_sequence); - if (ret) - goto out; - - set_device_status(uc_mgr, old_id, 0); - set_device_status(uc_mgr, new_id, 1); - } else { - /* use lock in config_use_case_device */ - pthread_mutex_unlock(&uc_mgr->mutex); - - config_use_case_device(uc_mgr, old, 0); - config_use_case_device(uc_mgr, new, 1); - - return 0; - } -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - -/* - * Check to make sure that the modifier actually supports any of the - * active devices. - */ -static int is_modifier_valid(snd_use_case_mgr_t *uc_mgr, - struct use_case_verb *verb, struct use_case_modifier *modifier) -{ - struct dev_list *dev_list; - int dev; - - /* check modifier list against each enabled device */ - for (dev = 0; dev < verb->num_devices; dev++) { - if (!get_device_status(uc_mgr, dev)) - continue; - - dev_list = modifier->dev_list; - uc_dbg("checking device %s for %s", verb->device[dev].name, - dev_list->name ? dev_list->name : ""); - - while (dev_list) { - uc_dbg("device supports %s", dev_list->name); - if (!strcmp(dev_list->name, verb->device[dev].name)) - return 1; - dev_list = dev_list->next; - } - } - return 0; -} - -static int config_use_case_mod(snd_use_case_mgr_t *uc_mgr, - const char *modifier_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) { - 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 modifier_name %s", uc_mgr, modifier_name); - - /* find modifier name */ - for (i = 0; i <verb->num_modifiers; i++) { - uc_dbg("verb->num_modifiers %d %s", i, verb->modifier[i].name); - if (!strcmp(verb->modifier[i].name, modifier_name) && - is_modifier_valid(uc_mgr, verb, &verb->modifier[i])) - goto found; - } - - uc_error("error: use case modifier %s not found or invalid", - modifier_name); - ret = -EINVAL; - goto out; - -found: - if (enable) { - /* Initialise the new use case device */ - ret = enable_use_case_modifier(uc_mgr, i, uc_mgr->list, - uc_mgr->handle); - if (ret < 0) - goto out; - } else { - /* disable the old device */ - ret = disable_use_case_modifier(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 modifier - * \param uc_mgr Use case manager - * \param modifier the modifier to be enabled - * \return 0 = successful negative = error - */ -int snd_use_case_enable_modifier(snd_use_case_mgr_t *uc_mgr, - const char *modifier) -{ - return config_use_case_mod(uc_mgr, modifier, 1); -} - -/** - * \brief Disable use case modifier - * \param uc_mgr Use case manager - * \param modifier the modifier to be disabled - * \return 0 = successful negative = error - */ -int snd_use_case_disable_modifier(snd_use_case_mgr_t *uc_mgr, - const char *modifier) -{ - return config_use_case_mod(uc_mgr, modifier, 0); -} - -static struct use_case_modifier *get_modifier(snd_use_case_mgr_t *uc_mgr, - const char *name, int *mod_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); - - uc_dbg("uc_mgr %p modifier_name %s", uc_mgr, name); - - for (i = 0; i < verb->num_modifiers; i++) { - uc_dbg("verb->num_devices %s", verb->modifier[i].name); - - if (!strcmp(verb->modifier[i].name, name)) { - if (mod_id) - *mod_id = i; - return &verb->modifier[i]; - } - } - - return NULL; -} - -/** - * \brief Disable old_modifier and then enable new_modifier. - * If old_modifier is not enabled just return. - * Check transition sequence firstly. - * \param uc_mgr Use case manager - * \param old the modifier to be closed - * \param new the modifier to be opened - * \return 0 = successful negative = error - */ -int snd_use_case_switch_modifier(snd_use_case_mgr_t *uc_mgr, - const char *old, const char *new) -{ - struct use_case_modifier *old_modifier; - struct use_case_modifier *new_modifier; - static struct sequence_element *trans_sequence; - int ret = 0, old_id, new_id - - uc_dbg("old %s, new %s", old, new); - - pthread_mutex_lock(&uc_mgr->mutex); - - old_modifier = get_modifier(uc_mgr, old, &old_id); - if (!old_modifier) { - uc_error("error: modifier %s not found", old); - ret = -EINVAL; - goto out; - } - - if (!get_modifier_status(uc_mgr, old_id)) { - uc_error("error: modifier %s not enabled", old); - ret = -EINVAL; - goto out; - } - - new_modifier = get_modifier(uc_mgr, new, &new_id); - if (!new_modifier) { - uc_error("error: modifier %s not found", new); - ret = -EINVAL; - goto out; - } - - trans_sequence = get_transition_sequence( - old_modifier->transition_list, new); - if (trans_sequence != NULL) { - uc_dbg("find transition sequence %s->%s", old, new); - - ret = exec_transition_sequence(uc_mgr, trans_sequence); - if (ret) - goto out; - - set_device_status(uc_mgr, old_id, 0); - set_device_status(uc_mgr, new_id, 1); - } else { - /* use lock in config_use_case_mod*/ - pthread_mutex_unlock(&uc_mgr->mutex); - - config_use_case_mod(uc_mgr, old, 0); - config_use_case_mod(uc_mgr, new, 1); - - return 0; - } -out: - pthread_mutex_unlock(&uc_mgr->mutex); - return ret; -} - -/** - * \brief Get current use case verb from sound card - * \param uc_mgr use case manager - * \return Verb Name if success, otherwise NULL - */ -const char *snd_use_case_get_verb(snd_use_case_mgr_t *uc_mgr) -{ - const char *ret = NULL; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) - ret = uc_mgr->verb_list[uc_mgr->card.current_verb]; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get device status for current use case verb - * \param uc_mgr Use case manager - * \param device_name The device we are interested in. - * \return - 1 = enabled, 0 = disabled, negative = error - */ -int snd_use_case_get_device_status(snd_use_case_mgr_t *uc_mgr, - const char *device_name) -{ - struct use_case_device *device; - int ret = -EINVAL, dev_id; - - pthread_mutex_lock(&uc_mgr->mutex); - - device = get_device(uc_mgr, device_name, &dev_id); - if (device == NULL) { - uc_error("error: use case device %s not found", device_name); - goto out; - } - - ret = get_device_status(uc_mgr, dev_id); -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get modifier status for current use case verb - * \param uc_mgr Use case manager - * \param device_name The device we are interested in. - * \return - 1 = enabled, 0 = disabled, negative = error - */ -int snd_use_case_get_modifier_status(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - int ret = -EINVAL, mod_id; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, &mod_id); - if (modifier == NULL) { - uc_error("error: use case modifier %s not found", modifier_name); - goto out; - } - - ret = get_modifier_status(uc_mgr, mod_id); -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case verb QoS - * \param uc_mgr use case manager - * \return QoS level - */ -enum snd_use_case_qos - snd_use_case_get_verb_qos(snd_use_case_mgr_t *uc_mgr) -{ - struct use_case_verb *verb; - enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->qos; - } - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case modifier QoS - * \param uc_mgr use case manager - * \return QoS level - */ -enum snd_use_case_qos - snd_use_case_get_mod_qos(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - enum snd_use_case_qos ret = SND_USE_CASE_QOS_UNKNOWN; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier != NULL) - ret = modifier->qos; - else - uc_error("error: use case modifier %s not found", modifier_name); - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case verb playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_verb_playback_pcm(snd_use_case_mgr_t *uc_mgr) -{ - struct use_case_verb *verb; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->playback_pcm; - } - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case verb playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_verb_capture_pcm(snd_use_case_mgr_t *uc_mgr) -{ - struct use_case_verb *verb; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - if (uc_mgr->card.current_verb != VERB_NOT_INITIALISED) { - verb = &uc_mgr->verb[uc_mgr->card.current_verb]; - ret = verb->capture_pcm; - } - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case modifier playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_mod_playback_pcm(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier == NULL) - uc_error("error: use case modifier %s not found", - modifier_name); - else - ret = modifier->playback_pcm; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get current use case modifier playback PCM - * \param uc_mgr use case manager - * \return PCM number if success, otherwise negative - */ -int snd_use_case_get_mod_capture_pcm(snd_use_case_mgr_t *uc_mgr, - const char *modifier_name) -{ - struct use_case_modifier *modifier; - int ret = -EINVAL; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (modifier == NULL) - uc_error("error: use case modifier %s not found", - modifier_name); - else - ret = modifier->capture_pcm; - - pthread_mutex_unlock(&uc_mgr->mutex); - - return ret; -} - -/** - * \brief Get volume/mute control name depending on use case device. - * \param uc_mgr use case manager - * \param type the control type we are looking for - * \param device_name The use case device we are interested in. - * \return control name if success, otherwise NULL - * - * Get the control id for common volume and mute controls that are aliased - * in the named use case device. - */ -const char *snd_use_case_get_device_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, - enum snd_use_case_control_alias type, const char *device_name) -{ - struct use_case_device *device; - const char *kcontrol_name = NULL; - - pthread_mutex_lock(&uc_mgr->mutex); - - device = get_device(uc_mgr, device_name, NULL); - if (!device) { - uc_error("error: device %s not found", device_name); - goto out; - } - - switch (type) { - case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: - kcontrol_name = device->playback_volume_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: - kcontrol_name = device->capture_volume_id; - break; - case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: - kcontrol_name = device->playback_switch_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: - kcontrol_name = device->capture_switch_id; - break; - default: - uc_error("error: invalid control alias %d", type); - break; - } - -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return kcontrol_name; -} - -/** - * \brief Get volume/mute control IDs depending on use case modifier. - * \param uc_mgr use case manager - * \param type the control type we are looking for - * \param modifier_name The use case modifier we are interested in. - * \return ID if success, otherwise a negative error code - * - * Get the control id for common volume and mute controls that are aliased - * in the named use case device. - */ -const char *snd_use_case_get_modifier_ctl_elem_name(snd_use_case_mgr_t *uc_mgr, - enum snd_use_case_control_alias type, const char *modifier_name) -{ - struct use_case_modifier *modifier; - const char *kcontrol_name = NULL; - - pthread_mutex_lock(&uc_mgr->mutex); - - modifier = get_modifier(uc_mgr, modifier_name, NULL); - if (!modifier) { - uc_error("error: modifier %s not found", modifier_name); - goto out; - } - - switch (type) { - case SND_USE_CASE_ALIAS_PLAYBACK_VOLUME: - kcontrol_name = modifier->playback_volume_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_VOLUME: - kcontrol_name = modifier->capture_volume_id; - break; - case SND_USE_CASE_ALIAS_PLAYBACK_SWITCH: - kcontrol_name = modifier->playback_switch_id; - break; - case SND_USE_CASE_ALIAS_CAPTURE_SWITCH: - kcontrol_name = modifier->capture_switch_id; - break; - default: - uc_error("error: invalid control alias %d", type); - break; - } - -out: - pthread_mutex_unlock(&uc_mgr->mutex); - - return kcontrol_name; -} |