Merge branch 'ucm'

This commit is contained in:
Jaroslav Kysela 2011-01-24 14:55:23 +01:00
commit 916a203d94
18 changed files with 3840 additions and 5 deletions

View file

@ -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 \

View file

@ -28,6 +28,7 @@ INPUT = @top_srcdir@/doc/index.doxygen \
@top_srcdir@/include/pcm_ioplug.h \
@top_srcdir@/include/control_external.h \
@top_srcdir@/include/mixer.h \
@top_srcdir@/include/use-case.h \
@top_srcdir@/src/error.c \
@top_srcdir@/src/dlmisc.c \
@top_srcdir@/src/async.c \
@ -76,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 \
@ -91,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

View file

@ -5,7 +5,7 @@ alsaincludedir = ${includedir}/alsa
alsainclude_HEADERS = asoundlib.h asoundef.h \
version.h global.h input.h output.h error.h \
conf.h control.h iatomic.h
conf.h control.h iatomic.h use-case.h
if BUILD_CTL_PLUGIN_EXT
alsainclude_HEADERS += control_external.h

View file

@ -284,6 +284,13 @@ unsigned int snd_ctl_event_elem_get_index(const snd_ctl_event_t *obj);
int snd_ctl_elem_list_alloc_space(snd_ctl_elem_list_t *obj, unsigned int entries);
void snd_ctl_elem_list_free_space(snd_ctl_elem_list_t *obj);
char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id);
int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str);
int snd_ctl_ascii_value_parse(snd_ctl_t *handle,
snd_ctl_elem_value_t *dst,
snd_ctl_elem_info_t *info,
const char *value);
size_t snd_ctl_elem_id_sizeof(void);
/** \hideinitializer
* \brief allocate an invalid #snd_ctl_elem_id_t using standard alloca

View file

@ -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 */

349
include/use-case.h Normal file
View file

@ -0,0 +1,349 @@
/**
* \file include/use-case.h
* \brief use case interface for the ALSA driver
* \author Liam Girdwood <lrg@slimlogic.co.uk>
* \author Stefan Schmidt <stefan@slimlogic.co.uk>
* \author Jaroslav Kysela <perex@perex.cz>
* \author Justin Xu <justinx@slimlogic.co.uk>
* \date 2008-2010
*/
/*
*
* 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.1 of
* the License, or (at your option) any later version.
*
* This program 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 Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright (C) 2008-2010 SlimLogic Ltd
* Copyright (C) 2010 Wolfson Microelectronics PLC
* Copyright (C) 2010 Texas Instruments Inc.
*
* 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.
*/
#ifndef __ALSA_USE_CASE_H
#define __ALSA_USE_CASE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup Use Case Interface
* The ALSA Use Case manager interface.
* See \ref Usecase page for more details.
* \{
*/
/**
* ALSA Use Case Interface
*
* The use case manager works by configuring the sound card ALSA kcontrols to
* change the hardware digital and analog audio routing to match the requested
* device use case. The use case manager kcontrol configurations are stored in
* easy to modify text files.
*
* An audio use case can be defined by a verb and device parameter. The verb
* describes the use case action i.e. a phone call, listening to music, recording
* a conversation etc. The device describes the physical audio capture and playback
* hardware i.e. headphones, phone handset, bluetooth headset, etc.
*
* It's intended clients will mostly only need to set the use case verb and
* device for each system use case change (as the verb and device parameters
* cover most audio use cases).
*
* However there are times when a use case has to be modified at runtime. e.g.
*
* o Incoming phone call when the device is playing music
* o Recording sections of a phone call
* o Playing tones during a call.
*
* In order to allow asynchronous runtime use case adaptations, we have a third
* optional modifier parameter that can be used to further configure
* the use case during live audio runtime.
*
* This interface allows clients to :-
*
* o Query the supported use case verbs, devices and modifiers for the machine.
* o Set and Get use case verbs, devices and modifiers for the machine.
* o Get the ALSA PCM playback and capture device PCMs for use case verb and
* modifier.
* o Get the TQ parameter for each use case verb and modifier.
* o Get the ALSA master playback and capture volume/switch kcontrols
* for each use case.
*/
/*
* Use Case Verb.
*
* The use case verb is the main device audio action. e.g. the "HiFi" use
* case verb will configure the audio hardware for HiFi Music playback
* and capture.
*/
#define SND_USE_CASE_VERB_INACTIVE "Inactive"
#define SND_USE_CASE_VERB_HIFI "HiFi"
#define SND_USE_CASE_VERB_HIFI_LOW_POWER "HiFi Low Power"
#define SND_USE_CASE_VERB_VOICE "Voice"
#define SND_USE_CASE_VERB_VOICE_LOW_POWER "Voice Low Power"
#define SND_USE_CASE_VERB_VOICECALL "Voice Call"
#define SND_USE_CASE_VERB_IP_VOICECALL "Voice Call IP"
#define SND_USE_CASE_VERB_ANALOG_RADIO "FM Analog Radio"
#define SND_USE_CASE_VERB_DIGITAL_RADIO "FM Digital Radio"
/* add new verbs to end of list */
/*
* Use Case Device.
*
* Physical system devices the render and capture audio. Devices can be OR'ed
* together to support audio on similtanious devices.
*/
#define SND_USE_CASE_DEV_NONE "None"
#define SND_USE_CASE_DEV_SPEAKER "Speaker"
#define SND_USE_CASE_DEV_LINE "Line"
#define SND_USE_CASE_DEV_HEADPHONES "Headphones"
#define SND_USE_CASE_DEV_HEADSET "Headset"
#define SND_USE_CASE_DEV_HANDSET "Handset"
#define SND_USE_CASE_DEV_BLUETOOTH "Bluetooth"
#define SND_USE_CASE_DEV_EARPIECE "Earpiece"
#define SND_USE_CASE_DEV_SPDIF "SPDIF"
#define SND_USE_CASE_DEV_HDMI "HDMI"
/* add new devices to end of list */
/*
* Use Case Modifiers.
*
* The use case modifier allows runtime configuration changes to deal with
* asynchronous events.
*
* e.g. to record a voice call :-
* 1. Set verb to SND_USE_CASE_VERB_VOICECALL (for voice call)
* 2. Set modifier SND_USE_CASE_MOD_CAPTURE_VOICE when capture required.
* 3. Call snd_use_case_get("_pcm_/_cdevice") to get ALSA source PCM name
* with captured voice pcm data.
*
* e.g. to play a ring tone when listenin to MP3 Music :-
* 1. Set verb to SND_USE_CASE_VERB_HIFI (for MP3 playback)
* 2. Set modifier to SND_USE_CASE_MOD_PLAY_TONE when incoming call happens.
* 3. Call snd_use_case_get("_pcm_/_pdevice") to get ALSA PCM sink name for
* ringtone pcm data.
*/
#define SND_USE_CASE_MOD_CAPTURE_VOICE "Capture Voice"
#define SND_USE_CASE_MOD_CAPTURE_MUSIC "Capture Music"
#define SND_USE_CASE_MOD_PLAY_MUSIC "Play Music"
#define SND_USE_CASE_MOD_PLAY_VOICE "Play Voice"
#define SND_USE_CASE_MOD_PLAY_TONE "Play Tone"
#define SND_USE_CASE_MOD_ECHO_REF "Echo Reference"
/* add new modifiers to end of list */
/**
* TQ - Tone Quality
*
* The interface allows clients to determine the audio TQ required for each
* use case verb and modifier. It's intended as an optional hint to the
* audio driver in order to lower power consumption.
*
*/
#define SND_USE_CASE_TQ_MUSIC "Music"
#define SND_USE_CASE_TQ_VOICE "Voice"
#define SND_USE_CASE_TQ_TONES "Tones"
/** use case container */
typedef struct snd_use_case_mgr snd_use_case_mgr_t;
/**
* \brief Create an identifier
* \param fmt Format (sprintf like)
* \param ... Optional arguments for sprintf like format
* \return Allocated string identifier or NULL on error
*/
char *snd_use_case_identifier(const char *fmt, ...);
/**
* \brief Free a string list
* \param list The string list to free
* \param items Count of strings
* \return Zero if success, otherwise a negative error code
*/
int snd_use_case_free_list(const char *list[], int items);
/**
* \brief Obtain a list of entries
* \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:
* NULL - get card list
* (in pair cardname+comment)
* _verbs - get verb list
* (in pair verb+comment)
* _devices[/<verb>] - get list of supported devices
* (in pair device+comment)
* _modifiers[/<verb>]- get list of supported modifiers
* (in pair modifier+comment)
* TQ[/<verb>] - get list of TQ identifiers
* _enadevs - get list of enabled devices
* _enamods - get list of enabled modifiers
*
*/
int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
const char *identifier,
const char **list[]);
/**
* \brief Get current - string
* \param uc_mgr Use case manager
* \param identifier
* \param value Value pointer
* \return Zero if success, otherwise a negative error code
*
* Note: String is dynamically allocated, use free() to
* deallocate this string.
*
* Known identifiers:
* NULL - return current card
* _verb - return current verb
* TQ[/<modifier>] - Tone Quality [for given modifier]
* PlaybackPCM[/<modifier>] - full PCM playback device name
* CapturePCM[/<modifier>] - full PCM capture device name
* PlaybackCTL[/<modifier>] - playback control device name
* PlaybackVolume[/<modifier>] - playback control volume ID string
* PlaybackSwitch[/<modifier>] - playback control switch ID string
* CaptureCTL[/<modifier>] - capture control device name
* CaptureVolume[/<modifier>] - capture control volume ID string
* CaptureSwitch[/<modifier>] - capture control switch ID string
* PlaybackMixer[/<modifier>] - name of playback mixer
* PlaybackMixerID[/<modifier>] - mixer playback ID
* CaptureMixer[/<modifier>] - name of capture mixer
* CaptureMixerID[/<modifier>] - mixer capture ID
*/
int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
const char *identifier,
const char **value);
/**
* \brief Get current - integer
* \param uc_mgr Use case manager
* \param identifier
* \param value result
* \return Zero if success, otherwise a negative error code
*
* Known identifiers:
* _devstatus/<device> - return status for given device
* _modstatus/<modifier> - return status for given modifier
*/
int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
const char *identifier,
long *value);
/**
* \brief Set new
* \param uc_mgr Use case manager
* \param identifier
* \param value Value
* \return Zero if success, otherwise a negative error code
*
* Known identifiers:
* _verb - set current verb = value
* _enadev - enable given device = value
* _disdev - disable given device = value
* _swdev/<old_device> - new_device = value
* - disable old_device and then enable new_device
* - if old_device is not enabled just return
* - check transmit sequence firstly
* _enamod - enable given modifier = value
* _dismod - disable given modifier = value
* _swmod/<old_modifier> - new_modifier = value
* - disable old_modifier and then enable new_modifier
* - if old_modifier is not enabled just return
* - check transmit sequence firstly
*/
int snd_use_case_set(snd_use_case_mgr_t *uc_mgr,
const char *identifier,
const char *value);
/**
* \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 zero if success, otherwise a negative error code
*/
int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr, const char *card_name);
/**
* \brief Reload and re-parse use case configuration files for sound card.
* \param uc_mgr Use case manager
* \return zero if success, otherwise a negative error code
*/
int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr);
/**
* \brief Close use case manager
* \param uc_mgr Use case manager
* \return zero if success, otherwise a negative error code
*/
int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr);
/**
* \brief Reset use case manager verb, device, modifier to deafult settings.
* \param uc_mgr Use case manager
* \return zero if success, otherwise a negative error code
*/
int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr);
/*
* helper functions
*/
/**
* \brief Obtain a list of cards
* \param list Returned allocated list
* \return Number of list entries if success, otherwise a negative error code
*/
static inline int snd_use_case_card_list(const char **list[])
{
return snd_use_case_get_list(NULL, NULL, list);
}
/**
* \brief Obtain a list of verbs
* \param uc_mgr Use case manager
* \param list Returned list of verbs
* \return Number of list entries if success, otherwise a negative error code
*/
static inline int snd_use_case_verb_list(snd_use_case_mgr_t *uc_mgr,
const char **list[])
{
return snd_use_case_get_list(uc_mgr, "_verbs", list);
}
/**
* \}
*/
#ifdef __cplusplus
}
#endif
#endif /* __ALSA_USE_CASE_H */

View file

@ -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

View file

@ -1,7 +1,8 @@
EXTRA_LTLIBRARIES = libcontrol.la
libcontrol_la_SOURCES = cards.c tlv.c namehint.c hcontrol.c \
control.c control_hw.c setup.c control_symbols.c
control.c control_hw.c setup.c ctlparse.c \
control_symbols.c
if BUILD_CTL_PLUGIN_SHM
libcontrol_la_SOURCES += control_shm.c
endif

351
src/control/ctlparse.c Normal file
View file

@ -0,0 +1,351 @@
/**
* \file control/control.c
* \brief CTL interface - parse ASCII identifiers and values
* \author Jaroslav Kysela <perex@perex.cz>
* \date 2010
*/
/*
* Control Interface - ASCII parser
* Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
*
*
* 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.1 of
* the License, or (at your option) any later version.
*
* This program 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 Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "control_local.h"
/* Function to convert from percentage to volume. val = percentage */
#define convert_prange1(val, min, max) \
ceil((val) * ((max) - (min)) * 0.01 + (min))
#define check_range(val, min, max) \
((val < min) ? (min) : ((val > max) ? (max) : (val)))
static long get_integer(const char **ptr, long min, long max)
{
long val = min;
char *p = (char *)*ptr, *s;
if (*p == ':')
p++;
if (*p == '\0' || (!isdigit(*p) && *p != '-'))
goto out;
s = p;
val = strtol(s, &p, 10);
if (*p == '.') {
p++;
strtol(p, &p, 10);
}
if (*p == '%') {
val = (long)convert_prange1(strtod(s, NULL), min, max);
p++;
}
val = check_range(val, min, max);
if (*p == ',')
p++;
out:
*ptr = p;
return val;
}
static long long get_integer64(const char **ptr, long long min, long long max)
{
long long val = min;
char *p = (char *)*ptr, *s;
if (*p == ':')
p++;
if (*p == '\0' || (!isdigit(*p) && *p != '-'))
goto out;
s = p;
val = strtol(s, &p, 10);
if (*p == '.') {
p++;
strtol(p, &p, 10);
}
if (*p == '%') {
val = (long long)convert_prange1(strtod(s, NULL), min, max);
p++;
}
val = check_range(val, min, max);
if (*p == ',')
p++;
out:
*ptr = p;
return val;
}
/**
* \brief return ASCII CTL element identifier name
* \param id CTL identifier
* \return ascii identifier of CTL element
*
* The string is allocated using strdup().
*/
char *snd_ctl_ascii_elem_id_get(snd_ctl_elem_id_t *id)
{
unsigned int index, device, subdevice;
char buf[256], buf1[32];
snprintf(buf, sizeof(buf), "numid=%u,iface=%s,name='%s'",
snd_ctl_elem_id_get_numid(id),
snd_ctl_elem_iface_name(
snd_ctl_elem_id_get_interface(id)),
snd_ctl_elem_id_get_name(id));
buf[sizeof(buf)-1] = '\0';
index = snd_ctl_elem_id_get_index(id);
device = snd_ctl_elem_id_get_device(id);
subdevice = snd_ctl_elem_id_get_subdevice(id);
if (index) {
snprintf(buf1, sizeof(buf1), ",index=%i", index);
if (strlen(buf) + strlen(buf1) < sizeof(buf))
strcat(buf, buf1);
}
if (device) {
snprintf(buf1, sizeof(buf1), ",device=%i", device);
if (strlen(buf) + strlen(buf1) < sizeof(buf))
strcat(buf, buf1);
}
if (subdevice) {
snprintf(buf1, sizeof(buf1), ",subdevice=%i", subdevice);
if (strlen(buf) + strlen(buf1) < sizeof(buf))
strcat(buf, buf1);
}
return strdup(buf);
}
/**
* \brief parse ASCII string as CTL element identifier
* \param dst destination CTL identifier
* \param str source ASCII string
* \return zero on success, otherwise a negative error code
*/
int snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst, const char *str)
{
int c, size, numid;
char *ptr;
while (*str == ' ' || *str == '\t')
str++;
if (!(*str))
return -EINVAL;
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER); /* default */
while (*str) {
if (!strncasecmp(str, "numid=", 6)) {
str += 6;
numid = atoi(str);
if (numid <= 0) {
fprintf(stderr, "amixer: Invalid numid %d\n", numid);
return -EINVAL;
}
snd_ctl_elem_id_set_numid(dst, atoi(str));
while (isdigit(*str))
str++;
} else if (!strncasecmp(str, "iface=", 6)) {
str += 6;
if (!strncasecmp(str, "card", 4)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_CARD);
str += 4;
} else if (!strncasecmp(str, "mixer", 5)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_MIXER);
str += 5;
} else if (!strncasecmp(str, "pcm", 3)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_PCM);
str += 3;
} else if (!strncasecmp(str, "rawmidi", 7)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_RAWMIDI);
str += 7;
} else if (!strncasecmp(str, "timer", 5)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_TIMER);
str += 5;
} else if (!strncasecmp(str, "sequencer", 9)) {
snd_ctl_elem_id_set_interface(dst, SND_CTL_ELEM_IFACE_SEQUENCER);
str += 9;
} else {
return -EINVAL;
}
} else if (!strncasecmp(str, "name=", 5)) {
char buf[64];
str += 5;
ptr = buf;
size = 0;
if (*str == '\'' || *str == '\"') {
c = *str++;
while (*str && *str != c) {
if (size < (int)sizeof(buf)) {
*ptr++ = *str;
size++;
}
str++;
}
if (*str == c)
str++;
} else {
while (*str && *str != ',') {
if (size < (int)sizeof(buf)) {
*ptr++ = *str;
size++;
}
str++;
}
}
*ptr = '\0';
snd_ctl_elem_id_set_name(dst, buf);
} else if (!strncasecmp(str, "index=", 6)) {
str += 6;
snd_ctl_elem_id_set_index(dst, atoi(str));
while (isdigit(*str))
str++;
} else if (!strncasecmp(str, "device=", 7)) {
str += 7;
snd_ctl_elem_id_set_device(dst, atoi(str));
while (isdigit(*str))
str++;
} else if (!strncasecmp(str, "subdevice=", 10)) {
str += 10;
snd_ctl_elem_id_set_subdevice(dst, atoi(str));
while (isdigit(*str))
str++;
}
if (*str == ',') {
str++;
} else {
if (*str)
return -EINVAL;
}
}
return 0;
}
static int get_ctl_enum_item_index(snd_ctl_t *handle,
snd_ctl_elem_info_t *info,
const char **ptrp)
{
char *ptr = (char *)*ptrp;
int items, i, len;
const char *name;
items = snd_ctl_elem_info_get_items(info);
if (items <= 0)
return -1;
for (i = 0; i < items; i++) {
snd_ctl_elem_info_set_item(info, i);
if (snd_ctl_elem_info(handle, info) < 0)
return -1;
name = snd_ctl_elem_info_get_item_name(info);
len = strlen(name);
if (! strncmp(name, ptr, len)) {
if (! ptr[len] || ptr[len] == ',' || ptr[len] == '\n') {
ptr += len;
*ptrp = ptr;
return i;
}
}
}
return -1;
}
/**
* \brief parse ASCII string as CTL element value
* \param dst destination CTL element value
* \param info CTL element info structure
* \param value source ASCII string
* \return zero on success, otherwise a negative error code
*/
int snd_ctl_ascii_value_parse(snd_ctl_t *handle,
snd_ctl_elem_value_t *dst,
snd_ctl_elem_info_t *info,
const char *value)
{
const char *ptr = value;
snd_ctl_elem_id_t *myid;
snd_ctl_elem_type_t type;
unsigned int idx, count;
long tmp;
long long tmp64;
snd_ctl_elem_id_alloca(&myid);
snd_ctl_elem_info_get_id(info, myid);
type = snd_ctl_elem_info_get_type(info);
count = snd_ctl_elem_info_get_count(info);
snd_ctl_elem_value_set_id(dst, myid);
for (idx = 0; idx < count && idx < 128 && ptr && *ptr; idx++) {
switch (type) {
case SND_CTL_ELEM_TYPE_BOOLEAN:
tmp = 0;
if (!strncasecmp(ptr, "on", 2) ||
!strncasecmp(ptr, "up", 2)) {
tmp = 1;
ptr += 2;
} else if (!strncasecmp(ptr, "yes", 3)) {
tmp = 1;
ptr += 3;
} else if (!strncasecmp(ptr, "toggle", 6)) {
tmp = snd_ctl_elem_value_get_boolean(dst, idx);
tmp = tmp > 0 ? 0 : 1;
ptr += 6;
} else if (isdigit(*ptr)) {
tmp = atoi(ptr) > 0 ? 1 : 0;
while (isdigit(*ptr))
ptr++;
} else {
while (*ptr && *ptr != ',')
ptr++;
}
snd_ctl_elem_value_set_boolean(dst, idx, tmp);
break;
case SND_CTL_ELEM_TYPE_INTEGER:
tmp = get_integer(&ptr,
snd_ctl_elem_info_get_min(info),
snd_ctl_elem_info_get_max(info));
snd_ctl_elem_value_set_integer(dst, idx, tmp);
break;
case SND_CTL_ELEM_TYPE_INTEGER64:
tmp64 = get_integer64(&ptr,
snd_ctl_elem_info_get_min64(info),
snd_ctl_elem_info_get_max64(info));
snd_ctl_elem_value_set_integer64(dst, idx, tmp64);
break;
case SND_CTL_ELEM_TYPE_ENUMERATED:
tmp = get_ctl_enum_item_index(handle, info, &ptr);
if (tmp < 0)
tmp = get_integer(&ptr, 0,
snd_ctl_elem_info_get_items(info) - 1);
snd_ctl_elem_value_set_enumerated(dst, idx, tmp);
break;
case SND_CTL_ELEM_TYPE_BYTES:
tmp = get_integer(&ptr, 0, 255);
snd_ctl_elem_value_set_byte(dst, idx, tmp);
break;
default:
break;
}
if (!strchr(value, ','))
ptr = value;
else if (*ptr == ',')
ptr++;
}
return 0;
}

10
src/ucm/Makefile.am Normal file
View 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

1437
src/ucm/main.c Normal file

File diff suppressed because it is too large Load diff

1180
src/ucm/parser.c Normal file

File diff suppressed because it is too large Load diff

208
src/ucm/ucm_local.h Normal file
View file

@ -0,0 +1,208 @@
/*
* 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 MAX_FILE 256
#define ALSA_USE_CASE_DIR ALSA_CONFIG_DIR "/ucm"
#define SEQUENCE_ELEMENT_TYPE_CDEV 1
#define SEQUENCE_ELEMENT_TYPE_CSET 2
#define SEQUENCE_ELEMENT_TYPE_SLEEP 3
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
struct ucm_value {
struct list_head list;
char *name;
char *data;
};
struct sequence_element {
struct list_head list;
unsigned int type;
union {
long sleep; /* Sleep time in msecs if sleep element, else 0 */
char *cdev;
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;
/* values */
struct list_head value_list;
};
/*
* 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;
char *name;
char *comment;
/* device enable and disable sequences */
struct list_head enable_list;
struct list_head disable_list;
/* device transition list */
struct list_head transition_list;
/* value list */
struct list_head value_list;
};
/*
* 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;
/* 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;
/* value list */
struct list_head value_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;
/* default settings - value list */
struct list_head value_list;
/* current status */
struct use_case_verb *active_verb;
struct list_head active_devices;
struct list_head active_modifiers;
/* locking */
pthread_mutex_t mutex;
/* change to list of ctl handles */
snd_ctl_t *ctl;
char *ctl_dev;
};
#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);
int uc_mgr_scan_master_configs(const char **_list[]);
void uc_mgr_free_sequence_element(struct sequence_element *seq);
void uc_mgr_free_transition_element(struct transition_sequence *seq);
void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr);
void uc_mgr_free(snd_use_case_mgr_t *uc_mgr);

236
src/ucm/utils.c Normal file
View file

@ -0,0 +1,236 @@
/*
* 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");
if (fp == NULL) {
err = -errno;
goto __err;
}
err = snd_input_stdio_attach(&in, fp, 1);
if (err < 0) {
__err:
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_value(struct list_head *base)
{
struct list_head *pos, *npos;
struct ucm_value *val;
list_for_each_safe(pos, npos, base) {
val = list_entry(pos, struct ucm_value, list);
free(val->name);
free(val->data);
list_del(&val->list);
free(val);
}
}
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);
list_del(&dlist->list);
free(dlist);
}
}
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);
list_del(&seq->list);
uc_mgr_free_sequence_element(seq);
}
}
void uc_mgr_free_transition_element(struct transition_sequence *tseq)
{
free(tseq->name);
uc_mgr_free_sequence(&tseq->transition_list);
free(tseq);
}
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);
list_del(&tseq->list);
uc_mgr_free_transition_element(tseq);
}
}
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);
uc_mgr_free_value(&mod->value_list);
list_del(&mod->list);
free(mod);
}
}
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);
uc_mgr_free_value(&dev->value_list);
list_del(&dev->list);
free(dev);
}
}
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);
uc_mgr_free_value(&verb->value_list);
uc_mgr_free_device(&verb->device_list);
uc_mgr_free_modifier(&verb->modifier_list);
list_del(&verb->list);
free(verb);
}
uc_mgr_free_sequence(&uc_mgr->default_list);
uc_mgr_free_value(&uc_mgr->value_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);
if (uc_mgr->ctl != NULL) {
snd_ctl_close(uc_mgr->ctl);
uc_mgr->ctl = NULL;
}
free(uc_mgr->ctl_dev);
uc_mgr->ctl_dev = NULL;
}
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);
}

View file

@ -0,0 +1,25 @@
SectionVerb {
EnableSequence [
exec "Case1 enable seq"
exec "Case1 enable seq 2"
]
DisableSequence [
exec "Case2 disable seq"
]
TransitionVerb."Case2" [
exec "Case1->Case2 transition seq"
]
Value {
TestValue1 "123"
}
}
SectionDevice."Device1".0 {
}
SectionModifier."Modifier1" {
SupportedDevice [
"Device1"
]
}

View file

@ -0,0 +1,11 @@
Comment "A test HDA card"
SectionUseCase."Case1" {
File "Case1.conf"
Comment "Case1 Comment"
}
SectionDefaults [
exec "my prg"
msleep 1
]

View file

@ -0,0 +1 @@
Comment "Another Card"

View file