]> git.alsa-project.org Git - alsa-lib.git/commitdiff
ucm: sort devices by priority
authorJaroslav Kysela <perex@perex.cz>
Tue, 18 Nov 2025 13:23:18 +0000 (14:23 +0100)
committerJaroslav Kysela <perex@perex.cz>
Mon, 1 Dec 2025 13:28:43 +0000 (14:28 +0100)
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/ucm/parser.c
src/ucm/ucm_confdoc.h
src/ucm/ucm_local.h

index 5205dff78ce8341b45d4fbb4074413c22337c930..0cb2f76e30d784b0fcfbd4732d7dda4c6af4455c 100644 (file)
@@ -2098,6 +2098,120 @@ static int verb_strip_single_device_index(struct use_case_verb *verb)
        return 0;
 }
 
+/*
+ * Determine priority for a device.
+ * Priority order:
+ *   1. If 'Priority' value exists, use it as the sort key
+ *   2. If 'PlaybackPriority' value exists, use it as the sort key
+ *   3. If 'CapturePriority' value exists, use it as the sort key
+ *   4. Fallback: LONG_MIN (no priority)
+ */
+static long verb_device_get_priority(struct use_case_device *device)
+{
+       struct list_head *pos;
+       struct ucm_value *val;
+       const char *priority_str = NULL;
+       long priority = LONG_MIN;
+       int err;
+
+       list_for_each(pos, &device->value_list) {
+               val = list_entry(pos, struct ucm_value, list);
+               if (strcmp(val->name, "Priority") == 0) {
+                       priority_str = val->data;
+                       break;
+               }
+       }
+
+       if (!priority_str) {
+               list_for_each(pos, &device->value_list) {
+                       val = list_entry(pos, struct ucm_value, list);
+                       if (strcmp(val->name, "PlaybackPriority") == 0) {
+                               priority_str = val->data;
+                               break;
+                       }
+               }
+       }
+
+       if (!priority_str) {
+               list_for_each(pos, &device->value_list) {
+                       val = list_entry(pos, struct ucm_value, list);
+                       if (strcmp(val->name, "CapturePriority") == 0) {
+                               priority_str = val->data;
+                               break;
+                       }
+               }
+       }
+
+       if (priority_str) {
+               err = safe_strtol(priority_str, &priority);
+               if (err < 0)
+                       priority = LONG_MIN;
+       }
+
+       return priority;
+}
+
+/*
+ * Sort devices based on priority values.
+ * Priority order:
+ *   1. If 'Priority' value exists, use it as the sort key
+ *   2. If 'PlaybackPriority' value exists, use it as the sort key
+ *   3. If 'CapturePriority' value exists, use it as the sort key
+ *   4. Fallback: use device->name (original) as the sort key
+ * Higher priority values are placed first in the list.
+ */
+static int verb_sort_devices(struct use_case_verb *verb)
+{
+       struct list_head sorted_list;
+       struct list_head *pos, *npos;
+       struct use_case_device *device, *insert_dev;
+
+       INIT_LIST_HEAD(&sorted_list);
+
+       /* First pass: determine and cache priority for all devices */
+       list_for_each(pos, &verb->device_list) {
+               device = list_entry(pos, struct use_case_device, list);
+               device->sort_priority = verb_device_get_priority(device);
+       }
+
+       /* Move devices from verb->device_list to sorted_list in sorted order */
+       list_for_each_safe(pos, npos, &verb->device_list) {
+               device = list_entry(pos, struct use_case_device, list);
+
+               /* Remove device from original list */
+               list_del(&device->list);
+
+               /* Find the insertion point in sorted_list */
+               /* Devices are sorted in descending order of priority (higher priority first) */
+               /* If priorities are equal or not defined, use device name as key */
+               if (list_empty(&sorted_list)) {
+                       list_add_tail(&device->list, &sorted_list);
+               } else {
+                       struct list_head *pos2, *insert_pos = &sorted_list;
+                       list_for_each(pos2, &sorted_list) {
+                               insert_dev = list_entry(pos2, struct use_case_device, list);
+
+                               if (device->sort_priority > insert_dev->sort_priority) {
+                                       insert_pos = pos2;
+                                       break;
+                               } else if (device->sort_priority == insert_dev->sort_priority) {
+                                       if (strcmp(device->name, insert_dev->name) < 0) {
+                                               insert_pos = pos2;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       list_add_tail(&device->list, insert_pos);
+               }
+       }
+
+       /* Move sorted list back to verb->device_list */
+       list_splice_init(&sorted_list, &verb->device_list);
+
+       return 0;
+}
+
 static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb)
 {
        struct list_head *pos;
@@ -2128,6 +2242,14 @@ static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_ve
        uc_mgr_free_dev_name_list(&verb->rename_list);
        uc_mgr_free_dev_name_list(&verb->remove_list);
 
+       /* strip index from single device names */
+       if (uc_mgr->conf_format >= 8) {
+               /* sort devices by priority */
+               err = verb_sort_devices(verb);
+               if (err < 0)
+                       return err;
+       }
+
        /* normalize device names to remove spaces per use-case.h specification */
        err = verb_normalize_device_names(uc_mgr, verb);
        if (err < 0)
@@ -2138,6 +2260,7 @@ static int verb_device_management(snd_use_case_mgr_t *uc_mgr, struct use_case_ve
                err = verb_strip_single_device_index(verb);
                if (err < 0)
                        return err;
+
        }
 
        /* handle conflicting/supported lists */
index 34269d7b859d96c4d3e7a8a27e667bf3138f7573..48dc45a9dea8915be2dd53eb992e98e563d11669 100644 (file)
@@ -395,6 +395,61 @@ in the laptop instead the microphone in headphones.
 
 The preference of the devices is determined by the priority value (higher value = higher priority).
 
+#### Device ordering (Syntax 8+)
+
+Starting with **Syntax 8**, devices are automatically sorted based on their priority values.
+The sorting is performed at the end of device management processing, after device renaming
+and index assignment.
+
+The priority key selection order is:
+1. **Priority** - If this value exists, use it as the sorting key
+2. **PlaybackPriority** - If Priority doesn't exist but PlaybackPriority exists, use it
+3. **CapturePriority** - If neither Priority nor PlaybackPriority exist, use CapturePriority
+4. **Fallback** - If no priority value is defined, use the device name for alphabetical sorting
+
+Devices are sorted in **descending order** of priority (higher priority values appear first
+in the device list). When two devices have the same priority value, they are sorted
+alphabetically by device name.
+
+Example - Device priority ordering:
+
+~~~{.html}
+SectionDevice."Speaker" {
+  Comment "Internal speaker"
+  EnableSequence [
+    cset "name='Speaker Switch' on"
+  ]
+  Value {
+    PlaybackPriority 100
+    PlaybackPCM "hw:${CardId},0"
+  }
+}
+
+SectionDevice."Headphones" {
+  Comment "Headphone jack"
+  EnableSequence [
+    cset "name='Headphone Switch' on"
+  ]
+  Value {
+    PlaybackPriority 200
+    PlaybackPCM "hw:${CardId},1"
+  }
+}
+
+SectionDevice."HDMI" {
+  Comment "HDMI output"
+  EnableSequence [
+    cset "name='HDMI Switch' on"
+  ]
+  Value {
+    PlaybackPriority 150
+    PlaybackPCM "hw:${CardId},3"
+  }
+}
+~~~
+
+In this example, the device list will be ordered as: Headphones (200), HDMI (150), Speaker (100).
+
 See the SND_USE_CASE_MOD constants like #SND_USE_CASE_MOD_ECHO_REF for the full list of known modifiers.
 
 ### Boot (alsactl)
index bf7bc3920591447a51fa3f7ab59d6b2928222098..e4fddb40dce3165b86115da7dcd5dfab13db5ed1 100644 (file)
@@ -179,6 +179,9 @@ struct use_case_device {
 
        /* value list */
        struct list_head value_list;
+
+       /* cached priority for sorting (LONG_MIN if not determined) */
+       long sort_priority;
 };
 
 /*