1/* 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. 6 * 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. 11 * 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 15 * 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. 22 * 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> 31 */ 32 33#include "ucm_local.h" 34 35void uc_mgr_error(const char *fmt,...) 36{ 37 va_list va; 38 va_start(va, fmt); 39 fprintf(stderr, "ucm: "); 40 vfprintf(stderr, fmt, va); 41 va_end(va); 42} 43 44void uc_mgr_stdout(const char *fmt,...) 45{ 46 va_list va; 47 va_start(va, fmt); 48 vfprintf(stdout, fmt, va); 49 va_end(va); 50} 51 52const char *uc_mgr_sysfs_root(void) 53{ 54 const char *e = getenv("SYSFS_PATH"); 55 if (e == NULL) 56 return "/sys"; 57 if (*e == '\0') 58 uc_error("no sysfs root!"); 59 return e; 60} 61 62struct ctl_list *uc_mgr_get_master_ctl(snd_use_case_mgr_t *uc_mgr) 63{ 64 struct list_head *pos; 65 struct ctl_list *ctl_list = NULL, *ctl_list2; 66 67 list_for_each(pos, &uc_mgr->ctl_list) { 68 ctl_list2 = list_entry(pos, struct ctl_list, list); 69 if (ctl_list2->slave) 70 continue; 71 if (ctl_list) { 72 uc_error("multiple control device names were found!"); 73 return NULL; 74 } 75 ctl_list = ctl_list2; 76 } 77 return ctl_list; 78} 79 80struct ctl_list *uc_mgr_get_ctl_by_card(snd_use_case_mgr_t *uc_mgr, int card) 81{ 82 struct ctl_list *ctl_list; 83 char cname[32]; 84 int err; 85 86 sprintf(cname, "hw:%d", card); 87 err = uc_mgr_open_ctl(uc_mgr, &ctl_list, cname, 1); 88 if (err < 0) 89 return NULL; 90 return ctl_list; 91} 92 93struct ctl_list *uc_mgr_get_ctl_by_name(snd_use_case_mgr_t *uc_mgr, const char *name, int idx) 94{ 95 struct list_head *pos; 96 struct ctl_list *ctl_list; 97 const char *s; 98 int idx2, card; 99 100 idx2 = idx; 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); 104 if (s == NULL) 105 continue; 106 if (strcmp(s, name) == 0) { 107 if (idx2 == 0) 108 return ctl_list; 109 idx2--; 110 } 111 } 112 113 idx2 = idx; 114 card = -1; 115 if (snd_card_next(&card) < 0 || card < 0) 116 return NULL; 117 118 while (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) { 124 if (idx2 == 0) 125 return ctl_list; 126 idx2--; 127 } 128 if (snd_card_next(&card) < 0) 129 break; 130 } 131 132 return NULL; 133} 134 135snd_ctl_t *uc_mgr_get_ctl(snd_use_case_mgr_t *uc_mgr) 136{ 137 struct ctl_list *ctl_list; 138 139 ctl_list = uc_mgr_get_master_ctl(uc_mgr); 140 if (ctl_list) 141 return ctl_list->ctl; 142 return NULL; 143} 144 145static void uc_mgr_free_ctl(struct ctl_list *ctl_list) 146{ 147 struct list_head *pos, *npos; 148 struct ctl_dev *ctl_dev; 149 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); 153 free(ctl_dev); 154 } 155 snd_ctl_card_info_free(ctl_list->ctl_info); 156 free(ctl_list); 157} 158 159void uc_mgr_free_ctl_list(snd_use_case_mgr_t *uc_mgr) 160{ 161 struct list_head *pos, *npos; 162 struct ctl_list *ctl_list; 163 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); 169 } 170} 171 172static int uc_mgr_ctl_add_dev(struct ctl_list *ctl_list, const char *device) 173{ 174 struct list_head *pos; 175 struct ctl_dev *ctl_dev; 176 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) 181 return 0; 182 } 183 184 /* allocate new device name */ 185 ctl_dev = malloc(sizeof(*ctl_dev)); 186 if (ctl_dev == NULL) 187 return -ENOMEM; 188 ctl_dev->device = strdup(device); 189 if (ctl_dev->device == NULL) { 190 free(ctl_dev); 191 return -ENOMEM; 192 } 193 list_add_tail(&ctl_dev->list, &ctl_list->dev_list); 194 return 0; 195} 196 197static 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, 201 const char *device, 202 int slave) 203{ 204 struct ctl_list *cl = NULL; 205 const char *id = snd_ctl_card_info_get_id(info); 206 char dev[MAX_CARD_LONG_NAME]; 207 int err, hit = 0; 208 209 if (id == NULL || id[0] == '\0') 210 return -ENOENT; 211 if (!(*ctl_list)) { 212 cl = malloc(sizeof(*cl)); 213 if (cl == NULL) 214 return -ENOMEM; 215 INIT_LIST_HEAD(&cl->dev_list); 216 cl->ctl = ctl; 217 if (snd_ctl_card_info_malloc(&cl->ctl_info) < 0) { 218 free(cl); 219 return -ENOMEM; 220 } 221 snd_ctl_card_info_copy(cl->ctl_info, info); 222 cl->slave = slave; 223 *ctl_list = cl; 224 } else { 225 if (!slave) 226 (*ctl_list)->slave = slave; 227 } 228 if (card >= 0) { 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); 232 if (err < 0) 233 goto __nomem; 234 } 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); 238 if (err < 0) 239 goto __nomem; 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); 243 if (err < 0) 244 goto __nomem; 245 } 246 247 list_add_tail(&(*ctl_list)->list, &uc_mgr->ctl_list); 248 return 0; 249 250__nomem: 251 if (*ctl_list == cl) { 252 uc_mgr_free_ctl(cl); 253 *ctl_list = NULL; 254 } 255 return -ENOMEM; 256} 257 258int uc_mgr_open_ctl(snd_use_case_mgr_t *uc_mgr, 259 struct ctl_list **ctll, 260 const char *device, 261 int slave) 262{ 263 struct list_head *pos1, *pos2; 264 snd_ctl_t *ctl; 265 struct ctl_list *ctl_list; 266 struct ctl_dev *ctl_dev; 267 snd_ctl_card_info_t *info; 268 const char *id; 269 int err, card, ucm_group, ucm_offset; 270 271 snd_ctl_card_info_alloca(&info); 272 273 ucm_group = _snd_is_ucm_device(device); 274 ucm_offset = ucm_group ? 8 : 0; 275 276 /* cache lookup */ 277 list_for_each(pos1, &uc_mgr->ctl_list) { 278 ctl_list = list_entry(pos1, struct ctl_list, list); 279 if (ctl_list->ucm_group != ucm_group) 280 continue; 281 list_for_each(pos2, &ctl_list->dev_list) { 282 ctl_dev = list_entry(pos2, struct ctl_dev, list); 283 if (strcmp(ctl_dev->device, device + ucm_offset) == 0) { 284 *ctll = ctl_list; 285 if (!slave) 286 ctl_list->slave = 0; 287 return 0; 288 } 289 } 290 } 291 292 err = snd_ctl_open(&ctl, device, 0); 293 if (err < 0) 294 return err; 295 296 id = NULL; 297 err = snd_ctl_card_info(ctl, info); 298 if (err == 0) 299 id = snd_ctl_card_info_get_id(info); 300 if (err < 0 || id == NULL || id[0] == '\0') { 301 uc_error("control hardware info (%s): %s", device, snd_strerror(err)); 302 snd_ctl_close(ctl); 303 return err >= 0 ? -EINVAL : err; 304 } 305 306 /* insert to cache, if just name differs */ 307 list_for_each(pos1, &uc_mgr->ctl_list) { 308 ctl_list = list_entry(pos1, struct ctl_list, list); 309 if (ctl_list->ucm_group != ucm_group) 310 continue; 311 if (strcmp(id, snd_ctl_card_info_get_id(ctl_list->ctl_info)) == 0) { 312 card = snd_card_get_index(id); 313 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, card, info, device + ucm_offset, slave); 314 if (err < 0) 315 goto __nomem; 316 snd_ctl_close(ctl); 317 ctl_list->ucm_group = ucm_group; 318 *ctll = ctl_list; 319 return 0; 320 } 321 } 322 323 ctl_list = NULL; 324 err = uc_mgr_ctl_add(uc_mgr, &ctl_list, ctl, -1, info, device + ucm_offset, slave); 325 if (err < 0) 326 goto __nomem; 327 328 ctl_list->ucm_group = ucm_group; 329 *ctll = ctl_list; 330 return 0; 331 332__nomem: 333 snd_ctl_close(ctl); 334 return -ENOMEM; 335} 336 337const char *uc_mgr_config_dir(int format) 338{ 339 const char *path; 340 341 if (format >= 2) { 342 path = getenv(ALSA_CONFIG_UCM2_VAR); 343 if (!path || path[0] == '\0') 344 path = ALSA_CONFIG_DIR "/ucm2"; 345 } else { 346 path = getenv(ALSA_CONFIG_UCM_VAR); 347 if (!path || path[0] == '\0') 348 path = ALSA_CONFIG_DIR "/ucm"; 349 } 350 return path; 351} 352 353int uc_mgr_config_load_into(int format, const char *file, snd_config_t *top) 354{ 355 FILE *fp; 356 snd_input_t *in; 357 const char *default_paths[2]; 358 int err; 359 360 fp = fopen(file, "r"); 361 if (!fp) { 362 err = -errno; 363 __err_open: 364 uc_error("could not open configuration file %s", file); 365 return err; 366 } 367 err = snd_input_stdio_attach(&in, fp, 1); 368 if (err < 0) 369 goto __err_open; 370 371 default_paths[0] = uc_mgr_config_dir(format); 372 default_paths[1] = NULL; 373 err = _snd_config_load_with_include(top, in, 0, default_paths); 374 if (err < 0) { 375 uc_error("could not load configuration file %s", file); 376 if (in) 377 snd_input_close(in); 378 return err; 379 } 380 err = snd_input_close(in); 381 if (err < 0) 382 return err; 383 return 0; 384} 385 386int uc_mgr_config_load(int format, const char *file, snd_config_t **cfg) 387{ 388 snd_config_t *top; 389 int err; 390 391 err = snd_config_top(&top); 392 if (err < 0) 393 return err; 394 err = uc_mgr_config_load_into(format, file, top); 395 if (err < 0) { 396 snd_config_delete(top); 397 return err; 398 } 399 *cfg = top; 400 return 0; 401} 402 403static void uc_mgr_free_value1(struct ucm_value *val) 404{ 405 free(val->name); 406 free(val->data); 407 list_del(&val->list); 408 free(val); 409} 410 411void uc_mgr_free_value(struct list_head *base) 412{ 413 struct list_head *pos, *npos; 414 struct ucm_value *val; 415 416 list_for_each_safe(pos, npos, base) { 417 val = list_entry(pos, struct ucm_value, list); 418 uc_mgr_free_value1(val); 419 } 420} 421 422void uc_mgr_free_dev_list(struct dev_list *dev_list) 423{ 424 struct list_head *pos, *npos; 425 struct dev_list_node *dlist; 426 427 list_for_each_safe(pos, npos, &dev_list->list) { 428 dlist = list_entry(pos, struct dev_list_node, list); 429 free(dlist->name); 430 list_del(&dlist->list); 431 free(dlist); 432 } 433} 434 435int uc_mgr_put_to_dev_list(struct dev_list *dev_list, const char *name) 436{ 437 struct list_head *pos; 438 struct dev_list_node *dlist; 439 char *n; 440 441 list_for_each(pos, &dev_list->list) { 442 dlist = list_entry(pos, struct dev_list_node, list); 443 if (strcmp(dlist->name, name) == 0) 444 return 0; 445 } 446 447 dlist = calloc(1, sizeof(*dlist)); 448 if (dlist == NULL) 449 return -ENOMEM; 450 n = strdup(name); 451 if (n == NULL) { 452 free(dlist); 453 return -ENOMEM; 454 } 455 dlist->name = n; 456 list_add(&dlist->list, &dev_list->list); 457 return 0; 458} 459 460int uc_mgr_rename_in_dev_list(struct dev_list *dev_list, const char *src, 461 const char *dst) 462{ 463 struct list_head *pos; 464 struct dev_list_node *dlist; 465 char *dst1; 466 467 list_for_each(pos, &dev_list->list) { 468 dlist = list_entry(pos, struct dev_list_node, list); 469 if (strcmp(dlist->name, src) == 0) { 470 dst1 = strdup(dst); 471 if (dst1 == NULL) 472 return -ENOMEM; 473 free(dlist->name); 474 dlist->name = dst1; 475 return 0; 476 } 477 } 478 return -ENODEV; 479} 480 481int uc_mgr_remove_from_dev_list(struct dev_list *dev_list, const char *name) 482{ 483 struct list_head *pos; 484 struct dev_list_node *dlist; 485 486 list_for_each(pos, &dev_list->list) { 487 dlist = list_entry(pos, struct dev_list_node, list); 488 if (strcmp(dlist->name, name) == 0) { 489 free(dlist->name); 490 list_del(&dlist->list); 491 free(dlist); 492 return 0; 493 } 494 } 495 return -ENODEV; 496} 497 498void uc_mgr_free_sequence_element(struct sequence_element *seq) 499{ 500 if (seq == NULL) 501 return; 502 switch (seq->type) { 503 case SEQUENCE_ELEMENT_TYPE_CDEV: 504 free(seq->data.cdev); 505 break; 506 case SEQUENCE_ELEMENT_TYPE_CSET: 507 case SEQUENCE_ELEMENT_TYPE_CSET_NEW: 508 case SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE: 509 case SEQUENCE_ELEMENT_TYPE_CSET_TLV: 510 case SEQUENCE_ELEMENT_TYPE_CTL_REMOVE: 511 free(seq->data.cset); 512 break; 513 case SEQUENCE_ELEMENT_TYPE_SYSSET: 514 free(seq->data.sysw); 515 break; 516 case SEQUENCE_ELEMENT_TYPE_EXEC: 517 case SEQUENCE_ELEMENT_TYPE_SHELL: 518 free(seq->data.exec); 519 break; 520 case SEQUENCE_ELEMENT_TYPE_CFGSAVE: 521 free(seq->data.cfgsave); 522 break; 523 case SEQUENCE_ELEMENT_TYPE_DEV_ENABLE_SEQ: 524 case SEQUENCE_ELEMENT_TYPE_DEV_DISABLE_SEQ: 525 free(seq->data.device); 526 break; 527 default: 528 break; 529 } 530 free(seq); 531} 532 533void uc_mgr_free_sequence(struct list_head *base) 534{ 535 struct list_head *pos, *npos; 536 struct sequence_element *seq; 537 538 list_for_each_safe(pos, npos, base) { 539 seq = list_entry(pos, struct sequence_element, list); 540 list_del(&seq->list); 541 uc_mgr_free_sequence_element(seq); 542 } 543} 544 545void uc_mgr_free_transition_element(struct transition_sequence *tseq) 546{ 547 free(tseq->name); 548 uc_mgr_free_sequence(&tseq->transition_list); 549 free(tseq); 550} 551 552void uc_mgr_free_transition(struct list_head *base) 553{ 554 struct list_head *pos, *npos; 555 struct transition_sequence *tseq; 556 557 list_for_each_safe(pos, npos, base) { 558 tseq = list_entry(pos, struct transition_sequence, list); 559 list_del(&tseq->list); 560 uc_mgr_free_transition_element(tseq); 561 } 562} 563 564void uc_mgr_free_dev_name_list(struct list_head *base) 565{ 566 struct list_head *pos, *npos; 567 struct ucm_dev_name *dev; 568 569 list_for_each_safe(pos, npos, base) { 570 dev = list_entry(pos, struct ucm_dev_name, list); 571 list_del(&dev->list); 572 free(dev->name1); 573 free(dev->name2); 574 free(dev); 575 } 576} 577 578void uc_mgr_free_modifier(struct list_head *base) 579{ 580 struct list_head *pos, *npos; 581 struct use_case_modifier *mod; 582 583 list_for_each_safe(pos, npos, base) { 584 mod = list_entry(pos, struct use_case_modifier, list); 585 free(mod->name); 586 free(mod->comment); 587 uc_mgr_free_sequence(&mod->enable_list); 588 uc_mgr_free_sequence(&mod->disable_list); 589 uc_mgr_free_transition(&mod->transition_list); 590 uc_mgr_free_dev_list(&mod->dev_list); 591 uc_mgr_free_value(&mod->value_list); 592 list_del(&mod->list); 593 free(mod); 594 } 595} 596 597void uc_mgr_free_device(struct use_case_device *dev) 598{ 599 free(dev->name); 600 free(dev->comment); 601 uc_mgr_free_sequence(&dev->enable_list); 602 uc_mgr_free_sequence(&dev->disable_list); 603 uc_mgr_free_transition(&dev->transition_list); 604 uc_mgr_free_dev_list(&dev->dev_list); 605 uc_mgr_free_value(&dev->value_list); 606 list_del(&dev->list); 607 free(dev); 608} 609 610void uc_mgr_free_device_list(struct list_head *base) 611{ 612 struct list_head *pos, *npos; 613 struct use_case_device *dev; 614 615 list_for_each_safe(pos, npos, base) { 616 dev = list_entry(pos, struct use_case_device, list); 617 uc_mgr_free_device(dev); 618 } 619} 620 621int uc_mgr_rename_device(struct use_case_verb *verb, const char *src, 622 const char *dst) 623{ 624 struct use_case_device *device; 625 struct list_head *pos, *npos; 626 char *dst1; 627 628 /* no errors when device is not found */ 629 list_for_each_safe(pos, npos, &verb->device_list) { 630 device = list_entry(pos, struct use_case_device, list); 631 if (strcmp(device->name, src) == 0) { 632 dst1 = strdup(dst); 633 if (dst1 == NULL) 634 return -ENOMEM; 635 free(device->name); 636 device->name = dst1; 637 continue; 638 } 639 uc_mgr_rename_in_dev_list(&device->dev_list, src, dst); 640 } 641 return 0; 642} 643 644int uc_mgr_remove_device(struct use_case_verb *verb, const char *name) 645{ 646 struct use_case_device *device; 647 struct list_head *pos, *npos; 648 int err, found = 0; 649 650 list_for_each_safe(pos, npos, &verb->device_list) { 651 device = list_entry(pos, struct use_case_device, list); 652 if (strcmp(device->name, name) == 0) { 653 uc_mgr_free_device(device); 654 found++; 655 continue; 656 } 657 err = uc_mgr_remove_from_dev_list(&device->dev_list, name); 658 if (err < 0 && err != -ENODEV) 659 return err; 660 if (err == 0) 661 found++; 662 } 663 return found == 0 ? -ENODEV : 0; 664} 665 666const char *uc_mgr_get_variable(snd_use_case_mgr_t *uc_mgr, const char *name) 667{ 668 struct list_head *pos; 669 struct ucm_value *value; 670 671 list_for_each(pos, &uc_mgr->variable_list) { 672 value = list_entry(pos, struct ucm_value, list); 673 if (strcmp(value->name, name) == 0) 674 return value->data; 675 } 676 return NULL; 677} 678 679int uc_mgr_set_variable(snd_use_case_mgr_t *uc_mgr, const char *name, 680 const char *val) 681{ 682 struct list_head *pos; 683 struct ucm_value *curr; 684 char *val2; 685 686 list_for_each(pos, &uc_mgr->variable_list) { 687 curr = list_entry(pos, struct ucm_value, list); 688 if (strcmp(curr->name, name) == 0) { 689 val2 = strdup(val); 690 if (val2 == NULL) 691 return -ENOMEM; 692 free(curr->data); 693 curr->data = val2; 694 return 0; 695 } 696 } 697 698 curr = calloc(1, sizeof(struct ucm_value)); 699 if (curr == NULL) 700 return -ENOMEM; 701 curr->name = strdup(name); 702 if (curr->name == NULL) { 703 free(curr); 704 return -ENOMEM; 705 } 706 curr->data = strdup(val); 707 if (curr->data == NULL) { 708 free(curr->name); 709 free(curr); 710 return -ENOMEM; 711 } 712 list_add_tail(&curr->list, &uc_mgr->variable_list); 713 return 0; 714} 715 716int uc_mgr_delete_variable(snd_use_case_mgr_t *uc_mgr, const char *name) 717{ 718 struct list_head *pos; 719 struct ucm_value *curr; 720 721 list_for_each(pos, &uc_mgr->variable_list) { 722 curr = list_entry(pos, struct ucm_value, list); 723 if (strcmp(curr->name, name) == 0) { 724 uc_mgr_free_value1(curr); 725 return 0; 726 } 727 } 728 729 return -ENOENT; 730} 731 732void uc_mgr_free_verb(snd_use_case_mgr_t *uc_mgr) 733{ 734 struct list_head *pos, *npos; 735 struct use_case_verb *verb; 736 737 if (uc_mgr->local_config) { 738 snd_config_delete(uc_mgr->local_config); 739 uc_mgr->local_config = NULL; 740 } 741 if (uc_mgr->macros) { 742 snd_config_delete(uc_mgr->macros); 743 uc_mgr->macros = NULL; 744 } 745 list_for_each_safe(pos, npos, &uc_mgr->verb_list) { 746 verb = list_entry(pos, struct use_case_verb, list); 747 free(verb->name); 748 free(verb->comment); 749 uc_mgr_free_sequence(&verb->enable_list); 750 uc_mgr_free_sequence(&verb->disable_list); 751 uc_mgr_free_transition(&verb->transition_list); 752 uc_mgr_free_value(&verb->value_list); 753 uc_mgr_free_device_list(&verb->device_list); 754 uc_mgr_free_device_list(&verb->cmpt_device_list); 755 uc_mgr_free_modifier(&verb->modifier_list); 756 uc_mgr_free_dev_name_list(&verb->rename_list); 757 uc_mgr_free_dev_name_list(&verb->remove_list); 758 list_del(&verb->list); 759 free(verb); 760 } 761 uc_mgr_free_sequence(&uc_mgr->fixedboot_list); 762 uc_mgr_free_sequence(&uc_mgr->boot_list); 763 uc_mgr_free_sequence(&uc_mgr->default_list); 764 uc_mgr_free_value(&uc_mgr->value_list); 765 uc_mgr_free_value(&uc_mgr->variable_list); 766 free(uc_mgr->comment); 767 free(uc_mgr->conf_dir_name); 768 free(uc_mgr->conf_file_name); 769 uc_mgr->comment = NULL; 770 uc_mgr->conf_dir_name = NULL; 771 uc_mgr->conf_file_name = NULL; 772 uc_mgr->active_verb = NULL; 773 INIT_LIST_HEAD(&uc_mgr->active_devices); 774 INIT_LIST_HEAD(&uc_mgr->active_modifiers); 775} 776 777void uc_mgr_free(snd_use_case_mgr_t *uc_mgr) 778{ 779 uc_mgr_free_verb(uc_mgr); 780 uc_mgr_free_ctl_list(uc_mgr); 781 free(uc_mgr->card_name); 782 free(uc_mgr); 783} 784 785/* 786 * UCM card list stuff 787 */ 788 789static pthread_mutex_t ucm_cards_mutex = PTHREAD_MUTEX_INITIALIZER; 790static LIST_HEAD(ucm_cards); 791static unsigned int ucm_card_assign; 792 793static snd_use_case_mgr_t *uc_mgr_card_find(unsigned int card_number) 794{ 795 struct list_head *pos; 796 snd_use_case_mgr_t *uc_mgr; 797 798 list_for_each(pos, &ucm_cards) { 799 uc_mgr = list_entry(pos, snd_use_case_mgr_t, cards_list); 800 if (uc_mgr->ucm_card_number == card_number) 801 return uc_mgr; 802 } 803 return NULL; 804} 805 806int uc_mgr_card_open(snd_use_case_mgr_t *uc_mgr) 807{ 808 unsigned int prev; 809 810 pthread_mutex_lock(&ucm_cards_mutex); 811 prev = ucm_card_assign++; 812 while (uc_mgr_card_find(ucm_card_assign)) { 813 ucm_card_assign++; 814 ucm_card_assign &= 0xffff; 815 /* avoid zero card instance number */ 816 if (ucm_card_assign == 0) 817 ucm_card_assign++; 818 if (ucm_card_assign == prev) { 819 pthread_mutex_unlock(&ucm_cards_mutex); 820 return -ENOMEM; 821 } 822 } 823 uc_mgr->ucm_card_number = ucm_card_assign; 824 list_add(&uc_mgr->cards_list, &ucm_cards); 825 pthread_mutex_unlock(&ucm_cards_mutex); 826 return 0; 827} 828 829void uc_mgr_card_close(snd_use_case_mgr_t *uc_mgr) 830{ 831 pthread_mutex_lock(&ucm_cards_mutex); 832 list_del(&uc_mgr->cards_list); 833 pthread_mutex_unlock(&ucm_cards_mutex); 834} 835 836/** 837 * \brief Get library configuration based on the private ALSA device name 838 * \param name[in] ALSA device name 839 * \retval config A configuration tree or NULL 840 * 841 * The returned configuration (non-NULL) should be unreferenced using 842 * snd_config_unref() call. 843 */ 844const char *uc_mgr_alibcfg_by_device(snd_config_t **top, const char *name) 845{ 846 char buf[5]; 847 long card_num; 848 snd_config_t *config; 849 snd_use_case_mgr_t *uc_mgr; 850 int err; 851 852 if (strncmp(name, "_ucm", 4) || strlen(name) < 12 || name[8] != '.') 853 return NULL; 854 strncpy(buf, name + 4, 4); 855 buf[4] = '\0'; 856 err = safe_strtol_base(buf, &card_num, 16); 857 if (err < 0 || card_num < 0 || card_num > 0xffff) 858 return NULL; 859 config = NULL; 860 pthread_mutex_lock(&ucm_cards_mutex); 861 uc_mgr = uc_mgr_card_find(card_num); 862 /* non-empty configs are accepted only */ 863 if (uc_mgr_has_local_config(uc_mgr)) { 864 config = uc_mgr->local_config; 865 snd_config_ref(config); 866 } 867 pthread_mutex_unlock(&ucm_cards_mutex); 868 if (!config) 869 return NULL; 870 *top = config; 871 return name + 9; 872} 873