2010-09-07 15:35:14 +02:00
|
|
|
/*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
2011-03-28 11:04:19 +02:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2017-11-14 14:29:26 +01:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2010-09-07 15:35:14 +02:00
|
|
|
*
|
|
|
|
|
* Support for the verb/device/modifier core logic and API,
|
|
|
|
|
* command line tool and file parser was kindly sponsored by
|
|
|
|
|
* Texas Instruments Inc.
|
|
|
|
|
* Support for multiple active modifiers and devices,
|
|
|
|
|
* transition sequences, multiple client access and user defined use
|
|
|
|
|
* cases was kindly sponsored by Wolfson Microelectronics PLC.
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2008-2010 SlimLogic Ltd
|
|
|
|
|
* Copyright (C) 2010 Wolfson Microelectronics PLC
|
|
|
|
|
* Copyright (C) 2010 Texas Instruments Inc.
|
|
|
|
|
* Copyright (C) 2010 Red Hat Inc.
|
|
|
|
|
* Authors: Liam Girdwood <lrg@slimlogic.co.uk>
|
|
|
|
|
* Stefan Schmidt <stefan@slimlogic.co.uk>
|
|
|
|
|
* Justin Xu <justinx@slimlogic.co.uk>
|
|
|
|
|
* Jaroslav Kysela <perex@perex.cz>
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "ucm_local.h"
|
2021-03-25 20:40:22 +01:00
|
|
|
#include "../control/control_local.h"
|
2021-03-05 19:55:06 +01:00
|
|
|
#include <stdbool.h>
|
2012-08-10 14:14:28 +02:00
|
|
|
#include <ctype.h>
|
2010-09-22 14:31:15 +02:00
|
|
|
#include <stdarg.h>
|
2010-09-07 15:35:14 +02:00
|
|
|
#include <pthread.h>
|
2015-01-22 09:32:47 +08:00
|
|
|
#include <sys/stat.h>
|
2021-06-14 12:21:35 +08:00
|
|
|
#include <sys/wait.h>
|
2016-11-28 13:34:21 +08:00
|
|
|
#include <limits.h>
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
/*
|
|
|
|
|
* misc
|
|
|
|
|
*/
|
|
|
|
|
|
2020-10-19 10:42:27 +02:00
|
|
|
static int get_value(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
char **value,
|
|
|
|
|
const char *mod_dev_name,
|
|
|
|
|
const char *verb_name,
|
|
|
|
|
int exact);
|
2019-11-04 16:18:07 +01:00
|
|
|
static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value,
|
2021-03-05 19:35:03 +01:00
|
|
|
struct list_head *value_list, const char *identifier);
|
2019-11-04 16:18:07 +01:00
|
|
|
static int get_value3(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
char **value,
|
2010-11-29 17:33:27 +01:00
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3);
|
|
|
|
|
|
2022-05-19 10:08:48 +02:00
|
|
|
static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_verb *verb,
|
|
|
|
|
struct list_head *seq,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3);
|
|
|
|
|
|
2016-11-28 13:34:21 +08:00
|
|
|
static int execute_component_seq(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct component_sequence *cmpt_seq,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3,
|
|
|
|
|
char *cdev);
|
|
|
|
|
|
2022-05-19 10:08:48 +02:00
|
|
|
static inline struct use_case_device *
|
|
|
|
|
find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
|
|
|
|
const char *device_name, int check_supported);
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
static int check_identifier(const char *identifier, const char *prefix)
|
|
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
len = strlen(prefix);
|
2019-11-30 03:35:30 +08:00
|
|
|
if (strncmp(identifier, prefix, len) != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (identifier[len] == 0 || identifier[len] == '/')
|
2010-09-22 14:31:15 +02:00
|
|
|
return 1;
|
2019-11-30 03:35:30 +08:00
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int list_count(struct list_head *list)
|
|
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct list_head *pos;
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
count += 1;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int alloc_str_list(struct list_head *list, int mult, char **result[])
|
|
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
|
|
cnt = list_count(list) * mult;
|
|
|
|
|
if (cnt == 0) {
|
2011-03-19 01:09:41 +01:00
|
|
|
*result = NULL;
|
2021-03-05 19:35:03 +01:00
|
|
|
return cnt;
|
2011-03-19 01:09:41 +01:00
|
|
|
}
|
2021-03-05 19:35:03 +01:00
|
|
|
res = calloc(mult, cnt * sizeof(char *));
|
|
|
|
|
if (res == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
*result = res;
|
|
|
|
|
return cnt;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Create an identifier
|
|
|
|
|
* \param fmt Format (sprintf like)
|
|
|
|
|
* \param ... Optional arguments for sprintf like format
|
|
|
|
|
* \return Allocated string identifier or NULL on error
|
|
|
|
|
*/
|
|
|
|
|
char *snd_use_case_identifier(const char *fmt, ...)
|
|
|
|
|
{
|
|
|
|
|
char *str, *res;
|
|
|
|
|
int size = strlen(fmt) + 512;
|
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
|
|
str = malloc(size);
|
|
|
|
|
if (str == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
vsnprintf(str, size, fmt, args);
|
|
|
|
|
va_end(args);
|
|
|
|
|
str[size-1] = '\0';
|
|
|
|
|
res = realloc(str, strlen(str) + 1);
|
|
|
|
|
if (res)
|
|
|
|
|
return res;
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Free a string list
|
|
|
|
|
* \param list The string list to free
|
|
|
|
|
* \param items Count of strings
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_free_list(const char *list[], int items)
|
|
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
int i;
|
2010-09-22 14:31:15 +02:00
|
|
|
if (list == NULL)
|
|
|
|
|
return 0;
|
2021-03-05 19:35:03 +01:00
|
|
|
for (i = 0; i < items; i++)
|
2010-09-22 14:31:15 +02:00
|
|
|
free((void *)list[i]);
|
2021-03-05 19:35:03 +01:00
|
|
|
free(list);
|
2010-09-22 14:31:15 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-13 18:53:09 +08:00
|
|
|
static int read_tlv_file(unsigned int **res,
|
|
|
|
|
const char *filepath)
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
int fd;
|
2022-05-19 17:37:46 +02:00
|
|
|
struct stat64 st;
|
2016-04-13 18:53:09 +08:00
|
|
|
size_t sz;
|
|
|
|
|
ssize_t sz_read;
|
|
|
|
|
struct snd_ctl_tlv *tlv;
|
|
|
|
|
|
|
|
|
|
fd = open(filepath, O_RDONLY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
err = -errno;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2022-05-19 17:37:46 +02:00
|
|
|
if (fstat64(fd, &st) == -1) {
|
2016-04-13 18:53:09 +08:00
|
|
|
err = -errno;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
sz = st.st_size;
|
|
|
|
|
if (sz > 16 * 1024 * 1024 || sz < 8 || sz % 4) {
|
|
|
|
|
uc_error("File size should be less than 16 MB "
|
|
|
|
|
"and multiple of 4");
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
*res = malloc(sz);
|
|
|
|
|
if (res == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
sz_read = read(fd, *res, sz);
|
|
|
|
|
if (sz_read < 0 || (size_t)sz_read != sz) {
|
|
|
|
|
err = -EIO;
|
|
|
|
|
free(*res);
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
/* Check if the tlv file specifies valid size. */
|
|
|
|
|
tlv = (struct snd_ctl_tlv *)(*res);
|
|
|
|
|
if (tlv->length + 2 * sizeof(unsigned int) != sz) {
|
|
|
|
|
uc_error("Invalid tlv size: %d", tlv->length);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
free(*res);
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__fail:
|
|
|
|
|
close(fd);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 09:32:47 +08:00
|
|
|
static int binary_file_parse(snd_ctl_elem_value_t *dst,
|
|
|
|
|
snd_ctl_elem_info_t *info,
|
|
|
|
|
const char *filepath)
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
int fd;
|
2022-05-19 17:37:46 +02:00
|
|
|
struct stat64 st;
|
2015-01-22 09:32:47 +08:00
|
|
|
size_t sz;
|
|
|
|
|
ssize_t sz_read;
|
|
|
|
|
char *res;
|
|
|
|
|
snd_ctl_elem_type_t type;
|
|
|
|
|
unsigned int idx, count;
|
|
|
|
|
|
|
|
|
|
type = snd_ctl_elem_info_get_type(info);
|
|
|
|
|
if (type != SND_CTL_ELEM_TYPE_BYTES) {
|
|
|
|
|
uc_error("only support byte type!");
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
fd = open(filepath, O_RDONLY);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
err = -errno;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2022-05-19 17:37:46 +02:00
|
|
|
if (stat64(filepath, &st) == -1) {
|
2015-01-22 09:32:47 +08:00
|
|
|
err = -errno;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
sz = st.st_size;
|
|
|
|
|
count = snd_ctl_elem_info_get_count(info);
|
|
|
|
|
if (sz != count || sz > sizeof(dst->value.bytes)) {
|
|
|
|
|
uc_error("invalid parameter size %d!", sz);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
res = malloc(sz);
|
|
|
|
|
if (res == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
sz_read = read(fd, res, sz);
|
|
|
|
|
if (sz_read < 0 || (size_t)sz_read != sz) {
|
|
|
|
|
err = -errno;
|
|
|
|
|
goto __fail_read;
|
|
|
|
|
}
|
|
|
|
|
for (idx = 0; idx < sz; idx++)
|
|
|
|
|
snd_ctl_elem_value_set_byte(dst, idx, *(res + idx));
|
|
|
|
|
__fail_read:
|
|
|
|
|
free(res);
|
|
|
|
|
__fail:
|
|
|
|
|
close(fd);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-25 20:36:54 +01:00
|
|
|
static const char *parse_type(const char *p, const char *prefix, size_t len,
|
|
|
|
|
snd_ctl_elem_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
if (strncasecmp(p, prefix, len))
|
|
|
|
|
return p;
|
|
|
|
|
p += len;
|
|
|
|
|
if (info->type != SND_CTL_ELEM_TYPE_NONE)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (strncasecmp(p, "bool", sizeof("bool") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_BOOLEAN;
|
|
|
|
|
else if (strncasecmp(p, "integer64", sizeof("integer64") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_INTEGER64;
|
|
|
|
|
else if (strncasecmp(p, "int64", sizeof("int64") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_INTEGER64;
|
|
|
|
|
else if (strncasecmp(p, "int", sizeof("int") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
|
else if (strncasecmp(p, "enum", sizeof("enum") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
|
else if (strncasecmp(p, "bytes", sizeof("bytes") - 1) == 0)
|
|
|
|
|
info->type = SND_CTL_ELEM_TYPE_BYTES;
|
|
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
while (isalpha(*p))
|
|
|
|
|
p++;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *parse_uint(const char *p, const char *prefix, size_t len,
|
|
|
|
|
unsigned int min, unsigned int max, unsigned int *rval)
|
|
|
|
|
{
|
|
|
|
|
long v;
|
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
|
|
if (strncasecmp(p, prefix, len))
|
|
|
|
|
return p;
|
|
|
|
|
p += len;
|
|
|
|
|
v = strtol(p, &end, 0);
|
|
|
|
|
if (*end != '\0' && *end != ' ' && *end != ',') {
|
|
|
|
|
uc_error("unable to parse '%s'", prefix);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2022-05-20 09:22:33 +02:00
|
|
|
if ((unsigned int)v < min || (unsigned int)v > max) {
|
2021-03-25 20:36:54 +01:00
|
|
|
uc_error("value '%s' out of range %u-%u %(%ld)", min, max, v);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
*rval = v;
|
|
|
|
|
return end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *parse_labels(const char *p, const char *prefix, size_t len,
|
|
|
|
|
snd_ctl_elem_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
const char *s;
|
|
|
|
|
char *buf, *bp;
|
|
|
|
|
size_t l;
|
|
|
|
|
int c;
|
|
|
|
|
|
|
|
|
|
if (info->type != SND_CTL_ELEM_TYPE_ENUMERATED)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (strncasecmp(p, prefix, len))
|
|
|
|
|
return p;
|
|
|
|
|
p += len;
|
|
|
|
|
s = p;
|
|
|
|
|
c = *s;
|
|
|
|
|
l = 0;
|
|
|
|
|
if (c == '\'' || c == '\"') {
|
|
|
|
|
s++;
|
|
|
|
|
while (*s && *s != c) {
|
|
|
|
|
s++, l++;
|
|
|
|
|
}
|
|
|
|
|
if (*s == c)
|
|
|
|
|
s++;
|
|
|
|
|
} else {
|
|
|
|
|
while (*s && *s != ',')
|
|
|
|
|
l++;
|
|
|
|
|
}
|
|
|
|
|
if (l == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
buf = malloc(l + 1);
|
|
|
|
|
if (buf == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
memcpy(buf, p + ((c == '\'' || c == '\"') ? 1 : 0), l);
|
|
|
|
|
buf[l] = '\0';
|
|
|
|
|
info->value.enumerated.items = 1;
|
|
|
|
|
for (bp = buf; *bp; bp++) {
|
|
|
|
|
if (*bp == ';') {
|
|
|
|
|
if (bp == buf || bp[1] == ';') {
|
|
|
|
|
free(buf);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
info->value.enumerated.items++;
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
info->value.enumerated.names_ptr = (uintptr_t)buf;
|
|
|
|
|
info->value.enumerated.names_length = l + 1;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int parse_cset_new_info(snd_ctl_elem_info_t *info, const char *s, const char **pos)
|
|
|
|
|
{
|
|
|
|
|
const char *p = s, *op;
|
|
|
|
|
|
|
|
|
|
info->count = 1;
|
|
|
|
|
while (*s) {
|
|
|
|
|
op = p;
|
|
|
|
|
p = parse_type(p, "type=", sizeof("type=") - 1, info);
|
|
|
|
|
if (p != op)
|
|
|
|
|
goto next;
|
|
|
|
|
p = parse_uint(p, "elements=", sizeof("elements=") - 1, 1, 128, (unsigned int *)&info->owner);
|
|
|
|
|
if (p != op)
|
|
|
|
|
goto next;
|
|
|
|
|
p = parse_uint(p, "count=", sizeof("count=") - 1, 1, 128, &info->count);
|
|
|
|
|
if (p != op)
|
|
|
|
|
goto next;
|
|
|
|
|
p = parse_labels(p, "labels=", sizeof("labels=") - 1, info);
|
|
|
|
|
next:
|
|
|
|
|
if (p == NULL)
|
|
|
|
|
goto er;
|
|
|
|
|
if (*p == ',')
|
|
|
|
|
p++;
|
|
|
|
|
if (isspace(*p))
|
|
|
|
|
break;
|
|
|
|
|
if (op == p)
|
|
|
|
|
goto er;
|
|
|
|
|
}
|
|
|
|
|
*pos = p;
|
|
|
|
|
return 0;
|
|
|
|
|
er:
|
|
|
|
|
uc_error("unknown syntax '%s'", p);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 09:32:47 +08:00
|
|
|
static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)
|
2010-11-29 17:33:27 +01:00
|
|
|
{
|
2012-08-10 14:14:28 +02:00
|
|
|
const char *pos;
|
2010-11-29 17:33:27 +01:00
|
|
|
int err;
|
|
|
|
|
snd_ctl_elem_id_t *id;
|
|
|
|
|
snd_ctl_elem_value_t *value;
|
2021-03-25 20:36:54 +01:00
|
|
|
snd_ctl_elem_info_t *info, *info2 = NULL;
|
2016-04-13 18:53:09 +08:00
|
|
|
unsigned int *res = NULL;
|
2010-11-29 17:33:27 +01:00
|
|
|
|
|
|
|
|
snd_ctl_elem_id_malloc(&id);
|
|
|
|
|
snd_ctl_elem_value_malloc(&value);
|
|
|
|
|
snd_ctl_elem_info_malloc(&info);
|
|
|
|
|
|
2012-08-10 14:14:28 +02:00
|
|
|
err = __snd_ctl_ascii_elem_id_parse(id, cset, &pos);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
while (*pos && isspace(*pos))
|
|
|
|
|
pos++;
|
2021-03-25 20:36:54 +01:00
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) {
|
|
|
|
|
snd_ctl_elem_info_malloc(&info2);
|
|
|
|
|
snd_ctl_elem_info_set_id(info2, id);
|
|
|
|
|
err = parse_cset_new_info(info2, pos, &pos);
|
|
|
|
|
if (err < 0 || !*pos) {
|
|
|
|
|
uc_error("undefined or wrong id config for cset-new", cset);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
while (*pos && isspace(*pos))
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
2012-08-10 14:14:28 +02:00
|
|
|
if (!*pos) {
|
2021-03-29 11:12:28 +02:00
|
|
|
if (type != SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
|
|
|
|
|
uc_error("undefined value for cset >%s<", cset);
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
} else if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
|
|
|
|
|
uc_error("extra value for ctl-remove >%s<", cset);
|
2011-08-19 16:05:30 +08:00
|
|
|
err = -EINVAL;
|
2011-05-03 10:52:34 -07:00
|
|
|
goto __fail;
|
2010-11-29 17:33:27 +01:00
|
|
|
}
|
2021-03-25 20:36:54 +01:00
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
snd_ctl_elem_info_set_id(info, id);
|
|
|
|
|
err = snd_ctl_elem_info(ctl, info);
|
2021-03-29 11:12:28 +02:00
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW ||
|
|
|
|
|
type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE) {
|
2021-03-25 20:36:54 +01:00
|
|
|
if (err >= 0) {
|
|
|
|
|
err = snd_ctl_elem_remove(ctl, id);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to remove control");
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-29 11:12:28 +02:00
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CTL_REMOVE)
|
|
|
|
|
goto __ok;
|
2021-03-25 20:36:54 +01:00
|
|
|
err = __snd_ctl_add_elem_set(ctl, info2, info2->owner, info2->count);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to create new control");
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
/* new id copy */
|
|
|
|
|
snd_ctl_elem_info_get_id(info2, id);
|
|
|
|
|
snd_ctl_elem_info_set_id(info, id);
|
|
|
|
|
} else if (err < 0)
|
2010-11-29 17:33:27 +01:00
|
|
|
goto __fail;
|
2016-04-13 18:53:09 +08:00
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CSET_TLV) {
|
|
|
|
|
if (!snd_ctl_elem_info_is_tlv_writable(info)) {
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
err = read_tlv_file(&res, pos);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = snd_ctl_elem_tlv_write(ctl, id, res);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
snd_ctl_elem_value_set_id(value, id);
|
|
|
|
|
err = snd_ctl_elem_read(ctl, value);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE)
|
|
|
|
|
err = binary_file_parse(value, info, pos);
|
|
|
|
|
else
|
|
|
|
|
err = snd_ctl_ascii_value_parse(ctl, value, info, pos);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = snd_ctl_elem_write(ctl, value);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
2021-03-25 20:36:54 +01:00
|
|
|
if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) {
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
for (idx = 1; idx < (unsigned int)info2->owner; idx++) {
|
|
|
|
|
value->id.numid += 1;
|
|
|
|
|
err = snd_ctl_elem_write(ctl, value);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-13 18:53:09 +08:00
|
|
|
}
|
2021-03-29 11:12:28 +02:00
|
|
|
__ok:
|
2010-11-29 17:33:27 +01:00
|
|
|
err = 0;
|
|
|
|
|
__fail:
|
2021-03-25 20:36:54 +01:00
|
|
|
free(id);
|
|
|
|
|
free(value);
|
|
|
|
|
if (info2) {
|
|
|
|
|
if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED)
|
2023-09-01 16:50:03 +02:00
|
|
|
free((void *)(size_t)info2->value.enumerated.names_ptr);
|
2021-03-25 20:36:54 +01:00
|
|
|
free(info2);
|
|
|
|
|
}
|
|
|
|
|
free(info);
|
|
|
|
|
free(res);
|
2011-05-03 10:52:34 -07:00
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-23 11:40:30 +01:00
|
|
|
static int execute_sysw(const char *sysw)
|
2021-03-05 19:55:06 +01:00
|
|
|
{
|
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
const char *e;
|
|
|
|
|
char *s, *value;
|
|
|
|
|
ssize_t wlen;
|
|
|
|
|
size_t len;
|
|
|
|
|
int fd, myerrno;
|
|
|
|
|
bool ignore_error = false;
|
|
|
|
|
|
2021-03-23 11:40:30 +01:00
|
|
|
if (sysw == NULL || *sysw == '\0')
|
2021-03-05 19:55:06 +01:00
|
|
|
return 0;
|
|
|
|
|
|
2021-05-03 22:52:44 +02:00
|
|
|
if (sysw[0] == '-') {
|
|
|
|
|
ignore_error = true;
|
|
|
|
|
sysw++;
|
|
|
|
|
}
|
2021-03-05 19:55:06 +01:00
|
|
|
|
2021-03-23 11:40:30 +01:00
|
|
|
if (sysw[0] == ':')
|
2021-03-05 19:55:06 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
2021-03-23 11:40:30 +01:00
|
|
|
s = strdup(sysw[0] != '/' ? sysw : sysw + 1);
|
2021-03-05 19:55:06 +01:00
|
|
|
if (s == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
|
|
value = strchr(s, ':');
|
|
|
|
|
if (!value) {
|
|
|
|
|
free(s);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
*value = '\0';
|
|
|
|
|
value++;
|
|
|
|
|
len = strlen(value);
|
|
|
|
|
if (len < 1) {
|
|
|
|
|
free(s);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
e = uc_mgr_sysfs_root();
|
|
|
|
|
if (e == NULL) {
|
|
|
|
|
free(s);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", e, s);
|
|
|
|
|
|
|
|
|
|
fd = open(path, O_WRONLY|O_CLOEXEC);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
free(s);
|
|
|
|
|
if (ignore_error)
|
|
|
|
|
return 0;
|
|
|
|
|
uc_error("unable to open '%s' for write", path);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
wlen = write(fd, value, len);
|
|
|
|
|
myerrno = errno;
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
|
|
if (ignore_error)
|
2022-12-07 14:49:48 +01:00
|
|
|
goto __end;
|
2021-03-05 19:55:06 +01:00
|
|
|
|
|
|
|
|
if (wlen != (ssize_t)len) {
|
|
|
|
|
uc_error("unable to write '%s' to '%s': %s", value, path, strerror(myerrno));
|
2023-01-05 15:42:31 +01:00
|
|
|
free(s);
|
2021-03-05 19:55:06 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-07 14:49:48 +01:00
|
|
|
__end:
|
|
|
|
|
free(s);
|
2021-03-05 19:55:06 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-18 12:22:46 +02:00
|
|
|
int _snd_config_save_node_value(snd_config_t *n, snd_output_t *out, unsigned int level);
|
|
|
|
|
|
2021-05-14 12:20:18 +02:00
|
|
|
static int execute_cfgsave(snd_use_case_mgr_t *uc_mgr, const char *filename)
|
|
|
|
|
{
|
|
|
|
|
snd_config_t *config = uc_mgr->local_config;
|
|
|
|
|
char *file, *root;
|
|
|
|
|
snd_output_t *out;
|
2021-05-18 12:22:46 +02:00
|
|
|
bool with_root = false;
|
2021-05-14 12:20:18 +02:00
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
file = strdup(filename);
|
|
|
|
|
if (!file)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
root = strchr(file, ':');
|
2021-05-18 12:22:46 +02:00
|
|
|
if (config && root) {
|
|
|
|
|
*root++ = '\0';
|
|
|
|
|
if (*root == '+') {
|
|
|
|
|
with_root = true;
|
|
|
|
|
root++;
|
|
|
|
|
}
|
|
|
|
|
err = snd_config_search(config, root, &config);
|
2021-05-14 12:20:18 +02:00
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("Unable to find subtree '%s'", root);
|
|
|
|
|
goto _err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = snd_output_stdio_open(&out, file, "w+");
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to open file '%s': %s", file, snd_strerror(err));
|
|
|
|
|
goto _err;
|
|
|
|
|
}
|
2021-06-02 19:51:13 +02:00
|
|
|
if (!config || snd_config_is_empty(config)) {
|
|
|
|
|
snd_output_close(out);
|
2021-05-18 12:22:46 +02:00
|
|
|
goto _err;
|
2021-06-02 19:51:13 +02:00
|
|
|
}
|
2021-05-18 12:22:46 +02:00
|
|
|
if (with_root) {
|
|
|
|
|
snd_output_printf(out, "%s ", root);
|
|
|
|
|
err = _snd_config_save_node_value(config, out, 0);
|
|
|
|
|
} else {
|
|
|
|
|
err = snd_config_save(config, out);
|
|
|
|
|
}
|
2021-05-14 12:20:18 +02:00
|
|
|
snd_output_close(out);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to save configuration: %s", snd_strerror(err));
|
|
|
|
|
goto _err;
|
|
|
|
|
}
|
|
|
|
|
_err:
|
|
|
|
|
free(file);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-12 18:09:21 +02:00
|
|
|
static int rewrite_device_value(snd_use_case_mgr_t *uc_mgr, const char *name, char **value)
|
|
|
|
|
{
|
|
|
|
|
char *sval;
|
|
|
|
|
size_t l;
|
|
|
|
|
static const char **s, *_prefix[] = {
|
|
|
|
|
"PlaybackCTL",
|
|
|
|
|
"CaptureCTL",
|
|
|
|
|
"PlaybackMixer",
|
|
|
|
|
"CaptureMixer",
|
|
|
|
|
"PlaybackPCM",
|
|
|
|
|
"CapturePCM",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2021-04-15 11:38:39 +02:00
|
|
|
if (!uc_mgr_has_local_config(uc_mgr))
|
|
|
|
|
return 0;
|
2021-04-12 18:09:21 +02:00
|
|
|
for (s = _prefix; *s && *value; s++) {
|
|
|
|
|
if (strcmp(*s, name) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
l = strlen(*value) + 9 + 1;
|
|
|
|
|
sval = malloc(l);
|
|
|
|
|
if (sval == NULL) {
|
|
|
|
|
free(*value);
|
|
|
|
|
*value = NULL;
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
snprintf(sval, l, "_ucm%04X.%s", uc_mgr->ucm_card_number, *value);
|
|
|
|
|
free(*value);
|
|
|
|
|
*value = sval;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-19 10:08:48 +02:00
|
|
|
static int run_device_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
|
|
|
|
const char *name, bool enable)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
|
|
|
|
|
if (verb == NULL) {
|
|
|
|
|
uc_error("error: enadev2 / disdev2 must be executed inside the verb context");
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device = find_device(uc_mgr, verb, name, 0);
|
|
|
|
|
if (device == NULL) {
|
|
|
|
|
uc_error("error: unable to find device '%s'\n", name);
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return execute_sequence(uc_mgr, verb,
|
|
|
|
|
enable ? &device->enable_list : &device->disable_list,
|
|
|
|
|
&device->value_list,
|
|
|
|
|
&verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 16:28:21 +02:00
|
|
|
static int run_device_all_sequence(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (verb == NULL) {
|
|
|
|
|
uc_error("error: disdevall must be executed inside the verb context");
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, &verb->device_list) {
|
|
|
|
|
device = list_entry(pos, struct use_case_device, list);
|
|
|
|
|
|
|
|
|
|
err = execute_sequence(uc_mgr, verb,
|
|
|
|
|
&device->disable_list,
|
|
|
|
|
&device->value_list,
|
|
|
|
|
&verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Execute the sequence
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param seq Sequence
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
2010-11-29 17:33:27 +01:00
|
|
|
static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
|
2022-05-19 10:08:48 +02:00
|
|
|
struct use_case_verb *verb,
|
2010-11-29 17:33:27 +01:00
|
|
|
struct list_head *seq,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
struct sequence_element *s;
|
2010-11-29 17:33:27 +01:00
|
|
|
char *cdev = NULL;
|
|
|
|
|
snd_ctl_t *ctl = NULL;
|
2020-06-03 17:12:19 +02:00
|
|
|
struct ctl_list *ctl_list;
|
2021-05-18 16:36:49 +02:00
|
|
|
bool ignore_error;
|
2010-11-29 17:33:27 +01:00
|
|
|
int err = 0;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2022-05-19 10:08:48 +02:00
|
|
|
if (uc_mgr->sequence_hops > 100) {
|
|
|
|
|
uc_error("error: too many inner sequences!");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
uc_mgr->sequence_hops++;
|
2010-09-07 15:35:14 +02:00
|
|
|
list_for_each(pos, seq) {
|
|
|
|
|
s = list_entry(pos, struct sequence_element, list);
|
|
|
|
|
switch (s->type) {
|
2010-11-23 15:58:14 +01:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CDEV:
|
2010-11-29 17:33:27 +01:00
|
|
|
cdev = strdup(s->data.cdev);
|
|
|
|
|
if (cdev == NULL)
|
|
|
|
|
goto __fail_nomem;
|
2021-04-12 18:09:21 +02:00
|
|
|
if (rewrite_device_value(uc_mgr, "PlaybackCTL", &cdev))
|
|
|
|
|
goto __fail_nomem;
|
2010-11-23 15:58:14 +01:00
|
|
|
break;
|
2010-09-07 15:35:14 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CSET:
|
2015-01-22 09:32:47 +08:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
|
2016-04-13 18:53:09 +08:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
|
2021-03-25 20:36:54 +01:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
|
2021-03-29 11:12:28 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
|
2016-11-28 13:34:21 +08:00
|
|
|
if (cdev == NULL && uc_mgr->in_component_domain) {
|
|
|
|
|
/* For sequence of a component device, use
|
|
|
|
|
* its parent's cdev stored by ucm manager.
|
|
|
|
|
*/
|
|
|
|
|
if (uc_mgr->cdev == NULL) {
|
|
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cdev = strndup(uc_mgr->cdev, PATH_MAX);
|
|
|
|
|
if (!cdev)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
} else if (cdev == NULL) {
|
2015-02-17 21:15:23 +02:00
|
|
|
char *playback_ctl = NULL;
|
|
|
|
|
char *capture_ctl = NULL;
|
2015-02-17 21:15:22 +02:00
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value3(uc_mgr, &playback_ctl, "PlaybackCTL",
|
2010-11-29 17:33:27 +01:00
|
|
|
value_list1,
|
|
|
|
|
value_list2,
|
|
|
|
|
value_list3);
|
2015-02-17 21:15:21 +02:00
|
|
|
if (err < 0 && err != -ENOENT) {
|
2010-11-29 17:33:27 +01:00
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value3(uc_mgr, &capture_ctl, "CaptureCTL",
|
2010-11-29 17:33:27 +01:00
|
|
|
value_list1,
|
|
|
|
|
value_list2,
|
|
|
|
|
value_list3);
|
2015-02-17 21:15:21 +02:00
|
|
|
if (err < 0 && err != -ENOENT) {
|
2015-02-17 21:15:23 +02:00
|
|
|
free(playback_ctl);
|
2010-11-29 17:33:27 +01:00
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2015-02-17 21:15:22 +02:00
|
|
|
if (playback_ctl == NULL &&
|
|
|
|
|
capture_ctl == NULL) {
|
|
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (playback_ctl != NULL &&
|
|
|
|
|
capture_ctl != NULL &&
|
|
|
|
|
strcmp(playback_ctl, capture_ctl) != 0) {
|
2015-02-17 21:15:23 +02:00
|
|
|
free(playback_ctl);
|
|
|
|
|
free(capture_ctl);
|
2019-11-05 14:07:26 +01:00
|
|
|
uc_error("cdev is not equal for playback and capture!");
|
2015-02-17 21:15:22 +02:00
|
|
|
return -EINVAL;
|
2010-11-29 17:33:27 +01:00
|
|
|
}
|
2015-02-17 21:15:22 +02:00
|
|
|
if (playback_ctl != NULL) {
|
2015-02-17 21:15:23 +02:00
|
|
|
cdev = playback_ctl;
|
|
|
|
|
free(capture_ctl);
|
2019-11-05 14:07:26 +01:00
|
|
|
} else {
|
2015-02-17 21:15:23 +02:00
|
|
|
cdev = capture_ctl;
|
2019-11-05 14:07:26 +01:00
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
}
|
|
|
|
|
if (ctl == NULL) {
|
2020-06-03 17:12:19 +02:00
|
|
|
err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cdev, 1);
|
2011-01-31 15:06:03 +01:00
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to open ctl device '%s'", cdev);
|
2010-11-29 17:33:27 +01:00
|
|
|
goto __fail;
|
2011-01-31 15:06:03 +01:00
|
|
|
}
|
2020-06-03 17:12:19 +02:00
|
|
|
ctl = ctl_list->ctl;
|
2010-11-29 17:33:27 +01:00
|
|
|
}
|
2015-01-22 09:32:47 +08:00
|
|
|
err = execute_cset(ctl, s->data.cset, s->type);
|
2011-01-31 15:06:03 +01:00
|
|
|
if (err < 0) {
|
2019-11-25 19:50:47 +01:00
|
|
|
uc_error("unable to execute cset '%s'", s->data.cset);
|
2010-11-29 17:33:27 +01:00
|
|
|
goto __fail;
|
2011-01-31 15:06:03 +01:00
|
|
|
}
|
2010-09-07 15:35:14 +02:00
|
|
|
break;
|
2021-03-05 19:55:06 +01:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_SYSSET:
|
2021-03-23 11:40:30 +01:00
|
|
|
err = execute_sysw(s->data.sysw);
|
2021-03-05 19:55:06 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
break;
|
2010-09-07 15:35:14 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_SLEEP:
|
|
|
|
|
usleep(s->data.sleep);
|
|
|
|
|
break;
|
|
|
|
|
case SEQUENCE_ELEMENT_TYPE_EXEC:
|
2021-05-18 16:36:49 +02:00
|
|
|
if (s->data.exec == NULL)
|
|
|
|
|
break;
|
|
|
|
|
ignore_error = s->data.exec[0] == '-';
|
|
|
|
|
err = uc_mgr_exec(s->data.exec + (ignore_error ? 1 : 0));
|
|
|
|
|
if (ignore_error == false && err != 0) {
|
2021-05-11 14:48:16 +02:00
|
|
|
uc_error("exec '%s' failed (exit code %d)", s->data.exec, err);
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SEQUENCE_ELEMENT_TYPE_SHELL:
|
2021-05-18 16:36:49 +02:00
|
|
|
if (s->data.exec == NULL)
|
|
|
|
|
break;
|
|
|
|
|
ignore_error = s->data.exec[0] == '-';
|
2021-05-11 14:48:16 +02:00
|
|
|
shell_retry:
|
2021-05-18 16:36:49 +02:00
|
|
|
err = system(s->data.exec + (ignore_error ? 1 : 0));
|
2021-04-13 09:52:35 +02:00
|
|
|
if (WIFSIGNALED(err)) {
|
|
|
|
|
err = -EINTR;
|
|
|
|
|
} if (WIFEXITED(err)) {
|
2021-05-18 16:36:49 +02:00
|
|
|
if (ignore_error == false && WEXITSTATUS(err) != 0) {
|
2021-04-13 09:52:35 +02:00
|
|
|
uc_error("command '%s' failed (exit code %d)", s->data.exec, WEXITSTATUS(err));
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
} else if (err < 0) {
|
2021-05-11 14:48:16 +02:00
|
|
|
if (errno == EAGAIN)
|
|
|
|
|
goto shell_retry;
|
2021-04-13 09:52:35 +02:00
|
|
|
err = -errno;
|
2010-12-21 23:11:53 +01:00
|
|
|
goto __fail;
|
2021-04-13 09:52:35 +02:00
|
|
|
}
|
2010-09-07 15:35:14 +02:00
|
|
|
break;
|
2016-11-28 13:34:21 +08:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CMPT_SEQ:
|
|
|
|
|
/* Execute enable or disable sequence of a component
|
|
|
|
|
* device. Pass the cdev defined by the machine device.
|
|
|
|
|
*/
|
|
|
|
|
err = execute_component_seq(uc_mgr,
|
|
|
|
|
&s->data.cmpt_seq,
|
|
|
|
|
value_list1,
|
|
|
|
|
value_list2,
|
|
|
|
|
value_list3,
|
|
|
|
|
cdev);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
break;
|
2021-05-14 12:20:18 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CFGSAVE:
|
|
|
|
|
err = execute_cfgsave(uc_mgr, s->data.cfgsave);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
break;
|
2022-05-19 10:08:48 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ:
|
|
|
|
|
case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ:
|
|
|
|
|
err = run_device_sequence(uc_mgr, verb, s->data.device,
|
|
|
|
|
s->type == SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
break;
|
2022-05-24 16:28:21 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_ALL:
|
|
|
|
|
err = run_device_all_sequence(uc_mgr, verb);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
break;
|
2010-09-07 15:35:14 +02:00
|
|
|
default:
|
|
|
|
|
uc_error("unknown sequence command %i", s->type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
free(cdev);
|
2022-05-19 10:08:48 +02:00
|
|
|
uc_mgr->sequence_hops--;
|
2010-09-07 15:35:14 +02:00
|
|
|
return 0;
|
2010-11-29 17:33:27 +01:00
|
|
|
__fail_nomem:
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
__fail:
|
|
|
|
|
free(cdev);
|
2022-05-19 10:08:48 +02:00
|
|
|
uc_mgr->sequence_hops--;
|
2010-11-29 17:33:27 +01:00
|
|
|
return err;
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2016-11-28 13:34:21 +08:00
|
|
|
/* Execute enable or disable sequence of a component device.
|
|
|
|
|
*
|
|
|
|
|
* For a component device (a codec or embedded DSP), its sequence doesn't
|
|
|
|
|
* specify the sound card device 'cdev', because a component can be reused
|
|
|
|
|
* by different sound cards (machines). So when executing its sequence, a
|
|
|
|
|
* parameter 'cdev' is used to pass cdev defined by the sequence of its
|
|
|
|
|
* parent, the machine device. UCM manger will store the cdev when entering
|
|
|
|
|
* the component domain.
|
|
|
|
|
*/
|
|
|
|
|
static int execute_component_seq(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct component_sequence *cmpt_seq,
|
2016-12-27 18:08:58 +08:00
|
|
|
struct list_head *value_list1 ATTRIBUTE_UNUSED,
|
|
|
|
|
struct list_head *value_list2 ATTRIBUTE_UNUSED,
|
|
|
|
|
struct list_head *value_list3 ATTRIBUTE_UNUSED,
|
2016-11-28 13:34:21 +08:00
|
|
|
char *cdev)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *device = cmpt_seq->device;
|
|
|
|
|
struct list_head *seq;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
/* enter component domain and store cdev for the component */
|
|
|
|
|
uc_mgr->in_component_domain = 1;
|
|
|
|
|
uc_mgr->cdev = cdev;
|
|
|
|
|
|
|
|
|
|
/* choose enable or disable sequence of the component device */
|
|
|
|
|
if (cmpt_seq->enable)
|
|
|
|
|
seq = &device->enable_list;
|
|
|
|
|
else
|
|
|
|
|
seq = &device->disable_list;
|
|
|
|
|
|
|
|
|
|
/* excecute the sequence of the component dev */
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
|
2016-11-28 13:34:21 +08:00
|
|
|
&device->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
|
|
|
|
|
|
|
|
|
/* exit component domain and clear cdev */
|
|
|
|
|
uc_mgr->in_component_domain = 0;
|
|
|
|
|
uc_mgr->cdev = NULL;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-26 17:35:15 +01:00
|
|
|
static int add_auto_value(snd_use_case_mgr_t *uc_mgr, const char *key, char *value)
|
|
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = get_value1(uc_mgr, &value, &uc_mgr->value_list, key);
|
|
|
|
|
if (err == -ENOENT) {
|
|
|
|
|
s = strdup(value);
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
return uc_mgr_add_value(&uc_mgr->value_list, key, s);
|
|
|
|
|
} else if (err < 0) {
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
free(value);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int add_auto_values(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
struct ctl_list *ctl_list;
|
|
|
|
|
const char *id;
|
|
|
|
|
char buf[40];
|
|
|
|
|
int err;
|
|
|
|
|
|
2020-06-03 17:12:19 +02:00
|
|
|
ctl_list = uc_mgr_get_master_ctl(uc_mgr);
|
2019-11-26 17:35:15 +01:00
|
|
|
if (ctl_list) {
|
|
|
|
|
id = snd_ctl_card_info_get_id(ctl_list->ctl_info);
|
|
|
|
|
snprintf(buf, sizeof(buf), "hw:%s", id);
|
|
|
|
|
err = add_auto_value(uc_mgr, "PlaybackCTL", buf);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = add_auto_value(uc_mgr, "CaptureCTL", buf);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 18:32:05 +02:00
|
|
|
/**
|
|
|
|
|
* \brief execute default commands
|
|
|
|
|
* \param uc_mgr Use case manager
|
2022-11-15 17:25:59 +01:00
|
|
|
* \param force Force run
|
2020-05-14 18:32:05 +02:00
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
2022-11-15 17:25:59 +01:00
|
|
|
static int set_defaults(snd_use_case_mgr_t *uc_mgr, bool force)
|
2020-05-14 18:32:05 +02:00
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
2022-11-15 17:25:59 +01:00
|
|
|
if (!force && uc_mgr->default_list_executed)
|
2020-05-14 18:32:05 +02:00
|
|
|
return 0;
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, NULL, &uc_mgr->default_list,
|
2020-05-14 18:32:05 +02:00
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("Unable to execute default sequence");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
uc_mgr->default_list_executed = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Import master config and execute the default sequence
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int import_master_config(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = uc_mgr_import_master_config(uc_mgr);
|
2019-11-26 17:35:15 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2020-05-14 16:12:23 +02:00
|
|
|
return add_auto_values(uc_mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2020-10-06 10:40:40 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Check, if the UCM configuration is empty
|
|
|
|
|
* \param uc_mgr Use case Manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int check_empty_configuration(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
2020-10-19 10:42:27 +02:00
|
|
|
int err;
|
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
|
|
err = get_value(uc_mgr, "Linked", &value, NULL, NULL, 1);
|
|
|
|
|
if (err >= 0) {
|
|
|
|
|
err = strcasecmp(value, "true") == 0 ||
|
|
|
|
|
strcmp(value, "1") == 0;
|
|
|
|
|
free(value);
|
|
|
|
|
if (err)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-10-06 10:40:40 +02:00
|
|
|
if (!list_empty(&uc_mgr->verb_list))
|
|
|
|
|
return 0;
|
2021-03-05 18:50:02 +01:00
|
|
|
if (!list_empty(&uc_mgr->fixedboot_list))
|
|
|
|
|
return 0;
|
2020-10-06 10:47:11 +02:00
|
|
|
if (!list_empty(&uc_mgr->boot_list))
|
2020-10-06 10:40:40 +02:00
|
|
|
return 0;
|
|
|
|
|
return -ENXIO;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Universal find - string in a list
|
|
|
|
|
* \param list List of structures
|
|
|
|
|
* \param offset Offset of list structure
|
|
|
|
|
* \param soffset Offset of string structure
|
|
|
|
|
* \param match String to match
|
|
|
|
|
* \return structure on success, otherwise a NULL (not found)
|
|
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static void *find0(struct list_head *list,
|
2010-09-07 15:35:14 +02:00
|
|
|
unsigned long offset,
|
|
|
|
|
unsigned long soffset,
|
|
|
|
|
const char *match)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
char *ptr, *str;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
ptr = list_entry_offset(pos, char, offset);
|
|
|
|
|
str = *((char **)(ptr + soffset));
|
|
|
|
|
if (strcmp(str, match) == 0)
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
#define find(list, type, member, value, match) \
|
|
|
|
|
find0(list, (unsigned long)(&((type *)0)->member), \
|
|
|
|
|
(unsigned long)(&((type *)0)->value), match)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Universal string list
|
|
|
|
|
* \param list List of structures
|
|
|
|
|
* \param result Result list
|
|
|
|
|
* \param offset Offset of list structure
|
|
|
|
|
* \param s1offset Offset of string structure
|
|
|
|
|
* \return count of items on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_list0(struct list_head *list,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char **result[],
|
|
|
|
|
unsigned long offset,
|
|
|
|
|
unsigned long s1offset)
|
2010-09-22 14:31:15 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
2010-09-22 14:31:15 +02:00
|
|
|
struct list_head *pos;
|
|
|
|
|
char *ptr, *str1;
|
|
|
|
|
|
|
|
|
|
cnt = alloc_str_list(list, 1, &res);
|
2012-08-01 16:06:16 +03:00
|
|
|
if (cnt <= 0) {
|
|
|
|
|
*result = NULL;
|
2021-03-05 19:35:03 +01:00
|
|
|
return cnt;
|
2012-08-01 16:06:16 +03:00
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
*result = (const char **)res;
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
ptr = list_entry_offset(pos, char, offset);
|
|
|
|
|
str1 = *((char **)(ptr + s1offset));
|
|
|
|
|
if (str1 != NULL) {
|
2021-03-05 19:35:03 +01:00
|
|
|
*res = strdup(str1);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
return cnt;
|
|
|
|
|
__fail:
|
2021-03-05 19:35:03 +01:00
|
|
|
snd_use_case_free_list(*result, cnt);
|
|
|
|
|
return -ENOMEM;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define get_list(list, result, type, member, s1) \
|
|
|
|
|
get_list0(list, result, \
|
2021-03-05 19:35:03 +01:00
|
|
|
(unsigned long)(&((type *)0)->member), \
|
2010-09-22 14:31:15 +02:00
|
|
|
(unsigned long)(&((type *)0)->s1))
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Universal string list - pair of strings
|
|
|
|
|
* \param list List of structures
|
|
|
|
|
* \param result Result list
|
|
|
|
|
* \param offset Offset of list structure
|
|
|
|
|
* \param s1offset Offset of string structure
|
|
|
|
|
* \param s1offset Offset of string structure
|
|
|
|
|
* \return count of items on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_list20(struct list_head *list,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char **result[],
|
|
|
|
|
unsigned long offset,
|
|
|
|
|
unsigned long s1offset,
|
|
|
|
|
unsigned long s2offset)
|
2010-09-22 14:31:15 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
2010-09-22 14:31:15 +02:00
|
|
|
struct list_head *pos;
|
|
|
|
|
char *ptr, *str1, *str2;
|
|
|
|
|
|
|
|
|
|
cnt = alloc_str_list(list, 2, &res);
|
2012-08-01 16:06:16 +03:00
|
|
|
if (cnt <= 0) {
|
|
|
|
|
*result = NULL;
|
2021-03-05 19:35:03 +01:00
|
|
|
return cnt;
|
2012-08-01 16:06:16 +03:00
|
|
|
}
|
2021-03-05 19:35:03 +01:00
|
|
|
*result = (const char **)res;
|
2010-09-22 14:31:15 +02:00
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
ptr = list_entry_offset(pos, char, offset);
|
|
|
|
|
str1 = *((char **)(ptr + s1offset));
|
|
|
|
|
if (str1 != NULL) {
|
2021-03-05 19:35:03 +01:00
|
|
|
*res = strdup(str1);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
2010-09-22 14:31:15 +02:00
|
|
|
str2 = *((char **)(ptr + s2offset));
|
|
|
|
|
if (str2 != NULL) {
|
2021-03-05 19:35:03 +01:00
|
|
|
*res = strdup(str2);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
return cnt;
|
|
|
|
|
__fail:
|
2021-03-05 19:35:03 +01:00
|
|
|
snd_use_case_free_list(*result, cnt);
|
|
|
|
|
return -ENOMEM;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define get_list2(list, result, type, member, s1, s2) \
|
|
|
|
|
get_list20(list, result, \
|
2021-03-05 19:35:03 +01:00
|
|
|
(unsigned long)(&((type *)0)->member), \
|
2010-09-22 14:31:15 +02:00
|
|
|
(unsigned long)(&((type *)0)->s1), \
|
|
|
|
|
(unsigned long)(&((type *)0)->s2))
|
2010-09-07 15:35:14 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Find verb
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param verb_name verb to find
|
|
|
|
|
* \return structure on success, otherwise a NULL (not found)
|
|
|
|
|
*/
|
|
|
|
|
static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr,
|
2010-09-22 14:31:15 +02:00
|
|
|
const char *verb_name)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02:00
|
|
|
return find(&uc_mgr->verb_list,
|
2010-09-07 15:35:14 +02:00
|
|
|
struct use_case_verb, list, name,
|
2010-09-22 14:31:15 +02:00
|
|
|
verb_name);
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
static int is_devlist_supported(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct dev_list *dev_list)
|
|
|
|
|
{
|
|
|
|
|
struct dev_list_node *device;
|
|
|
|
|
struct use_case_device *adev;
|
|
|
|
|
struct list_head *pos, *pos1;
|
|
|
|
|
int found_ret;
|
|
|
|
|
|
|
|
|
|
switch (dev_list->type) {
|
|
|
|
|
case DEVLIST_NONE:
|
|
|
|
|
default:
|
|
|
|
|
return 1;
|
|
|
|
|
case DEVLIST_SUPPORTED:
|
|
|
|
|
found_ret = 1;
|
|
|
|
|
break;
|
|
|
|
|
case DEVLIST_CONFLICTING:
|
|
|
|
|
found_ret = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, &dev_list->list) {
|
|
|
|
|
device = list_entry(pos, struct dev_list_node, list);
|
|
|
|
|
|
|
|
|
|
list_for_each(pos1, &uc_mgr->active_devices) {
|
|
|
|
|
adev = list_entry(pos1, struct use_case_device,
|
|
|
|
|
active_list);
|
|
|
|
|
if (!strcmp(device->name, adev->name))
|
|
|
|
|
return found_ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1 - found_ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int is_modifier_supported(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_modifier *modifier)
|
|
|
|
|
{
|
|
|
|
|
return is_devlist_supported(uc_mgr, &modifier->dev_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline int is_device_supported(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_device *device)
|
|
|
|
|
{
|
|
|
|
|
return is_devlist_supported(uc_mgr, &device->dev_list);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Find device
|
|
|
|
|
* \param verb Use case verb
|
|
|
|
|
* \param device_name device to find
|
|
|
|
|
* \return structure on success, otherwise a NULL (not found)
|
|
|
|
|
*/
|
|
|
|
|
static inline struct use_case_device *
|
2021-03-05 19:35:03 +01:00
|
|
|
find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
2011-06-03 14:56:33 -06:00
|
|
|
const char *device_name, int check_supported)
|
2010-12-21 23:11:58 +01:00
|
|
|
{
|
2011-06-03 14:56:31 -06:00
|
|
|
struct use_case_device *device;
|
|
|
|
|
struct list_head *pos;
|
2010-12-21 23:11:58 +01:00
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
list_for_each(pos, &verb->device_list) {
|
|
|
|
|
device = list_entry(pos, struct use_case_device, list);
|
UCM: Deprecate index on Section{Device,Modifier}
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 {
SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" {
SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an
index is present, but not exactly "0", parsing will appear to succeed,
but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used,
any index is not included in the name; i.e. both sets of syntax above
name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0",
but internally strips ".0" from the tail of any device name. Any other
name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string
compare is now used, since no index data is ever present in device or
modifier names.
The one functional change introduced here is that a SupportedDevice
entry of just "x" will now only ever match a single device. It previously
acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-06-03 14:56:30 -06:00
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
if (strcmp(device_name, device->name))
|
|
|
|
|
continue;
|
UCM: Deprecate index on Section{Device,Modifier}
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 {
SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" {
SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an
index is present, but not exactly "0", parsing will appear to succeed,
but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used,
any index is not included in the name; i.e. both sets of syntax above
name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0",
but internally strips ".0" from the tail of any device name. Any other
name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string
compare is now used, since no index data is ever present in device or
modifier names.
The one functional change introduced here is that a SupportedDevice
entry of just "x" will now only ever match a single device. It previously
acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-06-03 14:56:30 -06:00
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
if (check_supported &&
|
|
|
|
|
!is_device_supported(uc_mgr, device))
|
|
|
|
|
continue;
|
UCM: Deprecate index on Section{Device,Modifier}
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 {
SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" {
SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an
index is present, but not exactly "0", parsing will appear to succeed,
but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used,
any index is not included in the name; i.e. both sets of syntax above
name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0",
but internally strips ".0" from the tail of any device name. Any other
name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string
compare is now used, since no index data is ever present in device or
modifier names.
The one functional change introduced here is that a SupportedDevice
entry of just "x" will now only ever match a single device. It previously
acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-06-03 14:56:30 -06:00
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
return device;
|
2010-12-21 23:11:58 +01:00
|
|
|
}
|
2011-06-03 14:56:31 -06:00
|
|
|
return NULL;
|
2010-12-21 23:11:58 +01:00
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Find modifier
|
|
|
|
|
* \param verb Use case verb
|
|
|
|
|
* \param modifier_name modifier to find
|
|
|
|
|
* \return structure on success, otherwise a NULL (not found)
|
|
|
|
|
*/
|
2010-12-21 23:11:58 +01:00
|
|
|
static struct use_case_modifier *
|
2021-03-05 19:35:03 +01:00
|
|
|
find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
2011-06-03 14:56:33 -06:00
|
|
|
const char *modifier_name, int check_supported)
|
2010-09-22 14:31:15 +02:00
|
|
|
{
|
2010-12-21 23:11:58 +01:00
|
|
|
struct use_case_modifier *modifier;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, &verb->modifier_list) {
|
|
|
|
|
modifier = list_entry(pos, struct use_case_modifier, list);
|
|
|
|
|
|
UCM: Deprecate index on Section{Device,Modifier}
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 {
SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" {
SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an
index is present, but not exactly "0", parsing will appear to succeed,
but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used,
any index is not included in the name; i.e. both sets of syntax above
name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0",
but internally strips ".0" from the tail of any device name. Any other
name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string
compare is now used, since no index data is ever present in device or
modifier names.
The one functional change introduced here is that a SupportedDevice
entry of just "x" will now only ever match a single device. It previously
acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-06-03 14:56:30 -06:00
|
|
|
if (strcmp(modifier->name, modifier_name))
|
2010-12-21 23:11:58 +01:00
|
|
|
continue;
|
|
|
|
|
|
2011-06-03 14:56:31 -06:00
|
|
|
if (check_supported &&
|
|
|
|
|
!is_modifier_supported(uc_mgr, modifier))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
return modifier;
|
2010-12-21 23:11:58 +01:00
|
|
|
}
|
|
|
|
|
return NULL;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2012-05-23 18:16:33 +05:30
|
|
|
long device_status(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *device_name)
|
2012-05-23 18:16:33 +05:30
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_device *dev;
|
|
|
|
|
struct list_head *pos;
|
2012-05-23 18:16:33 +05:30
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
list_for_each(pos, &uc_mgr->active_devices) {
|
|
|
|
|
dev = list_entry(pos, struct use_case_device, active_list);
|
|
|
|
|
if (strcmp(dev->name, device_name) == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2012-05-23 18:16:33 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long modifier_status(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *modifier_name)
|
2012-05-23 18:16:33 +05:30
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_modifier *mod;
|
|
|
|
|
struct list_head *pos;
|
2012-05-23 18:16:33 +05:30
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
list_for_each(pos, &uc_mgr->active_modifiers) {
|
|
|
|
|
mod = list_entry(pos, struct use_case_modifier, active_list);
|
|
|
|
|
if (strcmp(mod->name, modifier_name) == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2012-05-23 18:16:33 +05:30
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Set verb
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param verb verb to set
|
2010-09-22 14:31:15 +02:00
|
|
|
* \param enable nonzero = enable, zero = disable
|
2010-09-07 15:35:14 +02:00
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int set_verb(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_verb *verb,
|
|
|
|
|
int enable)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *seq;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (enable) {
|
2022-11-15 17:25:59 +01:00
|
|
|
err = set_defaults(uc_mgr, false);
|
2020-05-14 18:32:05 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
seq = &verb->enable_list;
|
|
|
|
|
} else {
|
|
|
|
|
seq = &verb->disable_list;
|
|
|
|
|
}
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, verb, seq,
|
2010-11-29 17:33:27 +01:00
|
|
|
&verb->value_list,
|
|
|
|
|
&uc_mgr->value_list,
|
|
|
|
|
NULL);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (enable && err >= 0)
|
|
|
|
|
uc_mgr->active_verb = verb;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Set modifier
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param modifier modifier to set
|
|
|
|
|
* \param enable nonzero = enable, zero = disable
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int set_modifier(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_modifier *modifier,
|
|
|
|
|
int enable)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *seq;
|
|
|
|
|
int err;
|
|
|
|
|
|
2012-05-23 18:16:33 +05:30
|
|
|
if (modifier_status(uc_mgr, modifier->name) == enable)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
if (enable) {
|
|
|
|
|
seq = &modifier->enable_list;
|
|
|
|
|
} else {
|
|
|
|
|
seq = &modifier->disable_list;
|
|
|
|
|
}
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
|
2010-11-29 17:33:27 +01:00
|
|
|
&modifier->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2010-09-22 14:31:15 +02:00
|
|
|
if (enable && err >= 0) {
|
|
|
|
|
list_add_tail(&modifier->active_list, &uc_mgr->active_modifiers);
|
|
|
|
|
} else if (!enable) {
|
|
|
|
|
list_del(&modifier->active_list);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Set device
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param device device to set
|
|
|
|
|
* \param enable nonzero = enable, zero = disable
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int set_device(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_device *device,
|
|
|
|
|
int enable)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *seq;
|
|
|
|
|
int err;
|
|
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
if (device_status(uc_mgr, device->name) == enable)
|
2012-05-23 18:16:33 +05:30
|
|
|
return 0;
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
if (enable) {
|
|
|
|
|
seq = &device->enable_list;
|
|
|
|
|
} else {
|
|
|
|
|
seq = &device->disable_list;
|
|
|
|
|
}
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb, seq,
|
2010-11-29 17:33:27 +01:00
|
|
|
&device->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2010-09-22 14:31:15 +02:00
|
|
|
if (enable && err >= 0) {
|
|
|
|
|
list_add_tail(&device->active_list, &uc_mgr->active_devices);
|
|
|
|
|
} else if (!enable) {
|
|
|
|
|
list_del(&device->active_list);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-15 17:25:59 +01:00
|
|
|
/**
|
|
|
|
|
* \brief Do the full reset
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int do_reset(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = set_defaults(uc_mgr, true);
|
|
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_modifiers);
|
|
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_devices);
|
|
|
|
|
uc_mgr->active_verb = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-16 13:16:01 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Parse open arguments
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param name name of card to open
|
|
|
|
|
* \return the rest of the card name to open
|
|
|
|
|
*/
|
|
|
|
|
const char *parse_open_variables(snd_use_case_mgr_t *uc_mgr, const char *name)
|
|
|
|
|
{
|
|
|
|
|
const char *end, *id;
|
|
|
|
|
char *args, *var;
|
|
|
|
|
snd_config_t *cfg, *n;
|
|
|
|
|
snd_config_iterator_t i, next;
|
|
|
|
|
char vname[128];
|
|
|
|
|
size_t l;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
end = strstr(name, ">>>");
|
|
|
|
|
if (end == NULL)
|
|
|
|
|
return name;
|
|
|
|
|
l = end - name - 3;
|
|
|
|
|
args = alloca(l + 1);
|
|
|
|
|
strncpy(args, name + 3, l);
|
|
|
|
|
args[l] = '\0';
|
|
|
|
|
|
|
|
|
|
err = snd_config_load_string(&cfg, args, 0);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("error: open arguments are not valid (%s)", args);
|
|
|
|
|
goto skip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set arguments */
|
|
|
|
|
snd_config_for_each(i, next, cfg) {
|
|
|
|
|
n = snd_config_iterator_entry(i);
|
|
|
|
|
err = snd_config_get_id(n, &id);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto skip;
|
|
|
|
|
err = snd_config_get_ascii(n, &var);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto skip;
|
|
|
|
|
snprintf(vname, sizeof(vname), "@%s", id);
|
|
|
|
|
err = uc_mgr_set_variable(uc_mgr, vname, var);
|
|
|
|
|
free(var);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto skip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
skip:
|
2022-07-08 21:56:57 +02:00
|
|
|
snd_config_delete(cfg);
|
2022-05-16 13:16:01 +02:00
|
|
|
return end + 3;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-08 16:42:24 +02:00
|
|
|
int snd_use_case_mgr_open(snd_use_case_mgr_t **uc_mgr,
|
2010-09-07 15:35:14 +02:00
|
|
|
const char *card_name)
|
|
|
|
|
{
|
2013-04-08 16:42:24 +02:00
|
|
|
snd_use_case_mgr_t *mgr;
|
2010-09-07 15:35:14 +02:00
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
/* create a new UCM */
|
2013-04-08 16:42:24 +02:00
|
|
|
mgr = calloc(1, sizeof(snd_use_case_mgr_t));
|
|
|
|
|
if (mgr == NULL)
|
2010-09-07 15:35:14 +02:00
|
|
|
return -ENOMEM;
|
2013-04-08 16:42:24 +02:00
|
|
|
INIT_LIST_HEAD(&mgr->verb_list);
|
2021-03-05 18:50:02 +01:00
|
|
|
INIT_LIST_HEAD(&mgr->fixedboot_list);
|
2020-10-06 10:47:11 +02:00
|
|
|
INIT_LIST_HEAD(&mgr->boot_list);
|
2013-04-08 16:42:24 +02:00
|
|
|
INIT_LIST_HEAD(&mgr->default_list);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->value_list);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->active_modifiers);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->active_devices);
|
2019-11-04 14:42:45 +01:00
|
|
|
INIT_LIST_HEAD(&mgr->ctl_list);
|
2020-05-20 19:04:36 +02:00
|
|
|
INIT_LIST_HEAD(&mgr->variable_list);
|
2013-04-08 16:42:24 +02:00
|
|
|
pthread_mutex_init(&mgr->mutex, NULL);
|
|
|
|
|
|
2021-10-28 11:48:54 +02:00
|
|
|
if (card_name && *card_name == '-') {
|
|
|
|
|
card_name++;
|
|
|
|
|
mgr->suppress_nodev_errors = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-08 22:00:34 +02:00
|
|
|
if (card_name && card_name[0] == '<' && card_name[1] == '<' && card_name[2] == '<')
|
2022-05-16 13:16:01 +02:00
|
|
|
card_name = parse_open_variables(mgr, card_name);
|
|
|
|
|
|
2021-04-12 18:09:21 +02:00
|
|
|
err = uc_mgr_card_open(mgr);
|
2021-04-15 16:58:24 +02:00
|
|
|
if (err < 0) {
|
|
|
|
|
uc_mgr_free(mgr);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2021-04-12 18:09:21 +02:00
|
|
|
|
2013-04-08 16:42:24 +02:00
|
|
|
mgr->card_name = strdup(card_name);
|
|
|
|
|
if (mgr->card_name == NULL) {
|
2021-04-12 18:09:21 +02:00
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto _err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get info on use_cases and verify against card */
|
2013-04-08 16:42:24 +02:00
|
|
|
err = import_master_config(mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (err < 0) {
|
2021-10-28 11:48:54 +02:00
|
|
|
if (err == -ENXIO && mgr->suppress_nodev_errors)
|
|
|
|
|
goto _err;
|
2010-09-07 15:35:14 +02:00
|
|
|
uc_error("error: failed to import %s use case configuration %d",
|
2020-10-06 10:40:40 +02:00
|
|
|
card_name, err);
|
|
|
|
|
goto _err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = check_empty_configuration(mgr);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("error: failed to import %s (empty configuration)", card_name);
|
|
|
|
|
goto _err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2013-04-08 16:42:24 +02:00
|
|
|
*uc_mgr = mgr;
|
2010-09-07 15:35:14 +02:00
|
|
|
return 0;
|
|
|
|
|
|
2020-10-06 10:40:40 +02:00
|
|
|
_err:
|
2021-04-15 16:58:24 +02:00
|
|
|
uc_mgr_card_close(mgr);
|
2013-04-08 16:42:24 +02:00
|
|
|
uc_mgr_free(mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
|
|
|
|
|
2022-11-15 17:25:59 +01:00
|
|
|
do_reset(uc_mgr);
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
uc_mgr_free_verb(uc_mgr);
|
|
|
|
|
|
2020-05-14 16:12:23 +02:00
|
|
|
uc_mgr->default_list_executed = 0;
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/* reload all use cases */
|
|
|
|
|
err = import_master_config(uc_mgr);
|
|
|
|
|
if (err < 0) {
|
2019-11-25 19:50:47 +01:00
|
|
|
uc_error("error: failed to reload use cases");
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
2021-04-12 18:09:21 +02:00
|
|
|
uc_mgr_card_close(uc_mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
uc_mgr_free(uc_mgr);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
/*
|
|
|
|
|
* Tear down current use case verb, device and modifier.
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 15:35:11 +02:00
|
|
|
static int dismantle_use_case(snd_use_case_mgr_t *uc_mgr)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
|
|
|
|
struct list_head *pos, *npos;
|
|
|
|
|
struct use_case_modifier *modifier;
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(pos, npos, &uc_mgr->active_modifiers) {
|
|
|
|
|
modifier = list_entry(pos, struct use_case_modifier,
|
|
|
|
|
active_list);
|
2010-09-22 14:31:15 +02:00
|
|
|
err = set_modifier(uc_mgr, modifier, 0);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
uc_error("Unable to disable modifier %s", modifier->name);
|
|
|
|
|
}
|
|
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_modifiers);
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(pos, npos, &uc_mgr->active_devices) {
|
|
|
|
|
device = list_entry(pos, struct use_case_device,
|
|
|
|
|
active_list);
|
2010-09-22 14:31:15 +02:00
|
|
|
err = set_device(uc_mgr, device, 0);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
uc_error("Unable to disable device %s", device->name);
|
|
|
|
|
}
|
|
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_devices);
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
err = set_verb(uc_mgr, uc_mgr->active_verb, 0);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("Unable to disable verb %s", uc_mgr->active_verb->name);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
uc_mgr->active_verb = NULL;
|
|
|
|
|
|
2022-11-15 17:25:59 +01:00
|
|
|
err = set_defaults(uc_mgr, true);
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
int err;
|
2010-09-22 15:35:11 +02:00
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2022-11-15 17:25:59 +01:00
|
|
|
err = do_reset(uc_mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of verbs in pair verbname+comment
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static int get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
return get_list2(&uc_mgr->verb_list, list,
|
|
|
|
|
struct use_case_verb, list,
|
|
|
|
|
name, comment);
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of devices in pair devicename+comment
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param verbname For verb (NULL = current)
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_device_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
|
2021-03-05 19:35:03 +01:00
|
|
|
char *verbname)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
|
|
|
|
|
if (verbname) {
|
|
|
|
|
verb = find_verb(uc_mgr, verbname);
|
|
|
|
|
} else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
|
|
|
|
}
|
|
|
|
|
if (verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return get_list2(&verb->device_list, list,
|
|
|
|
|
struct use_case_device, list,
|
|
|
|
|
name, comment);
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of modifiers in pair devicename+comment
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param verbname For verb (NULL = current)
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[],
|
2021-03-05 19:35:03 +01:00
|
|
|
char *verbname)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
if (verbname) {
|
|
|
|
|
verb = find_verb(uc_mgr, verbname);
|
|
|
|
|
} else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
|
|
|
|
}
|
|
|
|
|
if (verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return get_list2(&verb->modifier_list, list,
|
|
|
|
|
struct use_case_modifier, list,
|
|
|
|
|
name, comment);
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
|
2011-06-03 14:56:33 -06:00
|
|
|
/**
|
|
|
|
|
* \brief Get list of supported/conflicting devices
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param name Name of modifier or verb to query
|
|
|
|
|
* \param type Type of device list entries to return
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_supcon_device_list(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char **list[], char *name,
|
|
|
|
|
enum dev_list_type type)
|
|
|
|
|
{
|
|
|
|
|
char *str;
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
struct use_case_modifier *modifier;
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
str = strchr(name, '/');
|
|
|
|
|
if (str) {
|
|
|
|
|
*str = '\0';
|
|
|
|
|
verb = find_verb(uc_mgr, str + 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
|
|
|
|
}
|
|
|
|
|
if (!verb)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
modifier = find_modifier(uc_mgr, verb, name, 0);
|
|
|
|
|
if (modifier) {
|
2019-11-29 22:28:26 +01:00
|
|
|
if (modifier->dev_list.type != type) {
|
|
|
|
|
*list = NULL;
|
2011-06-03 14:56:33 -06:00
|
|
|
return 0;
|
2019-11-29 22:28:26 +01:00
|
|
|
}
|
2011-06-03 14:56:33 -06:00
|
|
|
return get_list(&modifier->dev_list.list, list,
|
|
|
|
|
struct dev_list_node, list,
|
|
|
|
|
name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device = find_device(uc_mgr, verb, name, 0);
|
|
|
|
|
if (device) {
|
2019-11-29 22:28:26 +01:00
|
|
|
if (device->dev_list.type != type) {
|
|
|
|
|
*list = NULL;
|
2011-06-03 14:56:33 -06:00
|
|
|
return 0;
|
2019-11-29 22:28:26 +01:00
|
|
|
}
|
2011-06-03 14:56:33 -06:00
|
|
|
return get_list(&device->dev_list.list, list,
|
|
|
|
|
struct dev_list_node, list,
|
|
|
|
|
name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Get list of supported devices
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param name Name of verb or modifier to query
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_supported_device_list(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char **list[], char *name)
|
|
|
|
|
{
|
|
|
|
|
return get_supcon_device_list(uc_mgr, list, name, DEVLIST_SUPPORTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Get list of conflicting devices
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param name Name of verb or modifier to query
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_conflicting_device_list(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char **list[], char *name)
|
|
|
|
|
{
|
|
|
|
|
return get_supcon_device_list(uc_mgr, list, name, DEVLIST_CONFLICTING);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-08 16:42:24 +02:00
|
|
|
#ifndef DOC_HIDDEN
|
2010-09-22 14:31:15 +02:00
|
|
|
struct myvalue {
|
2019-11-30 20:31:55 +01:00
|
|
|
struct list_head list;
|
|
|
|
|
const char *text;
|
2010-09-22 14:31:15 +02:00
|
|
|
};
|
2013-04-08 16:42:24 +02:00
|
|
|
#endif
|
2010-09-22 14:31:15 +02:00
|
|
|
|
2019-11-30 20:31:55 +01:00
|
|
|
/**
|
|
|
|
|
* \brief Convert myvalue list string list
|
|
|
|
|
* \param list myvalue list
|
|
|
|
|
* \param res string list
|
|
|
|
|
* \retval Number of list entries if success, otherwise a negativer error code
|
|
|
|
|
*/
|
|
|
|
|
static int myvalue_to_str_list(struct list_head *list, char ***res)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
struct myvalue *value;
|
|
|
|
|
char **p;
|
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
|
|
cnt = alloc_str_list(list, 1, res);
|
|
|
|
|
if (cnt < 0)
|
|
|
|
|
return cnt;
|
|
|
|
|
p = *res;
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
value = list_entry(pos, struct myvalue, list);
|
|
|
|
|
*p = strdup(value->text);
|
|
|
|
|
if (*p == NULL) {
|
|
|
|
|
snd_use_case_free_list((const char **)p, cnt);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
return cnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Free myvalue list
|
|
|
|
|
* \param list myvalue list
|
|
|
|
|
*/
|
|
|
|
|
static void myvalue_list_free(struct list_head *list)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos, *npos;
|
|
|
|
|
struct myvalue *value;
|
|
|
|
|
|
|
|
|
|
list_for_each_safe(pos, npos, list) {
|
|
|
|
|
value = list_entry(pos, struct myvalue, list);
|
|
|
|
|
list_del(&value->list);
|
|
|
|
|
free(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Merge one value to the myvalue list
|
|
|
|
|
* \param list The list with values
|
|
|
|
|
* \param value The value to be merged (without duplicates)
|
|
|
|
|
* \return 1 if dup, 0 if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int merge_value(struct list_head *list, const char *text)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
struct myvalue *value;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
value = list_entry(pos, struct myvalue, list);
|
|
|
|
|
if (strcmp(value->text, text) == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
value = malloc(sizeof(*value));
|
|
|
|
|
if (value == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
value->text = text;
|
|
|
|
|
list_add_tail(&value->list, list);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Find all values for given identifier
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param source Source list with ucm_value structures
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int add_identifiers(struct list_head *list,
|
|
|
|
|
struct list_head *source)
|
|
|
|
|
{
|
|
|
|
|
struct ucm_value *v;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, source) {
|
|
|
|
|
v = list_entry(pos, struct ucm_value, list);
|
|
|
|
|
err = merge_value(list, v->name);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Find all values for given identifier
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param identifier Identifier
|
|
|
|
|
* \param source Source list with ucm_value structures
|
|
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static int add_values(struct list_head *list,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *source)
|
2010-09-22 14:31:15 +02:00
|
|
|
{
|
2019-11-30 20:31:55 +01:00
|
|
|
struct ucm_value *v;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int err;
|
2021-03-05 19:35:03 +01:00
|
|
|
|
2019-11-30 20:31:55 +01:00
|
|
|
list_for_each(pos, source) {
|
|
|
|
|
v = list_entry(pos, struct ucm_value, list);
|
|
|
|
|
if (check_identifier(identifier, v->name)) {
|
|
|
|
|
err = merge_value(list, v->data);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief compare two identifiers
|
|
|
|
|
*/
|
|
|
|
|
static int identifier_cmp(const void *_a, const void *_b)
|
|
|
|
|
{
|
|
|
|
|
const char * const *a = _a;
|
|
|
|
|
const char * const *b = _b;
|
|
|
|
|
return strcmp(*a, *b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Get list of available identifiers
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param name Name of verb or modifier to query
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_identifiers_list(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char **list[], char *name)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
struct use_case_modifier *modifier;
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
struct list_head mylist;
|
|
|
|
|
struct list_head *value_list;
|
|
|
|
|
char *str, **res;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
str = strchr(name, '/');
|
|
|
|
|
if (str) {
|
|
|
|
|
*str = '\0';
|
|
|
|
|
verb = find_verb(uc_mgr, str + 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
|
|
|
|
}
|
|
|
|
|
if (!verb)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
value_list = NULL;
|
|
|
|
|
modifier = find_modifier(uc_mgr, verb, name, 0);
|
|
|
|
|
if (modifier) {
|
|
|
|
|
value_list = &modifier->value_list;
|
|
|
|
|
} else {
|
|
|
|
|
device = find_device(uc_mgr, verb, name, 0);
|
|
|
|
|
if (device)
|
|
|
|
|
value_list = &device->value_list;
|
|
|
|
|
}
|
|
|
|
|
if (value_list == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&mylist);
|
|
|
|
|
err = add_identifiers(&mylist, &uc_mgr->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = add_identifiers(&mylist, &verb->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = add_identifiers(&mylist, value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = myvalue_to_str_list(&mylist, &res);
|
|
|
|
|
if (err > 0)
|
|
|
|
|
*list = (const char **)res;
|
|
|
|
|
else if (err == 0)
|
|
|
|
|
*list = NULL;
|
|
|
|
|
__fail:
|
|
|
|
|
myvalue_list_free(&mylist);
|
|
|
|
|
if (err <= 0)
|
|
|
|
|
return err;
|
|
|
|
|
qsort(*list, err, sizeof(char *), identifier_cmp);
|
|
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of values
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param verbname For verb (NULL = current)
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_value_list(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *identifier,
|
|
|
|
|
const char **list[],
|
|
|
|
|
char *verbname)
|
2010-09-22 14:31:15 +02:00
|
|
|
{
|
2019-11-30 20:31:55 +01:00
|
|
|
struct list_head mylist, *pos;
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
struct use_case_device *dev;
|
|
|
|
|
struct use_case_modifier *mod;
|
|
|
|
|
char **res;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (verbname) {
|
|
|
|
|
verb = find_verb(uc_mgr, verbname);
|
|
|
|
|
} else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
|
|
|
|
}
|
|
|
|
|
if (verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
INIT_LIST_HEAD(&mylist);
|
2010-11-29 15:41:34 +01:00
|
|
|
err = add_values(&mylist, identifier, &uc_mgr->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
2021-03-05 19:35:03 +01:00
|
|
|
err = add_values(&mylist, identifier, &verb->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
list_for_each(pos, &verb->device_list) {
|
|
|
|
|
dev = list_entry(pos, struct use_case_device, list);
|
|
|
|
|
err = add_values(&mylist, identifier, &dev->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
list_for_each(pos, &verb->modifier_list) {
|
|
|
|
|
mod = list_entry(pos, struct use_case_modifier, list);
|
|
|
|
|
err = add_values(&mylist, identifier, &mod->value_list);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
2019-11-30 20:31:55 +01:00
|
|
|
err = myvalue_to_str_list(&mylist, &res);
|
|
|
|
|
if (err > 0)
|
2021-03-05 19:35:03 +01:00
|
|
|
*list = (const char **)res;
|
2019-11-30 20:31:55 +01:00
|
|
|
else if (err == 0)
|
|
|
|
|
*list = NULL;
|
2010-09-22 14:31:15 +02:00
|
|
|
__fail:
|
2019-11-30 20:31:55 +01:00
|
|
|
myvalue_list_free(&mylist);
|
2021-03-05 19:35:03 +01:00
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of enabled devices
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param verbname For verb (NULL = current)
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static int get_enabled_device_list(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
return get_list(&uc_mgr->active_devices, list,
|
|
|
|
|
struct use_case_device, active_list,
|
|
|
|
|
name);
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get list of enabled modifiers
|
|
|
|
|
* \param list Returned list
|
|
|
|
|
* \param verbname For verb (NULL = current)
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static int get_enabled_modifier_list(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
return get_list(&uc_mgr->active_modifiers, list,
|
|
|
|
|
struct use_case_modifier, active_list,
|
|
|
|
|
name);
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02:00
|
|
|
char *str, *str1;
|
|
|
|
|
int err;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
if (uc_mgr == NULL || identifier == NULL)
|
2010-10-13 11:48:52 +02:00
|
|
|
return uc_mgr_scan_master_configs(list);
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2010-09-22 14:31:15 +02:00
|
|
|
if (strcmp(identifier, "_verbs") == 0)
|
|
|
|
|
err = get_verb_list(uc_mgr, list);
|
2021-03-05 19:35:03 +01:00
|
|
|
else if (strcmp(identifier, "_enadevs") == 0)
|
|
|
|
|
err = get_enabled_device_list(uc_mgr, list);
|
|
|
|
|
else if (strcmp(identifier, "_enamods") == 0)
|
|
|
|
|
err = get_enabled_modifier_list(uc_mgr, list);
|
|
|
|
|
else {
|
|
|
|
|
str1 = strchr(identifier, '/');
|
|
|
|
|
if (str1) {
|
|
|
|
|
str = strdup(str1 + 1);
|
|
|
|
|
if (str == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
str = NULL;
|
|
|
|
|
}
|
2019-11-30 20:31:55 +01:00
|
|
|
if (check_identifier(identifier, "_devices"))
|
|
|
|
|
err = get_device_list(uc_mgr, list, str);
|
2021-03-05 19:35:03 +01:00
|
|
|
else if (check_identifier(identifier, "_modifiers"))
|
2019-11-30 20:31:55 +01:00
|
|
|
err = get_modifier_list(uc_mgr, list, str);
|
|
|
|
|
else if (check_identifier(identifier, "_identifiers"))
|
|
|
|
|
err = get_identifiers_list(uc_mgr, list, str);
|
|
|
|
|
else if (check_identifier(identifier, "_supporteddevs"))
|
|
|
|
|
err = get_supported_device_list(uc_mgr, list, str);
|
|
|
|
|
else if (check_identifier(identifier, "_conflictingdevs"))
|
|
|
|
|
err = get_conflicting_device_list(uc_mgr, list, str);
|
2011-06-03 14:56:32 -06:00
|
|
|
else if (identifier[0] == '_')
|
|
|
|
|
err = -ENOENT;
|
2019-11-30 20:31:55 +01:00
|
|
|
else
|
|
|
|
|
err = get_value_list(uc_mgr, identifier, list, str);
|
|
|
|
|
if (str)
|
|
|
|
|
free(str);
|
|
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
__end:
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
2010-09-22 14:31:15 +02:00
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
static int get_value1(snd_use_case_mgr_t *uc_mgr, char **value,
|
|
|
|
|
struct list_head *value_list, const char *identifier)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct ucm_value *val;
|
|
|
|
|
struct list_head *pos;
|
2021-04-12 18:09:21 +02:00
|
|
|
int err;
|
2021-03-05 19:35:03 +01:00
|
|
|
|
2010-12-21 23:11:56 +01:00
|
|
|
if (!value_list)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
list_for_each(pos, value_list) {
|
2019-11-04 16:18:07 +01:00
|
|
|
val = list_entry(pos, struct ucm_value, list);
|
|
|
|
|
if (check_identifier(identifier, val->name)) {
|
|
|
|
|
if (uc_mgr->conf_format < 2) {
|
|
|
|
|
*value = strdup(val->data);
|
|
|
|
|
if (*value == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-04-12 18:09:21 +02:00
|
|
|
err = uc_mgr_get_substituted_value(uc_mgr, value, val->data);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return rewrite_device_value(uc_mgr, val->name, value);
|
2019-11-04 16:18:07 +01:00
|
|
|
}
|
2021-03-05 19:35:03 +01:00
|
|
|
}
|
|
|
|
|
return -ENOENT;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
static int get_value3(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
char **value,
|
2010-11-29 17:33:27 +01:00
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value, value_list1, identifier);
|
2010-11-29 17:33:27 +01:00
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value, value_list2, identifier);
|
2010-11-29 17:33:27 +01:00
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value, value_list3, identifier);
|
2010-11-29 17:33:27 +01:00
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get value
|
2010-11-29 15:41:34 +01:00
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param identifier Value identifier (string)
|
|
|
|
|
* \param value Returned value string
|
2011-03-18 11:26:22 -06:00
|
|
|
* \param item Modifier or Device name (string)
|
2010-11-29 15:41:34 +01:00
|
|
|
* \return Zero on success (value is filled), otherwise a negative error code
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
static int get_value(snd_use_case_mgr_t *uc_mgr,
|
2011-03-18 11:26:22 -06:00
|
|
|
const char *identifier,
|
2015-02-17 21:15:23 +02:00
|
|
|
char **value,
|
2011-06-06 14:16:57 -06:00
|
|
|
const char *mod_dev_name,
|
|
|
|
|
const char *verb_name,
|
|
|
|
|
int exact)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2011-06-06 14:16:57 -06:00
|
|
|
struct use_case_verb *verb;
|
2011-03-18 11:26:22 -06:00
|
|
|
struct use_case_modifier *mod;
|
|
|
|
|
struct use_case_device *dev;
|
2010-11-29 15:41:34 +01:00
|
|
|
int err;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2011-06-06 14:16:57 -06:00
|
|
|
if (mod_dev_name || verb_name || !exact) {
|
|
|
|
|
if (verb_name && strlen(verb_name)) {
|
|
|
|
|
verb = find_verb(uc_mgr, verb_name);
|
|
|
|
|
} else {
|
|
|
|
|
verb = uc_mgr->active_verb;
|
2010-11-29 15:41:34 +01:00
|
|
|
}
|
2011-06-06 14:16:57 -06:00
|
|
|
if (verb) {
|
|
|
|
|
if (mod_dev_name) {
|
|
|
|
|
mod = find_modifier(uc_mgr, verb,
|
|
|
|
|
mod_dev_name, 0);
|
|
|
|
|
if (mod) {
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value,
|
2011-06-06 14:16:57 -06:00
|
|
|
&mod->value_list,
|
|
|
|
|
identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev = find_device(uc_mgr, verb,
|
|
|
|
|
mod_dev_name, 0);
|
|
|
|
|
if (dev) {
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value,
|
2011-06-06 14:16:57 -06:00
|
|
|
&dev->value_list,
|
|
|
|
|
identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exact)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value, &verb->value_list, identifier);
|
2011-03-18 11:26:22 -06:00
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2011-06-06 14:16:57 -06:00
|
|
|
|
|
|
|
|
if (exact)
|
|
|
|
|
return -ENOENT;
|
2010-11-29 15:41:34 +01:00
|
|
|
}
|
2011-06-06 14:16:57 -06:00
|
|
|
|
2019-11-04 16:18:07 +01:00
|
|
|
err = get_value1(uc_mgr, value, &uc_mgr->value_list, identifier);
|
2010-11-29 15:41:34 +01:00
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
2011-06-06 14:16:57 -06:00
|
|
|
|
2010-11-29 15:41:34 +01:00
|
|
|
return -ENOENT;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-12 18:09:21 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Get private alsa-lib configuration (ASCII)
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param str Returned value string
|
|
|
|
|
* \return Zero on success (value is filled), otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_alibcfg(snd_use_case_mgr_t *uc_mgr, char **str)
|
|
|
|
|
{
|
|
|
|
|
snd_output_t *out;
|
|
|
|
|
size_t size;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = snd_output_buffer_open(&out);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = snd_config_save(uc_mgr->local_config, out);
|
|
|
|
|
if (err >= 0) {
|
|
|
|
|
size = snd_output_buffer_steal(out, str);
|
|
|
|
|
if (*str)
|
|
|
|
|
(*str)[size] = '\0';
|
|
|
|
|
}
|
|
|
|
|
snd_output_close(out);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 11:09:43 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Get device prefix for private alsa-lib configuration
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param str Returned value string
|
|
|
|
|
* \return Zero on success (value is filled), otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
static int get_alibpref(snd_use_case_mgr_t *uc_mgr, char **str)
|
|
|
|
|
{
|
2021-06-02 11:21:54 +02:00
|
|
|
const size_t l = 10;
|
2021-06-02 11:09:43 +02:00
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
s = malloc(l);
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
return -ENOMEM;
|
2021-06-02 11:21:54 +02:00
|
|
|
snprintf(s, l, "_ucm%04X.", uc_mgr->ucm_card_number);
|
2021-06-02 11:09:43 +02:00
|
|
|
*str = s;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
int snd_use_case_get(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char **value)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2011-06-06 14:16:57 -06:00
|
|
|
const char *slash1, *slash2, *mod_dev_after;
|
|
|
|
|
const char *ident, *mod_dev, *verb;
|
|
|
|
|
int exact = 0;
|
2021-03-05 19:35:03 +01:00
|
|
|
int err;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2010-09-22 14:31:15 +02:00
|
|
|
if (identifier == NULL) {
|
2021-03-05 19:35:03 +01:00
|
|
|
*value = strdup(uc_mgr->card_name);
|
|
|
|
|
if (*value == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
2011-06-02 16:44:39 -06:00
|
|
|
goto __end;
|
|
|
|
|
}
|
2021-03-05 19:35:03 +01:00
|
|
|
err = 0;
|
|
|
|
|
} else if (strcmp(identifier, "_verb") == 0) {
|
|
|
|
|
if (uc_mgr->active_verb == NULL) {
|
|
|
|
|
err = -ENOENT;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
*value = strdup(uc_mgr->active_verb->name);
|
|
|
|
|
if (*value == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
2017-01-18 11:53:42 +08:00
|
|
|
} else if (strcmp(identifier, "_file") == 0) {
|
|
|
|
|
/* get the conf file name of the opened card */
|
2020-01-15 10:31:56 +01:00
|
|
|
if ((uc_mgr->card_name == NULL) ||
|
|
|
|
|
(uc_mgr->conf_file_name == NULL) ||
|
|
|
|
|
(uc_mgr->conf_file_name[0] == '\0')) {
|
2017-01-18 11:53:42 +08:00
|
|
|
err = -ENOENT;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
2020-01-15 10:31:56 +01:00
|
|
|
*value = strdup(uc_mgr->conf_file_name);
|
2017-01-18 11:53:42 +08:00
|
|
|
if (*value == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
|
|
|
|
|
2021-04-12 18:09:21 +02:00
|
|
|
} else if (strcmp(identifier, "_alibcfg") == 0) {
|
|
|
|
|
err = get_alibcfg(uc_mgr, (char **)value);
|
2021-06-02 11:09:43 +02:00
|
|
|
} else if (strcmp(identifier, "_alibpref") == 0) {
|
|
|
|
|
err = get_alibpref(uc_mgr, (char **)value);
|
2011-06-03 14:56:32 -06:00
|
|
|
} else if (identifier[0] == '_') {
|
|
|
|
|
err = -ENOENT;
|
2021-03-05 19:35:03 +01:00
|
|
|
} else {
|
2011-06-06 14:16:57 -06:00
|
|
|
if (identifier[0] == '=') {
|
|
|
|
|
exact = 1;
|
|
|
|
|
identifier++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
slash1 = strchr(identifier, '/');
|
|
|
|
|
if (slash1) {
|
|
|
|
|
ident = strndup(identifier, slash1 - identifier);
|
|
|
|
|
|
|
|
|
|
slash2 = strchr(slash1 + 1, '/');
|
|
|
|
|
if (slash2) {
|
|
|
|
|
mod_dev_after = slash2;
|
|
|
|
|
verb = slash2 + 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
mod_dev_after = slash1 + strlen(slash1);
|
|
|
|
|
verb = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod_dev_after == slash1 + 1)
|
|
|
|
|
mod_dev = NULL;
|
|
|
|
|
else
|
|
|
|
|
mod_dev = strndup(slash1 + 1,
|
|
|
|
|
mod_dev_after - (slash1 + 1));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ident = identifier;
|
|
|
|
|
mod_dev = NULL;
|
|
|
|
|
verb = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 21:15:23 +02:00
|
|
|
err = get_value(uc_mgr, ident, (char **)value, mod_dev, verb,
|
2021-03-05 19:35:03 +01:00
|
|
|
exact);
|
2011-06-06 14:16:57 -06:00
|
|
|
if (ident != identifier)
|
|
|
|
|
free((void *)ident);
|
|
|
|
|
if (mod_dev)
|
|
|
|
|
free((void *)mod_dev);
|
2021-03-05 19:35:03 +01:00
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
__end:
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
2021-03-05 19:35:03 +01:00
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-15 18:32:20 +01:00
|
|
|
/*
|
|
|
|
|
* a helper macro to obtain status and existence
|
2023-01-13 13:21:55 -08:00
|
|
|
*/
|
2023-01-15 18:32:20 +01:00
|
|
|
#define geti(uc_mgr, status, ifind, str, value) ({ \
|
|
|
|
|
long val = -EINVAL; \
|
|
|
|
|
if (str) { \
|
|
|
|
|
val = (status)((uc_mgr), (str)); \
|
|
|
|
|
if (val >= 0) { \
|
|
|
|
|
if ((ifind)((uc_mgr), (uc_mgr)->active_verb, (str), 0)) { \
|
|
|
|
|
*(value) = val; \
|
2023-01-15 18:57:56 +01:00
|
|
|
val = 0; \
|
2023-01-15 18:32:20 +01:00
|
|
|
} else { \
|
|
|
|
|
val = -ENOENT; \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
; val; /* return value */ \
|
|
|
|
|
})
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2010-10-12 13:29:05 +02:00
|
|
|
int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
long *value)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
char *str, *str1;
|
2023-01-15 18:32:20 +01:00
|
|
|
int err;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2021-03-05 19:35:03 +01:00
|
|
|
if (0) {
|
|
|
|
|
/* nothing here - prepared for fixed identifiers */
|
|
|
|
|
} else {
|
|
|
|
|
str1 = strchr(identifier, '/');
|
|
|
|
|
if (str1) {
|
|
|
|
|
str = strdup(str1 + 1);
|
|
|
|
|
if (str == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
str = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (check_identifier(identifier, "_devstatus")) {
|
2023-01-15 18:32:20 +01:00
|
|
|
err = geti(uc_mgr, device_status, find_device, str, value);
|
2010-10-12 13:29:05 +02:00
|
|
|
} else if (check_identifier(identifier, "_modstatus")) {
|
2023-01-15 18:32:20 +01:00
|
|
|
err = geti(uc_mgr, modifier_status, find_modifier, str, value);
|
2011-06-03 14:56:32 -06:00
|
|
|
#if 0
|
|
|
|
|
/*
|
|
|
|
|
* enable this block if the else clause below is expanded to query
|
|
|
|
|
* user-supplied values
|
|
|
|
|
*/
|
2023-01-15 18:32:20 +01:00
|
|
|
} else if (identifier[0] == '_') {
|
2011-06-03 14:56:32 -06:00
|
|
|
err = -ENOENT;
|
|
|
|
|
#endif
|
2010-10-12 13:29:05 +02:00
|
|
|
} else
|
2021-03-05 19:35:03 +01:00
|
|
|
err = -ENOENT;
|
|
|
|
|
if (str)
|
|
|
|
|
free(str);
|
|
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
__end:
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
2021-03-05 19:35:03 +01:00
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-05 18:50:02 +01:00
|
|
|
static int set_fixedboot_user(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *value)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (value != NULL && *value) {
|
|
|
|
|
uc_error("error: wrong value for _fboot (%s)", value);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2021-04-13 19:13:20 +02:00
|
|
|
if (list_empty(&uc_mgr->fixedboot_list))
|
|
|
|
|
return -ENOENT;
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, NULL, &uc_mgr->fixedboot_list,
|
2021-03-05 18:50:02 +01:00
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("Unable to execute force boot sequence");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-25 19:19:56 +02:00
|
|
|
static int set_boot_user(snd_use_case_mgr_t *uc_mgr,
|
2020-05-14 16:23:17 +02:00
|
|
|
const char *value)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (value != NULL && *value) {
|
2020-05-25 19:19:56 +02:00
|
|
|
uc_error("error: wrong value for _boot (%s)", value);
|
2020-05-14 16:23:17 +02:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2020-10-29 14:51:30 +08:00
|
|
|
if (list_empty(&uc_mgr->boot_list))
|
|
|
|
|
return -ENOENT;
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, NULL, &uc_mgr->boot_list,
|
2020-05-14 16:23:17 +02:00
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
|
|
|
|
if (err < 0) {
|
2020-10-06 10:47:11 +02:00
|
|
|
uc_error("Unable to execute boot sequence");
|
2020-05-14 16:23:17 +02:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-14 18:32:05 +02:00
|
|
|
static int set_defaults_user(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *value)
|
|
|
|
|
{
|
|
|
|
|
if (value != NULL && *value) {
|
|
|
|
|
uc_error("error: wrong value for _defaults (%s)", value);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2022-11-15 17:25:59 +01:00
|
|
|
return set_defaults(uc_mgr, false);
|
2020-05-14 18:32:05 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_verb *new_verb)
|
2010-09-22 15:35:11 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct list_head *pos;
|
|
|
|
|
struct transition_sequence *trans;
|
|
|
|
|
int err;
|
2010-09-22 15:35:11 +02:00
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
list_for_each(pos, &uc_mgr->active_verb->transition_list) {
|
|
|
|
|
trans = list_entry(pos, struct transition_sequence, list);
|
|
|
|
|
if (strcmp(trans->name, new_verb->name) == 0) {
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb,
|
|
|
|
|
&trans->transition_list,
|
2010-11-29 17:33:27 +01:00
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list,
|
|
|
|
|
NULL);
|
2021-03-05 19:35:03 +01:00
|
|
|
if (err >= 0)
|
|
|
|
|
return 1;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int set_verb_user(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *verb_name)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
|
|
if (uc_mgr->active_verb &&
|
|
|
|
|
strcmp(uc_mgr->active_verb->name, verb_name) == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (strcmp(verb_name, SND_USE_CASE_VERB_INACTIVE) != 0) {
|
|
|
|
|
verb = find_verb(uc_mgr, verb_name);
|
|
|
|
|
if (verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
} else {
|
|
|
|
|
verb = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (uc_mgr->active_verb) {
|
|
|
|
|
err = handle_transition_verb(uc_mgr, verb);
|
|
|
|
|
if (err == 0) {
|
|
|
|
|
err = dismantle_use_case(uc_mgr);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
} else if (err == 1) {
|
|
|
|
|
uc_mgr->active_verb = verb;
|
|
|
|
|
verb = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
verb = NULL; /* show error */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (verb) {
|
|
|
|
|
err = set_verb(uc_mgr, verb, 1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
uc_error("error: failed to initialize new use case: %s",
|
|
|
|
|
verb_name);
|
|
|
|
|
}
|
|
|
|
|
return err;
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int set_device_user(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *device_name,
|
|
|
|
|
int enable)
|
2010-09-22 15:35:11 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_device *device;
|
2010-09-22 15:35:11 +02:00
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1);
|
|
|
|
|
if (device == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return set_device(uc_mgr, device, enable);
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int set_modifier_user(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *modifier_name,
|
|
|
|
|
int enable)
|
2010-09-22 15:35:11 +02:00
|
|
|
{
|
2021-03-05 19:35:03 +01:00
|
|
|
struct use_case_modifier *modifier;
|
2010-09-22 15:35:11 +02:00
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
2010-12-21 23:11:58 +01:00
|
|
|
|
2021-03-05 19:35:03 +01:00
|
|
|
modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1);
|
|
|
|
|
if (modifier == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return set_modifier(uc_mgr, modifier, enable);
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int switch_device(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *old_device,
|
|
|
|
|
const char *new_device)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *xold, *xnew;
|
|
|
|
|
struct transition_sequence *trans;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int err, seq_found = 0;
|
|
|
|
|
|
|
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
if (device_status(uc_mgr, old_device) == 0) {
|
|
|
|
|
uc_error("error: device %s not enabled", old_device);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (device_status(uc_mgr, new_device) != 0) {
|
|
|
|
|
uc_error("error: device %s already enabled", new_device);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1);
|
|
|
|
|
if (xold == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
list_del(&xold->active_list);
|
|
|
|
|
xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1);
|
|
|
|
|
list_add_tail(&xold->active_list, &uc_mgr->active_devices);
|
|
|
|
|
if (xnew == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
err = 0;
|
|
|
|
|
list_for_each(pos, &xold->transition_list) {
|
|
|
|
|
trans = list_entry(pos, struct transition_sequence, list);
|
|
|
|
|
if (strcmp(trans->name, new_device) == 0) {
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb,
|
|
|
|
|
&trans->transition_list,
|
2010-11-29 17:33:27 +01:00
|
|
|
&xold->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2021-03-05 19:35:03 +01:00
|
|
|
if (err >= 0) {
|
|
|
|
|
list_del(&xold->active_list);
|
|
|
|
|
list_add_tail(&xnew->active_list, &uc_mgr->active_devices);
|
|
|
|
|
}
|
|
|
|
|
seq_found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!seq_found) {
|
|
|
|
|
err = set_device(uc_mgr, xold, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = set_device(uc_mgr, xnew, 1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *old_modifier,
|
|
|
|
|
const char *new_modifier)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_modifier *xold, *xnew;
|
|
|
|
|
struct transition_sequence *trans;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int err, seq_found = 0;
|
|
|
|
|
|
|
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
if (modifier_status(uc_mgr, old_modifier) == 0) {
|
|
|
|
|
uc_error("error: modifier %s not enabled", old_modifier);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (modifier_status(uc_mgr, new_modifier) != 0) {
|
|
|
|
|
uc_error("error: modifier %s already enabled", new_modifier);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1);
|
|
|
|
|
if (xold == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1);
|
|
|
|
|
if (xnew == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
err = 0;
|
|
|
|
|
list_for_each(pos, &xold->transition_list) {
|
|
|
|
|
trans = list_entry(pos, struct transition_sequence, list);
|
|
|
|
|
if (strcmp(trans->name, new_modifier) == 0) {
|
2022-05-19 10:08:48 +02:00
|
|
|
err = execute_sequence(uc_mgr, uc_mgr->active_verb,
|
|
|
|
|
&trans->transition_list,
|
2010-11-29 17:33:27 +01:00
|
|
|
&xold->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2021-03-05 19:35:03 +01:00
|
|
|
if (err >= 0) {
|
|
|
|
|
list_del(&xold->active_list);
|
|
|
|
|
list_add_tail(&xnew->active_list, &uc_mgr->active_modifiers);
|
|
|
|
|
}
|
|
|
|
|
seq_found = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!seq_found) {
|
|
|
|
|
err = set_modifier(uc_mgr, xold, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = set_modifier(uc_mgr, xnew, 1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
2010-09-22 15:35:11 +02:00
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
int snd_use_case_set(snd_use_case_mgr_t *uc_mgr,
|
2021-03-05 19:35:03 +01:00
|
|
|
const char *identifier,
|
|
|
|
|
const char *value)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2011-01-31 14:24:19 +01:00
|
|
|
char *str, *str1;
|
2014-11-21 15:25:30 +01:00
|
|
|
int err = 0;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2021-03-05 18:50:02 +01:00
|
|
|
if (strcmp(identifier, "_fboot") == 0)
|
|
|
|
|
err = set_fixedboot_user(uc_mgr, value);
|
|
|
|
|
else if (strcmp(identifier, "_boot") == 0)
|
2020-05-25 19:19:56 +02:00
|
|
|
err = set_boot_user(uc_mgr, value);
|
2020-05-14 18:32:05 +02:00
|
|
|
else if (strcmp(identifier, "_defaults") == 0)
|
|
|
|
|
err = set_defaults_user(uc_mgr, value);
|
2020-05-14 16:23:17 +02:00
|
|
|
else if (strcmp(identifier, "_verb") == 0)
|
2021-03-05 19:35:03 +01:00
|
|
|
err = set_verb_user(uc_mgr, value);
|
|
|
|
|
else if (strcmp(identifier, "_enadev") == 0)
|
|
|
|
|
err = set_device_user(uc_mgr, value, 1);
|
|
|
|
|
else if (strcmp(identifier, "_disdev") == 0)
|
|
|
|
|
err = set_device_user(uc_mgr, value, 0);
|
|
|
|
|
else if (strcmp(identifier, "_enamod") == 0)
|
|
|
|
|
err = set_modifier_user(uc_mgr, value, 1);
|
|
|
|
|
else if (strcmp(identifier, "_dismod") == 0)
|
|
|
|
|
err = set_modifier_user(uc_mgr, value, 0);
|
|
|
|
|
else {
|
|
|
|
|
str1 = strchr(identifier, '/');
|
|
|
|
|
if (str1) {
|
|
|
|
|
str = strdup(str1 + 1);
|
|
|
|
|
if (str == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
if (check_identifier(identifier, "_swdev"))
|
|
|
|
|
err = switch_device(uc_mgr, str, value);
|
|
|
|
|
else if (check_identifier(identifier, "_swmod"))
|
|
|
|
|
err = switch_modifier(uc_mgr, str, value);
|
|
|
|
|
else
|
|
|
|
|
err = -EINVAL;
|
|
|
|
|
if (str)
|
|
|
|
|
free(str);
|
|
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
__end:
|
2010-09-07 15:35:14 +02:00
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
2021-03-05 19:35:03 +01:00
|
|
|
return err;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
2019-11-05 20:04:54 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Parse control element identifier
|
2023-07-29 11:08:25 +01:00
|
|
|
* \param dst Element identifier
|
2019-11-05 20:04:54 +01:00
|
|
|
* \param ucm_id Use case identifier
|
|
|
|
|
* \param value String value to be parsed
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_parse_ctl_elem_id(snd_ctl_elem_id_t *dst,
|
2019-11-11 14:22:36 +01:00
|
|
|
const char *ucm_id,
|
|
|
|
|
const char *value)
|
2019-11-05 20:04:54 +01:00
|
|
|
{
|
|
|
|
|
snd_ctl_elem_iface_t iface;
|
2019-11-11 14:22:36 +01:00
|
|
|
int jack_control;
|
|
|
|
|
|
|
|
|
|
jack_control = strcmp(ucm_id, "JackControl") == 0;
|
|
|
|
|
if (!jack_control &&
|
|
|
|
|
strcmp(ucm_id, "PlaybackVolume") &&
|
|
|
|
|
strcmp(ucm_id, "PlaybackSwitch") &&
|
|
|
|
|
strcmp(ucm_id, "CaptureVolume") &&
|
|
|
|
|
strcmp(ucm_id, "CaptureSwitch"))
|
|
|
|
|
return -EINVAL;
|
2019-11-05 20:04:54 +01:00
|
|
|
snd_ctl_elem_id_clear(dst);
|
2022-11-02 15:01:56 +01:00
|
|
|
if (strcasestr(value, "name="))
|
2019-11-05 20:04:54 +01:00
|
|
|
return __snd_ctl_ascii_elem_id_parse(dst, value, NULL);
|
|
|
|
|
iface = SND_CTL_ELEM_IFACE_MIXER;
|
2019-11-11 14:22:36 +01:00
|
|
|
if (jack_control)
|
2019-11-05 20:04:54 +01:00
|
|
|
iface = SND_CTL_ELEM_IFACE_CARD;
|
|
|
|
|
snd_ctl_elem_id_set_interface(dst, iface);
|
|
|
|
|
snd_ctl_elem_id_set_name(dst, value);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2019-11-11 14:22:36 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Parse mixer element identifier
|
|
|
|
|
* \param dst Simple mixer element identifier
|
|
|
|
|
* \param ucm_id Use case identifier
|
|
|
|
|
* \param value String value to be parsed
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_parse_selem_id(snd_mixer_selem_id_t *dst,
|
|
|
|
|
const char *ucm_id,
|
|
|
|
|
const char *value)
|
|
|
|
|
{
|
2019-12-03 18:56:40 +01:00
|
|
|
#ifdef BUILD_MIXER
|
2019-11-11 14:22:36 +01:00
|
|
|
if (strcmp(ucm_id, "PlaybackMixerId") == 0 ||
|
|
|
|
|
strcmp(ucm_id, "CaptureMixerId") == 0)
|
|
|
|
|
return snd_mixer_selem_id_parse(dst, value);
|
2019-12-03 18:56:40 +01:00
|
|
|
#endif
|
2019-11-11 14:22:36 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|