Blame src/ucm/parser.c

Packit Service db8eaa
/*
Packit Service db8eaa
 *  This library is free software; you can redistribute it and/or
Packit Service db8eaa
 *  modify it under the terms of the GNU Lesser General Public
Packit Service db8eaa
 *  License as published by the Free Software Foundation; either
Packit Service db8eaa
 *  version 2 of the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  This library is distributed in the hope that it will be useful,
Packit Service db8eaa
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service db8eaa
 *  Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *  License along with this library; if not, write to the Free Software  
Packit Service db8eaa
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 *  Support for the verb/device/modifier core logic and API,
Packit Service db8eaa
 *  command line tool and file parser was kindly sponsored by
Packit Service db8eaa
 *  Texas Instruments Inc.
Packit Service db8eaa
 *  Support for multiple active modifiers and devices,
Packit Service db8eaa
 *  transition sequences, multiple client access and user defined use
Packit Service db8eaa
 *  cases was kindly sponsored by Wolfson Microelectronics PLC.
Packit Service db8eaa
 *
Packit Service db8eaa
 *  Copyright (C) 2008-2010 SlimLogic Ltd
Packit Service db8eaa
 *  Copyright (C) 2010 Wolfson Microelectronics PLC
Packit Service db8eaa
 *  Copyright (C) 2010 Texas Instruments Inc.
Packit Service db8eaa
 *  Copyright (C) 2010 Red Hat Inc.
Packit Service db8eaa
 *  Authors: Liam Girdwood <lrg@slimlogic.co.uk>
Packit Service db8eaa
 *	         Stefan Schmidt <stefan@slimlogic.co.uk>
Packit Service db8eaa
 *	         Justin Xu <justinx@slimlogic.co.uk>
Packit Service db8eaa
 *               Jaroslav Kysela <perex@perex.cz>
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#include "ucm_local.h"
Packit Service db8eaa
#include <dirent.h>
Packit Service db8eaa
#include <limits.h>
Packit Service db8eaa
Packit Service db8eaa
/* Directories to store UCM configuration files for components, like
Packit Service db8eaa
 * off-soc codecs or embedded DSPs. Components can define their own
Packit Service db8eaa
 * devices and sequences, to be reused by sound cards/machines. UCM
Packit Service db8eaa
 * manager should not scan these component directories.
Packit Service db8eaa
 * Machine use case files can include component configratuation files
Packit Service db8eaa
 * via alsaconf syntax:
Packit Service db8eaa
 * <searchdir:component-directory-name> and <component-conf-file-name>.
Packit Service db8eaa
 * Alsaconf will import the included files automatically. After including
Packit Service db8eaa
 * a component file, a machine device's sequence can enable or disable
Packit Service db8eaa
 * a component device via syntax:
Packit Service db8eaa
 * enadev "component_device_name"
Packit Service db8eaa
 * disdev "component_device_name"
Packit Service db8eaa
 */
Packit Service db8eaa
static const char * const component_dir[] = {
Packit Service db8eaa
	"codecs",	/* for off-soc codecs */
Packit Service db8eaa
	"dsps",		/* for DSPs embedded in SoC */
Packit Service db8eaa
	"platforms",	/* for common platform implementations */
Packit Service db8eaa
	NULL,		/* terminator */
Packit Service db8eaa
};
Packit Service db8eaa
Packit Service db8eaa
static int filename_filter(const struct dirent *dirent);
Packit Service db8eaa
static int is_component_directory(const char *dir);
Packit Service db8eaa
Packit Service db8eaa
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			  struct list_head *base,
Packit Service db8eaa
			  snd_config_t *cfg);
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * compose the absolute ucm filename
Packit Service db8eaa
 */
Packit Service db8eaa
static void ucm_filename(char *fn, size_t fn_len, long version,
Packit Service db8eaa
			  const char *dir, const char *file)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR);
Packit Service db8eaa
Packit Service db8eaa
	if (env == NULL)
Packit Service db8eaa
		snprintf(fn, fn_len, "%s/%s/%s%s%s",
Packit Service db8eaa
			 snd_config_topdir(), version > 1 ? "ucm2" : "ucm",
Packit Service db8eaa
			 dir ?: "", dir ? "/" : "", file);
Packit Service db8eaa
	else
Packit Service db8eaa
		snprintf(fn, fn_len, "%s/%s%s%s",
Packit Service db8eaa
			 env, dir ?: "", dir ? "/" : "", file);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			     const char *file, snd_config_t **cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	char filename[PATH_MAX];
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	ucm_filename(filename, sizeof(filename), uc_mgr->conf_format,
Packit Service db8eaa
		     file[0] == '/' ? NULL : uc_mgr->conf_dir_name,
Packit Service db8eaa
		     file);
Packit Service db8eaa
	err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("error: failed to open file %s : %d", filename, -errno);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Replace mallocated string
Packit Service db8eaa
 */
Packit Service db8eaa
static char *replace_string(char **dst, const char *value)
Packit Service db8eaa
{
Packit Service db8eaa
	free(*dst);
Packit Service db8eaa
	*dst = value ? strdup(value) : NULL;
Packit Service db8eaa
	return *dst;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse string
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_string(snd_config_t *n, char **res)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_string(n, (const char **)res);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	*res = strdup(*res);
Packit Service db8eaa
	if (*res == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse string and substitute
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_string_substitute(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			    snd_config_t *n, char **res)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *str;
Packit Service db8eaa
	char *s;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_string(n, &str);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = uc_mgr_get_substituted_value(uc_mgr, &s, str);
Packit Service db8eaa
	if (err >= 0)
Packit Service db8eaa
		*res = s;
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse string and substitute
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_string_substitute3(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			     snd_config_t *n, char **res)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		return parse_string(n, res);
Packit Service db8eaa
	return parse_string_substitute(uc_mgr, n, res);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse integer with substitution
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_integer_substitute(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			     snd_config_t *n, long *res)
Packit Service db8eaa
{
Packit Service db8eaa
	char *s1, *s2;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_ascii(n, &s1;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
Packit Service db8eaa
	if (err >= 0)
Packit Service db8eaa
		err = safe_strtol(s2, res);
Packit Service db8eaa
	free(s2);
Packit Service db8eaa
	free(s1);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse integer with substitution
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_integer_substitute3(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			      snd_config_t *n, long *res)
Packit Service db8eaa
{
Packit Service db8eaa
	char *s1, *s2;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_ascii(n, &s1;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (uc_mgr->conf_format < 3)
Packit Service db8eaa
		s2 = s1;
Packit Service db8eaa
	else
Packit Service db8eaa
		err = uc_mgr_get_substituted_value(uc_mgr, &s2, s1);
Packit Service db8eaa
	if (err >= 0)
Packit Service db8eaa
		err = safe_strtol(s2, res);
Packit Service db8eaa
	if (s1 != s2)
Packit Service db8eaa
		free(s2);
Packit Service db8eaa
	free(s1);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse safe ID
Packit Service db8eaa
 */
Packit Service db8eaa
int parse_is_name_safe(const char *name)
Packit Service db8eaa
{
Packit Service db8eaa
	if (strchr(name, '.')) {
Packit Service db8eaa
		uc_error("char '.' not allowed in '%s'", name);
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 1;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
int get_string3(snd_use_case_mgr_t *uc_mgr, const char *s1, char **s)
Packit Service db8eaa
{
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		*s = strdup(s1);
Packit Service db8eaa
		if (*s == NULL)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	}
Packit Service db8eaa
	return uc_mgr_get_substituted_value(uc_mgr, s, s1);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n,
Packit Service db8eaa
			const char *alt, char **name)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (alt) {
Packit Service db8eaa
		id = alt;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		err = snd_config_get_id(n, &id;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = get_string3(uc_mgr, id, name);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (!parse_is_name_safe(*name)) {
Packit Service db8eaa
		free(*name);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Handle 'Error' configuration node.
Packit Service db8eaa
 */
Packit Service db8eaa
static int error_node(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	char *s;
Packit Service db8eaa
Packit Service db8eaa
	err = parse_string_substitute3(uc_mgr, cfg, &s);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("error: failed to get Error string");
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	uc_error("%s", s);
Packit Service db8eaa
	free(s);
Packit Service db8eaa
	return -ENXIO;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Evaluate variable regex definitions (in-place delete)
Packit Service db8eaa
 */
Packit Service db8eaa
static int evaluate_regex(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			  snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *d, *n;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_search(cfg, "DefineRegex", &d);
Packit Service db8eaa
	if (err == -ENOENT)
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for DefineRegex");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		uc_error("DefineRegex is supported in v3+ syntax");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, d) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		err = snd_config_get_id(n, &id;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		err = uc_mgr_define_regex(uc_mgr, id, n);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_delete(d);
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Evaluate variable definitions (in-place delete)
Packit Service db8eaa
 */
Packit Service db8eaa
static int evaluate_define(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			   snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *d, *n;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	char *var, *s;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_search(cfg, "Define", &d);
Packit Service db8eaa
	if (err == -ENOENT)
Packit Service db8eaa
		return evaluate_regex(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(d) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for Define");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format < 3) {
Packit Service db8eaa
		uc_error("Define is supported in v3+ syntax");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, d) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		err = snd_config_get_id(n, &id;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		err = snd_config_get_ascii(n, &var);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		err = uc_mgr_get_substituted_value(uc_mgr, &s, var);
Packit Service db8eaa
		free(var);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		uc_mgr_set_variable(uc_mgr, id, s);
Packit Service db8eaa
		free(s);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_delete(d);
Packit Service db8eaa
Packit Service db8eaa
	return evaluate_regex(uc_mgr, cfg);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Evaluate include (in-place)
Packit Service db8eaa
 */
Packit Service db8eaa
static int evaluate_include(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			    snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_search(cfg, "Include", &n);
Packit Service db8eaa
	if (err == -ENOENT)
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	err = uc_mgr_evaluate_include(uc_mgr, cfg, n);
Packit Service db8eaa
	snd_config_delete(n);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Evaluate condition (in-place)
Packit Service db8eaa
 */
Packit Service db8eaa
static int evaluate_condition(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			      snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_search(cfg, "If", &n);
Packit Service db8eaa
	if (err == -ENOENT)
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	err = uc_mgr_evaluate_condition(uc_mgr, cfg, n);
Packit Service db8eaa
	snd_config_delete(n);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * In-place evaluate
Packit Service db8eaa
 */
Packit Service db8eaa
int uc_mgr_evaluate_inplace(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			    snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	int err1 = 0, err2 = 0, err3 = 0;
Packit Service db8eaa
Packit Service db8eaa
	while (err1 == 0 || err2 == 0 || err3 == 0) {
Packit Service db8eaa
		/* variables at first */
Packit Service db8eaa
		err1 = evaluate_define(uc_mgr, cfg);
Packit Service db8eaa
		if (err1 < 0)
Packit Service db8eaa
			return err1;
Packit Service db8eaa
		/* include at second */
Packit Service db8eaa
		err2 = evaluate_include(uc_mgr, cfg);
Packit Service db8eaa
		if (err2 < 0)
Packit Service db8eaa
			return err2;
Packit Service db8eaa
		/* include may define another variables */
Packit Service db8eaa
		/* conditions may depend on them */
Packit Service db8eaa
		if (err2 == 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		err3 = evaluate_condition(uc_mgr, cfg);
Packit Service db8eaa
		if (err3 < 0)
Packit Service db8eaa
			return err3;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse transition
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_transition(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			    struct list_head *tlist,
Packit Service db8eaa
			    snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	struct transition_sequence *tseq;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_id(cfg, &id) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for %s", id);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
		tseq = calloc(1, sizeof(*tseq));
Packit Service db8eaa
		if (tseq == NULL)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		INIT_LIST_HEAD(&tseq->transition_list);
Packit Service db8eaa
Packit Service db8eaa
		err = get_string3(uc_mgr, id, &tseq->name);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free(tseq);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	
Packit Service db8eaa
		err = parse_sequence(uc_mgr, &tseq->transition_list, n);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_mgr_free_transition_element(tseq);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		list_add(&tseq->list, tlist);
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse compound
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
Packit Service db8eaa
	  int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
Packit Service db8eaa
	  void *data1, void *data2)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_id(cfg, &id) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for %s", id);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* parse compound */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
			uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg));
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		
Packit Service db8eaa
		err = fcn(uc_mgr, n, data1, data2);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int strip_legacy_dev_index(char *name)
Packit Service db8eaa
{
Packit Service db8eaa
	char *dot = strchr(name, '.');
Packit Service db8eaa
	if (!dot)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (dot[1] != '0' || dot[2] != '\0') {
Packit Service db8eaa
		uc_error("device name %s contains a '.',"
Packit Service db8eaa
			 " and is not legacy foo.0 format", name);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	*dot = '\0';
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse device list
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
Packit Service db8eaa
			     struct dev_list *dev_list,
Packit Service db8eaa
			     enum dev_list_type type,
Packit Service db8eaa
			     snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	struct dev_list_node *sdev;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (dev_list->type != DEVLIST_NONE) {
Packit Service db8eaa
		uc_error("error: multiple supported or"
Packit Service db8eaa
			" conflicting device lists");
Packit Service db8eaa
		return -EEXIST;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_id(cfg, &id) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for %s", id);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
		sdev = calloc(1, sizeof(struct dev_list_node));
Packit Service db8eaa
		if (sdev == NULL)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		err = parse_string_substitute3(uc_mgr, n, &sdev->name);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free(sdev);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = strip_legacy_dev_index(sdev->name);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free(sdev->name);
Packit Service db8eaa
			free(sdev);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		list_add(&sdev->list, &dev_list->list);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	dev_list->type = type;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* Find a component device by its name, and remove it from machine device
Packit Service db8eaa
 * list.
Packit Service db8eaa
 *
Packit Service db8eaa
 * Component devices are defined by machine components (usually off-soc
Packit Service db8eaa
 * codes or DSP embeded in SoC). Since alsaconf imports their configuration
Packit Service db8eaa
 * files automatically, we don't know which devices are component devices
Packit Service db8eaa
 * until they are referenced by a machine device sequence. So here when we
Packit Service db8eaa
 * find a referenced device, we move it from the machine device list to the
Packit Service db8eaa
 * component device list. Component devices will not be exposed to applications
Packit Service db8eaa
 * by the original API to list devices for backward compatibility. So sound
Packit Service db8eaa
 * servers can only see the machine devices.
Packit Service db8eaa
 */
Packit Service db8eaa
struct use_case_device *find_component_dev(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
	const char *name)
Packit Service db8eaa
{
Packit Service db8eaa
	struct list_head *pos, *posdev, *_posdev;
Packit Service db8eaa
	struct use_case_verb *verb;
Packit Service db8eaa
	struct use_case_device *dev;
Packit Service db8eaa
Packit Service db8eaa
	list_for_each(pos, &uc_mgr->verb_list) {
Packit Service db8eaa
		verb = list_entry(pos, struct use_case_verb, list);
Packit Service db8eaa
Packit Service db8eaa
		/* search in the component device list */
Packit Service db8eaa
		list_for_each(posdev, &verb->cmpt_device_list) {
Packit Service db8eaa
			dev = list_entry(posdev, struct use_case_device, list);
Packit Service db8eaa
			if (!strcmp(dev->name, name))
Packit Service db8eaa
				return dev;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* search the machine device list */
Packit Service db8eaa
		list_for_each_safe(posdev, _posdev, &verb->device_list) {
Packit Service db8eaa
			dev = list_entry(posdev, struct use_case_device, list);
Packit Service db8eaa
			if (!strcmp(dev->name, name)) {
Packit Service db8eaa
				/* find the component device, move it from the
Packit Service db8eaa
				 * machine device list to the component device
Packit Service db8eaa
				 * list.
Packit Service db8eaa
				 */
Packit Service db8eaa
				list_del(&dev->list);
Packit Service db8eaa
				list_add_tail(&dev->list,
Packit Service db8eaa
					      &verb->cmpt_device_list);
Packit Service db8eaa
				return dev;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return NULL;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* parse sequence of a component device
Packit Service db8eaa
 *
Packit Service db8eaa
 * This function will find the component device and mark if its enable or
Packit Service db8eaa
 * disable sequence is needed by its parenet device.
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_component_seq(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			       snd_config_t *n, int enable,
Packit Service db8eaa
			       struct component_sequence *cmpt_seq)
Packit Service db8eaa
{
Packit Service db8eaa
	char *val;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = parse_string_substitute3(uc_mgr, n, &val;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	cmpt_seq->device = find_component_dev(uc_mgr, val);
Packit Service db8eaa
	if (!cmpt_seq->device) {
Packit Service db8eaa
		uc_error("error: Cannot find component device %s", val);
Packit Service db8eaa
		free(val);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	free(val);
Packit Service db8eaa
Packit Service db8eaa
	/* Parent needs its enable or disable sequence */
Packit Service db8eaa
	cmpt_seq->enable = enable;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse sequences.
Packit Service db8eaa
 *
Packit Service db8eaa
 * Sequence controls elements  are in the following form:-
Packit Service db8eaa
 *
Packit Service db8eaa
 * cdev "hw:0"
Packit Service db8eaa
 * cset "element_id_syntax value_syntax"
Packit Service db8eaa
 * usleep time
Packit Service db8eaa
 * exec "any unix command with arguments"
Packit Service db8eaa
 * enadev "component device name"
Packit Service db8eaa
 * disdev "component device name"
Packit Service db8eaa
 *
Packit Service db8eaa
 * e.g.
Packit Service db8eaa
 *	cset "name='Master Playback Switch' 0,0"
Packit Service db8eaa
 *      cset "iface=PCM,name='Disable HDMI',index=1 0"
Packit Service db8eaa
 *	enadev "rt286:Headphones"
Packit Service db8eaa
 *	disdev "rt286:Speaker"
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			  struct list_head *base,
Packit Service db8eaa
			  snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	struct sequence_element *curr;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err, idx = 0;
Packit Service db8eaa
	const char *cmd = NULL;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("error: compound is expected for sequence definition");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		idx ^= 1;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		err = snd_config_get_id(n, &id;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		if (idx == 1) {
Packit Service db8eaa
			if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) {
Packit Service db8eaa
				uc_error("error: string type is expected for sequence command");
Packit Service db8eaa
				return -EINVAL;
Packit Service db8eaa
			}
Packit Service db8eaa
			snd_config_get_string(n, &cmd);
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* alloc new sequence element */
Packit Service db8eaa
		curr = calloc(1, sizeof(struct sequence_element));
Packit Service db8eaa
		if (curr == NULL)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		list_add_tail(&curr->list, base);
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "cdev") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CDEV;
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &curr->data.cdev);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: cdev requires a string!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "cset") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: cset requires a string!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "enadev") == 0) {
Packit Service db8eaa
			/* need to enable a component device */
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
Packit Service db8eaa
			err = parse_component_seq(uc_mgr, n, 1,
Packit Service db8eaa
						&curr->data.cmpt_seq);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: enadev requires a valid device!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "disdev") == 0) {
Packit Service db8eaa
			/* need to disable a component device */
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CMPT_SEQ;
Packit Service db8eaa
			err = parse_component_seq(uc_mgr, n, 0,
Packit Service db8eaa
						&curr->data.cmpt_seq);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: disdev requires a valid device!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "cset-bin-file") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: cset-bin-file requires a string!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "cset-tlv") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: cset-tlv requires a string!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "usleep") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
Packit Service db8eaa
			err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: usleep requires integer!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "msleep") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_SLEEP;
Packit Service db8eaa
			err = parse_integer_substitute3(uc_mgr, n, &curr->data.sleep);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: msleep requires integer!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			curr->data.sleep *= 1000L;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(cmd, "exec") == 0) {
Packit Service db8eaa
			curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: exec requires a string!");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
		
Packit Service db8eaa
		list_del(&curr->list);
Packit Service db8eaa
		uc_mgr_free_sequence_element(curr);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
int uc_mgr_add_value(struct list_head *base, const char *key, char *val)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ucm_value *curr;
Packit Service db8eaa
Packit Service db8eaa
	curr = calloc(1, sizeof(struct ucm_value));
Packit Service db8eaa
	if (curr == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	curr->name = strdup(key);
Packit Service db8eaa
	if (curr->name == NULL) {
Packit Service db8eaa
		free(curr);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	list_add_tail(&curr->list, base);
Packit Service db8eaa
	curr->data = val;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse values.
Packit Service db8eaa
 *
Packit Service db8eaa
 * Parse values describing PCM, control/mixer settings and stream parameters.
Packit Service db8eaa
 *
Packit Service db8eaa
 * Value {
Packit Service db8eaa
 *   TQ Voice
Packit Service db8eaa
 *   CapturePCM "hw:1"
Packit Service db8eaa
 *   PlaybackVolume "name='Master Playback Volume',index=2"
Packit Service db8eaa
 *   PlaybackSwitch "name='Master Playback Switch',index=2"
Packit Service db8eaa
 * }
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED,
Packit Service db8eaa
			  struct list_head *base,
Packit Service db8eaa
			  snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	char *s;
Packit Service db8eaa
	snd_config_type_t type;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("error: compound is expected for value definition");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		err = snd_config_get_id(n, &id;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		type = snd_config_get_type(n);
Packit Service db8eaa
		switch (type) {
Packit Service db8eaa
		case SND_CONFIG_TYPE_INTEGER:
Packit Service db8eaa
		case SND_CONFIG_TYPE_INTEGER64:
Packit Service db8eaa
		case SND_CONFIG_TYPE_REAL:
Packit Service db8eaa
			err = snd_config_get_ascii(n, &s);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: unable to parse value for id '%s': %s!", id, snd_strerror(err));
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			break;
Packit Service db8eaa
		case SND_CONFIG_TYPE_STRING:
Packit Service db8eaa
			err = parse_string_substitute(uc_mgr, n, &s);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: unable to parse a string for id '%s'!", id);
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			break;
Packit Service db8eaa
		default:
Packit Service db8eaa
			uc_error("error: invalid type %i in Value compound '%s'", type, id);
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = uc_mgr_add_value(base, id, s);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free(s);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse Modifier Use cases
Packit Service db8eaa
 *
Packit Service db8eaa
 * # Each modifier is described in new section. N modifiers are allowed
Packit Service db8eaa
 * SectionModifier."Capture Voice" {
Packit Service db8eaa
 *
Packit Service db8eaa
 *	Comment "Record voice call"
Packit Service db8eaa
 *
Packit Service db8eaa
 *	SupportedDevice [
Packit Service db8eaa
 *		"x"
Packit Service db8eaa
 *		"y"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	ConflictingDevice [
Packit Service db8eaa
 *		"x"
Packit Service db8eaa
 *		"y"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	EnableSequence [
Packit Service db8eaa
 *		....
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	DisableSequence [
Packit Service db8eaa
 *		...
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *      TransitionSequence."ToModifierName" [
Packit Service db8eaa
 *		...
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	# Optional TQ and ALSA PCMs
Packit Service db8eaa
 *	Value {
Packit Service db8eaa
 *		TQ Voice
Packit Service db8eaa
 *		CapturePCM "hw:1"
Packit Service db8eaa
 *		PlaybackVolume "name='Master Playback Volume',index=2"
Packit Service db8eaa
 *		PlaybackSwitch "name='Master Playback Switch',index=2"
Packit Service db8eaa
 *	}
Packit Service db8eaa
 * }
Packit Service db8eaa
 *
Packit Service db8eaa
 * SupportedDevice and ConflictingDevice cannot be specified together.
Packit Service db8eaa
 * Both are optional.
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_modifier(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			  snd_config_t *cfg,
Packit Service db8eaa
			  void *data1, void *data2)
Packit Service db8eaa
{
Packit Service db8eaa
	struct use_case_verb *verb = data1;
Packit Service db8eaa
	struct use_case_modifier *modifier;
Packit Service db8eaa
	char *name;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	/* allocate modifier */
Packit Service db8eaa
	modifier = calloc(1, sizeof(*modifier));
Packit Service db8eaa
	if (modifier == NULL) {
Packit Service db8eaa
		free(name);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	INIT_LIST_HEAD(&modifier->enable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&modifier->disable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&modifier->transition_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&modifier->dev_list.list);
Packit Service db8eaa
	INIT_LIST_HEAD(&modifier->value_list);
Packit Service db8eaa
	list_add_tail(&modifier->list, &verb->modifier_list);
Packit Service db8eaa
	modifier->name = name;
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Comment") == 0) {
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &modifier->comment);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to get modifier comment");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "SupportedDevice") == 0) {
Packit Service db8eaa
			err = parse_device_list(uc_mgr, &modifier->dev_list,
Packit Service db8eaa
						DEVLIST_SUPPORTED, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse supported"
Packit Service db8eaa
					" device list");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "ConflictingDevice") == 0) {
Packit Service db8eaa
			err = parse_device_list(uc_mgr, &modifier->dev_list,
Packit Service db8eaa
						DEVLIST_CONFLICTING, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse conflicting"
Packit Service db8eaa
					" device list");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "EnableSequence") == 0) {
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &modifier->enable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse modifier"
Packit Service db8eaa
					" enable sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "DisableSequence") == 0) {
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &modifier->disable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse modifier"
Packit Service db8eaa
					" disable sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "TransitionSequence") == 0) {
Packit Service db8eaa
			err = parse_transition(uc_mgr, &modifier->transition_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse transition"
Packit Service db8eaa
					" modifier");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Value") == 0) {
Packit Service db8eaa
			err = parse_value(uc_mgr, &modifier->value_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse Value");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse Device Use Cases
Packit Service db8eaa
 *
Packit Service db8eaa
 * # Each device is described in new section. N devices are allowed
Packit Service db8eaa
 * SectionDevice."Headphones" {
Packit Service db8eaa
 *	Comment "Headphones connected to 3.5mm jack"
Packit Service db8eaa
 *
Packit Service db8eaa
 *	SupportedDevice [
Packit Service db8eaa
 *		"x"
Packit Service db8eaa
 *		"y"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	ConflictingDevice [
Packit Service db8eaa
 *		"x"
Packit Service db8eaa
 *		"y"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	EnableSequence [
Packit Service db8eaa
 *		....
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	DisableSequence [
Packit Service db8eaa
 *		...
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *      TransitionSequence."ToDevice" [
Packit Service db8eaa
 *		...
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	Value {
Packit Service db8eaa
 *		PlaybackVolume "name='Master Playback Volume',index=2"
Packit Service db8eaa
 *		PlaybackSwitch "name='Master Playback Switch',index=2"
Packit Service db8eaa
 *	}
Packit Service db8eaa
 * }
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_device(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			snd_config_t *cfg,
Packit Service db8eaa
			void *data1, void *data2)
Packit Service db8eaa
{
Packit Service db8eaa
	struct use_case_verb *verb = data1;
Packit Service db8eaa
	char *name;
Packit Service db8eaa
	struct use_case_device *device;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (parse_get_safe_name(uc_mgr, cfg, data2, &name) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	device = calloc(1, sizeof(*device));
Packit Service db8eaa
	if (device == NULL) {
Packit Service db8eaa
		free(name);
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
	INIT_LIST_HEAD(&device->enable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&device->disable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&device->transition_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&device->dev_list.list);
Packit Service db8eaa
	INIT_LIST_HEAD(&device->value_list);
Packit Service db8eaa
	list_add_tail(&device->list, &verb->device_list);
Packit Service db8eaa
	device->name = name;
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Comment") == 0) {
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &device->comment);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to get device comment");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "SupportedDevice") == 0) {
Packit Service db8eaa
			err = parse_device_list(uc_mgr, &device->dev_list,
Packit Service db8eaa
						DEVLIST_SUPPORTED, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse supported"
Packit Service db8eaa
					" device list");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "ConflictingDevice") == 0) {
Packit Service db8eaa
			err = parse_device_list(uc_mgr, &device->dev_list,
Packit Service db8eaa
						DEVLIST_CONFLICTING, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse conflicting"
Packit Service db8eaa
					" device list");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "EnableSequence") == 0) {
Packit Service db8eaa
			uc_dbg("EnableSequence");
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &device->enable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse device enable"
Packit Service db8eaa
					 " sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "DisableSequence") == 0) {
Packit Service db8eaa
			uc_dbg("DisableSequence");
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &device->disable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse device disable"
Packit Service db8eaa
					 " sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "TransitionSequence") == 0) {
Packit Service db8eaa
			uc_dbg("TransitionSequence");
Packit Service db8eaa
			err = parse_transition(uc_mgr, &device->transition_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse transition"
Packit Service db8eaa
					" device");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Value") == 0) {
Packit Service db8eaa
			err = parse_value(uc_mgr, &device->value_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse Value");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse Device Rename/Delete Command
Packit Service db8eaa
 *
Packit Service db8eaa
 * # The devices might be renamed to allow the better conditional runtime
Packit Service db8eaa
 * # evaluation. Bellow example renames Speaker1 device to Speaker and
Packit Service db8eaa
 * # removes Speaker2 device.
Packit Service db8eaa
 * RenameDevice."Speaker1" "Speaker"
Packit Service db8eaa
 * RemoveDevice."Speaker2" "Speaker2"
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_dev_name_list(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			       snd_config_t *cfg,
Packit Service db8eaa
			       struct list_head *list)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	const char *id, *name1;
Packit Service db8eaa
	char *name1s, *name2;
Packit Service db8eaa
	struct ucm_dev_name *dev;
Packit Service db8eaa
	snd_config_iterator_t pos;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_id(cfg, &id) < 0)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for %s", id);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_id(n, &name1) < 0)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
		err = get_string3(uc_mgr, name1, &name1s);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
Packit Service db8eaa
		err = parse_string_substitute3(uc_mgr, n, &name2);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free(name1s);
Packit Service db8eaa
			uc_error("error: failed to get target device name for '%s'", name1);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* skip duplicates */
Packit Service db8eaa
		list_for_each(pos, list) {
Packit Service db8eaa
			dev = list_entry(pos, struct ucm_dev_name, list);
Packit Service db8eaa
			if (strcmp(dev->name1, name1s) == 0) {
Packit Service db8eaa
				free(name2);
Packit Service db8eaa
				free(name1s);
Packit Service db8eaa
				return 0;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		free(name1s);
Packit Service db8eaa
Packit Service db8eaa
		dev = calloc(1, sizeof(*dev));
Packit Service db8eaa
		if (dev == NULL) {
Packit Service db8eaa
			free(name2);
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		}
Packit Service db8eaa
		dev->name1 = strdup(name1);
Packit Service db8eaa
		if (dev->name1 == NULL) {
Packit Service db8eaa
			free(dev);
Packit Service db8eaa
			free(name2);
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
		}
Packit Service db8eaa
		dev->name2 = name2;
Packit Service db8eaa
		list_add_tail(&dev->list, list);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
	  snd_config_t *cfg,
Packit Service db8eaa
	  int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
Packit Service db8eaa
	  void *data1)
Packit Service db8eaa
{
Packit Service db8eaa
	const char *id, *idchild;
Packit Service db8eaa
	int child_ctr = 0, legacy_format = 1;
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *child;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_get_id(cfg, &id;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		child_ctr++;
Packit Service db8eaa
		if (child_ctr > 1) {
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		child = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
			legacy_format = 0;
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_id(child, &idchild) < 0)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(idchild, "0")) {
Packit Service db8eaa
			legacy_format = 0;
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	if (child_ctr != 1) {
Packit Service db8eaa
		legacy_format = 0;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (legacy_format)
Packit Service db8eaa
		return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id);
Packit Service db8eaa
	else
Packit Service db8eaa
		return fcn(uc_mgr, cfg, data1, NULL);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int parse_device_name(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			     snd_config_t *cfg,
Packit Service db8eaa
			     void *data1,
Packit Service db8eaa
			     void *data2 ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			     snd_config_t *cfg,
Packit Service db8eaa
			     void *data1,
Packit Service db8eaa
			     void *data2 ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	return parse_compound(uc_mgr, cfg, parse_modifier, data1, data2);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int verb_dev_list_add(struct use_case_verb *verb,
Packit Service db8eaa
			     enum dev_list_type dst_type,
Packit Service db8eaa
			     const char *dst,
Packit Service db8eaa
			     const char *src)
Packit Service db8eaa
{
Packit Service db8eaa
	struct use_case_device *device;
Packit Service db8eaa
	struct list_head *pos;
Packit Service db8eaa
Packit Service db8eaa
	list_for_each(pos, &verb->device_list) {
Packit Service db8eaa
		device = list_entry(pos, struct use_case_device, list);
Packit Service db8eaa
		if (strcmp(device->name, dst) != 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
		if (device->dev_list.type != dst_type) {
Packit Service db8eaa
			if (list_empty(&device->dev_list.list)) {
Packit Service db8eaa
				device->dev_list.type = dst_type;
Packit Service db8eaa
			} else {
Packit Service db8eaa
				uc_error("error: incompatible device list type ('%s', '%s')",
Packit Service db8eaa
					 device->name, src);
Packit Service db8eaa
				return -EINVAL;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
		return uc_mgr_put_to_dev_list(&device->dev_list, src);
Packit Service db8eaa
	}
Packit Service db8eaa
	uc_error("error: unable to find device '%s'", dst);
Packit Service db8eaa
	return -ENOENT;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int verb_dev_list_check(struct use_case_verb *verb)
Packit Service db8eaa
{
Packit Service db8eaa
	struct list_head *pos, *pos2;
Packit Service db8eaa
	struct use_case_device *device;
Packit Service db8eaa
	struct dev_list_node *dlist;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	list_for_each(pos, &verb->device_list) {
Packit Service db8eaa
		device = list_entry(pos, struct use_case_device, list);
Packit Service db8eaa
		list_for_each(pos2, &device->dev_list.list) {
Packit Service db8eaa
			dlist = list_entry(pos2, struct dev_list_node, list);
Packit Service db8eaa
			err = verb_dev_list_add(verb, device->dev_list.type,
Packit Service db8eaa
						dlist->name, device->name);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int verb_device_management(struct use_case_verb *verb)
Packit Service db8eaa
{
Packit Service db8eaa
	struct list_head *pos;
Packit Service db8eaa
	struct ucm_dev_name *dev;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	/* rename devices */
Packit Service db8eaa
	list_for_each(pos, &verb->rename_list) {
Packit Service db8eaa
		dev = list_entry(pos, struct ucm_dev_name, list);
Packit Service db8eaa
		err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("error: cannot rename device '%s' to '%s'", dev->name1, dev->name2);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* remove devices */
Packit Service db8eaa
	list_for_each(pos, &verb->remove_list) {
Packit Service db8eaa
		dev = list_entry(pos, struct ucm_dev_name, list);
Packit Service db8eaa
		err = uc_mgr_remove_device(verb, dev->name2);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("error: cannot remove device '%s'", dev->name2);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* those lists are no longer used */
Packit Service db8eaa
	uc_mgr_free_dev_name_list(&verb->rename_list);
Packit Service db8eaa
	uc_mgr_free_dev_name_list(&verb->remove_list);
Packit Service db8eaa
Packit Service db8eaa
	/* handle conflicting/supported lists */
Packit Service db8eaa
	return verb_dev_list_check(verb);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse Verb Section
Packit Service db8eaa
 *
Packit Service db8eaa
 * # Example Use case verb section for Voice call blah
Packit Service db8eaa
 * # By Joe Blogs <joe@blogs.com>
Packit Service db8eaa
 *
Packit Service db8eaa
 * SectionVerb {
Packit Service db8eaa
 *	# enable and disable sequences are compulsory
Packit Service db8eaa
 *	EnableSequence [
Packit Service db8eaa
 *		cset "name='Master Playback Switch',index=2 0,0"
Packit Service db8eaa
 *		cset "name='Master Playback Volume',index=2 25,25"
Packit Service db8eaa
 *		msleep 50
Packit Service db8eaa
 *		cset "name='Master Playback Switch',index=2 1,1"
Packit Service db8eaa
 *		cset "name='Master Playback Volume',index=2 50,50"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	DisableSequence [
Packit Service db8eaa
 *		cset "name='Master Playback Switch',index=2 0,0"
Packit Service db8eaa
 *		cset "name='Master Playback Volume',index=2 25,25"
Packit Service db8eaa
 *		msleep 50
Packit Service db8eaa
 *		cset "name='Master Playback Switch',index=2 1,1"
Packit Service db8eaa
 *		cset "name='Master Playback Volume',index=2 50,50"
Packit Service db8eaa
 *	]
Packit Service db8eaa
 *
Packit Service db8eaa
 *      # Optional transition verb
Packit Service db8eaa
 *      TransitionSequence."ToCaseName" [
Packit Service db8eaa
 *		msleep 1
Packit Service db8eaa
 *      ]
Packit Service db8eaa
 *
Packit Service db8eaa
 *	# Optional TQ and ALSA PCMs
Packit Service db8eaa
 *	Value {
Packit Service db8eaa
 *		TQ HiFi
Packit Service db8eaa
 *		CapturePCM "hw:0"
Packit Service db8eaa
 *		PlaybackPCM "hw:0"
Packit Service db8eaa
 *	}
Packit Service db8eaa
 * }
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_verb(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
		      struct use_case_verb *verb,
Packit Service db8eaa
		      snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	int err;
Packit Service db8eaa
	
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	/* parse verb section */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "EnableSequence") == 0) {
Packit Service db8eaa
			uc_dbg("Parse EnableSequence");
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &verb->enable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse verb enable sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "DisableSequence") == 0) {
Packit Service db8eaa
			uc_dbg("Parse DisableSequence");
Packit Service db8eaa
			err = parse_sequence(uc_mgr, &verb->disable_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse verb disable sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "TransitionSequence") == 0) {
Packit Service db8eaa
			uc_dbg("Parse TransitionSequence");
Packit Service db8eaa
			err = parse_transition(uc_mgr, &verb->transition_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse transition sequence");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Value") == 0) {
Packit Service db8eaa
			uc_dbg("Parse Value");
Packit Service db8eaa
			err = parse_value(uc_mgr, &verb->value_list, n);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse a Use case verb file.
Packit Service db8eaa
 *
Packit Service db8eaa
 * This file contains the following :-
Packit Service db8eaa
 *  o Verb enable and disable sequences.
Packit Service db8eaa
 *  o Supported Device enable and disable sequences for verb.
Packit Service db8eaa
 *  o Supported Modifier enable and disable sequences for verb
Packit Service db8eaa
 *  o Optional QoS for the verb and modifiers.
Packit Service db8eaa
 *  o Optional PCM device ID for verb and modifiers
Packit Service db8eaa
 *  o Alias kcontrols IDs for master and volumes and mutes.
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			   const char *use_case_name,
Packit Service db8eaa
			   const char *comment,
Packit Service db8eaa
			   const char *file)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	struct use_case_verb *verb;
Packit Service db8eaa
	snd_config_t *cfg;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	/* allocate verb */
Packit Service db8eaa
	verb = calloc(1, sizeof(struct use_case_verb));
Packit Service db8eaa
	if (verb == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->enable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->disable_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->transition_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->device_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->cmpt_device_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->modifier_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->value_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->rename_list);
Packit Service db8eaa
	INIT_LIST_HEAD(&verb->remove_list);
Packit Service db8eaa
	list_add_tail(&verb->list, &uc_mgr->verb_list);
Packit Service db8eaa
	if (use_case_name == NULL)
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	verb->name = strdup(use_case_name);
Packit Service db8eaa
	if (verb->name == NULL)
Packit Service db8eaa
		return -ENOMEM;
Packit Service db8eaa
Packit Service db8eaa
	if (comment != NULL) {
Packit Service db8eaa
		verb->comment = strdup(comment);
Packit Service db8eaa
		if (verb->comment == NULL)
Packit Service db8eaa
			return -ENOMEM;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* open Verb file for reading */
Packit Service db8eaa
	err = uc_mgr_config_load_file(uc_mgr, file, &cfg;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service 8875a0
		goto _err;
Packit Service db8eaa
Packit Service db8eaa
	/* parse master config sections */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		/* find verb section and parse it */
Packit Service db8eaa
		if (strcmp(id, "SectionVerb") == 0) {
Packit Service db8eaa
			err = parse_verb(uc_mgr, verb, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: %s failed to parse verb",
Packit Service db8eaa
						file);
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* find device sections and parse them */
Packit Service db8eaa
		if (strcmp(id, "SectionDevice") == 0) {
Packit Service db8eaa
			err = parse_compound(uc_mgr, n,
Packit Service db8eaa
						parse_device_name, verb, NULL);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: %s failed to parse device",
Packit Service db8eaa
						file);
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* find modifier sections and parse them */
Packit Service db8eaa
		if (strcmp(id, "SectionModifier") == 0) {
Packit Service db8eaa
			err = parse_compound(uc_mgr, n,
Packit Service db8eaa
					     parse_modifier_name, verb, NULL);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: %s failed to parse modifier",
Packit Service db8eaa
						file);
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* device renames */
Packit Service db8eaa
		if (strcmp(id, "RenameDevice") == 0) {
Packit Service db8eaa
			err = parse_dev_name_list(uc_mgr, n, &verb->rename_list);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: %s failed to parse device rename",
Packit Service db8eaa
						file);
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* device remove */
Packit Service db8eaa
		if (strcmp(id, "RemoveDevice") == 0) {
Packit Service db8eaa
			err = parse_dev_name_list(uc_mgr, n, &verb->remove_list);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: %s failed to parse device remove",
Packit Service db8eaa
						file);
Packit Service db8eaa
				goto _err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	snd_config_delete(cfg);
Packit Service db8eaa
Packit Service db8eaa
	/* use case verb must have at least 1 device */
Packit Service db8eaa
	if (list_empty(&verb->device_list)) {
Packit Service db8eaa
		uc_error("error: no use case device defined", file);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* do device rename and delete */
Packit Service db8eaa
	err = verb_device_management(verb);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("error: device management error in verb '%s'", verb->name);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
Packit Service db8eaa
       _err:
Packit Service db8eaa
	snd_config_delete(cfg);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Parse master section for "Use Case" and "File" tags.
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_master_section(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg,
Packit Service db8eaa
				void *data1 ATTRIBUTE_UNUSED,
Packit Service db8eaa
				void *data2 ATTRIBUTE_UNUSED)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	char *use_case_name, *file = NULL, *comment = NULL;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for use case section");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = parse_get_safe_name(uc_mgr, cfg, NULL, &use_case_name);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("unable to get name for use case section");
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
Packit Service db8eaa
	/* parse master config sections */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		const char *id;
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		/* get use case verb file name */
Packit Service db8eaa
		if (strcmp(id, "File") == 0) {
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &file;;
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("failed to get File");
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* get optional use case comment */
Packit Service db8eaa
		if (strncmp(id, "Comment", 7) == 0) {
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &comment);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to get Comment");
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		uc_error("unknown field %s in master section");
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	uc_dbg("use_case_name %s file '%s'", use_case_name, file);
Packit Service db8eaa
Packit Service db8eaa
	/* do we have both use case name and file ? */
Packit Service db8eaa
	if (!file) {
Packit Service db8eaa
		uc_error("error: use case missing file");
Packit Service db8eaa
		err = -EINVAL;
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* parse verb file */
Packit Service db8eaa
	err = parse_verb_file(uc_mgr, use_case_name, comment, file);
Packit Service db8eaa
Packit Service db8eaa
__error:
Packit Service db8eaa
	free(use_case_name);
Packit Service db8eaa
	free(file);
Packit Service db8eaa
	free(comment);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * parse controls which should be run only at initial boot
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_controls_boot(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (!list_empty(&uc_mgr->boot_list)) {
Packit Service db8eaa
		uc_error("Boot list is not empty");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = parse_sequence(uc_mgr, &uc_mgr->boot_list, cfg);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("Unable to parse BootSequence");
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * parse controls
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_controls(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (!list_empty(&uc_mgr->default_list)) {
Packit Service db8eaa
		uc_error("Default list is not empty");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = parse_sequence(uc_mgr, &uc_mgr->default_list, cfg);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("Unable to parse SectionDefaults");
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/*
Packit Service db8eaa
 * Each sound card has a master sound card file that lists all the supported
Packit Service db8eaa
 * use case verbs for that sound card. i.e.
Packit Service db8eaa
 *
Packit Service db8eaa
 * #Example master file for blah sound card
Packit Service db8eaa
 * #By Joe Blogs <joe@bloggs.org>
Packit Service db8eaa
 *
Packit Service db8eaa
 * Comment "Nice Abstracted Soundcard"
Packit Service db8eaa
 *
Packit Service db8eaa
 * # The file is divided into Use case sections. One section per use case verb.
Packit Service db8eaa
 *
Packit Service db8eaa
 * SectionUseCase."Voice Call" {
Packit Service db8eaa
 *	File "voice_call_blah"
Packit Service db8eaa
 *	Comment "Make a voice phone call."
Packit Service db8eaa
 * }
Packit Service db8eaa
 *
Packit Service db8eaa
 * SectionUseCase."HiFi" {
Packit Service db8eaa
 *	File "hifi_blah"
Packit Service db8eaa
 *	Comment "Play and record HiFi quality Music."
Packit Service db8eaa
 * }
Packit Service db8eaa
 *
Packit Service db8eaa
 * # Define Value defaults
Packit Service db8eaa
 *
Packit Service db8eaa
 * ValueDefaults {
Packit Service db8eaa
 *	PlaybackCTL "hw:CARD=0"
Packit Service db8eaa
 *	CaptureCTL "hw:CARD=0"
Packit Service db8eaa
 * }
Packit Service db8eaa
 *
Packit Service db8eaa
 * # The initial boot (run once) configuration.
Packit Service db8eaa
 *
Packit Service db8eaa
 * BootSequence [
Packit Service db8eaa
 *      cset "name='Master Playback Switch',index=2 1,1"
Packit Service db8eaa
 *	cset "name='Master Playback Volume',index=2 25,25"
Packit Service db8eaa
 * ]
Packit Service db8eaa
 *
Packit Service db8eaa
 * # This file also stores the default sound card state.
Packit Service db8eaa
 *
Packit Service db8eaa
 * SectionDefaults [
Packit Service db8eaa
 *	cset "name='Master Mono Playback',index=1 0"
Packit Service db8eaa
 *	cset "name='Master Mono Playback Volume',index=1 0"
Packit Service db8eaa
 *	cset "name='PCM Switch',index=2 1,1"
Packit Service db8eaa
 *      exec "some binary here"
Packit Service db8eaa
 *      msleep 50
Packit Service db8eaa
 *	........
Packit Service db8eaa
 * ]
Packit Service db8eaa
 *
Packit Service db8eaa
 * # End of example file.
Packit Service db8eaa
 */
Packit Service db8eaa
static int parse_master_file(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	long l;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for master file");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	if (uc_mgr->conf_format >= 2) {
Packit Service db8eaa
		err = snd_config_search(cfg, "Syntax", &n);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("Syntax field not found in %s", uc_mgr->conf_file_name);
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = snd_config_get_integer(n, &l);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("Syntax field is invalid in %s", uc_mgr->conf_file_name);
Packit Service db8eaa
			return err;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (l < 2 || l > SYNTAX_VERSION_MAX) {
Packit Service db8eaa
			uc_error("Incompatible syntax %d in %s", l, uc_mgr->conf_file_name);
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
		/* delete this field to avoid strcmp() call in the loop */
Packit Service db8eaa
		snd_config_delete(n);
Packit Service db8eaa
		uc_mgr->conf_format = l;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	/* parse master config sections */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "Comment") == 0) {
Packit Service db8eaa
			err = parse_string_substitute3(uc_mgr, n, &uc_mgr->comment);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to get master comment");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* find use case section and parse it */
Packit Service db8eaa
		if (strcmp(id, "SectionUseCase") == 0) {
Packit Service db8eaa
			err = parse_compound(uc_mgr, n,
Packit Service db8eaa
					     parse_master_section,
Packit Service db8eaa
					     NULL, NULL);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* find default control values section (first boot only) */
Packit Service db8eaa
		if (strcmp(id, "BootSequence") == 0) {
Packit Service db8eaa
			err = parse_controls_boot(uc_mgr, n);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* find default control values section and parse it */
Packit Service db8eaa
		if (strcmp(id, "SectionDefaults") == 0) {
Packit Service db8eaa
			err = parse_controls(uc_mgr, n);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* get the default values */
Packit Service db8eaa
		if (strcmp(id, "ValueDefaults") == 0) {
Packit Service db8eaa
			err = parse_value(uc_mgr, &uc_mgr->value_list, n);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				uc_error("error: failed to parse ValueDefaults");
Packit Service db8eaa
				return err;
Packit Service db8eaa
			}
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		/* error */
Packit Service db8eaa
		if (strcmp(id, "Error") == 0)
Packit Service db8eaa
			return error_node(uc_mgr, n);
Packit Service db8eaa
Packit Service db8eaa
		uc_error("uknown master file field %s", id);
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* get the card info */
Packit Service db8eaa
static int get_card_info(snd_use_case_mgr_t *mgr,
Packit Service db8eaa
			 const char *ctl_name,
Packit Service db8eaa
			 snd_ctl_card_info_t **info)
Packit Service db8eaa
{
Packit Service db8eaa
	struct ctl_list *ctl_list;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = uc_mgr_open_ctl(mgr, &ctl_list, ctl_name, 0);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	if (info)
Packit Service db8eaa
		*info = ctl_list->ctl_info;
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* find the card in the local machine */
Packit Service db8eaa
static int get_by_card_name(snd_use_case_mgr_t *mgr, const char *card_name)
Packit Service db8eaa
{
Packit Service db8eaa
	int card, err;
Packit Service db8eaa
	snd_ctl_card_info_t *info;
Packit Service db8eaa
	const char *_driver, *_name, *_long_name;
Packit Service db8eaa
Packit Service db8eaa
	snd_ctl_card_info_alloca(&info;;
Packit Service db8eaa
Packit Service db8eaa
	card = -1;
Packit Service db8eaa
	if (snd_card_next(&card) < 0 || card < 0) {
Packit Service db8eaa
		uc_error("no soundcards found...");
Packit Service db8eaa
		return -1;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	while (card >= 0) {
Packit Service db8eaa
		char name[32];
Packit Service db8eaa
Packit Service db8eaa
		/* clear the list, keep the only one CTL device */
Packit Service db8eaa
		uc_mgr_free_ctl_list(mgr);
Packit Service db8eaa
Packit Service db8eaa
		sprintf(name, "hw:%d", card);
Packit Service db8eaa
		err = get_card_info(mgr, name, &info;;
Packit Service db8eaa
Packit Service db8eaa
		if (err == 0) {
Packit Service db8eaa
			_driver = snd_ctl_card_info_get_driver(info);
Packit Service db8eaa
			_name = snd_ctl_card_info_get_name(info);
Packit Service db8eaa
			_long_name = snd_ctl_card_info_get_longname(info);
Packit Service db8eaa
			if (!strcmp(card_name, _driver) ||
Packit Service db8eaa
			    !strcmp(card_name, _name) ||
Packit Service db8eaa
			    !strcmp(card_name, _long_name))
Packit Service db8eaa
				return 0;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (snd_card_next(&card) < 0) {
Packit Service db8eaa
			uc_error("snd_card_next");
Packit Service db8eaa
			break;
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	uc_mgr_free_ctl_list(mgr);
Packit Service db8eaa
Packit Service db8eaa
	return -1;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* set the driver name and long name by the card ctl name */
Packit Service db8eaa
static inline int get_by_card(snd_use_case_mgr_t *mgr, const char *ctl_name)
Packit Service db8eaa
{
Packit Service db8eaa
	return get_card_info(mgr, ctl_name, NULL);
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
			       char *filename,
Packit Service db8eaa
			       snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next, i2, next2;
Packit Service db8eaa
	snd_config_t *n, *n2;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	char *dir = NULL, *file = NULL, fn[PATH_MAX];
Packit Service db8eaa
	long version;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for UseCasePath node");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	/* parse use case path config sections */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
Packit Service db8eaa
		if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
			uc_error("compound type expected for UseCasePath.something node");
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
			if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
				continue;
Packit Service db8eaa
Packit Service db8eaa
		version = 2;
Packit Service db8eaa
Packit Service db8eaa
		/* parse use case path config sections */
Packit Service db8eaa
		snd_config_for_each(i2, next2, n) {
Packit Service db8eaa
Packit Service db8eaa
			n2 = snd_config_iterator_entry(i2);
Packit Service db8eaa
			if (snd_config_get_id(n2, &id) < 0)
Packit Service db8eaa
				continue;
Packit Service db8eaa
Packit Service db8eaa
			if (strcmp(id, "Version") == 0) {
Packit Service db8eaa
				err = parse_integer_substitute(uc_mgr, n2, &version);
Packit Service db8eaa
				if (err < 0) {
Packit Service db8eaa
					uc_error("unable to parse UcmDirectory");
Packit Service db8eaa
					goto __error;
Packit Service db8eaa
				}
Packit Service db8eaa
				if (version < 1 || version > 2) {
Packit Service db8eaa
					uc_error("Version must be 1 or 2");
Packit Service db8eaa
					err = -EINVAL;
Packit Service db8eaa
					goto __error;
Packit Service db8eaa
				}
Packit Service db8eaa
				continue;
Packit Service db8eaa
			}
Packit Service db8eaa
Packit Service db8eaa
			if (strcmp(id, "Directory") == 0) {
Packit Service db8eaa
				err = parse_string_substitute(uc_mgr, n2, &dir;;
Packit Service db8eaa
				if (err < 0) {
Packit Service db8eaa
					uc_error("unable to parse Directory");
Packit Service db8eaa
					goto __error;
Packit Service db8eaa
				}
Packit Service db8eaa
				continue;
Packit Service db8eaa
			}
Packit Service db8eaa
Packit Service db8eaa
			if (strcmp(id, "File") == 0) {
Packit Service db8eaa
				err = parse_string_substitute(uc_mgr, n2, &file;;
Packit Service db8eaa
				if (err < 0) {
Packit Service db8eaa
					uc_error("unable to parse File");
Packit Service db8eaa
					goto __error;
Packit Service db8eaa
				}
Packit Service db8eaa
				continue;
Packit Service db8eaa
			}
Packit Service db8eaa
Packit Service db8eaa
			uc_error("unknown UseCasePath field %s", id);
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		if (dir == NULL) {
Packit Service db8eaa
			uc_error("Directory is not defined in %s!", filename);
Packit Service db8eaa
			goto __next;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (file == NULL) {
Packit Service db8eaa
			uc_error("File is not defined in %s!", filename);
Packit Service db8eaa
			goto __next;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		ucm_filename(fn, sizeof(fn), version, dir, file);
Packit Service db8eaa
		if (access(fn, R_OK) == 0) {
Packit Service db8eaa
			if (replace_string(&uc_mgr->conf_dir_name, dir) == NULL) {
Packit Service db8eaa
				err = -ENOMEM;
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			if (replace_string(&uc_mgr->conf_file_name, file) == NULL) {
Packit Service db8eaa
				err = -ENOMEM;
Packit Service db8eaa
				goto __error;
Packit Service db8eaa
			}
Packit Service db8eaa
			strncpy(filename, fn, PATH_MAX);
Packit Service db8eaa
			uc_mgr->conf_format = version;
Packit Service db8eaa
			goto __ok;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
__next:
Packit Service db8eaa
		free(file);
Packit Service db8eaa
		free(dir);
Packit Service db8eaa
		dir = NULL;
Packit Service db8eaa
		file = NULL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = -ENOENT;
Packit Service db8eaa
	goto __error;
Packit Service db8eaa
Packit Service db8eaa
__ok:
Packit Service db8eaa
	err = 0;
Packit Service db8eaa
__error:
Packit Service db8eaa
	free(file);
Packit Service db8eaa
	free(dir);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int parse_toplevel_config(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
				 char *filename,
Packit Service db8eaa
				 snd_config_t *cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_iterator_t i, next;
Packit Service db8eaa
	snd_config_t *n;
Packit Service db8eaa
	const char *id;
Packit Service db8eaa
	long l;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
Packit Service db8eaa
		uc_error("compound type expected for toplevel file");
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = snd_config_search(cfg, "Syntax", &n);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("Syntax field not found in %s", filename);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = snd_config_get_integer(n, &l);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("Syntax field is invalid in %s", filename);
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (l < 2 || l > SYNTAX_VERSION_MAX) {
Packit Service db8eaa
		uc_error("Incompatible syntax %d in %s", l, filename);
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	/* delete this field to avoid strcmp() call in the loop */
Packit Service db8eaa
	snd_config_delete(n);
Packit Service db8eaa
	uc_mgr->conf_format = l;
Packit Service db8eaa
Packit Service db8eaa
	/* in-place evaluation */
Packit Service db8eaa
	err = uc_mgr_evaluate_inplace(uc_mgr, cfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	/* parse toplevel config sections */
Packit Service db8eaa
	snd_config_for_each(i, next, cfg) {
Packit Service db8eaa
Packit Service db8eaa
		n = snd_config_iterator_entry(i);
Packit Service db8eaa
		if (snd_config_get_id(n, &id) < 0)
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		if (strcmp(id, "UseCasePath") == 0) {
Packit Service db8eaa
			err = parse_toplevel_path(uc_mgr, filename, n);
Packit Service db8eaa
			if (err == 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
Packit Service db8eaa
		uc_error("uknown toplevel field %s", id);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return -ENOENT;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int load_toplevel_config(snd_use_case_mgr_t *uc_mgr,
Packit Service db8eaa
				snd_config_t **cfg)
Packit Service db8eaa
{
Packit Service db8eaa
	char filename[PATH_MAX];
Packit Service db8eaa
	snd_config_t *tcfg;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	ucm_filename(filename, sizeof(filename), 2, NULL, "ucm.conf");
Packit Service db8eaa
Packit Service db8eaa
	if (access(filename, R_OK) != 0) {
Packit Service db8eaa
		uc_error("Unable to find the top-level configuration file '%s'.", filename);
Packit Service db8eaa
		return -ENOENT;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = uc_mgr_config_load(2, filename, &tcfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
Packit Service db8eaa
	/* filename is shared for function input and output! */
Packit Service db8eaa
	err = parse_toplevel_config(uc_mgr, filename, tcfg);
Packit Service db8eaa
	snd_config_delete(tcfg);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
Packit Service db8eaa
	err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_error("error: could not parse configuration for card %s",
Packit Service db8eaa
				uc_mgr->card_name);
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
Packit Service db8eaa
__error:
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* load master use case file for sound card based on rules in ucm2/ucm.conf
Packit Service db8eaa
 */
Packit Service db8eaa
int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr)
Packit Service db8eaa
{
Packit Service db8eaa
	snd_config_t *cfg;
Packit Service db8eaa
	const char *name;
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	name = uc_mgr->card_name;
Packit Service db8eaa
	if (strncmp(name, "hw:", 3) == 0) {
Packit Service db8eaa
		err = get_by_card(uc_mgr, name);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("card '%s' is not valid", name);
Packit Service db8eaa
			goto __error;
Packit Service db8eaa
		}
Packit Service db8eaa
	} else if (strncmp(name, "strict:", 7)) {
Packit Service db8eaa
		/* do not handle the error here */
Packit Service db8eaa
		/* we can refer the virtual UCM config */
Packit Service db8eaa
		get_by_card_name(uc_mgr, name);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	err = load_toplevel_config(uc_mgr, &cfg;;
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		goto __error;
Packit Service db8eaa
Packit Service db8eaa
	err = parse_master_file(uc_mgr, cfg);
Packit Service db8eaa
	snd_config_delete(cfg);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		uc_mgr_free_ctl_list(uc_mgr);
Packit Service db8eaa
		uc_mgr_free_verb(uc_mgr);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return err;
Packit Service db8eaa
Packit Service db8eaa
__error:
Packit Service db8eaa
	uc_mgr_free_ctl_list(uc_mgr);
Packit Service db8eaa
	replace_string(&uc_mgr->conf_dir_name, NULL);
Packit Service db8eaa
	return err;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int filename_filter(const struct dirent *dirent)
Packit Service db8eaa
{
Packit Service db8eaa
	if (dirent == NULL)
Packit Service db8eaa
		return 0;
Packit Service db8eaa
	if (dirent->d_type == DT_DIR) {
Packit Service db8eaa
		if (dirent->d_name[0] == '.') {
Packit Service db8eaa
			if (dirent->d_name[1] == '\0')
Packit Service db8eaa
				return 0;
Packit Service db8eaa
			if (dirent->d_name[1] == '.' &&
Packit Service db8eaa
			    dirent->d_name[2] == '\0')
Packit Service db8eaa
				return 0;
Packit Service db8eaa
		}
Packit Service db8eaa
		return 1;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* whether input dir is a predefined component directory */
Packit Service db8eaa
static int is_component_directory(const char *dir)
Packit Service db8eaa
{
Packit Service db8eaa
	int i = 0;
Packit Service db8eaa
Packit Service db8eaa
	while (component_dir[i]) {
Packit Service db8eaa
		if (!strncmp(dir, component_dir[i], PATH_MAX))
Packit Service db8eaa
			return 1;
Packit Service db8eaa
		i++;
Packit Service db8eaa
	};
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/* scan all cards and comments
Packit Service db8eaa
 *
Packit Service db8eaa
 * Cards are defined by machines. Each card/machine installs its UCM
Packit Service db8eaa
 * configuration files in a subdirectory with the same name as the sound
Packit Service db8eaa
 * card under /usr/share/alsa/ucm2. This function will scan all the card
Packit Service db8eaa
 * directories and skip the component directories defined in the array
Packit Service db8eaa
 * component_dir.
Packit Service db8eaa
 */
Packit Service db8eaa
int uc_mgr_scan_master_configs(const char **_list[])
Packit Service db8eaa
{
Packit Service db8eaa
	char filename[PATH_MAX], dfl[PATH_MAX], fn[FILENAME_MAX];
Packit Service db8eaa
	char *env = getenv(ALSA_CONFIG_UCM2_VAR);
Packit Service db8eaa
	const char **list, *d_name;
Packit Service db8eaa
	snd_config_t *cfg, *c;
Packit Service db8eaa
	int i, j, cnt, err;
Packit Service db8eaa
	long l;
Packit Service db8eaa
	ssize_t ss;
Packit Service db8eaa
	struct dirent **namelist;
Packit Service db8eaa
Packit Service db8eaa
	if (env)
Packit Service db8eaa
		snprintf(filename, sizeof(filename), "%s", env);
Packit Service db8eaa
	else
Packit Service db8eaa
		snprintf(filename, sizeof(filename), "%s/ucm2",
Packit Service db8eaa
			 snd_config_topdir());
Packit Service db8eaa
Packit Service db8eaa
#if defined(_GNU_SOURCE) && !defined(__NetBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(ANDROID)
Packit Service db8eaa
#define SORTFUNC	versionsort
Packit Service db8eaa
#else
Packit Service db8eaa
#define SORTFUNC	alphasort
Packit Service db8eaa
#endif
Packit Service db8eaa
	err = scandir(filename, &namelist, filename_filter, SORTFUNC);
Packit Service db8eaa
	if (err < 0) {
Packit Service db8eaa
		err = -errno;
Packit Service db8eaa
		uc_error("error: could not scan directory %s: %s",
Packit Service db8eaa
				filename, strerror(-err));
Packit Service db8eaa
		return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	cnt = err;
Packit Service db8eaa
Packit Service db8eaa
	dfl[0] = '\0';
Packit Service db8eaa
	if (strlen(filename) + 8 < sizeof(filename)) {
Packit Service db8eaa
		strcat(filename, "/default");
Packit Service db8eaa
		ss = readlink(filename, dfl, sizeof(dfl)-1);
Packit Service db8eaa
		if (ss >= 0) {
Packit Service db8eaa
			dfl[ss] = '\0';
Packit Service db8eaa
			dfl[sizeof(dfl)-1] = '\0';
Packit Service db8eaa
			if (dfl[0] && dfl[strlen(dfl)-1] == '/')
Packit Service db8eaa
				dfl[strlen(dfl)-1] = '\0';
Packit Service db8eaa
		} else {
Packit Service db8eaa
			dfl[0] = '\0';
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	list = calloc(1, cnt * 2 * sizeof(char *));
Packit Service db8eaa
	if (list == NULL) {
Packit Service db8eaa
		err = -ENOMEM;
Packit Service db8eaa
		goto __err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	for (i = j = 0; i < cnt; i++) {
Packit Service db8eaa
Packit Service db8eaa
		d_name = namelist[i]->d_name;
Packit Service db8eaa
Packit Service db8eaa
		/* Skip the directories for component devices */
Packit Service db8eaa
		if (is_component_directory(d_name))
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		snprintf(fn, sizeof(fn), "%s.conf", d_name);
Packit Service db8eaa
		ucm_filename(filename, sizeof(filename), 2, d_name, fn);
Packit Service db8eaa
		if (eaccess(filename, R_OK))
Packit Service db8eaa
			continue;
Packit Service db8eaa
Packit Service db8eaa
		err = uc_mgr_config_load(2, filename, &cfg;;
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			goto __err;
Packit Service db8eaa
		err = snd_config_search(cfg, "Syntax", &c);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("Syntax field not found in %s", d_name);
Packit Service db8eaa
			snd_config_delete(cfg);
Packit Service db8eaa
			continue;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = snd_config_get_integer(c, &l);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			uc_error("Syntax field is invalid in %s", d_name);
Packit Service db8eaa
			snd_config_delete(cfg);
Packit Service db8eaa
			goto __err;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (l < 2 || l > SYNTAX_VERSION_MAX) {
Packit Service db8eaa
			uc_error("Incompatible syntax %d in %s", l, d_name);
Packit Service db8eaa
			snd_config_delete(cfg);
Packit Service db8eaa
			goto __err;
Packit Service db8eaa
		}
Packit Service db8eaa
		err = snd_config_search(cfg, "Comment", &c);
Packit Service db8eaa
		if (err >= 0) {
Packit Service db8eaa
			err = parse_string(c, (char **)&list[j+1]);
Packit Service db8eaa
			if (err < 0) {
Packit Service db8eaa
				snd_config_delete(cfg);
Packit Service db8eaa
				goto __err;
Packit Service db8eaa
			}
Packit Service db8eaa
		}
Packit Service db8eaa
		snd_config_delete(cfg);
Packit Service db8eaa
		list[j] = strdup(d_name);
Packit Service db8eaa
		if (list[j] == NULL) {
Packit Service db8eaa
			err = -ENOMEM;
Packit Service db8eaa
			goto __err;
Packit Service db8eaa
		}
Packit Service db8eaa
		if (strcmp(dfl, list[j]) == 0) {
Packit Service db8eaa
			/* default to top */
Packit Service db8eaa
			const char *save1 = list[j];
Packit Service db8eaa
			const char *save2 = list[j + 1];
Packit Service db8eaa
			memmove(list + 2, list, j * sizeof(char *));
Packit Service db8eaa
			list[0] = save1;
Packit Service db8eaa
			list[1] = save2;
Packit Service db8eaa
		}
Packit Service db8eaa
		j += 2;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = j;
Packit Service db8eaa
Packit Service db8eaa
      __err:
Packit Service db8eaa
	for (i = 0; i < cnt; i++) {
Packit Service db8eaa
		free(namelist[i]);
Packit Service db8eaa
		if (err < 0) {
Packit Service db8eaa
			free((void *)list[i * 2]);
Packit Service db8eaa
			free((void *)list[i * 2 + 1]);
Packit Service db8eaa
		}
Packit Service db8eaa
	}
Packit Service db8eaa
	free(namelist);
Packit Service db8eaa
Packit Service db8eaa
	if (err >= 0) {
Packit Service db8eaa
		*_list = list;
Packit Service db8eaa
	} else {
Packit Service db8eaa
		free(list);
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return err;
Packit Service db8eaa
}