Blob Blame History Raw
From 251bc204a1e7f1bf1d12b452f2b62e15543bba94 Mon Sep 17 00:00:00 2001
From: Jaroslav Kysela <perex@perex.cz>
Date: Fri, 7 Feb 2020 10:09:07 +0100
Subject: [PATCH 68/74] ucm: implement RenameDevice and RemoveDevice verb
 management

With the conditionals, it may be useful to define the devices
in the included configuration files. To satisfy the specification
requirements (device naming) those device names might require
to be renamed or deleted wrong references from the conflicting
or supported lists.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
---
 src/ucm/parser.c    | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/ucm/ucm_local.h |  15 ++++++
 src/ucm/utils.c     | 123 ++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 254 insertions(+), 13 deletions(-)

diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index f576fde429cf..f9a8f6283c3a 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -985,6 +985,71 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr,
 	return 0;
 }
 
+/*
+ * Parse Device Rename/Delete Command
+ *
+ * # The devices might be renamed to allow the better conditional runtime
+ * # evaluation. Bellow example renames Speaker1 device to Speaker and
+ * # removes Speaker2 device.
+ * RenameDevice."Speaker1" "Speaker"
+ * RemoveDevice."Speaker2" "Speaker2"
+ */
+static int parse_dev_name_list(snd_config_t *cfg,
+			       struct list_head *list)
+{
+	snd_config_t *n;
+	snd_config_iterator_t i, next;
+	const char *id, *name1;
+	char *name2;
+	struct ucm_dev_name *dev;
+	snd_config_iterator_t pos;
+	int err;
+
+	if (snd_config_get_id(cfg, &id) < 0)
+		return -EINVAL;
+
+	if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) {
+		uc_error("compound type expected for %s", id);
+		return -EINVAL;
+	}
+
+	snd_config_for_each(i, next, cfg) {
+		n = snd_config_iterator_entry(i);
+
+		if (snd_config_get_id(n, &name1) < 0)
+			return -EINVAL;
+
+		err = parse_string(n, &name2);
+		if (err < 0) {
+			uc_error("error: failed to get target device name for '%s'", name1);
+			return err;
+		}
+
+		/* skip duplicates */
+		list_for_each(pos, list) {
+			dev = list_entry(pos, struct ucm_dev_name, list);
+			if (strcmp(dev->name1, name1) == 0) {
+				free(name2);
+				return 0;
+			}
+		}
+
+		dev = calloc(1, sizeof(*dev));
+		if (dev == NULL)
+			return -ENOMEM;
+		dev->name1 = strdup(name1);
+		if (dev->name1 == NULL) {
+			free(dev);
+			free(name2);
+			return -ENOMEM;
+		}
+		dev->name2 = name2;
+		list_add_tail(&dev->list, list);
+	}
+
+	return 0;
+}
+
 static int parse_compound_check_legacy(snd_use_case_mgr_t *uc_mgr,
 	  snd_config_t *cfg,
 	  int (*fcn)(snd_use_case_mgr_t *, snd_config_t *, void *, void *),
@@ -1044,7 +1109,39 @@ static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr,
 			     void *data1,
 			     void *data2 ATTRIBUTE_UNUSED)
 {
-	return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1);
+	return parse_compound(uc_mgr, cfg, parse_modifier, data1, data2);
+}
+
+static int verb_device_management(struct use_case_verb *verb)
+{
+	struct list_head *pos;
+	struct ucm_dev_name *dev;
+	int err;
+
+	/* rename devices */
+	list_for_each(pos, &verb->rename_list) {
+		dev = list_entry(pos, struct ucm_dev_name, list);
+		err = uc_mgr_rename_device(verb, dev->name1, dev->name2);
+		if (err < 0) {
+			uc_error("error: cannot rename device '%s' to '%s'", dev->name1, dev->name2);
+			return err;
+		}
+	}
+
+	/* remove devices */
+	list_for_each(pos, &verb->rename_list) {
+		dev = list_entry(pos, struct ucm_dev_name, list);
+		err = uc_mgr_remove_device(verb, dev->name2);
+		if (err < 0) {
+			uc_error("error: cannot remove device '%s'", dev->name2);
+			return err;
+		}
+	}
+
+	/* those lists are no longer used */
+	uc_mgr_free_dev_name_list(&verb->rename_list);
+	uc_mgr_free_dev_name_list(&verb->remove_list);
+	return 0;
 }
 
 /*
@@ -1180,6 +1277,8 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
 	INIT_LIST_HEAD(&verb->cmpt_device_list);
 	INIT_LIST_HEAD(&verb->modifier_list);
 	INIT_LIST_HEAD(&verb->value_list);
+	INIT_LIST_HEAD(&verb->rename_list);
+	INIT_LIST_HEAD(&verb->remove_list);
 	list_add_tail(&verb->list, &uc_mgr->verb_list);
 	if (use_case_name == NULL)
 		return -EINVAL;
@@ -1249,6 +1348,26 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
 			}
 			continue;
 		}
+
+		/* device renames */
+		if (strcmp(id, "RenameDevice") == 0) {
+			err = parse_dev_name_list(n, &verb->rename_list);
+			if (err < 0) {
+				uc_error("error: %s failed to parse device rename",
+						file);
+				goto _err;
+			}
+		}
+
+		/* device remove */
+		if (strcmp(id, "RemoveDevice") == 0) {
+			err = parse_dev_name_list(n, &verb->remove_list);
+			if (err < 0) {
+				uc_error("error: %s failed to parse device remove",
+						file);
+				goto _err;
+			}
+		}
 	}
 
 	snd_config_delete(cfg);
@@ -1258,6 +1377,14 @@ static int parse_verb_file(snd_use_case_mgr_t *uc_mgr,
 		uc_error("error: no use case device defined", file);
 		return -EINVAL;
 	}
+
+	/* do device rename and delete */
+	err = verb_device_management(verb);
+	if (err < 0) {
+		uc_error("error: device management error in verb '%s'", verb->name);
+		return err;
+	}
+
 	return 0;
 
        _err:
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index fa9fc16661bb..ba8d2acb3355 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -117,6 +117,12 @@ struct ctl_list {
 	snd_ctl_card_info_t *ctl_info;
 };
 
+struct ucm_dev_name {
+	struct list_head list;
+	char *name1;
+	char *name2;
+};
+
 /*
  * Describes a Use Case Modifier and it's enable and disable sequences.
  * A use case verb can have N modifiers.
@@ -196,6 +202,10 @@ struct use_case_verb {
 
 	/* value list */
 	struct list_head value_list;
+
+	/* temporary modifications lists */
+	struct list_head rename_list;
+	struct list_head remove_list;
 };
 
 /*
@@ -252,6 +262,11 @@ int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg);
 int uc_mgr_import_master_config(snd_use_case_mgr_t *uc_mgr);
 int uc_mgr_scan_master_configs(const char **_list[]);
 
+int uc_mgr_remove_device(struct use_case_verb *verb, const char *name);
+int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
+			 const char *dst);
+
+void uc_mgr_free_dev_name_list(struct list_head *base);
 void uc_mgr_free_sequence_element(struct sequence_element *seq);
 void uc_mgr_free_transition_element(struct transition_sequence *seq);
 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr);
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index daa568c16a30..60a591725835 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -328,6 +328,44 @@ void uc_mgr_free_dev_list(struct dev_list *dev_list)
 	}
 }
 
+int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
+			      const char *dst)
+{
+	struct list_head *pos;
+	struct dev_list_node *dlist;
+	char *dst1;
+
+	list_for_each(pos, &dev_list->list) {
+		dlist = list_entry(pos, struct dev_list_node, list);
+		if (strcmp(dlist->name, src) == 0) {
+			dst1 = strdup(dst);
+			if (dst1 == NULL)
+				return -ENOMEM;
+			free(dlist->name);
+			dlist->name = dst1;
+			return 0;
+		}
+	}
+	return -ENOENT;
+}
+
+int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
+{
+	struct list_head *pos;
+	struct dev_list_node *dlist;
+
+	list_for_each(pos, &dev_list->list) {
+		dlist = list_entry(pos, struct dev_list_node, list);
+		if (strcmp(dlist->name, name) == 0) {
+			free(dlist->name);
+			list_del(&dlist->list);
+			free(dlist);
+			return 0;
+		}
+	}
+	return -ENODEV;
+}
+
 void uc_mgr_free_sequence_element(struct sequence_element *seq)
 {
 	if (seq == NULL)
@@ -381,6 +419,20 @@ void uc_mgr_free_transition(struct list_head *base)
 	}
 }
 
+void uc_mgr_free_dev_name_list(struct list_head *base)
+{
+	struct list_head *pos, *npos;
+	struct ucm_dev_name *dev;
+
+	list_for_each_safe(pos, npos, base) {
+		dev = list_entry(pos, struct ucm_dev_name, list);
+		list_del(&dev->list);
+		free(dev->name1);
+		free(dev->name2);
+		free(dev);
+	}
+}
+
 void uc_mgr_free_modifier(struct list_head *base)
 {
 	struct list_head *pos, *npos;
@@ -400,23 +452,68 @@ void uc_mgr_free_modifier(struct list_head *base)
 	}
 }
 
-void uc_mgr_free_device(struct list_head *base)
+void uc_mgr_free_device(struct use_case_device *dev)
+{
+	free(dev->name);
+	free(dev->comment);
+	uc_mgr_free_sequence(&dev->enable_list);
+	uc_mgr_free_sequence(&dev->disable_list);
+	uc_mgr_free_transition(&dev->transition_list);
+	uc_mgr_free_dev_list(&dev->dev_list);
+	uc_mgr_free_value(&dev->value_list);
+	list_del(&dev->list);
+	free(dev);
+}
+
+void uc_mgr_free_device_list(struct list_head *base)
 {
 	struct list_head *pos, *npos;
 	struct use_case_device *dev;
 	
 	list_for_each_safe(pos, npos, base) {
 		dev = list_entry(pos, struct use_case_device, list);
-		free(dev->name);
-		free(dev->comment);
-		uc_mgr_free_sequence(&dev->enable_list);
-		uc_mgr_free_sequence(&dev->disable_list);
-		uc_mgr_free_transition(&dev->transition_list);
-		uc_mgr_free_dev_list(&dev->dev_list);
-		uc_mgr_free_value(&dev->value_list);
-		list_del(&dev->list);
-		free(dev);
+		uc_mgr_free_device(dev);
+	}
+}
+
+int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
+			 const char *dst)
+{
+	struct use_case_device *device;
+	struct list_head *pos, *npos;
+	char *dst1;
+
+	/* no errors when device is not found */
+	list_for_each_safe(pos, npos, &verb->device_list) {
+		device = list_entry(pos, struct use_case_device, list);
+		if (strcmp(device->name, src) == 0) {
+			dst1 = strdup(dst);
+			if (dst1 == NULL)
+				return -ENOMEM;
+			free(device->name);
+			device->name = dst1;
+			continue;
+		}
+		uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
+	}
+	return 0;
+}
+
+int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
+{
+	struct use_case_device *device;
+	struct list_head *pos, *npos;
+
+	list_for_each_safe(pos, npos, &verb->device_list) {
+		device = list_entry(pos, struct use_case_device, list);
+		if (strcmp(device->name, name) == 0) {
+			uc_mgr_free_device(device);
+			continue;
+		}
+		uc_mgr_remove_from_dev_list(&device->dev_list, name);
+		return 0;
 	}
+	return -ENOENT;
 }
 
 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
@@ -432,9 +529,11 @@ void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
 		uc_mgr_free_sequence(&verb->disable_list);
 		uc_mgr_free_transition(&verb->transition_list);
 		uc_mgr_free_value(&verb->value_list);
-		uc_mgr_free_device(&verb->device_list);
-		uc_mgr_free_device(&verb->cmpt_device_list);
+		uc_mgr_free_device_list(&verb->device_list);
+		uc_mgr_free_device_list(&verb->cmpt_device_list);
 		uc_mgr_free_modifier(&verb->modifier_list);
+		uc_mgr_free_dev_name_list(&verb->rename_list);
+		uc_mgr_free_dev_name_list(&verb->remove_list);
 		list_del(&verb->list);
 		free(verb);
 	}
-- 
2.16.4