From 07532ae8a8ae0f6b911c6a8db90bc2f8b3cc606c Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 18 Nov 2025 10:41:06 +0100 Subject: [PATCH] ucm: normalize device names Signed-off-by: Jaroslav Kysela --- src/ucm/parser.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 7cd3cbff..cb0f51a2 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -34,6 +34,7 @@ #include #include #include +#include #include static int filename_filter(const struct dirent64 *dirent); @@ -227,6 +228,66 @@ static int parse_get_safe_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *n, return 0; } +/* + * Parse device index from device name + * According to use-case.h specification, device names can have numeric suffixes + * like HDMI1, HDMI2, or "Line 1", "Line 2". + * This function extracts the index and modifies the name to contain only the base. + */ +static int parse_device_index(char **name, int *index) +{ + char *p, *num_start; + long idx; + size_t len; + int err; + + if (!name || !*name || !index) + return -EINVAL; + + len = strlen(*name); + if (len == 0) + return -EINVAL; + + /* Start from the end and find where digits begin */ + p = *name + len - 1; + + /* Skip trailing digits */ + while (p > *name && isdigit(*p)) + p--; + + /* If no digits found at the end, index is 0 (no index) */ + if (p == *name + len - 1) { + *index = 0; + return 0; + } + + /* Move to first digit */ + p++; + + /* Check if there's an optional space before the number */ + if (p > *name && *(p - 1) == ' ') + p--; + + /* Parse the index */ + num_start = *p == ' ' ? p + 1 : p; + err = safe_strtol(num_start, &idx); + if (err < 0) { + /* No valid number found */ + *index = 0; + return 0; + } + + *index = (int)idx; + + /* Truncate the name to remove the index part */ + if (*p == ' ') + *p = '\0'; /* Remove space and number */ + else if (p > *name) + *p = '\0'; /* Remove number only */ + + return 0; +} + /* * Handle 'Error' configuration node. */ @@ -1814,6 +1875,96 @@ static int verb_dev_list_check(struct use_case_verb *verb) return 0; } +/* + * Normalize device names according to use-case.h specification. + * Device names like "HDMI 1" or "Line 1" should be normalized to "HDMI1" and "Line1". + * Also updates dev_list members in modifiers and devices to reference the normalized names. + */ +static int verb_normalize_device_names(struct use_case_verb *verb) +{ + struct list_head *pos, *pos2, *pos3; + struct use_case_device *device, *device2; + struct use_case_modifier *modifier; + struct dev_list_node *dlist; + char *orig_name, *norm_name; + char temp[80]; + int err, index; + + list_for_each(pos, &verb->device_list) { + device = list_entry(pos, struct use_case_device, list); + + orig_name = strdup(device->name); + if (orig_name == NULL) + return -ENOMEM; + + norm_name = strdup(device->name); + if (norm_name == NULL) { + err = -ENOMEM; + goto __error; + } + + err = parse_device_index(&norm_name, &index); + if (err < 0) { + snd_error(UCM, "cannot parse device name '%s'", device->name); + goto __error; + } + + if (index <= 0) { + free(orig_name); + free(norm_name); + continue; + } + + snprintf(temp, sizeof(temp), "%s%d", norm_name, index); + free(device->name); + device->name = strdup(temp); + if (device->name == NULL) { + err = -ENOMEM; + goto __error; + } + + list_for_each(pos2, &verb->modifier_list) { + modifier = list_entry(pos2, struct use_case_modifier, list); + list_for_each(pos3, &modifier->dev_list.list) { + dlist = list_entry(pos3, struct dev_list_node, list); + if (strcmp(dlist->name, orig_name) == 0) { + free(dlist->name); + dlist->name = strdup(device->name); + if (dlist->name == NULL) { + err = -ENOMEM; + goto __error; + } + } + } + } + + list_for_each(pos2, &verb->device_list) { + device2 = list_entry(pos2, struct use_case_device, list); + list_for_each(pos3, &device2->dev_list.list) { + dlist = list_entry(pos3, struct dev_list_node, list); + if (strcmp(dlist->name, orig_name) == 0) { + free(dlist->name); + dlist->name = strdup(device->name); + if (dlist->name == NULL) { + err = -ENOMEM; + goto __error; + } + } + } + } + + free(orig_name); + free(norm_name); + } + + return 0; + +__error: + free(orig_name); + free(norm_name); + return err; +} + static int verb_device_management(struct use_case_verb *verb) { struct list_head *pos; @@ -1844,6 +1995,11 @@ static int verb_device_management(struct use_case_verb *verb) uc_mgr_free_dev_name_list(&verb->rename_list); uc_mgr_free_dev_name_list(&verb->remove_list); + /* normalize device names to remove spaces per use-case.h specification */ + err = verb_normalize_device_names(verb); + if (err < 0) + return err; + /* handle conflicting/supported lists */ return verb_dev_list_check(verb); } -- 2.47.3