OSDN Git Service

ucm: add cset-new sequence command to create new controls from UCM
authorJaroslav Kysela <perex@perex.cz>
Thu, 25 Mar 2021 19:36:54 +0000 (20:36 +0100)
committerJaroslav Kysela <perex@perex.cz>
Mon, 29 Mar 2021 09:00:25 +0000 (11:00 +0200)
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>
src/ucm/main.c
src/ucm/parser.c
src/ucm/ucm_local.h
src/ucm/utils.c

index d1b0ebb..02c5580 100644 (file)
@@ -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.
index 0f79a92..a060a3d 100644 (file)
@@ -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) {
index 9180a24..5e6c988 100644 (file)
@@ -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;
index 20a870d..b44a6f4 100644 (file)
@@ -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);