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