|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* This library is free software; you can redistribute it and/or
|
|
Packit |
4a16fb |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
4a16fb |
* License as published by the Free Software Foundation; either
|
|
Packit |
4a16fb |
* version 2 of the License, or (at your option) any later version.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
4a16fb |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4a16fb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
4a16fb |
* Lesser General Public License for more details.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
4a16fb |
* License along with this library; if not, write to the Free Software
|
|
Packit |
4a16fb |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Support for the verb/device/modifier core logic and API,
|
|
Packit |
4a16fb |
* command line tool and file parser was kindly sponsored by
|
|
Packit |
4a16fb |
* Texas Instruments Inc.
|
|
Packit |
4a16fb |
* Support for multiple active modifiers and devices,
|
|
Packit |
4a16fb |
* transition sequences, multiple client access and user defined use
|
|
Packit |
4a16fb |
* cases was kindly sponsored by Wolfson Microelectronics PLC.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Copyright (C) 2008-2010 SlimLogic Ltd
|
|
Packit |
4a16fb |
* Copyright (C) 2010 Wolfson Microelectronics PLC
|
|
Packit |
4a16fb |
* Copyright (C) 2010 Texas Instruments Inc.
|
|
Packit |
4a16fb |
* Copyright (C) 2010 Red Hat Inc.
|
|
Packit |
4a16fb |
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
|
Packit |
4a16fb |
* Stefan Schmidt <stefan@slimlogic.co.uk>
|
|
Packit |
4a16fb |
* Justin Xu <justinx@slimlogic.co.uk>
|
|
Packit |
4a16fb |
* Jaroslav Kysela <perex@perex.cz>
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#include "ucm_local.h"
|
|
Packit |
4a16fb |
#include <dirent.h>
|
|
Packit |
4a16fb |
#include <limits.h>
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Directories to store UCM configuration files for components, like
|
|
Packit |
4a16fb |
* off-soc codecs or embedded DSPs. Components can define their own
|
|
Packit |
4a16fb |
* devices and sequences, to be reused by sound cards/machines. UCM
|
|
Packit |
4a16fb |
* manager should not scan these component directories.
|
|
Packit |
4a16fb |
* Machine use case files can include component configratuation files
|
|
Packit |
4a16fb |
* via alsaconf syntax:
|
|
Packit |
4a16fb |
* <searchdir:component-directory-name> and <component-conf-file-name>.
|
|
Packit |
4a16fb |
* Alsaconf will import the included files automatically. After including
|
|
Packit |
4a16fb |
* a component file, a machine device's sequence can enable or disable
|
|
Packit |
4a16fb |
* a component device via syntax:
|
|
Packit |
4a16fb |
* enadev "component_device_name"
|
|
Packit |
4a16fb |
* disdev "component_device_name"
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static const char * const component_dir[] = {
|
|
Packit |
4a16fb |
"codecs", /* for off-soc codecs */
|
|
Packit |
4a16fb |
"dsps", /* for DSPs embedded in SoC */
|
|
Packit |
4a16fb |
"platforms", /* for common platform implementations */
|
|
Packit |
4a16fb |
NULL, /* terminator */
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int filename_filter(const struct dirent *dirent);
|
|
Packit |
4a16fb |
static int is_component_directory(const char *dir);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
struct list_head *base,
|
|
Packit |
4a16fb |
snd_config_t *cfg);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* compose configuration file
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static void configuration_filename2(char *fn, size_t fn_len, int format,
|
|
Packit |
4a16fb |
const char *dir, const char *file,
|
|
Packit |
4a16fb |
const char *suffix)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snprintf(fn, fn_len, "%s/ucm%s/%s/%s%s",
|
|
Packit |
4a16fb |
snd_config_topdir(), format >= 2 ? "2" : "",
|
|
Packit |
4a16fb |
dir, file, suffix);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static void configuration_filename(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
char *fn, size_t fn_len,
|
|
Packit |
4a16fb |
const char *dir, const char *file,
|
|
Packit |
4a16fb |
const char *suffix)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
const char *env;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (uc_mgr->conf_format > 0) {
|
|
Packit |
4a16fb |
/* known format */
|
|
Packit |
4a16fb |
env = getenv(uc_mgr->conf_format >= 2 ? ALSA_CONFIG_UCM2_VAR :
|
|
Packit |
4a16fb |
ALSA_CONFIG_UCM_VAR);
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
/* auto-detect */
|
|
Packit |
4a16fb |
env = getenv(ALSA_CONFIG_UCM2_VAR);
|
|
Packit |
4a16fb |
if (env == NULL) {
|
|
Packit |
4a16fb |
env = getenv(ALSA_CONFIG_UCM_VAR);
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 2;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (env) {
|
|
Packit |
4a16fb |
snprintf(fn, fn_len, "%s/%s/%s%s", env, dir, file, suffix);
|
|
Packit |
4a16fb |
return;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (uc_mgr->conf_format > 0) {
|
|
Packit |
4a16fb |
configuration_filename2(fn, fn_len, uc_mgr->conf_format,
|
|
Packit |
4a16fb |
dir, file, suffix);
|
|
Packit |
4a16fb |
return;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
configuration_filename2(fn, fn_len, 2, dir, file, suffix);
|
|
Packit |
4a16fb |
if (access(fn, R_OK) == 0) {
|
|
Packit |
4a16fb |
/* Found an ucm2 file, only look in the ucm2 dir from now on */
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 2;
|
|
Packit |
4a16fb |
return;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
configuration_filename2(fn, fn_len, 0, dir, file, suffix);
|
|
Packit |
4a16fb |
if (access(fn, R_OK) == 0) {
|
|
Packit |
4a16fb |
/* Found an ucm1 file, only look in the ucm dir from now on */
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 1;
|
|
Packit |
4a16fb |
return;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* make sure that the error message refers to the new path */
|
|
Packit |
4a16fb |
configuration_filename2(fn, fn_len, 2, dir, file, suffix);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse string
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int parse_string(snd_config_t *n, char **res)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_config_get_string(n, (const char **)res);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
*res = strdup(*res);
|
|
Packit |
4a16fb |
if (*res == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse safe ID
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int parse_is_name_safe(const char *name)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (strchr(name, '.')) {
|
|
Packit |
4a16fb |
uc_error("char '.' not allowed in '%s'", name);
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
int parse_get_safe_id(snd_config_t *n, const char **id)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_config_get_id(n, id);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
if (!parse_is_name_safe((char *)(*id)))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Evaluate condition (in-place)
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_config_search(cfg, "If", &n);
|
|
Packit |
4a16fb |
if (err == -ENOENT)
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
|
|
Packit |
4a16fb |
snd_config_delete(n);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse transition
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_transition(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
struct list_head *tlist,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct transition_sequence *tseq;
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(cfg, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for %s", id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tseq = calloc(1, sizeof(*tseq));
|
|
Packit |
4a16fb |
if (tseq == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&tseq->transition_list);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
tseq->name = strdup(id);
|
|
Packit |
4a16fb |
if (tseq->name == NULL) {
|
|
Packit |
4a16fb |
free(tseq);
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &tseq->transition_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_mgr_free_transition_element(tseq);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list_add(&tseq->list, tlist);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse compound
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
|
Packit |
4a16fb |
int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
|
|
Packit |
4a16fb |
void *data1, void *data2)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(cfg, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for %s", id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
/* parse compound */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = fcn(uc_mgr, n, data1, data2);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int strip_legacy_dev_index(char *name)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
char *dot = strchr(name, '.');
|
|
Packit |
4a16fb |
if (!dot)
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
if (dot[1] != '0' || dot[2] != '\0') {
|
|
Packit |
4a16fb |
uc_error("device name %s contains a '.',"
|
|
Packit |
4a16fb |
" and is not legacy foo.0 format", name);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
*dot = '\0';
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse device list
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
|
|
Packit |
4a16fb |
struct dev_list *dev_list,
|
|
Packit |
4a16fb |
enum dev_list_type type,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct dev_list_node *sdev;
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (dev_list->type != DEVLIST_NONE) {
|
|
Packit |
4a16fb |
uc_error("error: multiple supported or"
|
|
Packit |
4a16fb |
" conflicting device lists");
|
|
Packit |
4a16fb |
return -EEXIST;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(cfg, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for %s", id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
sdev = calloc(1, sizeof(struct dev_list_node));
|
|
Packit |
4a16fb |
if (sdev == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
err = parse_string(n, &sdev->name);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
free(sdev);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = strip_legacy_dev_index(sdev->name);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
free(sdev->name);
|
|
Packit |
4a16fb |
free(sdev);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
list_add(&sdev->list, &dev_list->list);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
dev_list->type = type;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Find a component device by its name, and remove it from machine device
|
|
Packit |
4a16fb |
* list.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Component devices are defined by machine components (usually off-soc
|
|
Packit |
4a16fb |
* codes or DSP embeded in SoC). Since alsaconf imports their configuration
|
|
Packit |
4a16fb |
* files automatically, we don't know which devices are component devices
|
|
Packit |
4a16fb |
* until they are referenced by a machine device sequence. So here when we
|
|
Packit |
4a16fb |
* find a referenced device, we move it from the machine device list to the
|
|
Packit |
4a16fb |
* component device list. Component devices will not be exposed to applications
|
|
Packit |
4a16fb |
* by the original API to list devices for backward compatibility. So sound
|
|
Packit |
4a16fb |
* servers can only see the machine devices.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
const char *name)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct list_head *pos, *posdev, *_posdev;
|
|
Packit |
4a16fb |
struct use_case_verb *verb;
|
|
Packit |
4a16fb |
struct use_case_device *dev;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list_for_each(pos, &uc_mgr->verb_list) {
|
|
Packit |
4a16fb |
verb = list_entry(pos, struct use_case_verb, list);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* search in the component device list */
|
|
Packit |
4a16fb |
list_for_each(posdev, &verb->cmpt_device_list) {
|
|
Packit |
4a16fb |
dev = list_entry(posdev, struct use_case_device, list);
|
|
Packit |
4a16fb |
if (!strcmp(dev->name, name))
|
|
Packit |
4a16fb |
return dev;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* search the machine device list */
|
|
Packit |
4a16fb |
list_for_each_safe(posdev, _posdev, &verb->device_list) {
|
|
Packit |
4a16fb |
dev = list_entry(posdev, struct use_case_device, list);
|
|
Packit |
4a16fb |
if (!strcmp(dev->name, name)) {
|
|
Packit |
4a16fb |
/* find the component device, move it from the
|
|
Packit |
4a16fb |
* machine device list to the component device
|
|
Packit |
4a16fb |
* list.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
list_del(&dev->list);
|
|
Packit |
4a16fb |
list_add_tail(&dev->list,
|
|
Packit |
4a16fb |
&verb->cmpt_device_list);
|
|
Packit |
4a16fb |
return dev;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return NULL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse sequence of a component device
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This function will find the component device and mark if its enable or
|
|
Packit |
4a16fb |
* disable sequence is needed by its parenet device.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *n, int enable,
|
|
Packit |
4a16fb |
struct component_sequence *cmpt_seq)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
const char *val;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_config_get_string(n, &val;;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
cmpt_seq->device = find_component_dev(uc_mgr, val);
|
|
Packit |
4a16fb |
if (!cmpt_seq->device) {
|
|
Packit |
4a16fb |
uc_error("error: Cannot find component device %s", val);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Parent needs its enable or disable sequence */
|
|
Packit |
4a16fb |
cmpt_seq->enable = enable;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse sequences.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Sequence controls elements are in the following form:-
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* cdev "hw:0"
|
|
Packit |
4a16fb |
* cset "element_id_syntax value_syntax"
|
|
Packit |
4a16fb |
* usleep time
|
|
Packit |
4a16fb |
* exec "any unix command with arguments"
|
|
Packit |
4a16fb |
* enadev "component device name"
|
|
Packit |
4a16fb |
* disdev "component device name"
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* e.g.
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch' 0,0"
|
|
Packit |
4a16fb |
* cset "iface=PCM,name='Disable HDMI',index=1 0"
|
|
Packit |
4a16fb |
* enadev "rt286:Headphones"
|
|
Packit |
4a16fb |
* disdev "rt286:Speaker"
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
struct list_head *base,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct sequence_element *curr;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err, idx = 0;
|
|
Packit |
4a16fb |
const char *cmd = NULL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("error: compound is expected for sequence definition");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
idx ^= 1;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
err = snd_config_get_id(n, &id;;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
if (idx == 1) {
|
|
Packit |
4a16fb |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
|
|
Packit |
4a16fb |
uc_error("error: string type is expected for sequence command");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
snd_config_get_string(n, &cmd);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* alloc new sequence element */
|
|
Packit |
4a16fb |
curr = calloc(1, sizeof(struct sequence_element));
|
|
Packit |
4a16fb |
if (curr == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
list_add_tail(&curr->list, base);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "cdev") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
|
|
Packit |
4a16fb |
err = parse_string(n, &curr->data.cdev);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: cdev requires a string!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "cset") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
|
|
Packit |
4a16fb |
err = parse_string(n, &curr->data.cset);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: cset requires a string!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "enadev") == 0) {
|
|
Packit |
4a16fb |
/* need to enable a component device */
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
|
|
Packit |
4a16fb |
err = parse_component_seq(uc_mgr, n, 1,
|
|
Packit |
4a16fb |
&curr->data.cmpt_seq);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: enadev requires a valid device!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "disdev") == 0) {
|
|
Packit |
4a16fb |
/* need to disable a component device */
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
|
|
Packit |
4a16fb |
err = parse_component_seq(uc_mgr, n, 0,
|
|
Packit |
4a16fb |
&curr->data.cmpt_seq);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: disdev requires a valid device!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "cset-bin-file") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
|
|
Packit |
4a16fb |
err = parse_string(n, &curr->data.cset);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: cset-bin-file requires a string!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "cset-tlv") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
|
|
Packit |
4a16fb |
err = parse_string(n, &curr->data.cset);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: cset-tlv requires a string!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "usleep") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
|
|
Packit |
4a16fb |
err = snd_config_get_integer(n, &curr->data.sleep);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: usleep requires integer!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "msleep") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
|
|
Packit |
4a16fb |
err = snd_config_get_integer(n, &curr->data.sleep);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: msleep requires integer!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
curr->data.sleep *= 1000L;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(cmd, "exec") == 0) {
|
|
Packit |
4a16fb |
curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
|
|
Packit |
4a16fb |
err = parse_string(n, &curr->data.exec);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: exec requires a string!");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list_del(&curr->list);
|
|
Packit |
4a16fb |
uc_mgr_free_sequence_element(curr);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct ucm_value *curr;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
curr = calloc(1, sizeof(struct ucm_value));
|
|
Packit |
4a16fb |
if (curr == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
curr->name = strdup(key);
|
|
Packit |
4a16fb |
if (curr->name == NULL) {
|
|
Packit |
4a16fb |
free(curr);
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
list_add_tail(&curr->list, base);
|
|
Packit |
4a16fb |
curr->data = val;
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse values.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Parse values describing PCM, control/mixer settings and stream parameters.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Value {
|
|
Packit |
4a16fb |
* TQ Voice
|
|
Packit |
4a16fb |
* CapturePCM "hw:1"
|
|
Packit |
4a16fb |
* PlaybackVolume "name='Master Playback Volume',index=2"
|
|
Packit |
4a16fb |
* PlaybackSwitch "name='Master Playback Switch',index=2"
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
|
|
Packit |
4a16fb |
struct list_head *base,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
char *s;
|
|
Packit |
4a16fb |
snd_config_type_t type;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("error: compound is expected for value definition");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
err = snd_config_get_id(n, &id;;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
type = snd_config_get_type(n);
|
|
Packit |
4a16fb |
switch (type) {
|
|
Packit |
4a16fb |
case SND_CONFIG_TYPE_INTEGER:
|
|
Packit |
4a16fb |
case SND_CONFIG_TYPE_INTEGER64:
|
|
Packit |
4a16fb |
case SND_CONFIG_TYPE_REAL:
|
|
Packit |
4a16fb |
err = snd_config_get_ascii(n, &s);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: unable to parse value for id '%s': %s!", id, snd_strerror(err));
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
case SND_CONFIG_TYPE_STRING:
|
|
Packit |
4a16fb |
err = parse_string(n, &s);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: unable to parse a string for id '%s'!", id);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
default:
|
|
Packit |
4a16fb |
uc_error("error: invalid type %i in Value compound '%s'", type, id);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = uc_mgr_add_value(base, id, s);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
free(s);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse Modifier Use cases
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Each modifier is described in new section. N modifiers are allowed
|
|
Packit |
4a16fb |
* SectionModifier."Capture Voice" {
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Comment "Record voice call"
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SupportedDevice [
|
|
Packit |
4a16fb |
* "x"
|
|
Packit |
4a16fb |
* "y"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* ConflictingDevice [
|
|
Packit |
4a16fb |
* "x"
|
|
Packit |
4a16fb |
* "y"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* EnableSequence [
|
|
Packit |
4a16fb |
* ....
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* DisableSequence [
|
|
Packit |
4a16fb |
* ...
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* TransitionSequence."ToModifierName" [
|
|
Packit |
4a16fb |
* ...
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Optional TQ and ALSA PCMs
|
|
Packit |
4a16fb |
* Value {
|
|
Packit |
4a16fb |
* TQ Voice
|
|
Packit |
4a16fb |
* CapturePCM "hw:1"
|
|
Packit |
4a16fb |
* PlaybackVolume "name='Master Playback Volume',index=2"
|
|
Packit |
4a16fb |
* PlaybackSwitch "name='Master Playback Switch',index=2"
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SupportedDevice and ConflictingDevice cannot be specified together.
|
|
Packit |
4a16fb |
* Both are optional.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg,
|
|
Packit |
4a16fb |
void *data1,
|
|
Packit |
4a16fb |
void *data2)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct use_case_verb *verb = data1;
|
|
Packit |
4a16fb |
struct use_case_modifier *modifier;
|
|
Packit |
4a16fb |
const char *name;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (data2) {
|
|
Packit |
4a16fb |
name = data2;
|
|
Packit |
4a16fb |
if (!parse_is_name_safe(name))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
if (parse_get_safe_id(cfg, &name) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* allocate modifier */
|
|
Packit |
4a16fb |
modifier = calloc(1, sizeof(*modifier));
|
|
Packit |
4a16fb |
if (modifier == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&modifier->enable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&modifier->disable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&modifier->transition_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&modifier->dev_list.list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&modifier->value_list);
|
|
Packit |
4a16fb |
list_add_tail(&modifier->list, &verb->modifier_list);
|
|
Packit |
4a16fb |
modifier->name = strdup(name);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Comment") == 0) {
|
|
Packit |
4a16fb |
err = parse_string(n, &modifier->comment);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to get modifier comment");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "SupportedDevice") == 0) {
|
|
Packit |
4a16fb |
err = parse_device_list(uc_mgr, &modifier->dev_list,
|
|
Packit |
4a16fb |
DEVLIST_SUPPORTED, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse supported"
|
|
Packit |
4a16fb |
" device list");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "ConflictingDevice") == 0) {
|
|
Packit |
4a16fb |
err = parse_device_list(uc_mgr, &modifier->dev_list,
|
|
Packit |
4a16fb |
DEVLIST_CONFLICTING, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse conflicting"
|
|
Packit |
4a16fb |
" device list");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "EnableSequence") == 0) {
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &modifier->enable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse modifier"
|
|
Packit |
4a16fb |
" enable sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "DisableSequence") == 0) {
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &modifier->disable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse modifier"
|
|
Packit |
4a16fb |
" disable sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "TransitionSequence") == 0) {
|
|
Packit |
4a16fb |
err = parse_transition(uc_mgr, &modifier->transition_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse transition"
|
|
Packit |
4a16fb |
" modifier");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Value") == 0) {
|
|
Packit |
4a16fb |
err = parse_value(uc_mgr, &modifier->value_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse Value");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse Device Use Cases
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
*# Each device is described in new section. N devices are allowed
|
|
Packit |
4a16fb |
*SectionDevice."Headphones" {
|
|
Packit |
4a16fb |
* Comment "Headphones connected to 3.5mm jack"
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* upportedDevice [
|
|
Packit |
4a16fb |
* "x"
|
|
Packit |
4a16fb |
* "y"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* ConflictingDevice [
|
|
Packit |
4a16fb |
* "x"
|
|
Packit |
4a16fb |
* "y"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* EnableSequence [
|
|
Packit |
4a16fb |
* ....
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* DisableSequence [
|
|
Packit |
4a16fb |
* ...
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* TransitionSequence."ToDevice" [
|
|
Packit |
4a16fb |
* ...
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Value {
|
|
Packit |
4a16fb |
* PlaybackVolume "name='Master Playback Volume',index=2"
|
|
Packit |
4a16fb |
* PlaybackSwitch "name='Master Playback Switch',index=2"
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_device(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg,
|
|
Packit |
4a16fb |
void *data1,
|
|
Packit |
4a16fb |
void *data2)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
struct use_case_verb *verb = data1;
|
|
Packit |
4a16fb |
const char *name;
|
|
Packit |
4a16fb |
struct use_case_device *device;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (data2) {
|
|
Packit |
4a16fb |
name = data2;
|
|
Packit |
4a16fb |
if (!parse_is_name_safe(name))
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
else {
|
|
Packit |
4a16fb |
if (parse_get_safe_id(cfg, &name) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
device = calloc(1, sizeof(*device));
|
|
Packit |
4a16fb |
if (device == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&device->enable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&device->disable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&device->transition_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&device->dev_list.list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&device->value_list);
|
|
Packit |
4a16fb |
list_add_tail(&device->list, &verb->device_list);
|
|
Packit |
4a16fb |
device->name = strdup(name);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Comment") == 0) {
|
|
Packit |
4a16fb |
err = parse_string(n, &device->comment);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to get device comment");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "SupportedDevice") == 0) {
|
|
Packit |
4a16fb |
err = parse_device_list(uc_mgr, &device->dev_list,
|
|
Packit |
4a16fb |
DEVLIST_SUPPORTED, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse supported"
|
|
Packit |
4a16fb |
" device list");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "ConflictingDevice") == 0) {
|
|
Packit |
4a16fb |
err = parse_device_list(uc_mgr, &device->dev_list,
|
|
Packit |
4a16fb |
DEVLIST_CONFLICTING, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse conflicting"
|
|
Packit |
4a16fb |
" device list");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "EnableSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("EnableSequence");
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &device->enable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse device enable"
|
|
Packit |
4a16fb |
" sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "DisableSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("DisableSequence");
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &device->disable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse device disable"
|
|
Packit |
4a16fb |
" sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "TransitionSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("TransitionSequence");
|
|
Packit |
4a16fb |
err = parse_transition(uc_mgr, &device->transition_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse transition"
|
|
Packit |
4a16fb |
" device");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Value") == 0) {
|
|
Packit |
4a16fb |
err = parse_value(uc_mgr, &device->value_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse Value");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg,
|
|
Packit |
4a16fb |
int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
|
|
Packit |
4a16fb |
void *data1)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
const char *id, *idchild;
|
|
Packit |
4a16fb |
int child_ctr = 0, legacy_format = 1;
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *child;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_config_get_id(cfg, &id;;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
child_ctr++;
|
|
Packit |
4a16fb |
if (child_ctr > 1) {
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
child = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
legacy_format = 0;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(child, &idchild) < 0)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(idchild, "0")) {
|
|
Packit |
4a16fb |
legacy_format = 0;
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (child_ctr != 1) {
|
|
Packit |
4a16fb |
legacy_format = 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (legacy_format)
|
|
Packit |
4a16fb |
return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
return fcn(uc_mgr, cfg, data1, NULL);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg,
|
|
Packit |
4a16fb |
void *data1,
|
|
Packit |
4a16fb |
void *data2 ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
snd_config_t *cfg,
|
|
Packit |
4a16fb |
void *data1,
|
|
Packit |
4a16fb |
void *data2 ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse Verb Section
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Example Use case verb section for Voice call blah
|
|
Packit |
4a16fb |
* # By Joe Blogs <joe@blogs.com>
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SectionVerb {
|
|
Packit |
4a16fb |
* # enable and disable sequences are compulsory
|
|
Packit |
4a16fb |
* EnableSequence [
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch',index=2 0,0"
|
|
Packit |
4a16fb |
* cset "name='Master Playback Volume',index=2 25,25"
|
|
Packit |
4a16fb |
* msleep 50
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch',index=2 1,1"
|
|
Packit |
4a16fb |
* cset "name='Master Playback Volume',index=2 50,50"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* DisableSequence [
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch',index=2 0,0"
|
|
Packit |
4a16fb |
* cset "name='Master Playback Volume',index=2 25,25"
|
|
Packit |
4a16fb |
* msleep 50
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch',index=2 1,1"
|
|
Packit |
4a16fb |
* cset "name='Master Playback Volume',index=2 50,50"
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Optional transition verb
|
|
Packit |
4a16fb |
* TransitionSequence."ToCaseName" [
|
|
Packit |
4a16fb |
* msleep 1
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Optional TQ and ALSA PCMs
|
|
Packit |
4a16fb |
* Value {
|
|
Packit |
4a16fb |
* TQ HiFi
|
|
Packit |
4a16fb |
* CapturePCM "hw:0"
|
|
Packit |
4a16fb |
* PlaybackPCM "hw:0"
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_verb(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
struct use_case_verb *verb,
|
|
Packit |
4a16fb |
snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse verb section */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "EnableSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("Parse EnableSequence");
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &verb->enable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse verb enable sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "DisableSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("Parse DisableSequence");
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &verb->disable_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse verb disable sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "TransitionSequence") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("Parse TransitionSequence");
|
|
Packit |
4a16fb |
err = parse_transition(uc_mgr, &verb->transition_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse transition sequence");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Value") == 0) {
|
|
Packit |
4a16fb |
uc_dbg("Parse Value");
|
|
Packit |
4a16fb |
err = parse_value(uc_mgr, &verb->value_list, n);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse a Use case verb file.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This file contains the following :-
|
|
Packit |
4a16fb |
* o Verb enable and disable sequences.
|
|
Packit |
4a16fb |
* o Supported Device enable and disable sequences for verb.
|
|
Packit |
4a16fb |
* o Supported Modifier enable and disable sequences for verb
|
|
Packit |
4a16fb |
* o Optional QoS for the verb and modifiers.
|
|
Packit |
4a16fb |
* o Optional PCM device ID for verb and modifiers
|
|
Packit |
4a16fb |
* o Alias kcontrols IDs for master and volumes and mutes.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
const char *use_case_name,
|
|
Packit |
4a16fb |
const char *comment,
|
|
Packit |
4a16fb |
const char *file)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
struct use_case_verb *verb;
|
|
Packit |
4a16fb |
snd_config_t *cfg;
|
|
Packit |
4a16fb |
char filename[MAX_FILE];
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* allocate verb */
|
|
Packit |
4a16fb |
verb = calloc(1, sizeof(struct use_case_verb));
|
|
Packit |
4a16fb |
if (verb == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->enable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->disable_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->transition_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->device_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->cmpt_device_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->modifier_list);
|
|
Packit |
4a16fb |
INIT_LIST_HEAD(&verb->value_list);
|
|
Packit |
4a16fb |
list_add_tail(&verb->list, &uc_mgr->verb_list);
|
|
Packit |
4a16fb |
if (use_case_name == NULL)
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
verb->name = strdup(use_case_name);
|
|
Packit |
4a16fb |
if (verb->name == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (comment != NULL) {
|
|
Packit |
4a16fb |
verb->comment = strdup(comment);
|
|
Packit |
4a16fb |
if (verb->comment == NULL)
|
|
Packit |
4a16fb |
return -ENOMEM;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* open Verb file for reading */
|
|
Packit |
4a16fb |
configuration_filename(uc_mgr, filename, sizeof(filename),
|
|
Packit |
4a16fb |
uc_mgr->conf_file_name, file, "");
|
|
Packit |
4a16fb |
err = uc_mgr_config_load(uc_mgr->conf_format, filename, &cfg;;
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to open verb file %s : %d",
|
|
Packit |
4a16fb |
filename, -errno);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse master config sections */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find verb section and parse it */
|
|
Packit |
4a16fb |
if (strcmp(id, "SectionVerb") == 0) {
|
|
Packit |
4a16fb |
err = parse_verb(uc_mgr, verb, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: %s failed to parse verb",
|
|
Packit |
4a16fb |
file);
|
|
Packit |
4a16fb |
goto _err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find device sections and parse them */
|
|
Packit |
4a16fb |
if (strcmp(id, "SectionDevice") == 0) {
|
|
Packit |
4a16fb |
err = parse_compound(uc_mgr, n,
|
|
Packit |
4a16fb |
parse_device_name, verb, NULL);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: %s failed to parse device",
|
|
Packit |
4a16fb |
file);
|
|
Packit |
4a16fb |
goto _err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find modifier sections and parse them */
|
|
Packit |
4a16fb |
if (strcmp(id, "SectionModifier") == 0) {
|
|
Packit |
4a16fb |
err = parse_compound(uc_mgr, n,
|
|
Packit |
4a16fb |
parse_modifier_name, verb, NULL);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: %s failed to parse modifier",
|
|
Packit |
4a16fb |
file);
|
|
Packit |
4a16fb |
goto _err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* use case verb must have at least 1 device */
|
|
Packit |
4a16fb |
if (list_empty(&verb->device_list)) {
|
|
Packit |
4a16fb |
uc_error("error: no use case device defined", file);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
_err:
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Parse master section for "Use Case" and "File" tags.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
|
|
Packit |
4a16fb |
void *data1 ATTRIBUTE_UNUSED,
|
|
Packit |
4a16fb |
void *data2 ATTRIBUTE_UNUSED)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *use_case_name, *file = NULL, *comment = NULL;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_id(cfg, &use_case_name) < 0) {
|
|
Packit |
4a16fb |
uc_error("unable to get name for use case section");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for use case section");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse master config sections */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get use case verb file name */
|
|
Packit |
4a16fb |
if (strcmp(id, "File") == 0) {
|
|
Packit |
4a16fb |
err = snd_config_get_string(n, &file;;
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("failed to get File");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get optional use case comment */
|
|
Packit |
4a16fb |
if (strncmp(id, "Comment", 7) == 0) {
|
|
Packit |
4a16fb |
err = snd_config_get_string(n, &comment);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to get Comment");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
uc_error("unknown field %s in master section");
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
uc_dbg("use_case_name %s file '%s'", use_case_name, file);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* do we have both use case name and file ? */
|
|
Packit |
4a16fb |
if (!file) {
|
|
Packit |
4a16fb |
uc_error("error: use case missing file");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse verb file */
|
|
Packit |
4a16fb |
return parse_verb_file(uc_mgr, use_case_name, comment, file);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* parse controls
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (!list_empty(&uc_mgr->default_list)) {
|
|
Packit |
4a16fb |
uc_error("Default list is not empty");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("Unable to parse SectionDefaults");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/*
|
|
Packit |
4a16fb |
* Each sound card has a master sound card file that lists all the supported
|
|
Packit |
4a16fb |
* use case verbs for that sound card. i.e.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* #Example master file for blah sound card
|
|
Packit |
4a16fb |
* #By Joe Blogs <joe@bloggs.org>
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Comment "Nice Abstracted Soundcard"
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # The file is divided into Use case sections. One section per use case verb.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SectionUseCase."Voice Call" {
|
|
Packit |
4a16fb |
* File "voice_call_blah"
|
|
Packit |
4a16fb |
* Comment "Make a voice phone call."
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SectionUseCase."HiFi" {
|
|
Packit |
4a16fb |
* File "hifi_blah"
|
|
Packit |
4a16fb |
* Comment "Play and record HiFi quality Music."
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # Define Value defaults
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* ValueDefaults {
|
|
Packit |
4a16fb |
* PlaybackCTL "hw:CARD=0"
|
|
Packit |
4a16fb |
* CaptureCTL "hw:CARD=0"
|
|
Packit |
4a16fb |
* }
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # This file also stores the default sound card state.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* SectionDefaults [
|
|
Packit |
4a16fb |
* cset "name='Master Playback Switch',index=2 1,1"
|
|
Packit |
4a16fb |
* cset "name='Master Playback Volume',index=2 25,25"
|
|
Packit |
4a16fb |
* cset "name='Master Mono Playback',index=1 0"
|
|
Packit |
4a16fb |
* cset "name='Master Mono Playback Volume',index=1 0"
|
|
Packit |
4a16fb |
* cset "name='PCM Switch',index=2 1,1"
|
|
Packit |
4a16fb |
* exec "some binary here"
|
|
Packit |
4a16fb |
* msleep 50
|
|
Packit |
4a16fb |
* ........
|
|
Packit |
4a16fb |
* ]
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* # End of example file.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_iterator_t i, next;
|
|
Packit |
4a16fb |
snd_config_t *n;
|
|
Packit |
4a16fb |
const char *id;
|
|
Packit |
4a16fb |
long l;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit |
4a16fb |
uc_error("compound type expected for master file");
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (uc_mgr->conf_format >= 2) {
|
|
Packit |
4a16fb |
err = snd_config_search(cfg, "Syntax", &n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("Syntax field not found in %s", uc_mgr->conf_file_name);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = snd_config_get_integer(n, &l);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (l < 2 || l > SYNTAX_VERSION_MAX) {
|
|
Packit |
4a16fb |
uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
/* delete this field to avoid strcmp() call in the loop */
|
|
Packit |
4a16fb |
snd_config_delete(n);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* in-place condition evaluation */
|
|
Packit |
4a16fb |
err = evaluate_condition(uc_mgr, cfg);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* parse master config sections */
|
|
Packit |
4a16fb |
snd_config_for_each(i, next, cfg) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
n = snd_config_iterator_entry(i);
|
|
Packit |
4a16fb |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strcmp(id, "Comment") == 0) {
|
|
Packit |
4a16fb |
err = parse_string(n, &uc_mgr->comment);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to get master comment");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find use case section and parse it */
|
|
Packit |
4a16fb |
if (strcmp(id, "SectionUseCase") == 0) {
|
|
Packit |
4a16fb |
err = parse_compound(uc_mgr, n,
|
|
Packit |
4a16fb |
parse_master_section,
|
|
Packit |
4a16fb |
NULL, NULL);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find default control values section and parse it */
|
|
Packit |
4a16fb |
if (strcmp(id, "SectionDefaults") == 0) {
|
|
Packit |
4a16fb |
err = parse_controls(uc_mgr, n);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get the default values */
|
|
Packit |
4a16fb |
if (strcmp(id, "ValueDefaults") == 0) {
|
|
Packit |
4a16fb |
err = parse_value(uc_mgr, &uc_mgr->value_list, n);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: failed to parse ValueDefaults");
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
uc_error("uknown master file field %s", id);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* get the card info */
|
|
Packit |
4a16fb |
static int get_card_info(snd_use_case_mgr_t *mgr,
|
|
Packit |
4a16fb |
const char *ctl_name,
|
|
Packit |
4a16fb |
snd_ctl_t **_handle,
|
|
Packit |
4a16fb |
snd_ctl_card_info_t *info)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_ctl_t *handle;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
*_handle = NULL;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = uc_mgr_open_ctl(mgr, &handle, ctl_name);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = snd_ctl_card_info(handle, info);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("control hardware info (%s): %s", ctl_name, snd_strerror(err));
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
*_handle = handle;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* find the card in the local machine and store the card long name */
|
|
Packit |
4a16fb |
static int get_card_long_name(snd_use_case_mgr_t *mgr, char *longname)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
const char *card_name = mgr->card_name;
|
|
Packit |
4a16fb |
int card, err;
|
|
Packit |
4a16fb |
snd_ctl_t *ctl;
|
|
Packit |
4a16fb |
snd_ctl_card_info_t *info;
|
|
Packit |
4a16fb |
const char *_name, *_long_name;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_ctl_card_info_alloca(&info;;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
card = -1;
|
|
Packit |
4a16fb |
if (snd_card_next(&card) < 0 || card < 0) {
|
|
Packit |
4a16fb |
uc_error("no soundcards found...");
|
|
Packit |
4a16fb |
return -1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
while (card >= 0) {
|
|
Packit |
4a16fb |
char name[32];
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* most probably, we do not need to cache all CTL devices here */
|
|
Packit |
4a16fb |
uc_mgr_free_ctl_list(mgr);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
sprintf(name, "hw:%d", card);
|
|
Packit |
4a16fb |
err = get_card_info(mgr, name, &ctl, info);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (err == 0) {
|
|
Packit |
4a16fb |
_name = snd_ctl_card_info_get_name(info);
|
|
Packit |
4a16fb |
_long_name = snd_ctl_card_info_get_longname(info);
|
|
Packit |
4a16fb |
if (!strcmp(card_name, _name) ||
|
|
Packit |
4a16fb |
!strcmp(card_name, _long_name)) {
|
|
Packit |
4a16fb |
snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME);
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (snd_card_next(&card) < 0) {
|
|
Packit |
4a16fb |
uc_error("snd_card_next");
|
|
Packit |
4a16fb |
break;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
uc_mgr_free_ctl_list(mgr);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return -1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* set the driver name and long name by the card ctl name */
|
|
Packit |
4a16fb |
static int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name, char *longname)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_ctl_t *ctl;
|
|
Packit |
4a16fb |
snd_ctl_card_info_t *info;
|
|
Packit |
4a16fb |
const char *_name, *_long_name;
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_ctl_card_info_alloca(&info;;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
err = get_card_info(mgr, ctl_name, &ctl, info);
|
|
Packit |
4a16fb |
if (err)
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
_name = snd_ctl_card_info_get_name(info);
|
|
Packit |
4a16fb |
_long_name = snd_ctl_card_info_get_longname(info);
|
|
Packit |
4a16fb |
snd_strlcpy(mgr->conf_file_name, _name, sizeof(mgr->conf_file_name));
|
|
Packit |
4a16fb |
snd_strlcpy(longname, _long_name, MAX_CARD_LONG_NAME);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int load_master_config(snd_use_case_mgr_t *uc_mgr,
|
|
Packit |
4a16fb |
const char *card_name, snd_config_t **cfg, int longname)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
char filename[MAX_FILE];
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strnlen(card_name, MAX_CARD_LONG_NAME) == MAX_CARD_LONG_NAME) {
|
|
Packit |
4a16fb |
uc_error("error: invalid card name %s (at most %d chars)",
|
|
Packit |
4a16fb |
card_name, MAX_CARD_LONG_NAME - 1);
|
|
Packit |
4a16fb |
return -EINVAL;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 0;
|
|
Packit |
4a16fb |
if (longname) {
|
|
Packit |
4a16fb |
if (getenv(ALSA_CONFIG_UCM2_VAR) || !getenv(ALSA_CONFIG_UCM_VAR)) {
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 2;
|
|
Packit |
4a16fb |
configuration_filename(uc_mgr, filename, sizeof(filename),
|
|
Packit |
4a16fb |
uc_mgr->conf_file_name, card_name, ".conf");
|
|
Packit |
4a16fb |
if (access(filename, R_OK) == 0)
|
|
Packit |
4a16fb |
goto __load;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
/* try the old ucm directory */
|
|
Packit |
4a16fb |
uc_mgr->conf_format = 1;
|
|
Packit |
4a16fb |
configuration_filename(uc_mgr, filename, sizeof(filename),
|
|
Packit |
4a16fb |
card_name, card_name, ".conf");
|
|
Packit |
4a16fb |
if (access(filename, R_OK) != 0)
|
|
Packit |
4a16fb |
return -ENOENT;
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
configuration_filename(uc_mgr, filename, sizeof(filename),
|
|
Packit |
4a16fb |
card_name, card_name, ".conf");
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__load:
|
|
Packit |
4a16fb |
err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("error: could not parse configuration for card %s",
|
|
Packit |
4a16fb |
card_name);
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* load master use case file for sound card
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* The same ASoC machine driver can be shared by many different devices.
|
|
Packit |
4a16fb |
* For user space to differentiate them and get the best device-specific
|
|
Packit |
4a16fb |
* configuration, ASoC machine drivers may use the DMI info
|
|
Packit |
4a16fb |
* (vendor-product-version-board) as the card long name. And user space can
|
|
Packit |
4a16fb |
* define configuration files like longnamei/longname.conf for a specific device.
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* This function will try to find the card in the local machine and get its
|
|
Packit |
4a16fb |
* long name, then load the file longname/longname.conf to get the best
|
|
Packit |
4a16fb |
* device-specific configuration. If the card is not found in the local
|
|
Packit |
4a16fb |
* machine or the device-specific file is not available, fall back to load
|
|
Packit |
4a16fb |
* the default configuration file name/name.conf.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
snd_config_t *cfg;
|
|
Packit |
4a16fb |
const char *name = uc_mgr->card_name;
|
|
Packit |
4a16fb |
char longname[MAX_CARD_LONG_NAME];
|
|
Packit |
4a16fb |
int err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
snd_strlcpy(uc_mgr->conf_file_name, uc_mgr->card_name, sizeof(uc_mgr->conf_file_name));
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (strncmp(name, "hw:", 3) == 0) {
|
|
Packit |
4a16fb |
err = get_by_card(uc_mgr, name, longname);
|
|
Packit |
4a16fb |
if (err == 0)
|
|
Packit |
4a16fb |
goto __longname;
|
|
Packit |
4a16fb |
uc_error("card '%s' is not valid", name);
|
|
Packit |
4a16fb |
goto __error;
|
|
Packit |
4a16fb |
} else if (strncmp(name, "strict:", 7)) {
|
|
Packit |
4a16fb |
err = get_card_long_name(uc_mgr, longname);
|
|
Packit |
4a16fb |
if (err == 0) { /* load file that matches the card long name */
|
|
Packit |
4a16fb |
__longname:
|
|
Packit |
4a16fb |
err = load_master_config(uc_mgr, longname, &cfg, 1);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (err == 0) {
|
|
Packit |
4a16fb |
/* got device-specific file that matches the card long name */
|
|
Packit |
4a16fb |
snd_strlcpy(uc_mgr->conf_file_name, longname, sizeof(uc_mgr->conf_file_name));
|
|
Packit |
4a16fb |
goto __parse;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* standard path */
|
|
Packit |
4a16fb |
err = load_master_config(uc_mgr, uc_mgr->conf_file_name, &cfg, 0);
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
goto __error;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__parse:
|
|
Packit |
4a16fb |
err = parse_master_file(uc_mgr, cfg);
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_mgr_free_ctl_list(uc_mgr);
|
|
Packit |
4a16fb |
uc_mgr_free_verb(uc_mgr);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__error:
|
|
Packit |
4a16fb |
uc_mgr_free_ctl_list(uc_mgr);
|
|
Packit |
4a16fb |
uc_mgr->conf_file_name[0] = '\0';
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
static int filename_filter(const struct dirent *dirent)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
if (dirent == NULL)
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
if (dirent->d_type == DT_DIR) {
|
|
Packit |
4a16fb |
if (dirent->d_name[0] == '.') {
|
|
Packit |
4a16fb |
if (dirent->d_name[1] == '\0')
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
if (dirent->d_name[1] == '.' &&
|
|
Packit |
4a16fb |
dirent->d_name[2] == '\0')
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 1;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* whether input dir is a predefined component directory */
|
|
Packit |
4a16fb |
static int is_component_directory(const char *dir)
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
int i = 0;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
while (component_dir[i]) {
|
|
Packit |
4a16fb |
if (!strncmp(dir, component_dir[i], PATH_MAX))
|
|
Packit |
4a16fb |
return 1;
|
|
Packit |
4a16fb |
i++;
|
|
Packit |
4a16fb |
};
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return 0;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* scan all cards and comments
|
|
Packit |
4a16fb |
*
|
|
Packit |
4a16fb |
* Cards are defined by machines. Each card/machine installs its UCM
|
|
Packit |
4a16fb |
* configuration files in a subdirectory with the same name as the sound
|
|
Packit |
4a16fb |
* card under /usr/share/alsa/ucm2. This function will scan all the card
|
|
Packit |
4a16fb |
* directories and skip the component directories defined in the array
|
|
Packit |
4a16fb |
* component_dir.
|
|
Packit |
4a16fb |
*/
|
|
Packit |
4a16fb |
int uc_mgr_scan_master_configs(const char **_list[])
|
|
Packit |
4a16fb |
{
|
|
Packit |
4a16fb |
char filename[MAX_FILE], dfl[MAX_FILE];
|
|
Packit |
4a16fb |
char *env = getenv(ALSA_CONFIG_UCM2_VAR);
|
|
Packit |
4a16fb |
const char **list, *d_name;
|
|
Packit |
4a16fb |
snd_config_t *cfg, *c;
|
|
Packit |
4a16fb |
int i, j, cnt, err;
|
|
Packit |
4a16fb |
long l;
|
|
Packit |
4a16fb |
ssize_t ss;
|
|
Packit |
4a16fb |
struct dirent **namelist;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (env)
|
|
Packit |
4a16fb |
snprintf(filename, sizeof(filename), "%s", env);
|
|
Packit |
4a16fb |
else
|
|
Packit |
4a16fb |
snprintf(filename, sizeof(filename), "%s/ucm2",
|
|
Packit |
4a16fb |
snd_config_topdir());
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID)
|
|
Packit |
4a16fb |
#define SORTFUNC versionsort
|
|
Packit |
4a16fb |
#else
|
|
Packit |
4a16fb |
#define SORTFUNC alphasort
|
|
Packit |
4a16fb |
#endif
|
|
Packit |
4a16fb |
err = scandir(filename, &namelist, filename_filter, SORTFUNC);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
err = -errno;
|
|
Packit |
4a16fb |
uc_error("error: could not scan directory %s: %s",
|
|
Packit |
4a16fb |
filename, strerror(-err));
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
cnt = err;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
dfl[0] = '\0';
|
|
Packit |
4a16fb |
if (strlen(filename) + 8 < sizeof(filename)) {
|
|
Packit |
4a16fb |
strcat(filename, "/default");
|
|
Packit |
4a16fb |
ss = readlink(filename, dfl, sizeof(dfl)-1);
|
|
Packit |
4a16fb |
if (ss >= 0) {
|
|
Packit |
4a16fb |
dfl[ss] = '\0';
|
|
Packit |
4a16fb |
dfl[sizeof(dfl)-1] = '\0';
|
|
Packit |
4a16fb |
if (dfl[0] && dfl[strlen(dfl)-1] == '/')
|
|
Packit |
4a16fb |
dfl[strlen(dfl)-1] = '\0';
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
dfl[0] = '\0';
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
list = calloc(1, cnt * 2 * sizeof(char *));
|
|
Packit |
4a16fb |
if (list == NULL) {
|
|
Packit |
4a16fb |
err = -ENOMEM;
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
for (i = j = 0; i < cnt; i++) {
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
d_name = namelist[i]->d_name;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
/* Skip the directories for component devices */
|
|
Packit |
4a16fb |
if (is_component_directory(d_name))
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
configuration_filename2(filename, sizeof(filename), 2,
|
|
Packit |
4a16fb |
d_name, d_name, ".conf");
|
|
Packit |
4a16fb |
err = uc_mgr_config_load(2, filename, &cfg;;
|
|
Packit |
4a16fb |
if (err < 0)
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
err = snd_config_search(cfg, "Syntax", &c);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("Syntax field not found in %s", d_name);
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
continue;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = snd_config_get_integer(c, &l);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
uc_error("Syntax field is invalid in %s", d_name);
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (l < 2 || l > SYNTAX_VERSION_MAX) {
|
|
Packit |
4a16fb |
uc_error("Incompatible syntax %d in %s", l, d_name);
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = snd_config_search(cfg, "Comment", &c);
|
|
Packit |
4a16fb |
if (err >= 0) {
|
|
Packit |
4a16fb |
err = parse_string(c, (char **)&list[j+1]);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
snd_config_delete(cfg);
|
|
Packit |
4a16fb |
list[j] = strdup(d_name);
|
|
Packit |
4a16fb |
if (list[j] == NULL) {
|
|
Packit |
4a16fb |
err = -ENOMEM;
|
|
Packit |
4a16fb |
goto __err;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
if (strcmp(dfl, list[j]) == 0) {
|
|
Packit |
4a16fb |
/* default to top */
|
|
Packit |
4a16fb |
const char *save1 = list[j];
|
|
Packit |
4a16fb |
const char *save2 = list[j + 1];
|
|
Packit |
4a16fb |
memmove(list + 2, list, j * sizeof(char *));
|
|
Packit |
4a16fb |
list[0] = save1;
|
|
Packit |
4a16fb |
list[1] = save2;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
j += 2;
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
err = j;
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
__err:
|
|
Packit |
4a16fb |
for (i = 0; i < cnt; i++) {
|
|
Packit |
4a16fb |
free(namelist[i]);
|
|
Packit |
4a16fb |
if (err < 0) {
|
|
Packit |
4a16fb |
free((void *)list[i * 2]);
|
|
Packit |
4a16fb |
free((void *)list[i * 2 + 1]);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
free(namelist);
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
if (err >= 0) {
|
|
Packit |
4a16fb |
*_list = list;
|
|
Packit |
4a16fb |
} else {
|
|
Packit |
4a16fb |
free(list);
|
|
Packit |
4a16fb |
}
|
|
Packit |
4a16fb |
|
|
Packit |
4a16fb |
return err;
|
|
Packit |
4a16fb |
}
|