mirror of
https://gitee.com/openharmony/third_party_alsa-lib
synced 2024-11-27 01:31:08 +00:00
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>
This commit is contained in:
parent
fcc9adb260
commit
1c79fad969
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -176,11 +176,18 @@ 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);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
10
src/ucm/Makefile.am
Normal file
10
src/ucm/Makefile.am
Normal file
@ -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
|
1448
src/ucm/main.c
Normal file
1448
src/ucm/main.c
Normal file
File diff suppressed because it is too large
Load Diff
956
src/ucm/parser.c
Normal file
956
src/ucm/parser.c
Normal file
@ -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;
|
||||
}
|
217
src/ucm/ucm_local.h
Normal file
217
src/ucm/ucm_local.h
Normal file
@ -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);
|
220
src/ucm/utils.c
Normal file
220
src/ucm/utils.c
Normal file
@ -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);
|
||||
}
|
3515
src/use-case.c
3515
src/use-case.c
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user