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