1 /* 2 * Advanced Linux Sound Architecture Control Program 3 * Copyright (c) by Abramo Bagnara <abramo (at) alsa-project.org> 4 * Jaroslav Kysela <perex (at) perex.cz> 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23 #include "aconfig.h" 24 #include "version.h" 25 #include <getopt.h> 26 #include <stdarg.h> 27 #include <stdio.h> 28 #include <assert.h> 29 #include <errno.h> 30 #include <alsa/asoundlib.h> 31 #include "alsactl.h" 32 33 34 #define ARRAY_SIZE(a) (sizeof (a) / sizeof (a)[0]) 35 36 37 static char *id_str(snd_ctl_elem_id_t *id) 38 { 39 static char str[128]; 40 assert(id); 41 sprintf(str, "%i,%i,%i,%s,%i", 42 snd_ctl_elem_id_get_interface(id), 43 snd_ctl_elem_id_get_device(id), 44 snd_ctl_elem_id_get_subdevice(id), 45 snd_ctl_elem_id_get_name(id), 46 snd_ctl_elem_id_get_index(id)); 47 return str; 48 } 49 50 static char *num_str(long n) 51 { 52 static char str[32]; 53 sprintf(str, "%ld", n); 54 return str; 55 } 56 57 static int snd_config_integer_add(snd_config_t *father, char *id, long integer) 58 { 59 int err; 60 snd_config_t *leaf; 61 err = snd_config_make_integer(&leaf, id); 62 if (err < 0) 63 return err; 64 err = snd_config_add(father, leaf); 65 if (err < 0) { 66 snd_config_delete(leaf); 67 return err; 68 } 69 err = snd_config_set_integer(leaf, integer); 70 if (err < 0) { 71 snd_config_delete(leaf); 72 return err; 73 } 74 return 0; 75 } 76 77 static int snd_config_integer64_add(snd_config_t *father, char *id, long long integer) 78 { 79 int err; 80 snd_config_t *leaf; 81 err = snd_config_make_integer64(&leaf, id); 82 if (err < 0) 83 return err; 84 err = snd_config_add(father, leaf); 85 if (err < 0) { 86 snd_config_delete(leaf); 87 return err; 88 } 89 err = snd_config_set_integer64(leaf, integer); 90 if (err < 0) { 91 snd_config_delete(leaf); 92 return err; 93 } 94 return 0; 95 } 96 97 static int snd_config_string_add(snd_config_t *father, const char *id, const char *string) 98 { 99 int err; 100 snd_config_t *leaf; 101 err = snd_config_make_string(&leaf, id); 102 if (err < 0) 103 return err; 104 err = snd_config_add(father, leaf); 105 if (err < 0) { 106 snd_config_delete(leaf); 107 return err; 108 } 109 err = snd_config_set_string(leaf, string); 110 if (err < 0) { 111 snd_config_delete(leaf); 112 return err; 113 } 114 return 0; 115 } 116 117 static int snd_config_compound_add(snd_config_t *father, const char *id, int join, 118 snd_config_t **node) 119 { 120 int err; 121 snd_config_t *leaf; 122 err = snd_config_make_compound(&leaf, id, join); 123 if (err < 0) 124 return err; 125 err = snd_config_add(father, leaf); 126 if (err < 0) { 127 snd_config_delete(leaf); 128 return err; 129 } 130 *node = leaf; 131 return 0; 132 } 133 134 #define MAX_USER_TLV_SIZE 64 135 136 static char *tlv_to_str(unsigned int *tlv) 137 { 138 int i, len = tlv[1] / 4 + 2; 139 char *s, *p; 140 141 if (len >= MAX_USER_TLV_SIZE) 142 return NULL; 143 s = malloc(len * 8 + 1); 144 if (! s) 145 return NULL; 146 p = s; 147 for (i = 0; i < len; i++) { 148 sprintf(p, "%08x", tlv[i]); 149 p += 8; 150 } 151 return s; 152 } 153 154 static unsigned int *str_to_tlv(const char *s) 155 { 156 int i, j, c, len; 157 unsigned int *tlv; 158 159 len = strlen(s); 160 if (len % 8) /* aligned to 4 bytes (= 8 letters) */ 161 return NULL; 162 len /= 8; 163 if (len > MAX_USER_TLV_SIZE) 164 return NULL; 165 tlv = malloc(sizeof(int) * len); 166 if (! tlv) 167 return NULL; 168 for (i = 0; i < len; i++) { 169 tlv[i] = 0; 170 for (j = 0; j < 8; j++) { 171 if ((c = hextodigit(*s++)) < 0) { 172 free(tlv); 173 return NULL; 174 } 175 tlv[i] = (tlv[i] << 4) | c; 176 } 177 } 178 return tlv; 179 } 180 181 /* 182 * add the TLV string and dB ranges to comment fields 183 */ 184 static int add_tlv_comments(snd_ctl_t *handle, snd_ctl_elem_id_t *id, 185 snd_ctl_elem_info_t *info, snd_config_t *comment) 186 { 187 unsigned int tlv[MAX_USER_TLV_SIZE]; 188 unsigned int *db; 189 long dbmin, dbmax; 190 int err; 191 192 if (snd_ctl_elem_tlv_read(handle, id, tlv, sizeof(tlv)) < 0) 193 return 0; /* ignore error */ 194 195 if (snd_ctl_elem_info_is_tlv_writable(info)) { 196 char *s = tlv_to_str(tlv); 197 if (s) { 198 err = snd_config_string_add(comment, "tlv", s); 199 if (err < 0) { 200 error("snd_config_string_add: %s", snd_strerror(err)); 201 return err; 202 } 203 free(s); 204 } 205 } 206 207 err = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &db); 208 if (err <= 0) 209 return 0; 210 211 snd_tlv_get_dB_range(db, snd_ctl_elem_info_get_min(info), 212 snd_ctl_elem_info_get_max(info), 213 &dbmin, &dbmax); 214 if (err < 0) 215 return err; 216 snd_config_integer_add(comment, "dbmin", dbmin); 217 snd_config_integer_add(comment, "dbmax", dbmax); 218 return 0; 219 } 220 221 static int get_control(snd_ctl_t *handle, snd_ctl_elem_id_t *id, snd_config_t *top) 222 { 223 snd_ctl_elem_value_t *ctl; 224 snd_ctl_elem_info_t *info; 225 snd_config_t *control, *comment, *item, *value; 226 const char *s; 227 char buf[256]; 228 unsigned int idx; 229 int err; 230 unsigned int device, subdevice, index; 231 const char *name; 232 snd_ctl_elem_type_t type; 233 unsigned int count; 234 snd_ctl_elem_value_alloca(&ctl); 235 snd_ctl_elem_info_alloca(&info); 236 snd_ctl_elem_info_set_id(info, id); 237 err = snd_ctl_elem_info(handle, info); 238 if (err < 0) { 239 error("Cannot read control info '%s': %s", id_str(id), snd_strerror(err)); 240 return err; 241 } 242 243 if (snd_ctl_elem_info_is_inactive(info) || 244 !snd_ctl_elem_info_is_readable(info)) 245 return 0; 246 snd_ctl_elem_value_set_id(ctl, id); 247 err = snd_ctl_elem_read(handle, ctl); 248 if (err < 0) { 249 error("Cannot read control '%s': %s", id_str(id), snd_strerror(err)); 250 return err; 251 } 252 253 err = snd_config_compound_add(top, num_str(snd_ctl_elem_info_get_numid(info)), 0, &control); 254 if (err < 0) { 255 error("snd_config_compound_add: %s", snd_strerror(err)); 256 return err; 257 } 258 err = snd_config_compound_add(control, "comment", 1, &comment); 259 if (err < 0) { 260 error("snd_config_compound_add: %s", snd_strerror(err)); 261 return err; 262 } 263 264 buf[0] = '\0'; 265 buf[1] = '\0'; 266 if (snd_ctl_elem_info_is_readable(info)) 267 strcat(buf, " read"); 268 if (snd_ctl_elem_info_is_writable(info)) 269 strcat(buf, " write"); 270 if (snd_ctl_elem_info_is_inactive(info)) 271 strcat(buf, " inactive"); 272 if (snd_ctl_elem_info_is_volatile(info)) 273 strcat(buf, " volatile"); 274 if (snd_ctl_elem_info_is_locked(info)) 275 strcat(buf, " locked"); 276 if (snd_ctl_elem_info_is_user(info)) 277 strcat(buf, " user"); 278 err = snd_config_string_add(comment, "access", buf + 1); 279 if (err < 0) { 280 error("snd_config_string_add: %s", snd_strerror(err)); 281 return err; 282 } 283 284 type = snd_ctl_elem_info_get_type(info); 285 device = snd_ctl_elem_info_get_device(info); 286 subdevice = snd_ctl_elem_info_get_subdevice(info); 287 index = snd_ctl_elem_info_get_index(info); 288 name = snd_ctl_elem_info_get_name(info); 289 count = snd_ctl_elem_info_get_count(info); 290 s = snd_ctl_elem_type_name(type); 291 err = snd_config_string_add(comment, "type", s); 292 if (err < 0) { 293 error("snd_config_string_add: %s", snd_strerror(err)); 294 return err; 295 } 296 err = snd_config_integer_add(comment, "count", count); 297 if (err < 0) { 298 error("snd_config_integer_add: %s", snd_strerror(err)); 299 return err; 300 } 301 302 switch (type) { 303 case SND_CTL_ELEM_TYPE_BOOLEAN: 304 break; 305 case SND_CTL_ELEM_TYPE_INTEGER: 306 { 307 long min = snd_ctl_elem_info_get_min(info); 308 long max = snd_ctl_elem_info_get_max(info); 309 long step = snd_ctl_elem_info_get_step(info); 310 if (step) 311 sprintf(buf, "%li - %li (step %li)", min, max, step); 312 else 313 sprintf(buf, "%li - %li", min, max); 314 err = snd_config_string_add(comment, "range", buf); 315 if (err < 0) { 316 error("snd_config_string_add: %s", snd_strerror(err)); 317 return err; 318 } 319 if (snd_ctl_elem_info_is_tlv_readable(info)) { 320 err = add_tlv_comments(handle, id, info, comment); 321 if (err < 0) 322 return err; 323 } 324 break; 325 } 326 case SND_CTL_ELEM_TYPE_INTEGER64: 327 { 328 long long min = snd_ctl_elem_info_get_min64(info); 329 long long max = snd_ctl_elem_info_get_max64(info); 330 long long step = snd_ctl_elem_info_get_step64(info); 331 if (step) 332 sprintf(buf, "%Li - %Li (step %Li)", min, max, step); 333 else 334 sprintf(buf, "%Li - %Li", min, max); 335 err = snd_config_string_add(comment, "range", buf); 336 if (err < 0) { 337 error("snd_config_string_add: %s", snd_strerror(err)); 338 return err; 339 } 340 break; 341 } 342 case SND_CTL_ELEM_TYPE_ENUMERATED: 343 { 344 unsigned int items; 345 err = snd_config_compound_add(comment, "item", 1, &item); 346 if (err < 0) { 347 error("snd_config_compound_add: %s", snd_strerror(err)); 348 return err; 349 } 350 items = snd_ctl_elem_info_get_items(info); 351 for (idx = 0; idx < items; idx++) { 352 snd_ctl_elem_info_set_item(info, idx); 353 err = snd_ctl_elem_info(handle, info); 354 if (err < 0) { 355 error("snd_ctl_card_info: %s", snd_strerror(err)); 356 return err; 357 } 358 err = snd_config_string_add(item, num_str(idx), snd_ctl_elem_info_get_item_name(info)); 359 if (err < 0) { 360 error("snd_config_string_add: %s", snd_strerror(err)); 361 return err; 362 } 363 } 364 break; 365 } 366 default: 367 break; 368 } 369 s = snd_ctl_elem_iface_name(snd_ctl_elem_info_get_interface(info)); 370 err = snd_config_string_add(control, "iface", s); 371 if (err < 0) { 372 error("snd_config_string_add: %s", snd_strerror(err)); 373 return err; 374 } 375 if (device != 0) { 376 err = snd_config_integer_add(control, "device", device); 377 if (err < 0) { 378 error("snd_config_integer_add: %s", snd_strerror(err)); 379 return err; 380 } 381 } 382 if (subdevice != 0) { 383 err = snd_config_integer_add(control, "subdevice", subdevice); 384 if (err < 0) { 385 error("snd_config_integer_add: %s", snd_strerror(err)); 386 return err; 387 } 388 } 389 err = snd_config_string_add(control, "name", name); 390 if (err < 0) { 391 error("snd_config_string_add: %s", snd_strerror(err)); 392 return err; 393 } 394 if (index != 0) { 395 err = snd_config_integer_add(control, "index", index); 396 if (err < 0) { 397 error("snd_config_integer_add: %s", snd_strerror(err)); 398 return err; 399 } 400 } 401 402 switch (type) { 403 case SND_CTL_ELEM_TYPE_BYTES: 404 case SND_CTL_ELEM_TYPE_IEC958: 405 { 406 size_t size = type == SND_CTL_ELEM_TYPE_BYTES ? 407 count : sizeof(snd_aes_iec958_t); 408 char buf[size * 2 + 1]; 409 char *p = buf; 410 char *hex = "0123456789abcdef"; 411 const unsigned char *bytes = 412 (const unsigned char *)snd_ctl_elem_value_get_bytes(ctl); 413 for (idx = 0; idx < size; idx++) { 414 int v = bytes[idx]; 415 *p++ = hex[v >> 4]; 416 *p++ = hex[v & 0x0f]; 417 } 418 *p = '\0'; 419 err = snd_config_string_add(control, "value", buf); 420 if (err < 0) { 421 error("snd_config_string_add: %s", snd_strerror(err)); 422 return err; 423 } 424 return 0; 425 } 426 default: 427 break; 428 } 429 430 if (count == 1) { 431 switch (type) { 432 case SND_CTL_ELEM_TYPE_BOOLEAN: 433 err = snd_config_string_add(control, "value", snd_ctl_elem_value_get_boolean(ctl, 0) ? "true" : "false"); 434 if (err < 0) { 435 error("snd_config_string_add: %s", snd_strerror(err)); 436 return err; 437 } 438 return 0; 439 case SND_CTL_ELEM_TYPE_INTEGER: 440 err = snd_config_integer_add(control, "value", snd_ctl_elem_value_get_integer(ctl, 0)); 441 if (err < 0) { 442 error("snd_config_integer_add: %s", snd_strerror(err)); 443 return err; 444 } 445 return 0; 446 case SND_CTL_ELEM_TYPE_INTEGER64: 447 err = snd_config_integer64_add(control, "value", snd_ctl_elem_value_get_integer64(ctl, 0)); 448 if (err < 0) { 449 error("snd_config_integer64_add: %s", snd_strerror(err)); 450 return err; 451 } 452 return 0; 453 case SND_CTL_ELEM_TYPE_ENUMERATED: 454 { 455 unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, 0); 456 snd_config_t *c; 457 err = snd_config_search(item, num_str(v), &c); 458 if (err == 0) { 459 err = snd_config_get_string(c, &s); 460 assert(err == 0); 461 err = snd_config_string_add(control, "value", s); 462 } else { 463 err = snd_config_integer_add(control, "value", v); 464 } 465 if (err < 0) 466 error("snd_config add: %s", snd_strerror(err)); 467 return 0; 468 } 469 default: 470 error("Unknown control type: %d\n", type); 471 return -EINVAL; 472 } 473 } 474 475 err = snd_config_compound_add(control, "value", 1, &value); 476 if (err < 0) { 477 error("snd_config_compound_add: %s", snd_strerror(err)); 478 return err; 479 } 480 481 switch (type) { 482 case SND_CTL_ELEM_TYPE_BOOLEAN: 483 for (idx = 0; idx < count; idx++) { 484 err = snd_config_string_add(value, num_str(idx), snd_ctl_elem_value_get_boolean(ctl, idx) ? "true" : "false"); 485 if (err < 0) { 486 error("snd_config_string_add: %s", snd_strerror(err)); 487 return err; 488 } 489 } 490 break; 491 case SND_CTL_ELEM_TYPE_INTEGER: 492 for (idx = 0; idx < count; idx++) { 493 err = snd_config_integer_add(value, num_str(idx), snd_ctl_elem_value_get_integer(ctl, idx)); 494 if (err < 0) { 495 error("snd_config_integer_add: %s", snd_strerror(err)); 496 return err; 497 } 498 } 499 break; 500 case SND_CTL_ELEM_TYPE_INTEGER64: 501 for (idx = 0; idx < count; idx++) { 502 err = snd_config_integer64_add(value, num_str(idx), snd_ctl_elem_value_get_integer64(ctl, idx)); 503 if (err < 0) { 504 error("snd_config_integer64_add: %s", snd_strerror(err)); 505 return err; 506 } 507 } 508 break; 509 case SND_CTL_ELEM_TYPE_ENUMERATED: 510 for (idx = 0; idx < count; idx++) { 511 unsigned int v = snd_ctl_elem_value_get_enumerated(ctl, idx); 512 snd_config_t *c; 513 err = snd_config_search(item, num_str(v), &c); 514 if (err == 0) { 515 err = snd_config_get_string(c, &s); 516 assert(err == 0); 517 err = snd_config_string_add(value, num_str(idx), s); 518 } else { 519 err = snd_config_integer_add(value, num_str(idx), v); 520 } 521 if (err < 0) { 522 error("snd_config add: %s", snd_strerror(err)); 523 return err; 524 } 525 } 526 break; 527 default: 528 error("Unknown control type: %d\n", type); 529 return -EINVAL; 530 } 531 532 return 0; 533 } 534 535 static int get_controls(int cardno, snd_config_t *top) 536 { 537 snd_ctl_t *handle; 538 snd_ctl_card_info_t *info; 539 snd_config_t *state, *card, *control; 540 snd_ctl_elem_list_t *list; 541 unsigned int idx; 542 int err; 543 char name[32]; 544 unsigned int count; 545 const char *id; 546 snd_ctl_card_info_alloca(&info); 547 snd_ctl_elem_list_alloca(&list); 548 549 sprintf(name, "hw:%d", cardno); 550 err = snd_ctl_open(&handle, name, SND_CTL_READONLY); 551 if (err < 0) { 552 error("snd_ctl_open error: %s", snd_strerror(err)); 553 return err; 554 } 555 err = snd_ctl_card_info(handle, info); 556 if (err < 0) { 557 error("snd_ctl_card_info error: %s", snd_strerror(err)); 558 goto _close; 559 } 560 id = snd_ctl_card_info_get_id(info); 561 err = snd_config_search(top, "state", &state); 562 if (err == 0 && 563 snd_config_get_type(state) != SND_CONFIG_TYPE_COMPOUND) { 564 error("config state node is not a compound"); 565 err = -EINVAL; 566 goto _close; 567 } 568 if (err < 0) { 569 err = snd_config_compound_add(top, "state", 1, &state); 570 if (err < 0) { 571 error("snd_config_compound_add: %s", snd_strerror(err)); 572 goto _close; 573 } 574 } 575 err = snd_config_search(state, id, &card); 576 if (err == 0 && 577 snd_config_get_type(card) != SND_CONFIG_TYPE_COMPOUND) { 578 error("config state.%s node is not a compound", id); 579 err = -EINVAL; 580 goto _close; 581 } 582 if (err < 0) { 583 err = snd_config_compound_add(state, id, 0, &card); 584 if (err < 0) { 585 error("snd_config_compound_add: %s", snd_strerror(err)); 586 goto _close; 587 } 588 } 589 err = snd_config_search(card, "control", &control); 590 if (err == 0) { 591 err = snd_config_delete(control); 592 if (err < 0) { 593 error("snd_config_delete: %s", snd_strerror(err)); 594 goto _close; 595 } 596 } 597 err = snd_ctl_elem_list(handle, list); 598 if (err < 0) { 599 error("Cannot determine controls: %s", snd_strerror(err)); 600 goto _close; 601 } 602 count = snd_ctl_elem_list_get_count(list); 603 err = snd_config_compound_add(card, "control", count > 0, &control); 604 if (err < 0) { 605 error("snd_config_compound_add: %s", snd_strerror(err)); 606 goto _close; 607 } 608 if (count == 0) { 609 err = 0; 610 goto _close; 611 } 612 snd_ctl_elem_list_set_offset(list, 0); 613 if (snd_ctl_elem_list_alloc_space(list, count) < 0) { 614 error("No enough memory..."); 615 goto _close; 616 } 617 if ((err = snd_ctl_elem_list(handle, list)) < 0) { 618 error("Cannot determine controls (2): %s", snd_strerror(err)); 619 goto _free; 620 } 621 for (idx = 0; idx < count; ++idx) { 622 snd_ctl_elem_id_t *id; 623 snd_ctl_elem_id_alloca(&id); 624 snd_ctl_elem_list_get_id(list, idx, id); 625 err = get_control(handle, id, control); 626 if (err < 0) 627 goto _free; 628 } 629 630 err = 0; 631 _free: 632 snd_ctl_elem_list_free_space(list); 633 _close: 634 snd_ctl_close(handle); 635 return err; 636 } 637 638 static long config_iface(snd_config_t *n) 639 { 640 long i; 641 long long li; 642 snd_ctl_elem_iface_t idx; 643 const char *str; 644 switch (snd_config_get_type(n)) { 645 case SND_CONFIG_TYPE_INTEGER: 646 snd_config_get_integer(n, &i); 647 return i; 648 case SND_CONFIG_TYPE_INTEGER64: 649 snd_config_get_integer64(n, &li); 650 return li; 651 case SND_CONFIG_TYPE_STRING: 652 snd_config_get_string(n, &str); 653 break; 654 default: 655 return -1; 656 } 657 for (idx = 0; idx <= SND_CTL_ELEM_IFACE_LAST; idx++) { 658 if (strcasecmp(snd_ctl_elem_iface_name(idx), str) == 0) 659 return idx; 660 } 661 return -1; 662 } 663 664 static int config_bool(snd_config_t *n, int doit) 665 { 666 const char *str; 667 long val; 668 long long lval; 669 670 switch (snd_config_get_type(n)) { 671 case SND_CONFIG_TYPE_INTEGER: 672 snd_config_get_integer(n, &val); 673 if (val < 0 || val > 1) 674 return -1; 675 return val; 676 case SND_CONFIG_TYPE_INTEGER64: 677 snd_config_get_integer64(n, &lval); 678 if (lval < 0 || lval > 1) 679 return -1; 680 return (int) lval; 681 case SND_CONFIG_TYPE_STRING: 682 snd_config_get_string(n, &str); 683 break; 684 case SND_CONFIG_TYPE_COMPOUND: 685 if (!force_restore || !doit) 686 return -1; 687 n = snd_config_iterator_entry(snd_config_iterator_first(n)); 688 return config_bool(n, doit); 689 default: 690 return -1; 691 } 692 if (strcmp(str, "on") == 0 || strcmp(str, "true") == 0) 693 return 1; 694 if (strcmp(str, "off") == 0 || strcmp(str, "false") == 0) 695 return 0; 696 return -1; 697 } 698 699 static int config_enumerated(snd_config_t *n, snd_ctl_t *handle, 700 snd_ctl_elem_info_t *info, int doit) 701 { 702 const char *str; 703 long val; 704 long long lval; 705 unsigned int idx, items; 706 707 switch (snd_config_get_type(n)) { 708 case SND_CONFIG_TYPE_INTEGER: 709 snd_config_get_integer(n, &val); 710 return val; 711 case SND_CONFIG_TYPE_INTEGER64: 712 snd_config_get_integer64(n, &lval); 713 return (int) lval; 714 case SND_CONFIG_TYPE_STRING: 715 snd_config_get_string(n, &str); 716 break; 717 case SND_CONFIG_TYPE_COMPOUND: 718 if (!force_restore || !doit) 719 return -1; 720 n = snd_config_iterator_entry(snd_config_iterator_first(n)); 721 return config_enumerated(n, handle, info, doit); 722 default: 723 return -1; 724 } 725 items = snd_ctl_elem_info_get_items(info); 726 for (idx = 0; idx < items; idx++) { 727 int err; 728 snd_ctl_elem_info_set_item(info, idx); 729 err = snd_ctl_elem_info(handle, info); 730 if (err < 0) { 731 error("snd_ctl_elem_info: %s", snd_strerror(err)); 732 return err; 733 } 734 if (strcmp(str, snd_ctl_elem_info_get_item_name(info)) == 0) 735 return idx; 736 } 737 return -1; 738 } 739 740 static int config_integer(snd_config_t *n, long *val, int doit) 741 { 742 int err = snd_config_get_integer(n, val); 743 if (err < 0 && force_restore && doit) { 744 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) 745 return err; 746 n = snd_config_iterator_entry(snd_config_iterator_first(n)); 747 return config_integer(n, val, doit); 748 } 749 return err; 750 } 751 752 static int config_integer64(snd_config_t *n, long long *val, int doit) 753 { 754 int err = snd_config_get_integer64(n, val); 755 if (err < 0 && force_restore && doit) { 756 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) 757 return err; 758 n = snd_config_iterator_entry(snd_config_iterator_first(n)); 759 return config_integer64(n, val, doit); 760 } 761 return err; 762 } 763 764 static int is_user_control(snd_config_t *conf) 765 { 766 snd_config_iterator_t i, next; 767 768 snd_config_for_each(i, next, conf) { 769 snd_config_t *n = snd_config_iterator_entry(i); 770 const char *id, *s; 771 if (snd_config_get_id(n, &id) < 0) 772 continue; 773 if (strcmp(id, "access") == 0) { 774 if (snd_config_get_string(n, &s) < 0) 775 return 0; 776 if (strstr(s, "user")) 777 return 1; 778 } 779 } 780 return 0; 781 } 782 783 /* 784 * get the item type from the given comment config 785 */ 786 static int get_comment_type(snd_config_t *n) 787 { 788 static const snd_ctl_elem_type_t types[] = { 789 SND_CTL_ELEM_TYPE_BOOLEAN, 790 SND_CTL_ELEM_TYPE_INTEGER, 791 SND_CTL_ELEM_TYPE_ENUMERATED, 792 SND_CTL_ELEM_TYPE_BYTES, 793 SND_CTL_ELEM_TYPE_IEC958, 794 SND_CTL_ELEM_TYPE_INTEGER64, 795 }; 796 const char *type; 797 unsigned int i; 798 799 if (snd_config_get_string(n, &type) < 0) 800 return -EINVAL; 801 for (i = 0; i < ARRAY_SIZE(types); ++i) 802 if (strcmp(type, snd_ctl_elem_type_name(types[i])) == 0) 803 return types[i]; 804 return -EINVAL; 805 } 806 807 /* 808 * get the value range from the given comment config 809 */ 810 static int get_comment_range(snd_config_t *n, int ctype, 811 long *imin, long *imax, long *istep) 812 { 813 const char *s; 814 int err; 815 816 if (snd_config_get_string(n, &s) < 0) 817 return -EINVAL; 818 switch (ctype) { 819 case SND_CTL_ELEM_TYPE_INTEGER: 820 err = sscanf(s, "%li - %li (step %li)", imin, imax, istep); 821 if (err != 3) { 822 istep = 0; 823 err = sscanf(s, "%li - %li", imin, imax); 824 if (err != 2) 825 return -EINVAL; 826 } 827 break; 828 default: 829 return -EINVAL; 830 } 831 return 0; 832 } 833 834 static int add_user_control(snd_ctl_t *handle, snd_ctl_elem_info_t *info, snd_config_t *conf) 835 { 836 snd_ctl_elem_id_t *id; 837 snd_config_iterator_t i, next; 838 long imin, imax, istep; 839 snd_ctl_elem_type_t ctype; 840 unsigned int count; 841 int err; 842 unsigned int *tlv; 843 844 imin = imax = istep = 0; 845 count = 0; 846 ctype = SND_CTL_ELEM_TYPE_NONE; 847 tlv = NULL; 848 snd_config_for_each(i, next, conf) { 849 snd_config_t *n = snd_config_iterator_entry(i); 850 const char *id; 851 if (snd_config_get_id(n, &id) < 0) 852 continue; 853 if (strcmp(id, "type") == 0) { 854 err = get_comment_type(n); 855 if (err < 0) 856 return err; 857 ctype = err; 858 continue; 859 } 860 if (strcmp(id, "range") == 0) { 861 err = get_comment_range(n, ctype, &imin, &imax, &istep); 862 if (err < 0) 863 return err; 864 continue; 865 } 866 if (strcmp(id, "count") == 0) { 867 long v; 868 if ((err = snd_config_get_integer(n, &v)) < 0) 869 return err; 870 count = v; 871 continue; 872 } 873 if (strcmp(id, "tlv") == 0) { 874 const char *s; 875 if ((err = snd_config_get_string(n, &s)) < 0) 876 return -EINVAL; 877 if (tlv) 878 free(tlv); 879 if ((tlv = str_to_tlv(s)) == NULL) 880 return -EINVAL; 881 continue; 882 } 883 } 884 885 snd_ctl_elem_id_alloca(&id); 886 snd_ctl_elem_info_get_id(info, id); 887 if (count <= 0) 888 count = 1; 889 switch (ctype) { 890 case SND_CTL_ELEM_TYPE_INTEGER: 891 if (imin > imax || istep > imax - imin) 892 return -EINVAL; 893 err = snd_ctl_elem_add_integer(handle, id, count, imin, imax, istep); 894 if (err < 0) 895 goto error; 896 if (tlv) 897 snd_ctl_elem_tlv_write(handle, id, tlv); 898 break; 899 case SND_CTL_ELEM_TYPE_BOOLEAN: 900 err = snd_ctl_elem_add_boolean(handle, id, count); 901 break; 902 case SND_CTL_ELEM_TYPE_IEC958: 903 err = snd_ctl_elem_add_iec958(handle, id); 904 break; 905 default: 906 err = -EINVAL; 907 break; 908 } 909 910 error: 911 free(tlv); 912 if (err < 0) 913 return err; 914 return snd_ctl_elem_info(handle, info); 915 } 916 917 /* 918 * look for a config node with the given item name 919 */ 920 static snd_config_t *search_comment_item(snd_config_t *conf, const char *name) 921 { 922 snd_config_iterator_t i, next; 923 snd_config_for_each(i, next, conf) { 924 snd_config_t *n = snd_config_iterator_entry(i); 925 const char *id; 926 if (snd_config_get_id(n, &id) < 0) 927 continue; 928 if (strcmp(id, name) == 0) 929 return n; 930 } 931 return NULL; 932 } 933 934 /* 935 * check whether the config item has the same of compatible type 936 */ 937 static int check_comment_type(snd_config_t *conf, int type) 938 { 939 snd_config_t *n = search_comment_item(conf, "type"); 940 int ctype; 941 942 if (!n) 943 return 0; /* not defined */ 944 ctype = get_comment_type(n); 945 if (ctype == type) 946 return 0; 947 if ((ctype == SND_CTL_ELEM_TYPE_BOOLEAN || 948 ctype == SND_CTL_ELEM_TYPE_INTEGER || 949 ctype == SND_CTL_ELEM_TYPE_INTEGER64 || 950 ctype == SND_CTL_ELEM_TYPE_ENUMERATED) && 951 (type == SND_CTL_ELEM_TYPE_BOOLEAN || 952 type == SND_CTL_ELEM_TYPE_INTEGER || 953 type == SND_CTL_ELEM_TYPE_INTEGER64 || 954 type == SND_CTL_ELEM_TYPE_ENUMERATED)) 955 return 0; /* OK, compatible */ 956 return -EINVAL; 957 } 958 959 /* 960 * convert from an old value to a new value with the same dB level 961 */ 962 static int convert_to_new_db(snd_config_t *value, long omin, long omax, 963 long nmin, long nmax, 964 long odbmin, long odbmax, 965 long ndbmin, long ndbmax, 966 int doit) 967 { 968 long val; 969 if (config_integer(value, &val, doit) < 0) 970 return -EINVAL; 971 if (val < omin || val > omax) 972 return -EINVAL; 973 val = ((val - omin) * (odbmax - odbmin)) / (omax - omin) + odbmin; 974 if (val < ndbmin) 975 val = ndbmin; 976 else if (val > ndbmax) 977 val = ndbmax; 978 val = ((val - ndbmin) * (nmax - nmin)) / (ndbmax - ndbmin) + nmin; 979 return snd_config_set_integer(value, val); 980 } 981 982 /* 983 * compare the current value range with the old range in comments. 984 * also, if dB information is available, try to compare them. 985 * if any change occurs, try to keep the same dB level. 986 */ 987 static int check_comment_range(snd_ctl_t *handle, snd_config_t *conf, 988 snd_ctl_elem_info_t *info, snd_config_t *value, 989 int doit) 990 { 991 snd_config_t *n; 992 long omin, omax, ostep; 993 long nmin, nmax; 994 long odbmin, odbmax; 995 long ndbmin, ndbmax; 996 snd_ctl_elem_id_t *id; 997 998 n = search_comment_item(conf, "range"); 999 if (!n) 1000 return 0; 1001 if (get_comment_range(n, SND_CTL_ELEM_TYPE_INTEGER, 1002 &omin, &omax, &ostep) < 0) 1003 return 0; 1004 nmin = snd_ctl_elem_info_get_min(info); 1005 nmax = snd_ctl_elem_info_get_max(info); 1006 if (omin != nmin && omax != nmax) { 1007 /* Hey, the range mismatches */ 1008 if (!force_restore || !doit) 1009 return -EINVAL; 1010 } 1011 if (omin >= omax || nmin >= nmax) 1012 return 0; /* invalid values */ 1013 1014 n = search_comment_item(conf, "dbmin"); 1015 if (!n) 1016 return 0; 1017 if (config_integer(n, &odbmin, doit) < 0) 1018 return 0; 1019 n = search_comment_item(conf, "dbmax"); 1020 if (!n) 1021 return 0; 1022 if (config_integer(n, &odbmax, doit) < 0) 1023 return 0; 1024 if (odbmin >= odbmax) 1025 return 0; /* invalid values */ 1026 snd_ctl_elem_id_alloca(&id); 1027 snd_ctl_elem_info_get_id(info, id); 1028 if (snd_ctl_get_dB_range(handle, id, &ndbmin, &ndbmax) < 0) 1029 return 0; 1030 if (ndbmin >= ndbmax) 1031 return 0; /* invalid values */ 1032 if (omin == nmin && omax == nmax && 1033 odbmin == ndbmin && odbmax == ndbmax) 1034 return 0; /* OK, identical one */ 1035 1036 /* Let's guess the current value from dB range */ 1037 if (snd_config_get_type(value) == SND_CONFIG_TYPE_COMPOUND) { 1038 snd_config_iterator_t i, next; 1039 snd_config_for_each(i, next, value) { 1040 snd_config_t *n = snd_config_iterator_entry(i); 1041 convert_to_new_db(n, omin, omax, nmin, nmax, 1042 odbmin, odbmax, ndbmin, ndbmax, doit); 1043 } 1044 } else 1045 convert_to_new_db(value, omin, omax, nmin, nmax, 1046 odbmin, odbmax, ndbmin, ndbmax, doit); 1047 return 0; 1048 } 1049 1050 static int restore_config_value(snd_ctl_t *handle, snd_ctl_elem_info_t *info, 1051 snd_ctl_elem_iface_t type, 1052 snd_config_t *value, 1053 snd_ctl_elem_value_t *ctl, int idx, 1054 int doit) 1055 { 1056 long val; 1057 long long lval; 1058 int err; 1059 1060 switch (type) { 1061 case SND_CTL_ELEM_TYPE_BOOLEAN: 1062 val = config_bool(value, doit); 1063 if (val >= 0) { 1064 snd_ctl_elem_value_set_boolean(ctl, idx, val); 1065 return 1; 1066 } 1067 break; 1068 case SND_CTL_ELEM_TYPE_INTEGER: 1069 err = config_integer(value, &val, doit); 1070 if (err == 0) { 1071 snd_ctl_elem_value_set_integer(ctl, idx, val); 1072 return 1; 1073 } 1074 break; 1075 case SND_CTL_ELEM_TYPE_INTEGER64: 1076 err = config_integer64(value, &lval, doit); 1077 if (err == 0) { 1078 snd_ctl_elem_value_set_integer64(ctl, idx, lval); 1079 return 1; 1080 } 1081 break; 1082 case SND_CTL_ELEM_TYPE_ENUMERATED: 1083 val = config_enumerated(value, handle, info, doit); 1084 if (val >= 0) { 1085 snd_ctl_elem_value_set_enumerated(ctl, idx, val); 1086 return 1; 1087 } 1088 break; 1089 case SND_CTL_ELEM_TYPE_BYTES: 1090 case SND_CTL_ELEM_TYPE_IEC958: 1091 break; 1092 default: 1093 cerror(doit, "Unknow control type: %d", type); 1094 return -EINVAL; 1095 } 1096 return 0; 1097 } 1098 1099 static int restore_config_value2(snd_ctl_t *handle, snd_ctl_elem_info_t *info, 1100 snd_ctl_elem_iface_t type, 1101 snd_config_t *value, 1102 snd_ctl_elem_value_t *ctl, int idx, 1103 unsigned int numid, int doit) 1104 { 1105 int err = restore_config_value(handle, info, type, value, ctl, idx, doit); 1106 long val; 1107 1108 if (err != 0) 1109 return err; 1110 switch (type) { 1111 case SND_CTL_ELEM_TYPE_BYTES: 1112 case SND_CTL_ELEM_TYPE_IEC958: 1113 err = snd_config_get_integer(value, &val); 1114 if (err < 0 || val < 0 || val > 255) { 1115 cerror(doit, "bad control.%d.value.%d content", numid, idx); 1116 return force_restore && doit ? 0 : -EINVAL; 1117 } 1118 snd_ctl_elem_value_set_byte(ctl, idx, val); 1119 return 1; 1120 break; 1121 default: 1122 break; 1123 } 1124 return 0; 1125 } 1126 1127 static int set_control(snd_ctl_t *handle, snd_config_t *control, 1128 int *maxnumid, int doit) 1129 { 1130 snd_ctl_elem_value_t *ctl; 1131 snd_ctl_elem_info_t *info; 1132 snd_config_iterator_t i, next; 1133 unsigned int numid1; 1134 snd_ctl_elem_iface_t iface = -1; 1135 int iface1; 1136 const char *name1; 1137 unsigned int numid; 1138 snd_ctl_elem_type_t type; 1139 unsigned int count; 1140 long device = -1; 1141 long device1; 1142 long subdevice = -1; 1143 long subdevice1; 1144 const char *name = NULL; 1145 long index1; 1146 long index = -1; 1147 snd_config_t *value = NULL; 1148 snd_config_t *comment = NULL; 1149 unsigned int idx; 1150 int err; 1151 char *set; 1152 const char *id; 1153 snd_ctl_elem_value_alloca(&ctl); 1154 snd_ctl_elem_info_alloca(&info); 1155 if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) { 1156 cerror(doit, "control is not a compound"); 1157 return -EINVAL; 1158 } 1159 err = snd_config_get_id(control, &id); 1160 if (err < 0) { 1161 cerror(doit, "unable to get id"); 1162 return -EINVAL; 1163 } 1164 numid = atoi(id); 1165 if ((int)numid > *maxnumid) 1166 *maxnumid = numid; 1167 snd_config_for_each(i, next, control) { 1168 snd_config_t *n = snd_config_iterator_entry(i); 1169 const char *fld; 1170 if (snd_config_get_id(n, &fld) < 0) 1171 continue; 1172 if (strcmp(fld, "comment") == 0) { 1173 if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) { 1174 cerror(doit, "control.%d.%s is invalid", numid, fld); 1175 return -EINVAL; 1176 } 1177 comment = n; 1178 continue; 1179 } 1180 if (strcmp(fld, "iface") == 0) { 1181 iface = (snd_ctl_elem_iface_t)config_iface(n); 1182 continue; 1183 } 1184 if (strcmp(fld, "device") == 0) { 1185 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { 1186 cerror(doit, "control.%d.%s is invalid", numid, fld); 1187 return -EINVAL; 1188 } 1189 snd_config_get_integer(n, &device); 1190 continue; 1191 } 1192 if (strcmp(fld, "subdevice") == 0) { 1193 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { 1194 cerror(doit, "control.%d.%s is invalid", numid, fld); 1195 return -EINVAL; 1196 } 1197 snd_config_get_integer(n, &subdevice); 1198 continue; 1199 } 1200 if (strcmp(fld, "name") == 0) { 1201 if (snd_config_get_type(n) != SND_CONFIG_TYPE_STRING) { 1202 cerror(doit, "control.%d.%s is invalid", numid, fld); 1203 return -EINVAL; 1204 } 1205 snd_config_get_string(n, &name); 1206 continue; 1207 } 1208 if (strcmp(fld, "index") == 0) { 1209 if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { 1210 cerror(doit, "control.%d.%s is invalid", numid, fld); 1211 return -EINVAL; 1212 } 1213 snd_config_get_integer(n, &index); 1214 continue; 1215 } 1216 if (strcmp(fld, "value") == 0) { 1217 value = n; 1218 continue; 1219 } 1220 cerror(doit, "unknown control.%d.%s field", numid, fld); 1221 } 1222 if (!value) { 1223 cerror(doit, "missing control.%d.value", numid); 1224 return -EINVAL; 1225 } 1226 if (device < 0) 1227 device = 0; 1228 if (subdevice < 0) 1229 subdevice = 0; 1230 if (index < 0) 1231 index = 0; 1232 1233 err = -EINVAL; 1234 if (!force_restore) { 1235 snd_ctl_elem_info_set_numid(info, numid); 1236 err = snd_ctl_elem_info(handle, info); 1237 } 1238 if (err < 0 && name) { 1239 snd_ctl_elem_info_set_numid(info, 0); 1240 snd_ctl_elem_info_set_interface(info, iface); 1241 snd_ctl_elem_info_set_device(info, device); 1242 snd_ctl_elem_info_set_subdevice(info, subdevice); 1243 snd_ctl_elem_info_set_name(info, name); 1244 snd_ctl_elem_info_set_index(info, index); 1245 err = snd_ctl_elem_info(handle, info); 1246 if (err < 0 && comment && is_user_control(comment)) { 1247 err = add_user_control(handle, info, comment); 1248 if (err < 0) { 1249 cerror(doit, "failed to add user control #%d (%s)", 1250 numid, snd_strerror(err)); 1251 return err; 1252 } 1253 } 1254 } 1255 if (err < 0) { 1256 cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err)); 1257 return -ENOENT; 1258 } 1259 numid1 = snd_ctl_elem_info_get_numid(info); 1260 iface1 = snd_ctl_elem_info_get_interface(info); 1261 device1 = snd_ctl_elem_info_get_device(info); 1262 subdevice1 = snd_ctl_elem_info_get_subdevice(info); 1263 name1 = snd_ctl_elem_info_get_name(info); 1264 index1 = snd_ctl_elem_info_get_index(info); 1265 count = snd_ctl_elem_info_get_count(info); 1266 type = snd_ctl_elem_info_get_type(info); 1267 if (err |= numid != numid1 && !force_restore) 1268 cerror(doit, "warning: numid mismatch (%d/%d) for control #%d", 1269 numid, numid1, numid); 1270 if (err |= iface != iface1) 1271 cerror(doit, "warning: iface mismatch (%d/%d) for control #%d", iface, iface1, numid); 1272 if (err |= device != device1) 1273 cerror(doit, "warning: device mismatch (%ld/%ld) for control #%d", device, device1, numid); 1274 if (err |= subdevice != subdevice1) 1275 cerror(doit, "warning: subdevice mismatch (%ld/%ld) for control #%d", subdevice, subdevice1, numid); 1276 if (err |= strcmp(name, name1)) 1277 cerror(doit, "warning: name mismatch (%s/%s) for control #%d", name, name1, numid); 1278 if (err |= index != index1) 1279 cerror(doit, "warning: index mismatch (%ld/%ld) for control #%d", index, index1, numid); 1280 if (err < 0) { 1281 cerror(doit, "failed to obtain info for control #%d (%s)", numid, snd_strerror(err)); 1282 return -ENOENT; 1283 } 1284 1285 if (comment) { 1286 if (check_comment_type(comment, type) < 0) 1287 cerror(doit, "incompatible field type for control #%d", numid); 1288 if (type == SND_CTL_ELEM_TYPE_INTEGER) { 1289 if (check_comment_range(handle, comment, info, value, doit) < 0) { 1290 cerror(doit, "value range mismatch for control #%d", 1291 numid); 1292 return -EINVAL; 1293 } 1294 } 1295 } 1296 1297 if (snd_ctl_elem_info_is_inactive(info) || 1298 !snd_ctl_elem_info_is_writable(info)) 1299 return 0; 1300 snd_ctl_elem_value_set_numid(ctl, numid1); 1301 1302 if (count == 1) { 1303 err = restore_config_value(handle, info, type, value, ctl, 0, doit); 1304 if (err < 0) 1305 return err; 1306 if (err > 0) 1307 goto _ok; 1308 } 1309 switch (type) { 1310 case SND_CTL_ELEM_TYPE_BYTES: 1311 case SND_CTL_ELEM_TYPE_IEC958: 1312 { 1313 const char *buf; 1314 err = snd_config_get_string(value, &buf); 1315 if (err >= 0) { 1316 int c1 = 0; 1317 int len = strlen(buf); 1318 unsigned int idx = 0; 1319 int size = type == SND_CTL_ELEM_TYPE_BYTES ? 1320 count : sizeof(snd_aes_iec958_t); 1321 if (size * 2 != len) { 1322 cerror(doit, "bad control.%d.value contents\n", numid); 1323 return -EINVAL; 1324 } 1325 while (*buf) { 1326 int c = *buf++; 1327 if ((c = hextodigit(c)) < 0) { 1328 cerror(doit, "bad control.%d.value contents\n", numid); 1329 return -EINVAL; 1330 } 1331 if (idx % 2 == 1) 1332 snd_ctl_elem_value_set_byte(ctl, idx / 2, c1 << 4 | c); 1333 else 1334 c1 = c; 1335 idx++; 1336 } 1337 goto _ok; 1338 } 1339 } 1340 default: 1341 break; 1342 } 1343 if (snd_config_get_type(value) != SND_CONFIG_TYPE_COMPOUND) { 1344 if (!force_restore || !doit) { 1345 cerror(doit, "bad control.%d.value type", numid); 1346 return -EINVAL; 1347 } 1348 for (idx = 0; idx < count; ++idx) { 1349 err = restore_config_value2(handle, info, type, value, 1350 ctl, idx, numid, doit); 1351 if (err < 0) 1352 return err; 1353 } 1354 goto _ok; 1355 } 1356 1357 set = (char*) alloca(count); 1358 memset(set, 0, count); 1359 snd_config_for_each(i, next, value) { 1360 snd_config_t *n = snd_config_iterator_entry(i); 1361 const char *id; 1362 if (snd_config_get_id(n, &id) < 0) 1363 continue; 1364 idx = atoi(id); 1365 if (idx >= count || set[idx]) { 1366 cerror(doit, "bad control.%d.value index", numid); 1367 if (!force_restore || !doit) 1368 return -EINVAL; 1369 continue; 1370 } 1371 err = restore_config_value2(handle, info, type, n, 1372 ctl, idx, numid, doit); 1373 if (err < 0) 1374 return err; 1375 if (err > 0) 1376 set[idx] = 1; 1377 } 1378 for (idx = 0; idx < count; ++idx) { 1379 if (!set[idx]) { 1380 cerror(doit, "control.%d.value.%d is not specified", numid, idx); 1381 if (!force_restore || !doit) 1382 return -EINVAL; 1383 } 1384 } 1385 1386 _ok: 1387 err = doit ? snd_ctl_elem_write(handle, ctl) : 0; 1388 if (err < 0) { 1389 error("Cannot write control '%d:%ld:%ld:%s:%ld' : %s", (int)iface, device, subdevice, name, index, snd_strerror(err)); 1390 return err; 1391 } 1392 return 0; 1393 } 1394 1395 static int set_controls(int card, snd_config_t *top, int doit) 1396 { 1397 snd_ctl_t *handle; 1398 snd_ctl_card_info_t *info; 1399 snd_config_t *control; 1400 snd_config_iterator_t i, next; 1401 int err, maxnumid = -1; 1402 char name[32], tmpid[16]; 1403 const char *id; 1404 snd_ctl_card_info_alloca(&info); 1405 1406 sprintf(name, "hw:%d", card); 1407 err = snd_ctl_open(&handle, name, 0); 1408 if (err < 0) { 1409 error("snd_ctl_open error: %s", snd_strerror(err)); 1410 return err; 1411 } 1412 err = snd_ctl_card_info(handle, info); 1413 if (err < 0) { 1414 error("snd_ctl_card_info error: %s", snd_strerror(err)); 1415 goto _close; 1416 } 1417 id = snd_ctl_card_info_get_id(info); 1418 err = snd_config_searchv(top, &control, "state", id, "control", 0); 1419 if (err < 0) { 1420 if (force_restore) { 1421 sprintf(tmpid, "card%d", card); 1422 err = snd_config_searchv(top, &control, "state", tmpid, "control", 0); 1423 if (! err) 1424 id = tmpid; 1425 } 1426 if (err < 0) { 1427 fprintf(stderr, "No state is present for card %s\n", id); 1428 goto _close; 1429 } 1430 id = tmpid; 1431 } 1432 if (snd_config_get_type(control) != SND_CONFIG_TYPE_COMPOUND) { 1433 cerror(doit, "state.%s.control is not a compound\n", id); 1434 return -EINVAL; 1435 } 1436 snd_config_for_each(i, next, control) { 1437 snd_config_t *n = snd_config_iterator_entry(i); 1438 err = set_control(handle, n, &maxnumid, doit); 1439 if (err < 0 && (!force_restore || !doit)) 1440 goto _close; 1441 } 1442 1443 /* check if we have additional controls in driver */ 1444 /* in this case we should go through init procedure */ 1445 if (!doit && maxnumid >= 0) { 1446 snd_ctl_elem_id_t *id; 1447 snd_ctl_elem_info_t *info; 1448 snd_ctl_elem_id_alloca(&id); 1449 snd_ctl_elem_info_alloca(&info); 1450 snd_ctl_elem_info_set_numid(info, maxnumid+1); 1451 if (snd_ctl_elem_info(handle, info) == 0) { 1452 /* not very informative */ 1453 /* but value is used for check only */ 1454 err = -EAGAIN; 1455 goto _close; 1456 } 1457 } 1458 1459 _close: 1460 snd_ctl_close(handle); 1461 return err; 1462 } 1463 1464 int save_state(const char *file, const char *cardname) 1465 { 1466 int err; 1467 snd_config_t *config; 1468 snd_input_t *in; 1469 snd_output_t *out; 1470 int stdio; 1471 1472 err = snd_config_top(&config); 1473 if (err < 0) { 1474 error("snd_config_top error: %s", snd_strerror(err)); 1475 return err; 1476 } 1477 stdio = !strcmp(file, "-"); 1478 if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) { 1479 err = snd_config_load(config, in); 1480 snd_input_close(in); 1481 #if 0 1482 if (err < 0) { 1483 error("snd_config_load error: %s", snd_strerror(err)); 1484 return err; 1485 } 1486 #endif 1487 } 1488 1489 if (!cardname) { 1490 int card, first = 1; 1491 1492 card = -1; 1493 /* find each installed soundcards */ 1494 while (1) { 1495 if (snd_card_next(&card) < 0) 1496 break; 1497 if (card < 0) { 1498 if (first) { 1499 if (ignore_nocards) { 1500 return 0; 1501 } else { 1502 error("No soundcards found..."); 1503 return -ENODEV; 1504 } 1505 } 1506 break; 1507 } 1508 first = 0; 1509 if ((err = get_controls(card, config))) 1510 return err; 1511 } 1512 } else { 1513 int cardno; 1514 1515 cardno = snd_card_get_index(cardname); 1516 if (cardno < 0) { 1517 error("Cannot find soundcard '%s'...", cardname); 1518 return cardno; 1519 } 1520 if ((err = get_controls(cardno, config))) { 1521 return err; 1522 } 1523 } 1524 1525 if (stdio) 1526 err = snd_output_stdio_attach(&out, stdout, 0); 1527 else 1528 err = snd_output_stdio_open(&out, file, "w"); 1529 if (err < 0) { 1530 error("Cannot open %s for writing: %s", file, snd_strerror(err)); 1531 return -errno; 1532 } 1533 err = snd_config_save(config, out); 1534 snd_output_close(out); 1535 if (err < 0) 1536 error("snd_config_save: %s", snd_strerror(err)); 1537 return 0; 1538 } 1539 1540 int load_state(const char *file, const char *initfile, const char *cardname, 1541 int do_init) 1542 { 1543 int err, finalerr = 0; 1544 snd_config_t *config; 1545 snd_input_t *in; 1546 int stdio; 1547 1548 err = snd_config_top(&config); 1549 if (err < 0) { 1550 error("snd_config_top error: %s", snd_strerror(err)); 1551 return err; 1552 } 1553 stdio = !strcmp(file, "-"); 1554 if (stdio) 1555 err = snd_input_stdio_attach(&in, stdin, 0); 1556 else 1557 err = snd_input_stdio_open(&in, file, "r"); 1558 if (err >= 0) { 1559 err = snd_config_load(config, in); 1560 snd_input_close(in); 1561 if (err < 0) { 1562 error("snd_config_load error: %s", snd_strerror(err)); 1563 return err; 1564 } 1565 } else { 1566 int card, first = 1; 1567 char cardname1[16]; 1568 1569 error("Cannot open %s for reading: %s", file, snd_strerror(err)); 1570 finalerr = err; 1571 card = -1; 1572 /* find each installed soundcards */ 1573 while (1) { 1574 if (snd_card_next(&card) < 0) 1575 break; 1576 if (card < 0) 1577 break; 1578 first = 0; 1579 if (!do_init) 1580 break; 1581 sprintf(cardname1, "%i", card); 1582 err = init(initfile, cardname1); 1583 if (err < 0) { 1584 finalerr = err; 1585 initfailed(card, "init"); 1586 } 1587 initfailed(card, "restore"); 1588 } 1589 if (first) 1590 finalerr = 0; /* no cards, no error code */ 1591 return finalerr; 1592 } 1593 1594 if (!cardname) { 1595 int card, first = 1; 1596 char cardname1[16]; 1597 1598 card = -1; 1599 /* find each installed soundcards */ 1600 while (1) { 1601 if (snd_card_next(&card) < 0) 1602 break; 1603 if (card < 0) { 1604 if (first) { 1605 if (ignore_nocards) { 1606 return 0; 1607 } else { 1608 error("No soundcards found..."); 1609 return -ENODEV; 1610 } 1611 } 1612 break; 1613 } 1614 first = 0; 1615 /* do a check if controls matches state file */ 1616 if (do_init && set_controls(card, config, 0)) { 1617 sprintf(cardname1, "%i", card); 1618 err = init(initfile, cardname1); 1619 if (err < 0) { 1620 initfailed(card, "init"); 1621 finalerr = err; 1622 } 1623 } 1624 if ((err = set_controls(card, config, 1))) { 1625 if (!force_restore) 1626 finalerr = err; 1627 initfailed(card, "restore"); 1628 } 1629 } 1630 } else { 1631 int cardno; 1632 1633 cardno = snd_card_get_index(cardname); 1634 if (cardno < 0) { 1635 error("Cannot find soundcard '%s'...", cardname); 1636 return -ENODEV; 1637 } 1638 /* do a check if controls matches state file */ 1639 if (do_init && set_controls(cardno, config, 0)) { 1640 err = init(initfile, cardname); 1641 if (err < 0) { 1642 initfailed(cardno, "init"); 1643 return err; 1644 } 1645 } 1646 if ((err = set_controls(cardno, config, 1))) { 1647 initfailed(cardno, "restore"); 1648 if (!force_restore) 1649 return err; 1650 } 1651 } 1652 return finalerr; 1653 } 1654