From 02423dc970c00587b62e0d8d3ace7a24b29f1ec1 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 25 Mar 2021 20:36:54 +0100 Subject: [PATCH] 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 --- src/ucm/main.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/ucm/parser.c | 22 +++--- src/ucm/ucm_local.h | 5 +- src/ucm/utils.c | 1 + 4 files changed, 197 insertions(+), 25 deletions(-) diff --git a/src/ucm/main.c b/src/ucm/main.c index d1b0ebbd..02c5580e 100644 --- a/src/ucm/main.c +++ b/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. diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 0f79a927..a060a3d9 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -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) { diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 9180a242..5e6c988f 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -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; diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 20a870d5..b44a6f48 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -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); -- 2.11.0