2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 * Support for the verb/device/modifier core logic and API,
17 * command line tool and file parser was kindly sponsored by
18 * Texas Instruments Inc.
19 * Support for multiple active modifiers and devices,
20 * transition sequences, multiple client access and user defined use
21 * cases was kindly sponsored by Wolfson Microelectronics PLC.
23 * Copyright (C) 2008-2010 SlimLogic Ltd
24 * Copyright (C) 2010 Wolfson Microelectronics PLC
25 * Copyright (C) 2010 Texas Instruments Inc.
26 * Copyright (C) 2010 Red Hat Inc.
27 * Authors: Liam Girdwood <lrg@slimlogic.co.uk>
28 * Stefan Schmidt <stefan@slimlogic.co.uk>
29 * Justin Xu <justinx@slimlogic.co.uk>
30 * Jaroslav Kysela <perex@perex.cz>
33 #include "ucm_local.h"
35 void uc_mgr_error(const char *fmt,...)
39 fprintf(stderr, "ucm: ");
40 vfprintf(stderr, fmt, va);
44 void uc_mgr_stdout(const char *fmt,...)
48 vfprintf(stdout, fmt, va);
52 const char *uc_mgr_sysfs_root(void)
54 const char *e = getenv("SYSFS_PATH");
58 uc_error("no sysfs root!");
62 struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr)
64 struct list_head *pos;
65 struct ctl_list *ctl_list = NULL, *ctl_list2;
67 list_for_each(pos, &uc_mgr->ctl_list) {
68 ctl_list2 = list_entry(pos, struct ctl_list, list);
72 uc_error("multiple control device names were found!");
80 struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card)
82 struct ctl_list *ctl_list;
86 sprintf(cname, "hw:%d", card);
87 err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1);
93 struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx)
95 struct list_head *pos;
96 struct ctl_list *ctl_list;
101 list_for_each(pos, &uc_mgr->ctl_list) {
102 ctl_list = list_entry(pos, struct ctl_list, list);
103 s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
106 if (strcmp(s, name) == 0) {
115 if (snd_card_next(&card) < 0 || card < 0)
119 ctl_list = uc_mgr_get_ctl_by_card(uc_mgr, card);
120 if (ctl_list == NULL)
121 continue; /* really? */
122 s = snd_ctl_card_info_get_name(ctl_list->ctl_info);
123 if (s && strcmp(s, name) == 0) {
128 if (snd_card_next(&card) < 0)
135 snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr)
137 struct ctl_list *ctl_list;
139 ctl_list = uc_mgr_get_master_ctl(uc_mgr);
141 return ctl_list->ctl;
145 static void uc_mgr_free_ctl(struct ctl_list *ctl_list)
147 struct list_head *pos, *npos;
148 struct ctl_dev *ctl_dev;
150 list_for_each_safe(pos, npos, &ctl_list->dev_list) {
151 ctl_dev = list_entry(pos, struct ctl_dev, list);
152 free(ctl_dev->device);
155 snd_ctl_card_info_free(ctl_list->ctl_info);
159 void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr)
161 struct list_head *pos, *npos;
162 struct ctl_list *ctl_list;
164 list_for_each_safe(pos, npos, &uc_mgr->ctl_list) {
165 ctl_list = list_entry(pos, struct ctl_list, list);
166 snd_ctl_close(ctl_list->ctl);
167 list_del(&ctl_list->list);
168 uc_mgr_free_ctl(ctl_list);
172 static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device)
174 struct list_head *pos;
175 struct ctl_dev *ctl_dev;
177 /* skip duplicates */
178 list_for_each(pos, &ctl_list->dev_list) {
179 ctl_dev = list_entry(pos, struct ctl_dev, list);
180 if (strcmp(ctl_dev->device, device) == 0)
184 /* allocate new device name */
185 ctl_dev = malloc(sizeof(*ctl_dev));
188 ctl_dev->device = strdup(device);
189 if (ctl_dev->device == NULL) {
193 list_add_tail(&ctl_dev->list, &ctl_list->dev_list);
197 static int uc_mgr_ctl_add(snd_use_case_mgr_t *uc_mgr,
198 struct ctl_list **ctl_list,
199 snd_ctl_t *ctl, int card,
200 snd_ctl_card_info_t *info,
204 struct ctl_list *cl = NULL;
205 const char *id = snd_ctl_card_info_get_id(info);
206 char dev[MAX_CARD_LONG_NAME];
209 if (id == NULL || id[0] == '\0')
212 cl = malloc(sizeof(*cl));
215 INIT_LIST_HEAD(&cl->dev_list);
217 if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) {
221 snd_ctl_card_info_copy(cl->ctl_info, info);
226 (*ctl_list)->slave = slave;
229 snprintf(dev, sizeof(dev), "hw:%d", card);
230 hit |= !!(device && (strcmp(dev, device) == 0));
231 err = uc_mgr_ctl_add_dev(*ctl_list, dev);
235 snprintf(dev, sizeof(dev), "hw:%s", id);
236 hit |= !!(device && (strcmp(dev, device) == 0));
237 err = uc_mgr_ctl_add_dev(*ctl_list, dev);
240 /* the UCM name not based on the card name / id */
241 if (!hit && device) {
242 err = uc_mgr_ctl_add_dev(*ctl_list, device);
247 list_add_tail(&(*ctl_list)->list, &uc_mgr->ctl_list);
251 if (*ctl_list == cl) {
258 int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr,
259 struct ctl_list **ctll,
263 struct list_head *pos1, *pos2;
265 struct ctl_list *ctl_list;
266 struct ctl_dev *ctl_dev;
267 snd_ctl_card_info_t *info;
271 snd_ctl_card_info_alloca(&info);
274 list_for_each(pos1, &uc_mgr->ctl_list) {
275 ctl_list = list_entry(pos1, struct ctl_list, list);
276 list_for_each(pos2, &ctl_list->dev_list) {
277 ctl_dev = list_entry(pos2, struct ctl_dev, list);
278 if (strcmp(ctl_dev->device, device) == 0) {
287 err = snd_ctl_open(&ctl, device, 0);
292 err = snd_ctl_card_info(ctl, info);
294 id = snd_ctl_card_info_get_id(info);
295 if (err < 0 || id == NULL || id[0] == '\0') {
296 uc_error("control hardware info (%s): %s", device, snd_strerror(err));
298 return err >= 0 ? -EINVAL : err;
301 /* insert to cache, if just name differs */
302 list_for_each(pos1, &uc_mgr->ctl_list) {
303 ctl_list = list_entry(pos1, struct ctl_list, list);
304 if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) {
305 card = snd_card_get_index(id);
306 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, card, info, device, slave);
316 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, -1, info, device, slave);
328 const char *uc_mgr_config_dir(int format)
333 path = getenv(ALSA_CONFIG_UCM2_VAR);
334 if (!path || path[0] == '\0')
335 path = ALSA_CONFIG_DIR "/ucm2";
337 path = getenv(ALSA_CONFIG_UCM_VAR);
338 if (!path || path[0] == '\0')
339 path = ALSA_CONFIG_DIR "/ucm";
344 int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top)
348 const char *default_paths[2];
351 fp = fopen(file, "r");
355 uc_error("could not open configuration file %s", file);
358 err = snd_input_stdio_attach(&in, fp, 1);
362 default_paths[0] = uc_mgr_config_dir(format);
363 default_paths[1] = NULL;
364 err = _snd_config_load_with_include(top, in, 0, default_paths);
366 uc_error("could not load configuration file %s", file);
371 err = snd_input_close(in);
377 int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg)
382 err = snd_config_top(&top);
385 err = uc_mgr_config_load_into(format, file, top);
387 snd_config_delete(top);
394 void uc_mgr_free_value(struct list_head *base)
396 struct list_head *pos, *npos;
397 struct ucm_value *val;
399 list_for_each_safe(pos, npos, base) {
400 val = list_entry(pos, struct ucm_value, list);
403 list_del(&val->list);
408 void uc_mgr_free_dev_list(struct dev_list *dev_list)
410 struct list_head *pos, *npos;
411 struct dev_list_node *dlist;
413 list_for_each_safe(pos, npos, &dev_list->list) {
414 dlist = list_entry(pos, struct dev_list_node, list);
416 list_del(&dlist->list);
421 int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name)
423 struct list_head *pos;
424 struct dev_list_node *dlist;
427 list_for_each(pos, &dev_list->list) {
428 dlist = list_entry(pos, struct dev_list_node, list);
429 if (strcmp(dlist->name, name) == 0)
433 dlist = calloc(1, sizeof(*dlist));
442 list_add(&dlist->list, &dev_list->list);
446 int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src,
449 struct list_head *pos;
450 struct dev_list_node *dlist;
453 list_for_each(pos, &dev_list->list) {
454 dlist = list_entry(pos, struct dev_list_node, list);
455 if (strcmp(dlist->name, src) == 0) {
467 int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name)
469 struct list_head *pos;
470 struct dev_list_node *dlist;
472 list_for_each(pos, &dev_list->list) {
473 dlist = list_entry(pos, struct dev_list_node, list);
474 if (strcmp(dlist->name, name) == 0) {
476 list_del(&dlist->list);
484 void uc_mgr_free_sequence_element(struct sequence_element *seq)
489 case SEQUENCE_ELEMENT_TYPE_CDEV:
490 free(seq->data.cdev);
492 case SEQUENCE_ELEMENT_TYPE_CSET:
493 case SEQUENCE_ELEMENT_TYPE_CSET_NEW:
494 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE:
495 case SEQUENCE_ELEMENT_TYPE_CSET_TLV:
496 case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE:
497 free(seq->data.cset);
499 case SEQUENCE_ELEMENT_TYPE_SYSSET:
500 free(seq->data.sysw);
502 case SEQUENCE_ELEMENT_TYPE_EXEC:
503 free(seq->data.exec);
511 void uc_mgr_free_sequence(struct list_head *base)
513 struct list_head *pos, *npos;
514 struct sequence_element *seq;
516 list_for_each_safe(pos, npos, base) {
517 seq = list_entry(pos, struct sequence_element, list);
518 list_del(&seq->list);
519 uc_mgr_free_sequence_element(seq);
523 void uc_mgr_free_transition_element(struct transition_sequence *tseq)
526 uc_mgr_free_sequence(&tseq->transition_list);
530 void uc_mgr_free_transition(struct list_head *base)
532 struct list_head *pos, *npos;
533 struct transition_sequence *tseq;
535 list_for_each_safe(pos, npos, base) {
536 tseq = list_entry(pos, struct transition_sequence, list);
537 list_del(&tseq->list);
538 uc_mgr_free_transition_element(tseq);
542 void uc_mgr_free_dev_name_list(struct list_head *base)
544 struct list_head *pos, *npos;
545 struct ucm_dev_name *dev;
547 list_for_each_safe(pos, npos, base) {
548 dev = list_entry(pos, struct ucm_dev_name, list);
549 list_del(&dev->list);
556 void uc_mgr_free_modifier(struct list_head *base)
558 struct list_head *pos, *npos;
559 struct use_case_modifier *mod;
561 list_for_each_safe(pos, npos, base) {
562 mod = list_entry(pos, struct use_case_modifier, list);
565 uc_mgr_free_sequence(&mod->enable_list);
566 uc_mgr_free_sequence(&mod->disable_list);
567 uc_mgr_free_transition(&mod->transition_list);
568 uc_mgr_free_dev_list(&mod->dev_list);
569 uc_mgr_free_value(&mod->value_list);
570 list_del(&mod->list);
575 void uc_mgr_free_device(struct use_case_device *dev)
579 uc_mgr_free_sequence(&dev->enable_list);
580 uc_mgr_free_sequence(&dev->disable_list);
581 uc_mgr_free_transition(&dev->transition_list);
582 uc_mgr_free_dev_list(&dev->dev_list);
583 uc_mgr_free_value(&dev->value_list);
584 list_del(&dev->list);
588 void uc_mgr_free_device_list(struct list_head *base)
590 struct list_head *pos, *npos;
591 struct use_case_device *dev;
593 list_for_each_safe(pos, npos, base) {
594 dev = list_entry(pos, struct use_case_device, list);
595 uc_mgr_free_device(dev);
599 int uc_mgr_rename_device(struct use_case_verb *verb, const char *src,
602 struct use_case_device *device;
603 struct list_head *pos, *npos;
606 /* no errors when device is not found */
607 list_for_each_safe(pos, npos, &verb->device_list) {
608 device = list_entry(pos, struct use_case_device, list);
609 if (strcmp(device->name, src) == 0) {
617 uc_mgr_rename_in_dev_list(&device->dev_list, src, dst);
622 int uc_mgr_remove_device(struct use_case_verb *verb, const char *name)
624 struct use_case_device *device;
625 struct list_head *pos, *npos;
628 list_for_each_safe(pos, npos, &verb->device_list) {
629 device = list_entry(pos, struct use_case_device, list);
630 if (strcmp(device->name, name) == 0) {
631 uc_mgr_free_device(device);
635 err = uc_mgr_remove_from_dev_list(&device->dev_list, name);
636 if (err < 0 && err != -ENODEV)
641 return found == 0 ? -ENODEV : 0;
644 const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name)
646 struct list_head *pos;
647 struct ucm_value *value;
649 list_for_each(pos, &uc_mgr->variable_list) {
650 value = list_entry(pos, struct ucm_value, list);
651 if (strcmp(value->name, name) == 0)
657 int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name,
660 struct list_head *pos;
661 struct ucm_value *curr;
664 list_for_each(pos, &uc_mgr->variable_list) {
665 curr = list_entry(pos, struct ucm_value, list);
666 if (strcmp(curr->name, name) == 0) {
676 curr = calloc(1, sizeof(struct ucm_value));
679 curr->name = strdup(name);
680 if (curr->name == NULL) {
684 curr->data = strdup(val);
685 if (curr->data == NULL) {
690 list_add_tail(&curr->list, &uc_mgr->variable_list);
694 void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr)
696 struct list_head *pos, *npos;
697 struct use_case_verb *verb;
699 list_for_each_safe(pos, npos, &uc_mgr->verb_list) {
700 verb = list_entry(pos, struct use_case_verb, list);
703 uc_mgr_free_sequence(&verb->enable_list);
704 uc_mgr_free_sequence(&verb->disable_list);
705 uc_mgr_free_transition(&verb->transition_list);
706 uc_mgr_free_value(&verb->value_list);
707 uc_mgr_free_device_list(&verb->device_list);
708 uc_mgr_free_device_list(&verb->cmpt_device_list);
709 uc_mgr_free_modifier(&verb->modifier_list);
710 uc_mgr_free_dev_name_list(&verb->rename_list);
711 uc_mgr_free_dev_name_list(&verb->remove_list);
712 list_del(&verb->list);
715 uc_mgr_free_sequence(&uc_mgr->fixedboot_list);
716 uc_mgr_free_sequence(&uc_mgr->boot_list);
717 uc_mgr_free_sequence(&uc_mgr->default_list);
718 uc_mgr_free_value(&uc_mgr->value_list);
719 uc_mgr_free_value(&uc_mgr->variable_list);
720 free(uc_mgr->comment);
721 free(uc_mgr->conf_dir_name);
722 free(uc_mgr->conf_file_name);
723 uc_mgr->comment = NULL;
724 uc_mgr->conf_dir_name = NULL;
725 uc_mgr->conf_file_name = NULL;
726 uc_mgr->active_verb = NULL;
727 INIT_LIST_HEAD(&uc_mgr->active_devices);
728 INIT_LIST_HEAD(&uc_mgr->active_modifiers);
731 void uc_mgr_free(snd_use_case_mgr_t *uc_mgr)
733 snd_config_delete(uc_mgr->local_config);
734 uc_mgr_free_verb(uc_mgr);
735 uc_mgr_free_ctl_list(uc_mgr);
736 free(uc_mgr->card_name);
741 * UCM card list stuff
744 static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER;
745 static LIST_HEAD(ucm_cards);
746 static unsigned int ucm_card_assign;
748 static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number)
750 struct list_head *pos;
751 snd_use_case_mgr_t *uc_mgr;
753 list_for_each(pos, &ucm_cards) {
754 uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list);
755 if (uc_mgr->ucm_card_number == card_number)
761 int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr)
765 pthread_mutex_lock(&ucm_cards_mutex);
766 prev = ucm_card_assign++;
767 while (uc_mgr_card_find(ucm_card_assign)) {
769 ucm_card_assign &= 0xffff;
770 if (ucm_card_assign == prev) {
771 pthread_mutex_unlock(&ucm_cards_mutex);
775 uc_mgr->ucm_card_number = ucm_card_assign;
776 list_add(&uc_mgr->cards_list, &ucm_cards);
777 pthread_mutex_unlock(&ucm_cards_mutex);
781 void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr)
783 pthread_mutex_lock(&ucm_cards_mutex);
784 list_del(&uc_mgr->cards_list);
785 pthread_mutex_unlock(&ucm_cards_mutex);
789 * \brief Get library configuration based on the private ALSA device name
790 * \param name[in] ALSA device name
791 * \retval config A configuration tree or NULL
793 * The returned configuration (non-NULL) should be unreferenced using
794 * snd_config_unref() call.
796 const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name)
800 snd_config_t *config;
801 snd_use_case_mgr_t *uc_mgr;
804 if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.')
806 strncpy(buf, name + 4, 4);
808 err = safe_strtol(buf, &card_num);
809 if (err < 0 || card_num < 0 || card_num > 0xffff)
812 pthread_mutex_lock(&ucm_cards_mutex);
813 uc_mgr = uc_mgr_card_find(card_num);
814 /* non-empty configs are accepted only */
815 if (uc_mgr_has_local_config(uc_mgr)) {
816 config = uc_mgr->local_config;
817 snd_config_ref(config);
819 pthread_mutex_unlock(&ucm_cards_mutex);