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
|
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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"
|
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>
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
/*
|
|
|
|
|
* misc
|
|
|
|
|
*/
|
|
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
static int get_value1(const char **value, struct list_head *value_list,
|
|
|
|
|
const char *identifier);
|
|
|
|
|
static int get_value3(const char **value,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3);
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
static int check_identifier(const char *identifier, const char *prefix)
|
|
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
if (strcmp(identifier, prefix) == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
len = strlen(prefix);
|
|
|
|
|
if (memcmp(identifier, prefix, len) == 0 && identifier[len] == '/')
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int list_count(struct list_head *list)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, list) {
|
|
|
|
|
count += 1;
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int alloc_str_list(struct list_head *list, int mult, char **result[])
|
|
|
|
|
{
|
|
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
|
|
cnt = list_count(list) * mult;
|
2011-03-19 01:09:41 +01:00
|
|
|
if (cnt == 0) {
|
|
|
|
|
*result = NULL;
|
2010-09-22 14:31:15 +02:00
|
|
|
return cnt;
|
2011-03-19 01:09:41 +01:00
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
res = calloc(mult, cnt * sizeof(char *));
|
|
|
|
|
if (res == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
*result = res;
|
|
|
|
|
return cnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \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)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
if (list == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
for (i = 0; i < items; i++)
|
|
|
|
|
free((void *)list[i]);
|
|
|
|
|
free(list);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
static int open_ctl(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
snd_ctl_t **ctl,
|
|
|
|
|
const char *ctl_dev)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
/* FIXME: add a list of ctl devices to uc_mgr structure and
|
|
|
|
|
cache accesses for multiple opened ctl devices */
|
|
|
|
|
if (uc_mgr->ctl_dev != NULL && strcmp(ctl_dev, uc_mgr->ctl_dev) == 0) {
|
|
|
|
|
*ctl = uc_mgr->ctl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (uc_mgr->ctl_dev) {
|
|
|
|
|
free(uc_mgr->ctl_dev);
|
|
|
|
|
uc_mgr->ctl_dev = NULL;
|
|
|
|
|
snd_ctl_close(uc_mgr->ctl);
|
2013-01-26 14:20:20 +02:00
|
|
|
uc_mgr->ctl = NULL;
|
2010-11-29 17:33:27 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
err = snd_ctl_open(ctl, ctl_dev, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
uc_mgr->ctl_dev = strdup(ctl_dev);
|
|
|
|
|
if (uc_mgr->ctl_dev == NULL) {
|
|
|
|
|
snd_ctl_close(*ctl);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
uc_mgr->ctl = *ctl;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-10 14:14:28 +02:00
|
|
|
extern int __snd_ctl_ascii_elem_id_parse(snd_ctl_elem_id_t *dst,
|
|
|
|
|
const char *str,
|
|
|
|
|
const char **ret_ptr);
|
|
|
|
|
|
2012-08-10 14:19:17 +02:00
|
|
|
static int execute_cset(snd_ctl_t *ctl, const char *cset)
|
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;
|
|
|
|
|
snd_ctl_elem_info_t *info;
|
|
|
|
|
|
|
|
|
|
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++;
|
|
|
|
|
if (!*pos) {
|
2010-11-29 17:33:27 +01:00
|
|
|
uc_error("undefined value for cset >%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
|
|
|
}
|
|
|
|
|
snd_ctl_elem_value_set_id(value, id);
|
|
|
|
|
snd_ctl_elem_info_set_id(info, id);
|
|
|
|
|
err = snd_ctl_elem_read(ctl, value);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = snd_ctl_elem_info(ctl, info);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
2012-08-10 14:14:28 +02:00
|
|
|
err = snd_ctl_ascii_value_parse(ctl, value, info, pos);
|
2010-11-29 17:33:27 +01:00
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = snd_ctl_elem_write(ctl, value);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
|
|
|
|
err = 0;
|
|
|
|
|
__fail:
|
2011-05-03 10:52:34 -07:00
|
|
|
if (id != NULL)
|
|
|
|
|
free(id);
|
|
|
|
|
if (value != NULL)
|
|
|
|
|
free(value);
|
|
|
|
|
if (info != NULL)
|
|
|
|
|
free(info);
|
|
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
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,
|
|
|
|
|
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;
|
|
|
|
|
int err = 0;
|
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;
|
2010-11-23 15:58:14 +01:00
|
|
|
break;
|
2010-09-07 15:35:14 +02:00
|
|
|
case SEQUENCE_ELEMENT_TYPE_CSET:
|
2010-11-29 17:33:27 +01:00
|
|
|
if (cdev == NULL) {
|
|
|
|
|
const char *cdev1 = NULL, *cdev2 = NULL;
|
|
|
|
|
err = get_value3(&cdev1, "PlaybackCTL",
|
|
|
|
|
value_list1,
|
|
|
|
|
value_list2,
|
|
|
|
|
value_list3);
|
|
|
|
|
if (err < 0 && err != ENOENT) {
|
|
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = get_value3(&cdev1, "CaptureCTL",
|
|
|
|
|
value_list1,
|
|
|
|
|
value_list2,
|
|
|
|
|
value_list3);
|
|
|
|
|
if (err < 0 && err != ENOENT) {
|
|
|
|
|
free((char *)cdev1);
|
|
|
|
|
uc_error("cdev is not defined!");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
if (cdev1 == NULL || cdev2 == NULL ||
|
|
|
|
|
strcmp(cdev1, cdev2) == 0) {
|
|
|
|
|
cdev = (char *)cdev1;
|
|
|
|
|
free((char *)cdev2);
|
|
|
|
|
} else {
|
|
|
|
|
free((char *)cdev1);
|
|
|
|
|
free((char *)cdev2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ctl == NULL) {
|
|
|
|
|
err = open_ctl(uc_mgr, &ctl, cdev);
|
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
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
}
|
|
|
|
|
err = execute_cset(ctl, s->data.cset);
|
2011-01-31 15:06:03 +01:00
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("unable to execute cset '%s'\n", 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;
|
|
|
|
|
case SEQUENCE_ELEMENT_TYPE_SLEEP:
|
|
|
|
|
usleep(s->data.sleep);
|
|
|
|
|
break;
|
|
|
|
|
case SEQUENCE_ELEMENT_TYPE_EXEC:
|
2010-12-21 23:11:53 +01:00
|
|
|
err = system(s->data.exec);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto __fail;
|
2010-09-07 15:35:14 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
uc_error("unknown sequence command %i", s->type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
free(cdev);
|
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);
|
|
|
|
|
return err;
|
|
|
|
|
|
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);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &uc_mgr->default_list,
|
|
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
2010-09-07 15:35:14 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
uc_error("Unable to execute default sequence");
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \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,
|
|
|
|
|
const char **result[],
|
|
|
|
|
unsigned long offset,
|
|
|
|
|
unsigned long s1offset)
|
|
|
|
|
{
|
|
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
|
|
|
|
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;
|
2010-09-22 14:31:15 +02: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) {
|
|
|
|
|
*res = strdup(str1);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
return cnt;
|
|
|
|
|
__fail:
|
|
|
|
|
snd_use_case_free_list((const char **)res, cnt);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define get_list(list, result, type, member, s1) \
|
|
|
|
|
get_list0(list, result, \
|
|
|
|
|
(unsigned long)(&((type *)0)->member), \
|
|
|
|
|
(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,
|
|
|
|
|
const char **result[],
|
|
|
|
|
unsigned long offset,
|
|
|
|
|
unsigned long s1offset,
|
|
|
|
|
unsigned long s2offset)
|
|
|
|
|
{
|
|
|
|
|
char **res;
|
|
|
|
|
int cnt;
|
|
|
|
|
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;
|
2010-09-22 14:31:15 +02: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) {
|
|
|
|
|
*res = strdup(str1);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
|
|
|
|
str2 = *((char **)(ptr + s2offset));
|
|
|
|
|
if (str2 != NULL) {
|
|
|
|
|
*res = strdup(str2);
|
|
|
|
|
if (*res == NULL)
|
|
|
|
|
goto __fail;
|
|
|
|
|
} else {
|
|
|
|
|
*res = NULL;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
return cnt;
|
|
|
|
|
__fail:
|
|
|
|
|
snd_use_case_free_list((const char **)res, cnt);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define get_list2(list, result, type, member, s1, s2) \
|
|
|
|
|
get_list20(list, result, \
|
|
|
|
|
(unsigned long)(&((type *)0)->member), \
|
|
|
|
|
(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 *
|
2011-06-03 14:56:33 -06:00
|
|
|
find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
|
|
|
|
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 *
|
2011-06-03 14:56:33 -06:00
|
|
|
find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb,
|
|
|
|
|
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,
|
|
|
|
|
const char *device_name)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *dev;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long modifier_status(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *modifier_name)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_modifier *mod;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
seq = &verb->enable_list;
|
|
|
|
|
} else {
|
|
|
|
|
seq = &verb->disable_list;
|
|
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, seq,
|
|
|
|
|
&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;
|
|
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, seq,
|
|
|
|
|
&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;
|
|
|
|
|
|
2012-05-23 18:16:33 +05:30
|
|
|
if (device_status(uc_mgr, device->name) == enable)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
if (enable) {
|
|
|
|
|
seq = &device->enable_list;
|
|
|
|
|
} else {
|
|
|
|
|
seq = &device->disable_list;
|
|
|
|
|
}
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, seq,
|
|
|
|
|
&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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
|
|
|
|
* \brief Init sound card use case manager.
|
|
|
|
|
* \param uc_mgr Returned use case manager pointer
|
|
|
|
|
* \param card_name name of card to open
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
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);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->default_list);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->value_list);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->active_modifiers);
|
|
|
|
|
INIT_LIST_HEAD(&mgr->active_devices);
|
|
|
|
|
pthread_mutex_init(&mgr->mutex, NULL);
|
|
|
|
|
|
|
|
|
|
mgr->card_name = strdup(card_name);
|
|
|
|
|
if (mgr->card_name == NULL) {
|
|
|
|
|
free(mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
|
uc_error("error: failed to import %s use case configuration %d",
|
|
|
|
|
card_name, err);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-08 16:42:24 +02:00
|
|
|
*uc_mgr = mgr;
|
2010-09-07 15:35:14 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err:
|
2013-04-08 16:42:24 +02:00
|
|
|
uc_mgr_free(mgr);
|
2010-09-07 15:35:14 +02:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Reload and reparse all use case files.
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_mgr_reload(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
|
|
|
|
|
|
|
|
|
uc_mgr_free_verb(uc_mgr);
|
|
|
|
|
|
|
|
|
|
/* reload all use cases */
|
|
|
|
|
err = import_master_config(uc_mgr);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
uc_error("error: failed to reload use cases\n");
|
|
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&uc_mgr->mutex);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Close use case manager.
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_mgr_close(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &uc_mgr->default_list,
|
|
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
2010-09-07 15:35:14 +02:00
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Reset sound card controls to default values.
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \return zero on success, otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &uc_mgr->default_list,
|
|
|
|
|
&uc_mgr->value_list, NULL, NULL);
|
2010-09-22 15:35:11 +02:00
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_modifiers);
|
|
|
|
|
INIT_LIST_HEAD(&uc_mgr->active_devices);
|
|
|
|
|
uc_mgr->active_verb = NULL;
|
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
|
|
|
|
|
* \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_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02: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[],
|
|
|
|
|
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[],
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
|
|
|
|
if (modifier->dev_list.type != type)
|
|
|
|
|
return 0;
|
|
|
|
|
return get_list(&modifier->dev_list.list, list,
|
|
|
|
|
struct dev_list_node, list,
|
|
|
|
|
name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device = find_device(uc_mgr, verb, name, 0);
|
|
|
|
|
if (device) {
|
|
|
|
|
if (device->dev_list.type != type)
|
|
|
|
|
return 0;
|
|
|
|
|
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 {
|
|
|
|
|
struct list_head list;
|
|
|
|
|
char *value;
|
|
|
|
|
};
|
2013-04-08 16:42:24 +02:00
|
|
|
#endif
|
2010-09-22 14:31:15 +02:00
|
|
|
|
|
|
|
|
static int add_values(struct list_head *list,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *source)
|
|
|
|
|
{
|
|
|
|
|
struct ucm_value *v;
|
|
|
|
|
struct myvalue *val;
|
|
|
|
|
struct list_head *pos, *pos1;
|
|
|
|
|
int match;
|
|
|
|
|
|
|
|
|
|
list_for_each(pos, source) {
|
|
|
|
|
v = list_entry(pos, struct ucm_value, list);
|
|
|
|
|
if (check_identifier(identifier, v->name)) {
|
|
|
|
|
match = 0;
|
|
|
|
|
list_for_each(pos1, list) {
|
|
|
|
|
val = list_entry(pos1, struct myvalue, list);
|
|
|
|
|
if (strcmp(val->value, v->data) == 0) {
|
|
|
|
|
match = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!match) {
|
|
|
|
|
val = malloc(sizeof(struct myvalue));
|
|
|
|
|
if (val == NULL)
|
|
|
|
|
return -ENOMEM;
|
2011-03-19 00:54:43 +01:00
|
|
|
val->value = v->data;
|
2010-09-22 14:31:15 +02:00
|
|
|
list_add_tail(&val->list, list);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
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,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
const char **list[],
|
|
|
|
|
char *verbname)
|
|
|
|
|
{
|
|
|
|
|
struct list_head mylist, *pos, *npos;
|
|
|
|
|
struct myvalue *val;
|
|
|
|
|
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;
|
2010-09-22 14:31:15 +02: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;
|
|
|
|
|
}
|
|
|
|
|
err = alloc_str_list(&mylist, 1, &res);
|
|
|
|
|
if (err >= 0) {
|
2011-03-19 01:09:41 +01:00
|
|
|
*list = (const char **)res;
|
2010-09-22 14:31:15 +02:00
|
|
|
list_for_each(pos, &mylist) {
|
|
|
|
|
val = list_entry(pos, struct myvalue, list);
|
|
|
|
|
*res = strdup(val->value);
|
|
|
|
|
if (*res == NULL) {
|
|
|
|
|
snd_use_case_free_list((const char **)res, err);
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __fail;
|
|
|
|
|
}
|
|
|
|
|
res++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
__fail:
|
|
|
|
|
list_for_each_safe(pos, npos, &mylist) {
|
|
|
|
|
val = list_entry(pos, struct myvalue, list);
|
|
|
|
|
list_del(&val->list);
|
|
|
|
|
free(val);
|
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
|
const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02: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,
|
|
|
|
|
const char **list[])
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02: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
|
|
|
* \brief Obtain a list of entries
|
|
|
|
|
* \param uc_mgr Use case manager (may be NULL - card list)
|
|
|
|
|
* \param identifier (may be NULL - card list)
|
|
|
|
|
* \param list Returned allocated list
|
|
|
|
|
* \return Number of list entries if success, otherwise a negative error code
|
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);
|
2010-11-10 16:06:29 +01:00
|
|
|
else if (strcmp(identifier, "_enadevs") == 0)
|
2010-09-22 14:31:15 +02:00
|
|
|
err = get_enabled_device_list(uc_mgr, list);
|
2010-11-10 16:06:29 +01:00
|
|
|
else if (strcmp(identifier, "_enamods") == 0)
|
2010-09-22 14:31:15 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
if (check_identifier(identifier, "_devices"))
|
|
|
|
|
err = get_device_list(uc_mgr, list, str);
|
|
|
|
|
else if (check_identifier(identifier, "_modifiers"))
|
|
|
|
|
err = get_modifier_list(uc_mgr, list, str);
|
2011-06-03 14:56:33 -06:00
|
|
|
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;
|
2010-09-22 14:31:15 +02:00
|
|
|
else
|
|
|
|
|
err = get_value_list(uc_mgr, identifier, list, str);
|
|
|
|
|
if (str)
|
|
|
|
|
free(str);
|
|
|
|
|
}
|
|
|
|
|
__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
|
|
|
}
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
static int get_value1(const char **value, struct list_head *value_list,
|
|
|
|
|
const char *identifier)
|
2010-09-07 15:35:14 +02:00
|
|
|
{
|
2010-09-22 14:31:15 +02:00
|
|
|
struct ucm_value *val;
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
|
2010-12-21 23:11:56 +01:00
|
|
|
if (!value_list)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
2010-09-22 14:31:15 +02:00
|
|
|
list_for_each(pos, value_list) {
|
|
|
|
|
val = list_entry(pos, struct ucm_value, list);
|
|
|
|
|
if (check_identifier(identifier, val->name)) {
|
|
|
|
|
*value = strdup(val->data);
|
|
|
|
|
if (*value == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-29 15:41:34 +01:00
|
|
|
return -ENOENT;
|
2010-09-07 15:35:14 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-29 17:33:27 +01:00
|
|
|
static int get_value3(const char **value,
|
|
|
|
|
const char *identifier,
|
|
|
|
|
struct list_head *value_list1,
|
|
|
|
|
struct list_head *value_list2,
|
|
|
|
|
struct list_head *value_list3)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = get_value1(value, value_list1, identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
err = get_value1(value, value_list2, identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
err = get_value1(value, value_list3, identifier);
|
|
|
|
|
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,
|
|
|
|
|
const 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) {
|
|
|
|
|
err = get_value1(value,
|
|
|
|
|
&mod->value_list,
|
|
|
|
|
identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dev = find_device(uc_mgr, verb,
|
|
|
|
|
mod_dev_name, 0);
|
|
|
|
|
if (dev) {
|
|
|
|
|
err = get_value1(value,
|
|
|
|
|
&dev->value_list,
|
|
|
|
|
identifier);
|
|
|
|
|
if (err >= 0 || err != -ENOENT)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exact)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = get_value1(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
|
|
|
|
2010-11-29 15:41:34 +01:00
|
|
|
err = get_value1(value, &uc_mgr->value_list, identifier);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get current - string
|
2010-09-07 15:35:14 +02:00
|
|
|
* \param uc_mgr Use case manager
|
2010-09-22 14:31:15 +02:00
|
|
|
* \param identifier
|
|
|
|
|
* \param value Value pointer
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* Note: String is dynamically allocated, use free() to
|
|
|
|
|
* deallocate this string.
|
|
|
|
|
*/
|
|
|
|
|
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;
|
2010-09-22 14:31:15 +02: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) {
|
|
|
|
|
*value = strdup(uc_mgr->card_name);
|
|
|
|
|
if (*value == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
|
|
|
|
} else if (strcmp(identifier, "_verb") == 0) {
|
2011-06-02 16:44:39 -06:00
|
|
|
if (uc_mgr->active_verb == NULL) {
|
|
|
|
|
err = -ENOENT;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
2010-09-22 14:31:15 +02:00
|
|
|
*value = strdup(uc_mgr->active_verb->name);
|
|
|
|
|
if (*value == NULL) {
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
goto __end;
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
2011-06-03 14:56:32 -06:00
|
|
|
} else if (identifier[0] == '_') {
|
|
|
|
|
err = -ENOENT;
|
|
|
|
|
goto __end;
|
2010-09-22 14:31:15 +02: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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = get_value(uc_mgr, ident, value, mod_dev, verb, exact);
|
|
|
|
|
if (ident != identifier)
|
|
|
|
|
free((void *)ident);
|
|
|
|
|
if (mod_dev)
|
|
|
|
|
free((void *)mod_dev);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Get current - integer
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param identifier
|
|
|
|
|
* \return Value if success, otherwise a negative error code
|
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
|
|
|
{
|
2010-09-22 14:31:15 +02:00
|
|
|
char *str, *str1;
|
|
|
|
|
long err;
|
2010-09-07 15:35:14 +02:00
|
|
|
|
|
|
|
|
pthread_mutex_lock(&uc_mgr->mutex);
|
2010-09-22 14:31:15 +02: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;
|
|
|
|
|
}
|
2010-10-12 13:29:05 +02:00
|
|
|
if (check_identifier(identifier, "_devstatus")) {
|
2010-09-22 14:31:15 +02:00
|
|
|
err = device_status(uc_mgr, str);
|
2010-10-12 13:29:05 +02:00
|
|
|
if (err >= 0) {
|
|
|
|
|
*value = err;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
} else if (check_identifier(identifier, "_modstatus")) {
|
2010-09-22 14:31:15 +02:00
|
|
|
err = modifier_status(uc_mgr, str);
|
2010-10-12 13:29:05 +02:00
|
|
|
if (err >= 0) {
|
|
|
|
|
*value = err;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
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
|
|
|
|
|
*/
|
|
|
|
|
} else if (identifier[0] == '_')
|
|
|
|
|
err = -ENOENT;
|
|
|
|
|
#endif
|
2010-10-12 13:29:05 +02:00
|
|
|
} else
|
2011-06-03 14:56:32 -06:00
|
|
|
err = -ENOENT;
|
2010-09-22 14:31:15 +02:00
|
|
|
if (str)
|
|
|
|
|
free(str);
|
|
|
|
|
}
|
|
|
|
|
__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
|
|
|
}
|
|
|
|
|
|
2010-09-22 15:35:11 +02:00
|
|
|
static int handle_transition_verb(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
struct use_case_verb *new_verb)
|
|
|
|
|
{
|
|
|
|
|
struct list_head *pos;
|
|
|
|
|
struct transition_sequence *trans;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
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) {
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &trans->transition_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list,
|
|
|
|
|
NULL);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (err >= 0)
|
|
|
|
|
return 1;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int set_verb_user(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *verb_name)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_verb *verb;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int set_device_user(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *device_name,
|
|
|
|
|
int enable)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_device *device;
|
|
|
|
|
|
|
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
2011-06-03 14:56:33 -06:00
|
|
|
device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (device == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return set_device(uc_mgr, device, enable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int set_modifier_user(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
const char *modifier_name,
|
|
|
|
|
int enable)
|
|
|
|
|
{
|
|
|
|
|
struct use_case_modifier *modifier;
|
|
|
|
|
|
|
|
|
|
if (uc_mgr->active_verb == NULL)
|
|
|
|
|
return -ENOENT;
|
2010-12-21 23:11:58 +01:00
|
|
|
|
2011-06-03 14:56:33 -06:00
|
|
|
modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (modifier == NULL)
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
return set_modifier(uc_mgr, modifier, enable);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int switch_device(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
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;
|
|
|
|
|
}
|
2011-06-03 14:56:33 -06:00
|
|
|
xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (xold == NULL)
|
|
|
|
|
return -ENOENT;
|
2011-06-03 14:56:31 -06:00
|
|
|
list_del(&xold->active_list);
|
2011-06-03 14:56:33 -06:00
|
|
|
xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1);
|
2011-06-03 14:56:31 -06:00
|
|
|
list_add_tail(&xold->active_list, &uc_mgr->active_devices);
|
2011-01-11 18:56:30 +01:00
|
|
|
if (xnew == NULL)
|
2010-09-22 15:35:11 +02:00
|
|
|
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) {
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &trans->transition_list,
|
|
|
|
|
&xold->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2010-09-22 15:35:11 +02: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;
|
2011-01-11 18:56:30 +01:00
|
|
|
err = set_device(uc_mgr, xnew, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int switch_modifier(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
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;
|
|
|
|
|
}
|
2011-06-03 14:56:33 -06:00
|
|
|
xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (xold == NULL)
|
|
|
|
|
return -ENOENT;
|
2011-06-03 14:56:33 -06:00
|
|
|
xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1);
|
2010-12-21 23:11:57 +01:00
|
|
|
if (xnew == NULL)
|
2010-09-22 15:35:11 +02:00
|
|
|
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) {
|
2010-11-29 17:33:27 +01:00
|
|
|
err = execute_sequence(uc_mgr, &trans->transition_list,
|
|
|
|
|
&xold->value_list,
|
|
|
|
|
&uc_mgr->active_verb->value_list,
|
|
|
|
|
&uc_mgr->value_list);
|
2010-09-22 15:35:11 +02: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;
|
2011-01-11 18:56:30 +01:00
|
|
|
err = set_modifier(uc_mgr, xnew, 1);
|
2010-09-22 15:35:11 +02:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 15:35:14 +02:00
|
|
|
/**
|
2010-09-22 14:31:15 +02:00
|
|
|
* \brief Set new
|
|
|
|
|
* \param uc_mgr Use case manager
|
|
|
|
|
* \param identifier
|
|
|
|
|
* \param value Value
|
|
|
|
|
* \return Zero if success, otherwise a negative error code
|
2010-09-07 15:35:14 +02:00
|
|
|
*/
|
2010-09-22 14:31:15 +02:00
|
|
|
int snd_use_case_set(snd_use_case_mgr_t *uc_mgr,
|
|
|
|
|
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;
|
|
|
|
|
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 (strcmp(identifier, "_verb") == 0)
|
|
|
|
|
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 {
|
2013-05-24 14:01:25 +03:00
|
|
|
err = -EINVAL;
|
|
|
|
|
goto __end;
|
2010-09-22 14:31:15 +02:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
__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
|
|
|
}
|