diff --git a/src/ucm/parser.c b/src/ucm/parser.c index f576fde4..f9a8f628 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 fa9fc166..ba8d2acb 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 daa568c1..60a59172 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,25 +452,70 @@ 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) { struct list_head *pos, *npos; @@ -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); }