]> git.alsa-project.org Git - alsa-lib.git/commitdiff
ucm: complete dependency graphs for conflicting/supported device lists
authorJaroslav Kysela <perex@perex.cz>
Mon, 1 Dec 2025 14:56:56 +0000 (15:56 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 4 Dec 2025 11:11:41 +0000 (12:11 +0100)
Modify verb_dev_list_check() to ensure all devices in a conflicting or
supported group reference each other. Previously, the function only
ensured bidirectional relationships. Now it ensures all devices in
the same group have complete dependency lists.

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/ucm/parser.c
src/ucm/utils.c

index 7ab7c99e1ae90b68da696c1d802eddd5c57eb1fa..9787c84b76b6ab75d9f4a9aca7225bb31235c77f 100644 (file)
@@ -2043,11 +2043,14 @@ static int verb_dev_list_add(struct use_case_verb *verb,
 
 static int verb_dev_list_check(struct use_case_verb *verb)
 {
-       struct list_head *pos, *pos2;
-       struct use_case_device *device;
-       struct dev_list_node *dlist;
-       int err;
-
+       struct list_head *pos, *pos2, *pos3;
+       struct use_case_device *device, *target_dev;
+       struct dev_list_node *dlist, *dlist2;
+       int err, iteration;
+       int max_iterations = 100; /* safety limit to prevent infinite loops */
+       bool added_something;
+
+       /* First pass: ensure bidirectional relationships */
        list_for_each(pos, &verb->device_list) {
                device = list_entry(pos, struct use_case_device, list);
                list_for_each(pos2, &device->dev_list.list) {
@@ -2058,6 +2061,62 @@ static int verb_dev_list_check(struct use_case_verb *verb)
                                return err;
                }
        }
+
+       /* Second pass: complete other relationships for devices in group.
+        * For n devices, at most n-1 iterations are needed.
+        */
+       for (iteration = 0; iteration < max_iterations; iteration++) {
+               added_something = false;
+
+               list_for_each(pos, &verb->device_list) {
+                       device = list_entry(pos, struct use_case_device, list);
+
+                       if (device->dev_list.type == DEVLIST_NONE)
+                               continue;
+
+                       list_for_each(pos2, &device->dev_list.list) {
+                               dlist = list_entry(pos2, struct dev_list_node, list);
+
+                               target_dev = NULL;
+                               list_for_each(pos3, &verb->device_list) {
+                                       struct use_case_device *tmp_dev;
+                                       tmp_dev = list_entry(pos3, struct use_case_device, list);
+                                       if (strcmp(tmp_dev->name, dlist->name) == 0) {
+                                               target_dev = tmp_dev;
+                                               break;
+                                       }
+                               }
+
+                               if (!target_dev)
+                                       continue;
+
+                               list_for_each(pos3, &device->dev_list.list) {
+                                       dlist2 = list_entry(pos3, struct dev_list_node, list);
+
+                                       if (strcmp(dlist2->name, target_dev->name) == 0)
+                                               continue;
+
+                                       /* verb_dev_list_add returns 1 if device was added, 0 if already exists */
+                                       err = verb_dev_list_add(verb, device->dev_list.type,
+                                                               target_dev->name, dlist2->name);
+                                       if (err < 0)
+                                               return err;
+                                       if (err > 0)
+                                               added_something = true;
+                               }
+                       }
+               }
+
+               /* If nothing was added in this iteration, we're done */
+               if (!added_something)
+                       break;
+       }
+
+       if (iteration >= max_iterations) {
+               snd_error(UCM, "too many device list iterations for verb '%s'", verb->name);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
index 2d15a035173d0981b3b84dd18a35be6d97b5e7bf..712ef6136f07d3b8823a2b40233de959fee70fef 100644 (file)
@@ -448,7 +448,7 @@ int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
        list_for_each(pos, &dev_list->list) {
                dlist = list_entry(pos, struct dev_list_node, list);
                if (strcmp(dlist->name, name) == 0)
-                       return 0;
+                       return 0; /* already exists, no change */
        }
 
        dlist = calloc(1, sizeof(*dlist));
@@ -461,7 +461,7 @@ int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
        }
        dlist->name = n;
        list_add(&dlist->list, &dev_list->list);
-       return 0;
+       return 1; /* new device added */
 }
 
 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,