Blob Blame History Raw
From ebdd2b6cdb8119cf75f0dd0a3b283d271b3a547e Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Sat, 30 Nov 2019 20:31:55 +0100
Subject: [PATCH 03/30] ucm: add _identifiers list

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
 include/use-case.h |   1 +
 src/ucm/main.c     | 268 +++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 208 insertions(+), 61 deletions(-)

diff --git a/include/use-case.h b/include/use-case.h
index 8e7e838c9cb9..85c58ac0614a 100644
--- a/include/use-case.h
+++ b/include/use-case.h
@@ -206,6 +206,7 @@ int snd_use_case_free_list(const char *list[], int items);
  *   - _enadevs			- get list of enabled devices
  *   - _enamods			- get list of enabled modifiers
  *
+ *   - _identifiers/{modifier}|{device}[/{verb}]     - list of value identifiers
  *   - _supporteddevs/{modifier}|{device}[/{verb}]   - list of supported devices
  *   - _conflictingdevs/{modifier}|{device}[/{verb}] - list of conflicting devices
  *
diff --git a/src/ucm/main.c b/src/ucm/main.c
index b80db65fa93b..d2078a2381d3 100644
--- a/src/ucm/main.c
+++ b/src/ucm/main.c
@@ -1072,7 +1072,6 @@ int snd_use_case_mgr_reset(snd_use_case_mgr_t *uc_mgr)
 /**
  * \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
  */
 static int get_verb_list(snd_use_case_mgr_t *uc_mgr, const char **list[])
@@ -1181,7 +1180,6 @@ static int get_supcon_device_list(snd_use_case_mgr_t *uc_mgr,
 	}
 
 	return -ENOENT;
-
 }
 
 /**
@@ -1210,41 +1208,201 @@ static int get_conflicting_device_list(snd_use_case_mgr_t *uc_mgr,
 
 #ifndef DOC_HIDDEN
 struct myvalue {
-        struct list_head list;
-        char *value;
+	struct list_head list;
+	const char *text;
 };
 #endif
 
+/**
+ * \brief Convert myvalue list string list
+ * \param list myvalue list
+ * \param res string list
+ * \retval Number of list entries if success, otherwise a negativer error code
+ */
+static int myvalue_to_str_list(struct list_head *list, char ***res)
+{
+	struct list_head *pos;
+	struct myvalue *value;
+	char **p;
+	int cnt;
+
+	cnt = alloc_str_list(list, 1, res);
+	if (cnt < 0)
+		return cnt;
+	p = *res;
+	list_for_each(pos, list) {
+		value = list_entry(pos, struct myvalue, list);
+		*p = strdup(value->text);
+		if (*p == NULL) {
+			snd_use_case_free_list((const char **)p, cnt);
+			return -ENOMEM;
+		}
+		p++;
+	}
+	return cnt;
+}
+
+/**
+ * \brief Free myvalue list
+ * \param list myvalue list
+ */
+static void myvalue_list_free(struct list_head *list)
+{
+	struct list_head *pos, *npos;
+	struct myvalue *value;
+
+	list_for_each_safe(pos, npos, list) {
+		value = list_entry(pos, struct myvalue, list);
+		list_del(&value->list);
+		free(value);
+	}
+}
+
+/**
+ * \brief Merge one value to the myvalue list
+ * \param list The list with values
+ * \param value The value to be merged (without duplicates)
+ * \return 1 if dup, 0 if success, otherwise a negative error code
+ */
+static int merge_value(struct list_head *list, const char *text)
+{
+	struct list_head *pos;
+	struct myvalue *value;
+
+	list_for_each(pos, list) {
+		value = list_entry(pos, struct myvalue, list);
+		if (strcmp(value->text, text) == 0)
+			return 1;
+	}
+	value = malloc(sizeof(*value));
+	if (value == NULL)
+		return -ENOMEM;
+	value->text = text;
+	list_add_tail(&value->list, list);
+	return 0;
+}
+
+/**
+ * \brief Find all values for given identifier
+ * \param list Returned list
+ * \param source Source list with ucm_value structures
+ * \return Zero if success, otherwise a negative error code
+ */
+static int add_identifiers(struct list_head *list,
+			   struct list_head *source)
+{
+	struct ucm_value *v;
+	struct list_head *pos;
+	int err;
+
+	list_for_each(pos, source) {
+		v = list_entry(pos, struct ucm_value, list);
+		err = merge_value(list, v->name);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/**
+ * \brief Find all values for given identifier
+ * \param list Returned list
+ * \param identifier Identifier
+ * \param source Source list with ucm_value structures
+ */
 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;
+	struct ucm_value *v;
+	struct list_head *pos;
+	int err;
         
-        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;
-				val->value = v->data;
-                                list_add_tail(&val->list, list);
-                        }
-                }
-        }
-        return 0;
+	list_for_each(pos, source) {
+		v = list_entry(pos, struct ucm_value, list);
+		if (check_identifier(identifier, v->name)) {
+			err = merge_value(list, v->data);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+/**
+ * \brief compare two identifiers
+ */
+static int identifier_cmp(const void *_a, const void *_b)
+{
+	const char * const *a = _a;
+	const char * const *b = _b;
+	return strcmp(*a, *b);
+}
+
+/**
+ * \brief Get list of available identifiers
+ * \param list Returned list
+ * \param name Name of verb or modifier to query
+ * \return Number of list entries if success, otherwise a negative error code
+ */
+static int get_identifiers_list(snd_use_case_mgr_t *uc_mgr,
+				const char **list[], char *name)
+{
+	struct use_case_verb *verb;
+	struct use_case_modifier *modifier;
+	struct use_case_device *device;
+	struct list_head mylist;
+	struct list_head *value_list;
+	char *str, **res;
+	int err;
+
+	if (!name)
+		return -ENOENT;
+
+	str = strchr(name, '/');
+	if (str) {
+		*str = '\0';
+		verb = find_verb(uc_mgr, str + 1);
+	}
+	else {
+		verb = uc_mgr->active_verb;
+	}
+	if (!verb)
+		return -ENOENT;
+
+	value_list = NULL;
+	modifier = find_modifier(uc_mgr, verb, name, 0);
+	if (modifier) {
+		value_list = &modifier->value_list;
+	} else {
+		device = find_device(uc_mgr, verb, name, 0);
+		if (device)
+			value_list = &device->value_list;
+	}
+	if (value_list == NULL)
+		return -ENOENT;
+
+	INIT_LIST_HEAD(&mylist);
+	err = add_identifiers(&mylist, &uc_mgr->value_list);
+	if (err < 0)
+		goto __fail;
+	err = add_identifiers(&mylist, &verb->value_list);
+	if (err < 0)
+		goto __fail;
+	err = add_identifiers(&mylist, value_list);
+	if (err < 0)
+		goto __fail;
+	err = myvalue_to_str_list(&mylist, &res);
+	if (err > 0)
+		*list = (const char **)res;
+	else if (err == 0)
+		*list = NULL;
+__fail:
+	myvalue_list_free(&mylist);
+	if (err <= 0)
+		return err;
+	qsort(*list, err, sizeof(char *), identifier_cmp);
+	return err;
 }
 
 /**
@@ -1258,8 +1416,7 @@ static int get_value_list(snd_use_case_mgr_t *uc_mgr,
                           const char **list[],
                           char *verbname)
 {
-        struct list_head mylist, *pos, *npos;
-        struct myvalue *val;
+	struct list_head mylist, *pos;
         struct use_case_verb *verb;
         struct use_case_device *dev;
         struct use_case_modifier *mod;
@@ -1292,26 +1449,13 @@ static int get_value_list(snd_use_case_mgr_t *uc_mgr,
                 if (err < 0)
                         goto __fail;
         }
-        err = alloc_str_list(&mylist, 1, &res);
-        if (err >= 0) {
+	err = myvalue_to_str_list(&mylist, &res);
+	if (err > 0)
 	        *list = (const char **)res;
-                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++;
-                }
-        }
+	else if (err == 0)
+		*list = NULL;
       __fail:
-        list_for_each_safe(pos, npos, &mylist) {
-                val = list_entry(pos, struct myvalue, list);
-                list_del(&val->list);
-                free(val);
-        }
+	myvalue_list_free(&mylist);
         return err;
 }
 
@@ -1381,21 +1525,23 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr,
                 } else {
                         str = NULL;
                 }
-        	if (check_identifier(identifier, "_devices"))
-          		err = get_device_list(uc_mgr, list, str);
+		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);
-                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);
+			err = get_modifier_list(uc_mgr, list, str);
+		else if (check_identifier(identifier, "_identifiers"))
+			err = get_identifiers_list(uc_mgr, list, str);
+		else if (check_identifier(identifier, "_supporteddevs"))
+			err = get_supported_device_list(uc_mgr, list, str);
+		else if (check_identifier(identifier, "_conflictingdevs"))
+			err = get_conflicting_device_list(uc_mgr, list, str);
 		else if (identifier[0] == '_')
 			err = -ENOENT;
-                else
-                        err = get_value_list(uc_mgr, identifier, list, str);
-        	if (str)
-        		free(str);
-        }
+		else
+			err = get_value_list(uc_mgr, identifier, list, str);
+		if (str)
+			free(str);
+	}
       __end:
 	pthread_mutex_unlock(&uc_mgr->mutex);
 	return err;
-- 
2.16.4