mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
ucm: add cset-new sequence command to create new controls from UCM
This command create a new control using ID, description and value.
Syntax:
cset-new "name='ABCD',index=2 type=boolean,count=2 on,on"
cset-new "name='Enum' type=enum,labels='L1;L2;L3' 'L2'"
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
parent
b0b9e4b88d
commit
02423dc970
4 changed files with 197 additions and 25 deletions
194
src/ucm/main.c
194
src/ucm/main.c
|
|
@ -254,13 +254,146 @@ static int binary_file_parse(snd_ctl_elem_value_t *dst,
|
|||
return err;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (v < min || v > max) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)
|
||||
{
|
||||
const char *pos;
|
||||
int err;
|
||||
snd_ctl_elem_id_t *id;
|
||||
snd_ctl_elem_value_t *value;
|
||||
snd_ctl_elem_info_t *info;
|
||||
snd_ctl_elem_info_t *info, *info2 = NULL;
|
||||
unsigned int *res = NULL;
|
||||
|
||||
snd_ctl_elem_id_malloc(&id);
|
||||
|
|
@ -272,14 +405,44 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)
|
|||
goto __fail;
|
||||
while (*pos && isspace(*pos))
|
||||
pos++;
|
||||
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++;
|
||||
}
|
||||
if (!*pos) {
|
||||
uc_error("undefined value for cset >%s<", cset);
|
||||
err = -EINVAL;
|
||||
goto __fail;
|
||||
}
|
||||
|
||||
snd_ctl_elem_info_set_id(info, id);
|
||||
err = snd_ctl_elem_info(ctl, info);
|
||||
if (err < 0)
|
||||
if (type == SEQUENCE_ELEMENT_TYPE_CSET_NEW) {
|
||||
if (err >= 0) {
|
||||
err = snd_ctl_elem_remove(ctl, id);
|
||||
if (err < 0) {
|
||||
uc_error("unable to remove control");
|
||||
err = -EINVAL;
|
||||
goto __fail;
|
||||
}
|
||||
}
|
||||
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)
|
||||
goto __fail;
|
||||
if (type == SEQUENCE_ELEMENT_TYPE_CSET_TLV) {
|
||||
if (!snd_ctl_elem_info_is_tlv_writable(info)) {
|
||||
|
|
@ -306,17 +469,27 @@ static int execute_cset(snd_ctl_t *ctl, const char *cset, unsigned int type)
|
|||
err = snd_ctl_elem_write(ctl, value);
|
||||
if (err < 0)
|
||||
goto __fail;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
__fail:
|
||||
if (id != NULL)
|
||||
free(id);
|
||||
if (value != NULL)
|
||||
free(value);
|
||||
if (info != NULL)
|
||||
free(info);
|
||||
if (res != NULL)
|
||||
free(res);
|
||||
free(id);
|
||||
free(value);
|
||||
if (info2) {
|
||||
if (info2->type == SND_CTL_ELEM_TYPE_ENUMERATED)
|
||||
free((void *)info->value.enumerated.names_ptr);
|
||||
free(info2);
|
||||
}
|
||||
free(info);
|
||||
free(res);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -417,6 +590,7 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
|
|||
case SEQUENCE_ELEMENT_TYPE_CSET:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
|
||||
if (cdev == NULL && uc_mgr->in_component_domain) {
|
||||
/* For sequence of a component device, use
|
||||
* its parent's cdev stored by ucm manager.
|
||||
|
|
|
|||
|
|
@ -720,9 +720,10 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
|||
|
||||
if (strcmp(cmd, "cset") == 0) {
|
||||
curr->type = SEQUENCE_ELEMENT_TYPE_CSET;
|
||||
cset:
|
||||
err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
|
||||
if (err < 0) {
|
||||
uc_error("error: cset requires a string!");
|
||||
uc_error("error: %s requires a string!", cmd);
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
|
|
@ -754,22 +755,17 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr,
|
|||
|
||||
if (strcmp(cmd, "cset-bin-file") == 0) {
|
||||
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE;
|
||||
err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
|
||||
if (err < 0) {
|
||||
uc_error("error: cset-bin-file requires a string!");
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
goto cset;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "cset-tlv") == 0) {
|
||||
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_TLV;
|
||||
err = parse_string_substitute3(uc_mgr, n, &curr->data.cset);
|
||||
if (err < 0) {
|
||||
uc_error("error: cset-tlv requires a string!");
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
goto cset;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "cset-new") == 0) {
|
||||
curr->type = SEQUENCE_ELEMENT_TYPE_CSET_NEW;
|
||||
goto cset;
|
||||
}
|
||||
|
||||
if (strcmp(cmd, "sysw") == 0) {
|
||||
|
|
|
|||
|
|
@ -51,8 +51,9 @@
|
|||
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
|
||||
#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5
|
||||
#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6
|
||||
#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 7
|
||||
#define SEQUENCE_ELEMENT_TYPE_SYSSET 8
|
||||
#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 7
|
||||
#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 8
|
||||
#define SEQUENCE_ELEMENT_TYPE_SYSSET 9
|
||||
|
||||
struct ucm_value {
|
||||
struct list_head list;
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@ void uc_mgr_free_sequence_element(struct sequence_element *seq)
|
|||
free(seq->data.cdev);
|
||||
break;
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
|
||||
case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
|
||||
free(seq->data.cset);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue