[alsa-devel] [PATCH 1/5] UCM: Deprecate index on Section{Device, Modifier}
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 { SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" { SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an index is present, but not exactly "0", parsing will appear to succeed, but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used, any index is not included in the name; i.e. both sets of syntax above name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0", but internally strips ".0" from the tail of any device name. Any other name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string compare is now used, since no index data is ever present in device or modifier names.
The one functional change introduced here is that a SupportedDevice entry of just "x" will now only ever match a single device. It previously acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren swarren@nvidia.com --- src/ucm/main.c | 43 ++++----------- src/ucm/parser.c | 156 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 114 insertions(+), 85 deletions(-)
diff --git a/src/ucm/main.c b/src/ucm/main.c index fb4aa91..5acb5a8 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -484,33 +484,18 @@ static int is_modifier_supported(snd_use_case_mgr_t *uc_mgr, struct dev_list *device; struct use_case_device *adev; struct list_head *pos, *pos1; - char *cpos; - int dlen, len;
list_for_each(pos, &modifier->dev_list) { device = list_entry(pos, struct dev_list, list); - cpos = strchr(device->name, '.'); - if (cpos) { - if (find(&uc_mgr->active_devices, - struct use_case_device, active_list, - name, device->name)) - return 1; - } else { - dlen = strlen(device->name); - list_for_each(pos1, &uc_mgr->active_devices) { - adev = list_entry(pos1, struct use_case_device, - active_list); - cpos = strchr(adev->name, '.'); - if (cpos) - len = cpos - adev->name; - else - len = strlen(adev->name); - if (len != dlen) - continue; - if (memcmp(adev->name, device->name, len)) - continue; - return 1; - } + + list_for_each(pos1, &uc_mgr->active_devices) { + adev = list_entry(pos1, struct use_case_device, + active_list); + + if (strcmp(adev->name, device->name)) + continue; + + return 1; } } return 0; @@ -528,19 +513,11 @@ static struct use_case_modifier * struct use_case_modifier *modifier; struct use_case_verb *verb = uc_mgr->active_verb; struct list_head *pos; - char name[64], *cpos;
list_for_each(pos, &verb->modifier_list) { modifier = list_entry(pos, struct use_case_modifier, list);
- strncpy(name, modifier->name, sizeof(name)); - name[sizeof(name)-1] = '\0'; - cpos = strchr(name, '.'); - if (!cpos) - continue; - *cpos= '\0'; - - if (strcmp(name, modifier_name)) + if (strcmp(modifier->name, modifier_name)) continue;
if (is_modifier_supported(uc_mgr, modifier)) diff --git a/src/ucm/parser.c b/src/ucm/parser.c index f7de9bd..b0bebe0 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -59,7 +59,7 @@ int parse_string(snd_config_t *n, char **res) /* * Parse safe ID */ -int parse_is_name_safe(char *name) +int parse_is_name_safe(const char *name) { if (strchr(name, '.')) { uc_error("char '.' not allowed in '%s'", name); @@ -75,7 +75,9 @@ int parse_get_safe_id(snd_config_t *n, const char **id) err = snd_config_get_id(n, id); if (err < 0) return err; - return parse_is_name_safe((char *)(*id)); + if (!parse_is_name_safe((char *)(*id))) + return -EINVAL; + return 0; }
/* @@ -151,7 +153,7 @@ static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, n = snd_config_iterator_entry(i);
if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { - uc_error("compound type expected for %s", id); + uc_error("compound type expected for %s, is %d", id, snd_config_get_type(cfg)); return -EINVAL; } @@ -163,6 +165,19 @@ static int parse_compound(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, return 0; }
+static int strip_legacy_dev_index(char *name) +{ + char *dot = strchr(name, '.'); + if (!dot) + return 0; + if (dot[1] != '0' || dot[2] != '\0') { + uc_error("device name %s contains a '.'," + " and is not legacy foo.0 format", name); + return -EINVAL; + } + *dot = '\0'; + return 0; +}
/* * Parse transition @@ -199,6 +214,12 @@ static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, free(sdev); return err; } + err = strip_legacy_dev_index(sdev->name); + if (err < 0) { + free(sdev->name); + free(sdev); + return err; + } list_add(&sdev->list, dlist); } return 0; @@ -389,12 +410,12 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, * Parse Modifier Use cases * * # Each modifier is described in new section. N modifiers are allowed - * SectionModifier."Capture Voice".0 { + * SectionModifier."Capture Voice" { * * Comment "Record voice call" * SupportedDevice [ - * "x" # all x device instances - * "y.0" # device y instance 0 only + * "x" + * "y" * ] * * EnableSequence [ @@ -426,14 +447,21 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, { struct use_case_verb *verb = data1; struct use_case_modifier *modifier; - char *name = data2; - const char *id; + const char *name; snd_config_iterator_t i, next; snd_config_t *n; int err;
- if (!parse_is_name_safe(name)) - return -EINVAL; + if (data2) { + name = data2; + if (!parse_is_name_safe(name)) + return -EINVAL; + } + else { + if (parse_get_safe_id(cfg, &name) < 0) + return -EINVAL; + } + /* allocate modifier */ modifier = calloc(1, sizeof(*modifier)); if (modifier == NULL) @@ -444,15 +472,7 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&modifier->dev_list); INIT_LIST_HEAD(&modifier->value_list); list_add_tail(&modifier->list, &verb->modifier_list); - err = parse_get_safe_id(cfg, &id); - if (err < 0) - return err; - modifier->name = malloc(strlen(name) + strlen(id) + 2); - if (modifier->name == NULL) - return -ENOMEM; - strcpy(modifier->name, name); - strcat(modifier->name, "."); - strcat(modifier->name, id); + modifier->name = strdup(name);
snd_config_for_each(i, next, cfg) { const char *id; @@ -530,7 +550,7 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, * Parse Device Use Cases * *# Each device is described in new section. N devices are allowed - *SectionDevice."Headphones".0 { + *SectionDevice."Headphones" { * Comment "Headphones connected to 3.5mm jack" * * EnableSequence [ @@ -551,21 +571,28 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, * } * } */ -static int parse_device_index(snd_use_case_mgr_t *uc_mgr, - snd_config_t *cfg, - void *data1, - void *data2) +static int parse_device(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, + void *data1, + void *data2) { struct use_case_verb *verb = data1; - char *name = data2; + const char *name; struct use_case_device *device; - const char *id; snd_config_iterator_t i, next; snd_config_t *n; int err; - - if (!parse_is_name_safe(name)) - return -EINVAL; + + if (data2) { + name = data2; + if (!parse_is_name_safe(name)) + return -EINVAL; + } + else { + if (parse_get_safe_id(cfg, &name) < 0) + return -EINVAL; + } + device = calloc(1, sizeof(*device)); if (device == NULL) return -ENOMEM; @@ -574,14 +601,7 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&device->transition_list); INIT_LIST_HEAD(&device->value_list); list_add_tail(&device->list, &verb->device_list); - if (parse_get_safe_id(cfg, &id) < 0) - return -EINVAL; - device->name = malloc(strlen(name) + strlen(id) + 2); - if (device->name == NULL) - return -ENOMEM; - strcpy(device->name, name); - strcat(device->name, "."); - strcat(device->name, id); + device->name = strdup(name);
snd_config_for_each(i, next, cfg) { const char *id; @@ -643,34 +663,66 @@ static int parse_device_index(snd_use_case_mgr_t *uc_mgr, return 0; }
-static int parse_device_name(snd_use_case_mgr_t *uc_mgr, - snd_config_t *cfg, - void *data1, - void *data2 ATTRIBUTE_UNUSED) +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 *), + void *data1) { - const char *id; + const char *id, *idchild; + int child_ctr = 0, legacy_format = 1; + snd_config_iterator_t i, next; + snd_config_t *child; int err;
err = snd_config_get_id(cfg, &id); if (err < 0) return err; - return parse_compound(uc_mgr, cfg, parse_device_index, - data1, (void *)id); + + snd_config_for_each(i, next, cfg) { + child_ctr++; + if (child_ctr > 1) { + break; + } + + child = snd_config_iterator_entry(i); + + if (snd_config_get_type(cfg) != SND_CONFIG_TYPE_COMPOUND) { + legacy_format = 0; + break; + } + + if (snd_config_get_id(child, &idchild) < 0) + return -EINVAL; + + if (strcmp(idchild, "0")) { + legacy_format = 0; + break; + } + } + if (child_ctr != 1) { + legacy_format = 0; + } + + if (legacy_format) + return parse_compound(uc_mgr, cfg, fcn, data1, (void *)id); + else + return fcn(uc_mgr, cfg, data1, NULL); }
-static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr, +static int parse_device_name(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, void *data1, void *data2 ATTRIBUTE_UNUSED) { - const char *id; - int err; + return parse_compound_check_legacy(uc_mgr, cfg, parse_device, data1); +}
- err = snd_config_get_id(cfg, &id); - if (err < 0) - return err; - return parse_compound(uc_mgr, cfg, parse_modifier, - data1, (void *)id); +static int parse_modifier_name(snd_use_case_mgr_t *uc_mgr, + snd_config_t *cfg, + void *data1, + void *data2 ATTRIBUTE_UNUSED) +{ + return parse_compound_check_legacy(uc_mgr, cfg, parse_modifier, data1); }
/*
Wherever SupportedDevice can appear, also allow ConflictingDevice. Only one or the other (or neither) may be specified. When neither is specified, allow anything. Sometimes, listing ConflictingDevices may result in a shorter list than explicitly listing all SupportedDevices.
Add support for SupportedDevice and ConflictingDevice to SectionDevice. This allows representing devices which are mutually exclusive, e.g. due to a mux that switches between capturing from two different microphones, without the possibility of mixing.
Enhance is_modifier_supported to allow ignoring SupportedDevice and ConflictingDevice. This is useful when querying values from a SectionModifier; there's no reason we shouldn't be able to query values just because the current configuration would prevent enabling that device. The new is_device_supported is implemented similarly.
Enhance switch_device to remove the old device from the current device list before querying for the new device, and add it back immediately afterwards. This allows the query for the new device to ignore any conflicts caused solely by the old device.
Signed-off-by: Stephen Warren swarren@nvidia.com --- src/ucm/main.c | 110 +++++++++++++++++++++++++++++++++++---------------- src/ucm/parser.c | 84 ++++++++++++++++++++++++++++++++------ src/ucm/ucm_local.h | 19 +++++++- src/ucm/utils.c | 9 ++-- 4 files changed, 167 insertions(+), 55 deletions(-)
diff --git a/src/ucm/main.c b/src/ucm/main.c index 5acb5a8..e5d06e6 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -463,6 +463,51 @@ static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr, verb_name); }
+static int is_devlist_supported(snd_use_case_mgr_t *uc_mgr, + struct dev_list *dev_list) +{ + struct dev_list_node *device; + struct use_case_device *adev; + struct list_head *pos, *pos1; + int found_ret; + + switch (dev_list->type) { + case DEVLIST_NONE: + default: + return 1; + case DEVLIST_SUPPORTED: + found_ret = 1; + break; + case DEVLIST_CONFLICTING: + found_ret = 0; + break; + } + + list_for_each(pos, &dev_list->list) { + device = list_entry(pos, struct dev_list_node, list); + + list_for_each(pos1, &uc_mgr->active_devices) { + adev = list_entry(pos1, struct use_case_device, + active_list); + if (!strcmp(device->name, adev->name)) + return found_ret; + } + } + return 1 - found_ret; +} + +static inline int is_modifier_supported(snd_use_case_mgr_t *uc_mgr, + struct use_case_modifier *modifier) +{ + return is_devlist_supported(uc_mgr, &modifier->dev_list); +} + +static inline int is_device_supported(snd_use_case_mgr_t *uc_mgr, + struct use_case_device *device) +{ + return is_devlist_supported(uc_mgr, &device->dev_list); +} + /** * \brief Find device * \param verb Use case verb @@ -470,35 +515,26 @@ static inline struct use_case_verb *find_verb(snd_use_case_mgr_t *uc_mgr, * \return structure on success, otherwise a NULL (not found) */ static inline struct use_case_device * - find_device(struct use_case_verb *verb, - const char *device_name) -{ - return find(&verb->device_list, - struct use_case_device, list, name, - device_name); -} - -static int is_modifier_supported(snd_use_case_mgr_t *uc_mgr, - struct use_case_modifier *modifier) + find_device(snd_use_case_mgr_t *uc_mgr, const char *device_name, + int check_supported) { - struct dev_list *device; - struct use_case_device *adev; - struct list_head *pos, *pos1; + struct use_case_device *device; + struct use_case_verb *verb = uc_mgr->active_verb; + struct list_head *pos;
- list_for_each(pos, &modifier->dev_list) { - device = list_entry(pos, struct dev_list, list); + list_for_each(pos, &verb->device_list) { + device = list_entry(pos, struct use_case_device, list);
- list_for_each(pos1, &uc_mgr->active_devices) { - adev = list_entry(pos1, struct use_case_device, - active_list); + if (strcmp(device_name, device->name)) + continue;
- if (strcmp(adev->name, device->name)) - continue; + if (check_supported && + !is_device_supported(uc_mgr, device)) + continue;
- return 1; - } + return device; } - return 0; + return NULL; }
/** @@ -508,7 +544,8 @@ static int is_modifier_supported(snd_use_case_mgr_t *uc_mgr, * \return structure on success, otherwise a NULL (not found) */ static struct use_case_modifier * - find_modifier(snd_use_case_mgr_t *uc_mgr, const char *modifier_name) + find_modifier(snd_use_case_mgr_t *uc_mgr, const char *modifier_name, + int check_supported) { struct use_case_modifier *modifier; struct use_case_verb *verb = uc_mgr->active_verb; @@ -520,8 +557,11 @@ static struct use_case_modifier * if (strcmp(modifier->name, modifier_name)) continue;
- if (is_modifier_supported(uc_mgr, modifier)) - return modifier; + if (check_supported && + !is_modifier_supported(uc_mgr, modifier)) + continue; + + return modifier; } return NULL; } @@ -1061,13 +1101,13 @@ static int get_value(snd_use_case_mgr_t *uc_mgr, int err;
if (item != NULL) { - mod = find_modifier(uc_mgr, item); + mod = find_modifier(uc_mgr, item, 0); if (mod != NULL) { err = get_value1(value, &mod->value_list, identifier); if (err >= 0 || err != -ENOENT) return err; } - dev = find_device(uc_mgr->active_verb, item); + dev = find_device(uc_mgr, item, 0); if (dev != NULL) { err = get_value1(value, &dev->value_list, identifier); if (err >= 0 || err != -ENOENT) @@ -1286,7 +1326,7 @@ static int set_device_user(snd_use_case_mgr_t *uc_mgr,
if (uc_mgr->active_verb == NULL) return -ENOENT; - device = find_device(uc_mgr->active_verb, device_name); + device = find_device(uc_mgr, device_name, 1); if (device == NULL) return -ENOENT; return set_device(uc_mgr, device, enable); @@ -1301,7 +1341,7 @@ static int set_modifier_user(snd_use_case_mgr_t *uc_mgr, if (uc_mgr->active_verb == NULL) return -ENOENT;
- modifier = find_modifier(uc_mgr, modifier_name); + modifier = find_modifier(uc_mgr, modifier_name, 1); if (modifier == NULL) return -ENOENT; return set_modifier(uc_mgr, modifier, enable); @@ -1326,10 +1366,12 @@ static int switch_device(snd_use_case_mgr_t *uc_mgr, uc_error("error: device %s already enabled", new_device); return -EINVAL; } - xold = find_device(uc_mgr->active_verb, old_device); + xold = find_device(uc_mgr, old_device, 1); if (xold == NULL) return -ENOENT; - xnew = find_device(uc_mgr->active_verb, new_device); + list_del(&xold->active_list); + xnew = find_device(uc_mgr, new_device, 1); + list_add_tail(&xold->active_list, &uc_mgr->active_devices); if (xnew == NULL) return -ENOENT; err = 0; @@ -1378,10 +1420,10 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr, uc_error("error: modifier %s already enabled", new_modifier); return -EINVAL; } - xold = find_modifier(uc_mgr, old_modifier); + xold = find_modifier(uc_mgr, old_modifier, 1); if (xold == NULL) return -ENOENT; - xnew = find_modifier(uc_mgr, new_modifier); + xnew = find_modifier(uc_mgr, new_modifier, 1); if (xnew == NULL) return -ENOENT; err = 0; diff --git a/src/ucm/parser.c b/src/ucm/parser.c index b0bebe0..23b67bc 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -180,18 +180,25 @@ static int strip_legacy_dev_index(char *name) }
/* - * Parse transition + * Parse device list */ -static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, - struct list_head *dlist, - snd_config_t *cfg) +static int parse_device_list(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, + struct dev_list *dev_list, + enum dev_list_type type, + snd_config_t *cfg) { - struct dev_list *sdev; + struct dev_list_node *sdev; const char *id; snd_config_iterator_t i, next; snd_config_t *n; int err;
+ if (dev_list->type != DEVLIST_NONE) { + uc_error("error: multiple supported or" + " conflicting device lists"); + return -EEXIST; + } + if (snd_config_get_id(cfg, &id) < 0) return -EINVAL;
@@ -206,7 +213,7 @@ static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, if (snd_config_get_id(n, &id) < 0) return -EINVAL;
- sdev = calloc(1, sizeof(struct dev_list)); + sdev = calloc(1, sizeof(struct dev_list_node)); if (sdev == NULL) return -ENOMEM; err = parse_string(n, &sdev->name); @@ -220,8 +227,11 @@ static int parse_supported_device(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, free(sdev); return err; } - list_add(&sdev->list, dlist); + list_add(&sdev->list, &dev_list->list); } + + dev_list->type = type; + return 0; }
@@ -413,11 +423,17 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, * SectionModifier."Capture Voice" { * * Comment "Record voice call" + * * SupportedDevice [ * "x" * "y" * ] * + * ConflictingDevice [ + * "x" + * "y" + * ] + * * EnableSequence [ * .... * ] @@ -439,6 +455,9 @@ static int parse_value(snd_use_case_mgr_t *uc_mgr ATTRIBUTE_UNUSED, * } * * } + * + * SupportedDevice and ConflictingDevice cannot be specified together. + * Both are optional. */ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg, @@ -469,7 +488,7 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&modifier->enable_list); INIT_LIST_HEAD(&modifier->disable_list); INIT_LIST_HEAD(&modifier->transition_list); - INIT_LIST_HEAD(&modifier->dev_list); + INIT_LIST_HEAD(&modifier->dev_list.list); INIT_LIST_HEAD(&modifier->value_list); list_add_tail(&modifier->list, &verb->modifier_list); modifier->name = strdup(name); @@ -490,7 +509,8 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, }
if (strcmp(id, "SupportedDevice") == 0) { - err = parse_supported_device(uc_mgr, &modifier->dev_list, n); + err = parse_device_list(uc_mgr, &modifier->dev_list, + DEVLIST_SUPPORTED, n); if (err < 0) { uc_error("error: failed to parse supported" " device list"); @@ -498,6 +518,16 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, } }
+ if (strcmp(id, "ConflictingDevice") == 0) { + err = parse_device_list(uc_mgr, &modifier->dev_list, + DEVLIST_CONFLICTING, n); + if (err < 0) { + uc_error("error: failed to parse conflicting" + " device list"); + return err; + } + } + if (strcmp(id, "EnableSequence") == 0) { err = parse_sequence(uc_mgr, &modifier->enable_list, n); if (err < 0) { @@ -538,11 +568,6 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, } }
- if (list_empty(&modifier->dev_list)) { - uc_error("error: %s: modifier missing supported device sequence", modifier->name); - return -EINVAL; - } - return 0; }
@@ -553,6 +578,16 @@ static int parse_modifier(snd_use_case_mgr_t *uc_mgr, *SectionDevice."Headphones" { * Comment "Headphones connected to 3.5mm jack" * + * upportedDevice [ + * "x" + * "y" + * ] + * + * ConflictingDevice [ + * "x" + * "y" + * ] + * * EnableSequence [ * .... * ] @@ -599,6 +634,7 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, INIT_LIST_HEAD(&device->enable_list); INIT_LIST_HEAD(&device->disable_list); INIT_LIST_HEAD(&device->transition_list); + INIT_LIST_HEAD(&device->dev_list.list); INIT_LIST_HEAD(&device->value_list); list_add_tail(&device->list, &verb->device_list); device->name = strdup(name); @@ -618,6 +654,26 @@ static int parse_device(snd_use_case_mgr_t *uc_mgr, continue; }
+ if (strcmp(id, "SupportedDevice") == 0) { + err = parse_device_list(uc_mgr, &device->dev_list, + DEVLIST_SUPPORTED, n); + if (err < 0) { + uc_error("error: failed to parse supported" + " device list"); + return err; + } + } + + if (strcmp(id, "ConflictingDevice") == 0) { + err = parse_device_list(uc_mgr, &device->dev_list, + DEVLIST_CONFLICTING, n); + if (err < 0) { + uc_error("error: failed to parse conflicting" + " device list"); + return err; + } + } + if (strcmp(id, "EnableSequence") == 0) { uc_dbg("EnableSequence"); err = parse_sequence(uc_mgr, &device->enable_list, n); diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h index 2ceceaa..0522bf5 100644 --- a/src/ucm/ucm_local.h +++ b/src/ucm/ucm_local.h @@ -76,11 +76,21 @@ struct transition_sequence { /* * Modifier Supported Devices. */ -struct dev_list { +enum dev_list_type { + DEVLIST_NONE, + DEVLIST_SUPPORTED, + DEVLIST_CONFLICTING +}; + +struct dev_list_node { struct list_head list; char *name; };
+struct dev_list { + enum dev_list_type type; + struct list_head list; +};
/* * Describes a Use Case Modifier and it's enable and disable sequences. @@ -100,8 +110,8 @@ struct use_case_modifier { /* modifier transition list */ struct list_head transition_list;
- /* list of supported devices per modifier */ - struct list_head dev_list; + /* list of devices supported or conflicting */ + struct dev_list dev_list;
/* values */ struct list_head value_list; @@ -125,6 +135,9 @@ struct use_case_device { /* device transition list */ struct list_head transition_list;
+ /* list of devices supported or conflicting */ + struct dev_list dev_list; + /* value list */ struct list_head value_list; }; diff --git a/src/ucm/utils.c b/src/ucm/utils.c index 85549e1..45307b0 100644 --- a/src/ucm/utils.c +++ b/src/ucm/utils.c @@ -99,13 +99,13 @@ void uc_mgr_free_value(struct list_head *base) } }
-void uc_mgr_free_dev_list(struct list_head *base) +void uc_mgr_free_dev_list(struct dev_list *dev_list) { struct list_head *pos, *npos; - struct dev_list *dlist; + struct dev_list_node *dlist; - list_for_each_safe(pos, npos, base) { - dlist = list_entry(pos, struct dev_list, list); + list_for_each_safe(pos, npos, &dev_list->list) { + dlist = list_entry(pos, struct dev_list_node, list); free(dlist->name); list_del(&dlist->list); free(dlist); @@ -189,6 +189,7 @@ void uc_mgr_free_device(struct list_head *base) 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);
In UCM, there are various system-defined properties whose names start with "_". Explicitly prevent any gets from falling back to properties defined in a config file if the property name starts with "_", in order to reserve the entire "_" namespace for system-defined properties.
Signed-off-by: Stephen Warren swarren@nvidia.com --- src/ucm/main.c | 15 ++++++++++++++- 1 files changed, 14 insertions(+), 1 deletions(-)
diff --git a/src/ucm/main.c b/src/ucm/main.c index e5d06e6..d319160 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1032,6 +1032,8 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, err = get_device_list(uc_mgr, list, str); else if (check_identifier(identifier, "_modifiers")) err = get_modifier_list(uc_mgr, list, str); + else if (identifier[0] == '_') + err = -ENOENT; else err = get_value_list(uc_mgr, identifier, list, str); if (str) @@ -1159,6 +1161,9 @@ int snd_use_case_get(snd_use_case_mgr_t *uc_mgr, goto __end; } err = 0; + } else if (identifier[0] == '_') { + err = -ENOENT; + goto __end; } else { str1 = strchr(identifier, '/'); if (str1) { @@ -1247,8 +1252,16 @@ int snd_use_case_geti(snd_use_case_mgr_t *uc_mgr, *value = err; err = 0; } +#if 0 + /* + * enable this block if the else clause below is expanded to query + * user-supplied values + */ + } else if (identifier[0] == '_') + err = -ENOENT; +#endif } else - err = -EINVAL; + err = -ENOENT; if (str) free(str); }
At any time, one can query:
_supporteddevs/<modifier>|<device>/<verb> _conflictingdevs/<modifier>|<device>/<verb>
If a verb is current, one can query:
_supporteddevs/<modifier>|<device> _conflictingdevs/<modifier>|<device>
Note that at most one of the supported/conflicting devs lists has any entries, and when neither is present, all devices are supported.
Signed-off-by: Stephen Warren swarren@nvidia.com --- include/use-case.h | 5 ++ src/ucm/main.c | 106 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 14 deletions(-)
diff --git a/include/use-case.h b/include/use-case.h index cb3176e..b346877 100644 --- a/include/use-case.h +++ b/include/use-case.h @@ -205,6 +205,11 @@ int snd_use_case_free_list(const char *list[], int items); * _enadevs - get list of enabled devices * _enamods - get list of enabled modifiers * + * _supporteddevs/<modifier>|<device>[/<verb>] - list of supported devices + * _conflictingdevs/<modifier>|<device>[/<verb>] - list of conflicting devices + * Note that at most one of the supported/conflicting devs lists has + * any entries, and when neither is present, all devices are supported. + * */ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, const char *identifier, diff --git a/src/ucm/main.c b/src/ucm/main.c index d319160..db71f03 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -515,11 +515,10 @@ static inline int is_device_supported(snd_use_case_mgr_t *uc_mgr, * \return structure on success, otherwise a NULL (not found) */ static inline struct use_case_device * - find_device(snd_use_case_mgr_t *uc_mgr, const char *device_name, - int check_supported) + find_device(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + const char *device_name, int check_supported) { struct use_case_device *device; - struct use_case_verb *verb = uc_mgr->active_verb; struct list_head *pos;
list_for_each(pos, &verb->device_list) { @@ -544,11 +543,10 @@ static inline struct use_case_device * * \return structure on success, otherwise a NULL (not found) */ static struct use_case_modifier * - find_modifier(snd_use_case_mgr_t *uc_mgr, const char *modifier_name, - int check_supported) + find_modifier(snd_use_case_mgr_t *uc_mgr, struct use_case_verb *verb, + const char *modifier_name, int check_supported) { struct use_case_modifier *modifier; - struct use_case_verb *verb = uc_mgr->active_verb; struct list_head *pos;
list_for_each(pos, &verb->modifier_list) { @@ -857,6 +855,82 @@ static int get_modifier_list(snd_use_case_mgr_t *uc_mgr, const char **list[], name, comment); }
+/** + * \brief Get list of supported/conflicting devices + * \param list Returned list + * \param name Name of modifier or verb to query + * \param type Type of device list entries to return + * \return Number of list entries if success, otherwise a negative error code + */ +static int get_supcon_device_list(snd_use_case_mgr_t *uc_mgr, + const char **list[], char *name, + enum dev_list_type type) +{ + char *str; + struct use_case_verb *verb; + struct use_case_modifier *modifier; + struct use_case_device *device; + + 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; + + modifier = find_modifier(uc_mgr, verb, name, 0); + if (modifier) { + if (modifier->dev_list.type != type) + return 0; + return get_list(&modifier->dev_list.list, list, + struct dev_list_node, list, + name); + } + + device = find_device(uc_mgr, verb, name, 0); + if (device) { + if (device->dev_list.type != type) + return 0; + return get_list(&device->dev_list.list, list, + struct dev_list_node, list, + name); + } + + return -ENOENT; + +} + +/** + * \brief Get list of supported devices + * \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_supported_device_list(snd_use_case_mgr_t *uc_mgr, + const char **list[], char *name) +{ + return get_supcon_device_list(uc_mgr, list, name, DEVLIST_SUPPORTED); +} + +/** + * \brief Get list of conflicting devices + * \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_conflicting_device_list(snd_use_case_mgr_t *uc_mgr, + const char **list[], char *name) +{ + return get_supcon_device_list(uc_mgr, list, name, DEVLIST_CONFLICTING); +} + struct myvalue { struct list_head list; char *value; @@ -1032,6 +1106,10 @@ int snd_use_case_get_list(snd_use_case_mgr_t *uc_mgr, 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); else if (identifier[0] == '_') err = -ENOENT; else @@ -1103,13 +1181,13 @@ static int get_value(snd_use_case_mgr_t *uc_mgr, int err;
if (item != NULL) { - mod = find_modifier(uc_mgr, item, 0); + mod = find_modifier(uc_mgr, uc_mgr->active_verb, item, 0); if (mod != NULL) { err = get_value1(value, &mod->value_list, identifier); if (err >= 0 || err != -ENOENT) return err; } - dev = find_device(uc_mgr, item, 0); + dev = find_device(uc_mgr, uc_mgr->active_verb, item, 0); if (dev != NULL) { err = get_value1(value, &dev->value_list, identifier); if (err >= 0 || err != -ENOENT) @@ -1339,7 +1417,7 @@ static int set_device_user(snd_use_case_mgr_t *uc_mgr,
if (uc_mgr->active_verb == NULL) return -ENOENT; - device = find_device(uc_mgr, device_name, 1); + device = find_device(uc_mgr, uc_mgr->active_verb, device_name, 1); if (device == NULL) return -ENOENT; return set_device(uc_mgr, device, enable); @@ -1354,7 +1432,7 @@ static int set_modifier_user(snd_use_case_mgr_t *uc_mgr, if (uc_mgr->active_verb == NULL) return -ENOENT;
- modifier = find_modifier(uc_mgr, modifier_name, 1); + modifier = find_modifier(uc_mgr, uc_mgr->active_verb, modifier_name, 1); if (modifier == NULL) return -ENOENT; return set_modifier(uc_mgr, modifier, enable); @@ -1379,11 +1457,11 @@ static int switch_device(snd_use_case_mgr_t *uc_mgr, uc_error("error: device %s already enabled", new_device); return -EINVAL; } - xold = find_device(uc_mgr, old_device, 1); + xold = find_device(uc_mgr, uc_mgr->active_verb, old_device, 1); if (xold == NULL) return -ENOENT; list_del(&xold->active_list); - xnew = find_device(uc_mgr, new_device, 1); + xnew = find_device(uc_mgr, uc_mgr->active_verb, new_device, 1); list_add_tail(&xold->active_list, &uc_mgr->active_devices); if (xnew == NULL) return -ENOENT; @@ -1433,10 +1511,10 @@ static int switch_modifier(snd_use_case_mgr_t *uc_mgr, uc_error("error: modifier %s already enabled", new_modifier); return -EINVAL; } - xold = find_modifier(uc_mgr, old_modifier, 1); + xold = find_modifier(uc_mgr, uc_mgr->active_verb, old_modifier, 1); if (xold == NULL) return -ENOENT; - xnew = find_modifier(uc_mgr, new_modifier, 1); + xnew = find_modifier(uc_mgr, uc_mgr->active_verb, new_modifier, 1); if (xnew == NULL) return -ENOENT; err = 0;
get_value, called by snd_use_case_get, uses uc_mgr->active_verb without checking there is one, and hence can segfault. Fix this.
Signed-off-by: Stephen Warren swarren@nvidia.com --- src/ucm/main.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/src/ucm/main.c b/src/ucm/main.c index db71f03..13ea1ed 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -1180,6 +1180,9 @@ static int get_value(snd_use_case_mgr_t *uc_mgr, struct use_case_device *dev; int err;
+ if (!uc_mgr->active_verb) + return -ENOENT; + if (item != NULL) { mod = find_modifier(uc_mgr, uc_mgr->active_verb, item, 0); if (mod != NULL) {
On 03/06/11 21:56, Stephen Warren wrote:
The previous supported "legacy" syntax was:
SectionDevice."Speaker".0 { SectionModifier."Capture Voice".0 {
This change supports new syntax:
SectionDevice."Speaker" { SectionModifier."Capture Voice" {
... but also allows the old syntax, iff the index is exactly "0". If an index is present, but not exactly "0", parsing will appear to succeed, but produce an empty device or modifier.
When naming devices and modifiers, even if the legacy format is used, any index is not included in the name; i.e. both sets of syntax above name the device just "Speaker".
The SupportedDevice list syntax still also accepts either "x" or "x.0", but internally strips ".0" from the tail of any device name. Any other name including "." is disallowed.
Finally, when comparing device or modifier names, a simple exact string compare is now used, since no index data is ever present in device or modifier names.
The one functional change introduced here is that a SupportedDevice entry of just "x" will now only ever match a single device. It previously acted as a wildcard for any device named "x.foo".
Signed-off-by: Stephen Warren swarren@nvidia.com
All
Acked-by: Liam Girdwood lrg@ti.com
participants (2)
-
Liam Girdwood
-
Stephen Warren