1/** 2 * \file control/setup.c 3 * \brief Routines to setup control primitives from configuration 4 * \author Abramo Bagnara <abramo@alsa-project.org> 5 * \author Jaroslav Kysela <perex@perex.cz> 6 * \date 2001 7 * 8 * Routines to setup control primitives from configuration 9 */ 10/* 11 * Control Interface - routines for setup from configuration 12 * Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org> 13 * Jaroslav Kysela <perex@perex.cz> 14 * 15 * 16 * This library is free software; you can redistribute it and/or modify 17 * it under the terms of the GNU Lesser General Public License as 18 * published by the Free Software Foundation; either version 2.1 of 19 * the License, or (at your option) any later version. 20 * 21 * This program is distributed in the hope that it will be useful, 22 * but WITHOUT ANY WARRANTY; without even the implied warranty of 23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 * GNU Lesser General Public License for more details. 25 * 26 * You should have received a copy of the GNU Lesser General Public 27 * License along with this library; if not, write to the Free Software 28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 29 * 30 */ 31 32#include "local.h" 33#include <stdio.h> 34#include <stdlib.h> 35#include <stdarg.h> 36#include <unistd.h> 37#include <string.h> 38#include <ctype.h> 39 40#ifndef DOC_HIDDEN 41typedef struct { 42 unsigned int lock: 1; 43 unsigned int preserve: 1; 44 snd_ctl_elem_id_t *id; 45 snd_ctl_elem_info_t *info; 46 snd_ctl_elem_value_t *val; 47 snd_ctl_elem_value_t *mask; 48 snd_ctl_elem_value_t *old; 49 struct list_head list; 50} snd_sctl_elem_t; 51 52struct _snd_sctl { 53 int mode; 54 snd_ctl_t *ctl; 55 struct list_head elems; 56}; 57#endif /* DOC_HIDDEN */ 58 59static int free_elems(snd_sctl_t *h) 60{ 61 int err = 0; 62 while (!list_empty(&h->elems)) { 63 snd_sctl_elem_t *elem = list_entry(h->elems.next, snd_sctl_elem_t, list); 64 snd_ctl_elem_id_free(elem->id); 65 snd_ctl_elem_info_free(elem->info); 66 snd_ctl_elem_value_free(elem->val); 67 snd_ctl_elem_value_free(elem->mask); 68 snd_ctl_elem_value_free(elem->old); 69 list_del(&elem->list); 70 free(elem); 71 } 72 if ((h->mode & SND_SCTL_NOFREE) == 0) 73 err = snd_ctl_close(h->ctl); 74 free(h); 75 return err; 76} 77 78/** 79 * \brief Install given values to control elements 80 * \param h Setup control handle 81 * \result zero if success, otherwise a negative error code 82 */ 83int snd_sctl_install(snd_sctl_t *h) 84{ 85 struct list_head *pos; 86 int err; 87 unsigned int k; 88 assert(h); 89 list_for_each(pos, &h->elems) { 90 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list); 91 unsigned int count; 92 snd_ctl_elem_type_t type; 93 if (elem->lock) { 94 err = snd_ctl_elem_lock(h->ctl, elem->id); 95 if (err < 0) { 96 SNDERR("Cannot lock ctl elem"); 97 return err; 98 } 99 } 100 err = snd_ctl_elem_read(h->ctl, elem->old); 101 if (err < 0) { 102 SNDERR("Cannot read ctl elem"); 103 return err; 104 } 105 count = snd_ctl_elem_info_get_count(elem->info); 106 type = snd_ctl_elem_info_get_type(elem->info); 107 switch (type) { 108 case SND_CTL_ELEM_TYPE_BOOLEAN: 109 for (k = 0; k < count; ++k) { 110 int old, val, mask; 111 old = snd_ctl_elem_value_get_boolean(elem->old, k); 112 mask = snd_ctl_elem_value_get_boolean(elem->mask, k); 113 old &= ~mask; 114 if (old) { 115 val = snd_ctl_elem_value_get_boolean(elem->val, k); 116 val |= old; 117 snd_ctl_elem_value_set_boolean(elem->val, k, val); 118 } 119 } 120 break; 121 case SND_CTL_ELEM_TYPE_INTEGER: 122 for (k = 0; k < count; ++k) { 123 long old, val, mask; 124 old = snd_ctl_elem_value_get_integer(elem->old, k); 125 mask = snd_ctl_elem_value_get_integer(elem->mask, k); 126 old &= ~mask; 127 if (old) { 128 val = snd_ctl_elem_value_get_integer(elem->val, k); 129 val |= old; 130 snd_ctl_elem_value_set_integer(elem->val, k, val); 131 } 132 } 133 break; 134 case SND_CTL_ELEM_TYPE_ENUMERATED: 135 for (k = 0; k < count; ++k) { 136 unsigned int old, val, mask; 137 old = snd_ctl_elem_value_get_enumerated(elem->old, k); 138 mask = snd_ctl_elem_value_get_enumerated(elem->mask, k); 139 old &= ~mask; 140 if (old) { 141 val = snd_ctl_elem_value_get_enumerated(elem->val, k); 142 val |= old; 143 snd_ctl_elem_value_set_enumerated(elem->val, k, val); 144 } 145 } 146 break; 147 case SND_CTL_ELEM_TYPE_IEC958: 148 count = sizeof(snd_aes_iec958_t); 149 /* Fall through */ 150 case SND_CTL_ELEM_TYPE_BYTES: 151 for (k = 0; k < count; ++k) { 152 unsigned char old, val, mask; 153 old = snd_ctl_elem_value_get_byte(elem->old, k); 154 mask = snd_ctl_elem_value_get_byte(elem->mask, k); 155 old &= ~mask; 156 if (old) { 157 val = snd_ctl_elem_value_get_byte(elem->val, k); 158 val |= old; 159 snd_ctl_elem_value_set_byte(elem->val, k, val); 160 } 161 } 162 break; 163 default: 164 assert(0); 165 break; 166 } 167 err = snd_ctl_elem_write(h->ctl, elem->val); 168 if (err < 0) { 169 SNDERR("Cannot write ctl elem"); 170 return err; 171 } 172 } 173 return 0; 174} 175 176/** 177 * \brief Remove (restore) previous values from control elements 178 * \param h Setup control handle 179 * \result zero if success, otherwise a negative error code 180 */ 181int snd_sctl_remove(snd_sctl_t *h) 182{ 183 struct list_head *pos; 184 int err; 185 assert(h); 186 list_for_each(pos, &h->elems) { 187 snd_sctl_elem_t *elem = list_entry(pos, snd_sctl_elem_t, list); 188 if (elem->lock) { 189 err = snd_ctl_elem_unlock(h->ctl, elem->id); 190 if (err < 0) { 191 SNDERR("Cannot unlock ctl elem"); 192 return err; 193 } 194 } 195 /* Only restore the old value if it differs from the requested 196 * value, because if it has changed restoring the old value 197 * overrides the change. Take for example, a voice modem with 198 * a .conf that sets preserve off-hook. Start playback (on-hook 199 * to off-hook), start record (off-hook to off-hook), stop 200 * playback (off-hook to restore on-hook), stop record (on-hook 201 * to restore off-hook), Clearly you don't want to leave the 202 * modem "on the phone" now that there isn't any playback or 203 * recording active. 204 */ 205 if (elem->preserve && snd_ctl_elem_value_compare(elem->val, elem->old)) { 206 err = snd_ctl_elem_write(h->ctl, elem->old); 207 if (err < 0) { 208 SNDERR("Cannot restore ctl elem"); 209 return err; 210 } 211 } 212 } 213 return 0; 214} 215 216static int snd_config_get_ctl_elem_enumerated(snd_config_t *n, snd_ctl_t *ctl, 217 snd_ctl_elem_info_t *info) 218{ 219 const char *str; 220 long val; 221 unsigned int idx, items; 222 switch (snd_config_get_type(n)) { 223 case SND_CONFIG_TYPE_INTEGER: 224 snd_config_get_integer(n, &val); 225 return val; 226 case SND_CONFIG_TYPE_STRING: 227 snd_config_get_string(n, &str); 228 break; 229 default: 230 return -1; 231 } 232 items = snd_ctl_elem_info_get_items(info); 233 for (idx = 0; idx < items; idx++) { 234 int err; 235 snd_ctl_elem_info_set_item(info, idx); 236 err = snd_ctl_elem_info(ctl, info); 237 if (err < 0) { 238 SNDERR("Cannot obtain info for CTL elem"); 239 return err; 240 } 241 if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0) 242 return idx; 243 } 244 return -1; 245} 246 247static int snd_config_get_ctl_elem_value(snd_config_t *conf, 248 snd_ctl_t *ctl, 249 snd_ctl_elem_value_t *val, 250 snd_ctl_elem_value_t *mask, 251 snd_ctl_elem_info_t *info) 252{ 253 int err; 254 snd_config_iterator_t i, next; 255 snd_ctl_elem_id_t id = {0}; 256 snd_ctl_elem_type_t type; 257 unsigned int count; 258 long v; 259 long idx; 260 snd_ctl_elem_value_get_id(val, &id); 261 count = snd_ctl_elem_info_get_count(info); 262 type = snd_ctl_elem_info_get_type(info); 263 if (count == 1) { 264 switch (type) { 265 case SND_CTL_ELEM_TYPE_BOOLEAN: 266 v = snd_config_get_bool(conf); 267 if (v >= 0) { 268 snd_ctl_elem_value_set_boolean(val, 0, v); 269 if (mask) 270 snd_ctl_elem_value_set_boolean(mask, 0, 1); 271 return 0; 272 } 273 break; 274 case SND_CTL_ELEM_TYPE_INTEGER: 275 err = snd_config_get_integer(conf, &v); 276 if (err == 0) { 277 snd_ctl_elem_value_set_integer(val, 0, v); 278 if (mask) 279 snd_ctl_elem_value_set_integer(mask, 0, ~0L); 280 return 0; 281 } 282 break; 283 case SND_CTL_ELEM_TYPE_ENUMERATED: 284 v = snd_config_get_ctl_elem_enumerated(conf, ctl, info); 285 if (v >= 0) { 286 snd_ctl_elem_value_set_enumerated(val, 0, v); 287 if (mask) 288 snd_ctl_elem_value_set_enumerated(mask, 0, ~0); 289 return 0; 290 } 291 break; 292 case SND_CTL_ELEM_TYPE_BYTES: 293 case SND_CTL_ELEM_TYPE_IEC958: 294 break; 295 default: 296 SNDERR("Unknown control type: %d", type); 297 return -EINVAL; 298 } 299 } 300 switch (type) { 301 case SND_CTL_ELEM_TYPE_IEC958: 302 count = sizeof(snd_aes_iec958_t); 303 /* Fall through */ 304 case SND_CTL_ELEM_TYPE_BYTES: 305 { 306 const char *buf; 307 err = snd_config_get_string(conf, &buf); 308 if (err >= 0) { 309 int c1 = 0; 310 unsigned int len = strlen(buf); 311 unsigned int idx = 0; 312 if (len % 2 != 0 || len > count * 2) { 313 _bad_content: 314 SNDERR("bad value content"); 315 return -EINVAL; 316 } 317 while (*buf) { 318 int c = *buf++; 319 if (c >= '0' && c <= '9') 320 c -= '0'; 321 else if (c >= 'a' && c <= 'f') 322 c = c - 'a' + 10; 323 else if (c >= 'A' && c <= 'F') 324 c = c - 'A' + 10; 325 else { 326 goto _bad_content; 327 } 328 if (idx % 2 == 1) { 329 snd_ctl_elem_value_set_byte(val, idx / 2, c1 << 4 | c); 330 if (mask) 331 snd_ctl_elem_value_set_byte(mask, idx / 2, 0xff); 332 } else 333 c1 = c; 334 idx++; 335 } 336 return 0; 337 } 338 } 339 default: 340 break; 341 } 342 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) { 343 SNDERR("bad value type"); 344 return -EINVAL; 345 } 346 347 snd_config_for_each(i, next, conf) { 348 snd_config_t *n = snd_config_iterator_entry(i); 349 const char *id; 350 if (snd_config_get_id(n, &id) < 0) 351 continue; 352 err = safe_strtol(id, &idx); 353 if (err < 0 || idx < 0 || (unsigned int) idx >= count) { 354 SNDERR("bad value index"); 355 return -EINVAL; 356 } 357 switch (type) { 358 case SND_CTL_ELEM_TYPE_BOOLEAN: 359 v = snd_config_get_bool(n); 360 if (v < 0) 361 goto _bad_content; 362 snd_ctl_elem_value_set_boolean(val, idx, v); 363 if (mask) 364 snd_ctl_elem_value_set_boolean(mask, idx, 1); 365 break; 366 case SND_CTL_ELEM_TYPE_INTEGER: 367 err = snd_config_get_integer(n, &v); 368 if (err < 0) 369 goto _bad_content; 370 snd_ctl_elem_value_set_integer(val, idx, v); 371 if (mask) 372 snd_ctl_elem_value_set_integer(mask, idx, ~0L); 373 break; 374 case SND_CTL_ELEM_TYPE_ENUMERATED: 375 v = snd_config_get_ctl_elem_enumerated(n, ctl, info); 376 if (v < 0) 377 goto _bad_content; 378 snd_ctl_elem_value_set_enumerated(val, idx, v); 379 if (mask) 380 snd_ctl_elem_value_set_enumerated(mask, idx, ~0); 381 break; 382 case SND_CTL_ELEM_TYPE_BYTES: 383 case SND_CTL_ELEM_TYPE_IEC958: 384 err = snd_config_get_integer(n, &v); 385 if (err < 0 || v < 0 || v > 255) 386 goto _bad_content; 387 snd_ctl_elem_value_set_byte(val, idx, v); 388 if (mask) 389 snd_ctl_elem_value_set_byte(mask, idx, 0xff); 390 break; 391 default: 392 break; 393 } 394 } 395 return 0; 396} 397 398static int add_elem(snd_sctl_t *h, snd_config_t *_conf, snd_config_t *private_data, int *quit) 399{ 400 snd_config_t *conf; 401 snd_config_iterator_t i, next; 402 int iface = SND_CTL_ELEM_IFACE_MIXER; 403 const char *name = NULL; 404 long index = 0; 405 long device = -1; 406 long subdevice = -1; 407 int lock = 0; 408 int preserve = 0; 409 int optional = 0; 410 int skip_rest = 0; 411 snd_config_t *value = NULL, *mask = NULL; 412 snd_sctl_elem_t *elem = NULL; 413 int err; 414 err = snd_config_expand(_conf, _conf, NULL, private_data, &conf); 415 if (err < 0) 416 return err; 417 snd_config_for_each(i, next, conf) { 418 snd_config_t *n = snd_config_iterator_entry(i); 419 const char *id; 420 if (snd_config_get_id(n, &id) < 0) 421 continue; 422 if (strcmp(id, "comment") == 0) 423 continue; 424 if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) { 425 const char *ptr; 426 if ((err = snd_config_get_string(n, &ptr)) < 0) { 427 SNDERR("field %s is not a string", id); 428 goto _err; 429 } 430 if ((err = snd_config_get_ctl_iface_ascii(ptr)) < 0) { 431 SNDERR("Invalid value for '%s'", id); 432 goto _err; 433 } 434 iface = err; 435 continue; 436 } 437 if (strcmp(id, "name") == 0) { 438 if ((err = snd_config_get_string(n, &name)) < 0) { 439 SNDERR("field %s is not a string", id); 440 goto _err; 441 } 442 continue; 443 } 444 if (strcmp(id, "index") == 0) { 445 if ((err = snd_config_get_integer(n, &index)) < 0) { 446 SNDERR("field %s is not an integer", id); 447 goto _err; 448 } 449 continue; 450 } 451 if (strcmp(id, "device") == 0) { 452 if ((err = snd_config_get_integer(n, &device)) < 0) { 453 SNDERR("field %s is not an integer", id); 454 goto _err; 455 } 456 continue; 457 } 458 if (strcmp(id, "subdevice") == 0) { 459 if ((err = snd_config_get_integer(n, &subdevice)) < 0) { 460 SNDERR("field %s is not an integer", id); 461 goto _err; 462 } 463 continue; 464 } 465 if (strcmp(id, "lock") == 0) { 466 err = snd_config_get_bool(n); 467 if (err < 0) 468 goto _err; 469 lock = err; 470 continue; 471 } 472 if (strcmp(id, "preserve") == 0) { 473 err = snd_config_get_bool(n); 474 if (err < 0) 475 goto _err; 476 preserve = err; 477 continue; 478 } 479 if (strcmp(id, "value") == 0) { 480 value = n; 481 continue; 482 } 483 if (strcmp(id, "mask") == 0) { 484 mask = n; 485 continue; 486 } 487 if (strcmp(id, "optional") == 0) { 488 err = snd_config_get_bool(n); 489 if (err < 0) 490 goto _err; 491 optional = err; 492 continue; 493 } 494 if (strcmp(id, "skip_rest") == 0) { 495 err = snd_config_get_bool(n); 496 if (err < 0) 497 goto _err; 498 skip_rest = err; 499 continue; 500 } 501 SNDERR("Unknown field %s", id); 502 return -EINVAL; 503 } 504 if (name == NULL) { 505 SNDERR("Missing control name"); 506 err = -EINVAL; 507 goto _err; 508 } 509 if (value == NULL) { 510 SNDERR("Missing control value"); 511 err = -EINVAL; 512 goto _err; 513 } 514 if (device < 0) 515 device = 0; 516 if (subdevice < 0) 517 subdevice = 0; 518 elem = calloc(1, sizeof(*elem)); 519 if (!elem) 520 return -ENOMEM; 521 err = snd_ctl_elem_id_malloc(&elem->id); 522 if (err < 0) 523 goto _err; 524 err = snd_ctl_elem_info_malloc(&elem->info); 525 if (err < 0) 526 goto _err; 527 err = snd_ctl_elem_value_malloc(&elem->val); 528 if (err < 0) 529 goto _err; 530 err = snd_ctl_elem_value_malloc(&elem->mask); 531 if (err < 0) 532 goto _err; 533 err = snd_ctl_elem_value_malloc(&elem->old); 534 if (err < 0) 535 goto _err; 536 elem->lock = lock; 537 elem->preserve = preserve; 538 snd_ctl_elem_id_set_interface(elem->id, iface); 539 snd_ctl_elem_id_set_name(elem->id, name); 540 snd_ctl_elem_id_set_index(elem->id, index); 541 snd_ctl_elem_id_set_device(elem->id, device); 542 snd_ctl_elem_id_set_subdevice(elem->id, subdevice); 543 snd_ctl_elem_info_set_id(elem->info, elem->id); 544 err = snd_ctl_elem_info(h->ctl, elem->info); 545 if (err < 0) { 546 if (! optional) 547 SNDERR("Cannot obtain info for CTL elem (%s,'%s',%li,%li,%li): %s", snd_ctl_elem_iface_name(iface), name, index, device, subdevice, snd_strerror(err)); 548 goto _err; 549 } else { 550 if (skip_rest) 551 *quit = 1; 552 } 553 snd_ctl_elem_value_set_id(elem->val, elem->id); 554 snd_ctl_elem_value_set_id(elem->old, elem->id); 555 if (mask) { 556 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, NULL, elem->info); 557 if (err < 0) 558 goto _err; 559 err = snd_config_get_ctl_elem_value(mask, h->ctl, elem->mask, NULL, elem->info); 560 if (err < 0) 561 goto _err; 562 } else { 563 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info); 564 if (err < 0) 565 goto _err; 566 } 567 568 err = snd_config_get_ctl_elem_value(value, h->ctl, elem->val, elem->mask, elem->info); 569 if (err < 0) 570 goto _err; 571 list_add_tail(&elem->list, &h->elems); 572 573 _err: 574 if (err < 0 && elem) { 575 if (elem->id) 576 snd_ctl_elem_id_free(elem->id); 577 if (elem->info) 578 snd_ctl_elem_info_free(elem->info); 579 if (elem->val) 580 snd_ctl_elem_value_free(elem->val); 581 if (elem->mask) 582 snd_ctl_elem_value_free(elem->mask); 583 if (elem->old) 584 snd_ctl_elem_value_free(elem->old); 585 free(elem); 586 if (err != -ENOMEM && optional) 587 err = 0; /* ignore the error */ 588 } 589 if (conf) 590 snd_config_delete(conf); 591 return err; 592} 593 594/** 595 * \brief Build setup control handle 596 * \param sctl Result - setup control handle 597 * \param handle Master control handle 598 * \param conf Setup configuration 599 * \param private_data Private data for runtime evaluation 600 * \param mode Build mode - SND_SCTL_xxxx 601 * \result zero if success, otherwise a negative error code 602 */ 603int snd_sctl_build(snd_sctl_t **sctl, snd_ctl_t *handle, snd_config_t *conf, snd_config_t *private_data, int mode) 604{ 605 snd_sctl_t *h; 606 snd_config_iterator_t i, next; 607 int err, quit = 0; 608 609 assert(sctl); 610 assert(handle); 611 assert(conf); 612 *sctl = NULL; 613 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) 614 return -EINVAL; 615 h = calloc(1, sizeof(*h)); 616 if (!h) { 617 if (mode & SND_SCTL_NOFREE) 618 return -ENOMEM; 619 snd_ctl_close(handle); 620 return -ENOMEM; 621 } 622 h->mode = mode; 623 h->ctl = handle; 624 INIT_LIST_HEAD(&h->elems); 625 snd_config_for_each(i, next, conf) { 626 snd_config_t *n = snd_config_iterator_entry(i); 627 err = add_elem(h, n, private_data, &quit); 628 if (err < 0) { 629 free_elems(h); 630 return err; 631 } 632 if (quit) 633 break; 634 } 635 *sctl = h; 636 return 0; 637} 638 639/** 640 * \brief Free setup control handle 641 * \param sctl Setup control handle 642 * \result zero if success, otherwise a negative error code 643 */ 644int snd_sctl_free(snd_sctl_t *sctl) 645{ 646 assert(sctl); 647 return free_elems(sctl); 648} 649