audio_route.c revision cef935958069ffba745cd091e6e1687095ea6785
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * Inspired by TinyHW, written by Mark Brown at Wolfson Micro 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18#define LOG_TAG "audio_route" 19/*#define LOG_NDEBUG 0*/ 20 21#include <errno.h> 22#include <expat.h> 23#include <stdbool.h> 24#include <stdio.h> 25#include <string.h> 26 27#include <cutils/log.h> 28 29#include <tinyalsa/asoundlib.h> 30 31#define BUF_SIZE 1024 32#define MIXER_XML_PATH "/system/etc/mixer_paths.xml" 33#define INITIAL_MIXER_PATH_SIZE 8 34 35struct mixer_state { 36 struct mixer_ctl *ctl; 37 unsigned int num_values; 38 int *old_value; 39 int *new_value; 40 int *reset_value; 41 /* If linked is true, only the first element in each array is valid */ 42 bool old_linked; 43 bool new_linked; 44 bool reset_linked; 45}; 46 47struct mixer_setting { 48 struct mixer_ctl *ctl; 49 unsigned int num_values; 50 int *value; 51 /* If linked is true, only the first element in each array is valid */ 52 bool linked; 53}; 54 55struct mixer_value { 56 struct mixer_ctl *ctl; 57 int index; 58 int value; 59}; 60 61struct mixer_path { 62 char *name; 63 unsigned int size; 64 unsigned int length; 65 struct mixer_setting *setting; 66}; 67 68struct audio_route { 69 struct mixer *mixer; 70 unsigned int num_mixer_ctls; 71 struct mixer_state *mixer_state; 72 73 unsigned int mixer_path_size; 74 unsigned int num_mixer_paths; 75 struct mixer_path *mixer_path; 76}; 77 78struct config_parse_state { 79 struct audio_route *ar; 80 struct mixer_path *path; 81 int level; 82}; 83 84/* path functions */ 85 86static void path_print(struct mixer_path *path) 87{ 88 unsigned int i; 89 unsigned int j; 90 91 ALOGE("Path: %s, length: %d", path->name, path->length); 92 for (i = 0; i < path->length; i++) { 93 ALOGE(" id=%d: ctl=%s linked=%c", i, 94 mixer_ctl_get_name(path->setting[i].ctl), 95 path->setting[i].linked ? 'y' : 'n'); 96 for (j = 0; j < path->setting[i].num_values; j++) 97 ALOGE(" id=%d value=%d", j, path->setting[i].value[j]); 98 } 99} 100 101static void path_free(struct audio_route *ar) 102{ 103 unsigned int i; 104 105 for (i = 0; i < ar->num_mixer_paths; i++) { 106 if (ar->mixer_path[i].name) 107 free(ar->mixer_path[i].name); 108 if (ar->mixer_path[i].setting) { 109 if (ar->mixer_path[i].setting->value) 110 free(ar->mixer_path[i].setting->value); 111 free(ar->mixer_path[i].setting); 112 } 113 } 114 free(ar->mixer_path); 115} 116 117static struct mixer_path *path_get_by_name(struct audio_route *ar, 118 const char *name) 119{ 120 unsigned int i; 121 122 for (i = 0; i < ar->num_mixer_paths; i++) 123 if (strcmp(ar->mixer_path[i].name, name) == 0) 124 return &ar->mixer_path[i]; 125 126 return NULL; 127} 128 129static struct mixer_path *path_create(struct audio_route *ar, const char *name) 130{ 131 struct mixer_path *new_mixer_path = NULL; 132 133 if (path_get_by_name(ar, name)) { 134 ALOGE("Path name '%s' already exists", name); 135 return NULL; 136 } 137 138 /* check if we need to allocate more space for mixer paths */ 139 if (ar->mixer_path_size <= ar->num_mixer_paths) { 140 if (ar->mixer_path_size == 0) 141 ar->mixer_path_size = INITIAL_MIXER_PATH_SIZE; 142 else 143 ar->mixer_path_size *= 2; 144 145 new_mixer_path = realloc(ar->mixer_path, ar->mixer_path_size * 146 sizeof(struct mixer_path)); 147 if (new_mixer_path == NULL) { 148 ALOGE("Unable to allocate more paths"); 149 return NULL; 150 } else { 151 ar->mixer_path = new_mixer_path; 152 } 153 } 154 155 /* initialise the new mixer path */ 156 ar->mixer_path[ar->num_mixer_paths].name = strdup(name); 157 ar->mixer_path[ar->num_mixer_paths].size = 0; 158 ar->mixer_path[ar->num_mixer_paths].length = 0; 159 ar->mixer_path[ar->num_mixer_paths].setting = NULL; 160 161 /* return the mixer path just added, then increment number of them */ 162 return &ar->mixer_path[ar->num_mixer_paths++]; 163} 164 165static int find_ctl_in_path(struct mixer_path *path, struct mixer_ctl *ctl) 166{ 167 unsigned int i; 168 169 for (i = 0; i < path->length; i++) 170 if (path->setting[i].ctl == ctl) 171 return i; 172 173 return -1; 174} 175 176static int alloc_path_setting(struct mixer_path *path) 177{ 178 struct mixer_setting *new_path_setting; 179 int path_index; 180 181 /* check if we need to allocate more space for path settings */ 182 if (path->size <= path->length) { 183 if (path->size == 0) 184 path->size = INITIAL_MIXER_PATH_SIZE; 185 else 186 path->size *= 2; 187 188 new_path_setting = realloc(path->setting, 189 path->size * sizeof(struct mixer_setting)); 190 if (new_path_setting == NULL) { 191 ALOGE("Unable to allocate more path settings"); 192 return -1; 193 } else { 194 path->setting = new_path_setting; 195 } 196 } 197 198 path_index = path->length; 199 path->length++; 200 201 return path_index; 202} 203 204static int path_add_setting(struct mixer_path *path, 205 struct mixer_setting *setting) 206{ 207 unsigned int i; 208 int path_index; 209 210 if (find_ctl_in_path(path, setting->ctl) != -1) { 211 ALOGE("Control '%s' already exists in path '%s'", 212 mixer_ctl_get_name(setting->ctl), path->name); 213 return -1; 214 } 215 216 path_index = alloc_path_setting(path); 217 if (path_index < 0) 218 return -1; 219 220 path->setting[path_index].ctl = setting->ctl; 221 path->setting[path_index].num_values = setting->num_values; 222 path->setting[path_index].value = malloc(setting->num_values * sizeof(int)); 223 path->setting[path_index].linked = setting->linked; 224 if (setting->linked) { 225 path->setting[path_index].value[0] = setting->value[0]; 226 } else { 227 for (i = 0; i < setting->num_values; i++) 228 path->setting[path_index].value[i] = setting->value[i]; 229 } 230 231 return 0; 232} 233 234static int path_add_value(struct mixer_path *path, 235 struct mixer_value *mixer_value) 236{ 237 unsigned int i; 238 int path_index; 239 unsigned int num_values; 240 241 /* Check that mixer value index is within range */ 242 num_values = mixer_ctl_get_num_values(mixer_value->ctl); 243 if (mixer_value->index >= (int)num_values) { 244 ALOGE("mixer index %d is out of range for '%s'", mixer_value->index, 245 mixer_ctl_get_name(mixer_value->ctl)); 246 return -1; 247 } 248 249 path_index = find_ctl_in_path(path, mixer_value->ctl); 250 if (path_index < 0) { 251 /* New path */ 252 253 path_index = alloc_path_setting(path); 254 if (path_index < 0) 255 return -1; 256 257 /* initialise the new path setting */ 258 path->setting[path_index].ctl = mixer_value->ctl; 259 path->setting[path_index].num_values = num_values; 260 path->setting[path_index].value = malloc(num_values * sizeof(int)); 261 path->setting[path_index].linked = true; 262 path->setting[path_index].value[0] = mixer_value->value; 263 } 264 265 if (mixer_value->index == -1) { 266 /* Linked, so only set the first value */ 267 path->setting[path_index].linked = true; 268 path->setting[path_index].value[0] = mixer_value->value; 269 } else { 270 if (path->setting[path_index].linked && (num_values > 1)) { 271 /* Unlinking the values, so duplicate them across */ 272 for (i = 1; i < num_values; i++) { 273 path->setting[path_index].value[i] = 274 path->setting[path_index].value[0]; 275 } 276 path->setting[path_index].linked = false; 277 } 278 path->setting[path_index].value[mixer_value->index] = mixer_value->value; 279 } 280 281 return 0; 282} 283 284static int path_add_path(struct mixer_path *path, struct mixer_path *sub_path) 285{ 286 unsigned int i; 287 288 for (i = 0; i < sub_path->length; i++) 289 if (path_add_setting(path, &sub_path->setting[i]) < 0) 290 return -1; 291 292 return 0; 293} 294 295static int path_apply(struct audio_route *ar, struct mixer_path *path) 296{ 297 unsigned int i; 298 unsigned int j; 299 unsigned int ctl_index; 300 301 for (i = 0; i < path->length; i++) { 302 struct mixer_ctl *ctl = path->setting[i].ctl; 303 304 /* locate the mixer ctl in the list */ 305 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) 306 if (ar->mixer_state[ctl_index].ctl == ctl) 307 break; 308 309 /* apply the new value(s) */ 310 for (j = 0; j < ar->mixer_state[ctl_index].num_values; j++) { 311 ar->mixer_state[ctl_index].new_value[j] = path->setting[i].value[j]; 312 if (path->setting[i].linked) 313 break; 314 } 315 ar->mixer_state[ctl_index].new_linked = path->setting[i].linked; 316 } 317 318 return 0; 319} 320 321/* mixer helper function */ 322static int mixer_enum_string_to_value(struct mixer_ctl *ctl, const char *string) 323{ 324 unsigned int i; 325 326 /* Search the enum strings for a particular one */ 327 for (i = 0; i < mixer_ctl_get_num_enums(ctl); i++) { 328 if (strcmp(mixer_ctl_get_enum_string(ctl, i), string) == 0) 329 break; 330 } 331 332 return i; 333} 334 335static void start_tag(void *data, const XML_Char *tag_name, 336 const XML_Char **attr) 337{ 338 const XML_Char *attr_name = NULL; 339 const XML_Char *attr_id = NULL; 340 const XML_Char *attr_value = NULL; 341 struct config_parse_state *state = data; 342 struct audio_route *ar = state->ar; 343 unsigned int i; 344 unsigned int ctl_index; 345 struct mixer_ctl *ctl; 346 int value; 347 unsigned int id; 348 struct mixer_value mixer_value; 349 350 /* Get name, id and value attributes (these may be empty) */ 351 for (i = 0; attr[i]; i += 2) { 352 if (strcmp(attr[i], "name") == 0) 353 attr_name = attr[i + 1]; 354 if (strcmp(attr[i], "id") == 0) 355 attr_id = attr[i + 1]; 356 else if (strcmp(attr[i], "value") == 0) 357 attr_value = attr[i + 1]; 358 } 359 360 /* Look at tags */ 361 if (strcmp(tag_name, "path") == 0) { 362 if (attr_name == NULL) { 363 ALOGE("Unnamed path!"); 364 } else { 365 if (state->level == 1) { 366 /* top level path: create and stash the path */ 367 state->path = path_create(ar, (char *)attr_name); 368 } else { 369 /* nested path */ 370 struct mixer_path *sub_path = path_get_by_name(ar, attr_name); 371 path_add_path(state->path, sub_path); 372 } 373 } 374 } 375 376 else if (strcmp(tag_name, "ctl") == 0) { 377 /* Obtain the mixer ctl and value */ 378 ctl = mixer_get_ctl_by_name(ar->mixer, attr_name); 379 switch (mixer_ctl_get_type(ctl)) { 380 case MIXER_CTL_TYPE_BOOL: 381 case MIXER_CTL_TYPE_INT: 382 value = atoi((char *)attr_value); 383 break; 384 case MIXER_CTL_TYPE_ENUM: 385 value = mixer_enum_string_to_value(ctl, (char *)attr_value); 386 break; 387 default: 388 value = 0; 389 break; 390 } 391 392 if (state->level == 1) { 393 /* top level ctl (initial setting) */ 394 395 /* locate the mixer ctl in the list */ 396 for (ctl_index = 0; ctl_index < ar->num_mixer_ctls; ctl_index++) { 397 if (ar->mixer_state[ctl_index].ctl == ctl) 398 break; 399 } 400 401 /* apply the new value */ 402 if (attr_id) { 403 /* set only one value */ 404 id = atoi((char *)attr_id); 405 if (id < ar->mixer_state[ctl_index].num_values) { 406 if (ar->mixer_state[ctl_index].new_linked) { 407 /* 408 * We're unlinking the values, so copy old_value[0] into 409 * all the new_value elements. 410 */ 411 for (i = 0; i < ar->mixer_state[ctl_index].num_values; i++) { 412 ar->mixer_state[ctl_index].new_value[i] = 413 ar->mixer_state[ctl_index].old_value[0]; 414 } 415 ar->mixer_state[ctl_index].new_linked = false; 416 } 417 ar->mixer_state[ctl_index].new_value[id] = value; 418 } else { 419 ALOGE("value id out of range for mixer ctl '%s'", 420 mixer_ctl_get_name(ctl)); 421 } 422 } else { 423 ar->mixer_state[ctl_index].new_value[0] = value; 424 ar->mixer_state[ctl_index].new_linked = true; 425 } 426 } else { 427 /* nested ctl (within a path) */ 428 mixer_value.ctl = ctl; 429 mixer_value.value = value; 430 if (attr_id) 431 mixer_value.index = atoi((char *)attr_id); 432 else 433 mixer_value.index = -1; 434 path_add_value(state->path, &mixer_value); 435 } 436 } 437 438 state->level++; 439} 440 441static void end_tag(void *data, const XML_Char *tag_name) 442{ 443 struct config_parse_state *state = data; 444 445 state->level--; 446} 447 448static int alloc_mixer_state(struct audio_route *ar) 449{ 450 unsigned int i; 451 unsigned int j; 452 unsigned int num_values; 453 struct mixer_ctl *ctl; 454 bool linked; 455 456 ar->num_mixer_ctls = mixer_get_num_ctls(ar->mixer); 457 ar->mixer_state = malloc(ar->num_mixer_ctls * sizeof(struct mixer_state)); 458 if (!ar->mixer_state) 459 return -1; 460 461 for (i = 0; i < ar->num_mixer_ctls; i++) { 462 ctl = mixer_get_ctl(ar->mixer, i); 463 num_values = mixer_ctl_get_num_values(ctl); 464 465 ar->mixer_state[i].old_value = malloc(num_values * sizeof(int)); 466 ar->mixer_state[i].new_value = malloc(num_values * sizeof(int)); 467 ar->mixer_state[i].reset_value = malloc(num_values * sizeof(int)); 468 469 /* 470 * Get all mixer values for controls with multiple values. If all 471 * values are the same, set the linked flag. 472 */ 473 linked = true; 474 for (j = 0; j < num_values; j++) { 475 ar->mixer_state[i].old_value[j] = mixer_ctl_get_value(ctl, j); 476 ar->mixer_state[i].new_value[j] = ar->mixer_state[i].old_value[j]; 477 478 /* 479 * If the next value is different from the last, set linked to 480 * false. 481 */ 482 if ((j > 0) && (ar->mixer_state[i].old_value[j - 1] != 483 ar->mixer_state[i].old_value[j])) { 484 linked = false; 485 } 486 } 487 ar->mixer_state[i].ctl = ctl; 488 ar->mixer_state[i].old_linked = linked; 489 ar->mixer_state[i].new_linked = linked; 490 ar->mixer_state[i].num_values = num_values; 491 } 492 493 return 0; 494} 495 496static void free_mixer_state(struct audio_route *ar) 497{ 498 unsigned int i; 499 500 for (i = 0; i < ar->num_mixer_ctls; i++) { 501 free(ar->mixer_state[i].old_value); 502 free(ar->mixer_state[i].new_value); 503 free(ar->mixer_state[i].reset_value); 504 } 505 506 free(ar->mixer_state); 507 ar->mixer_state = NULL; 508} 509 510/* Update the mixer with any changed values */ 511int audio_route_update_mixer(struct audio_route *ar) 512{ 513 unsigned int i; 514 unsigned int j; 515 516 for (i = 0; i < ar->num_mixer_ctls; i++) { 517 unsigned int num_values = ar->mixer_state[i].num_values; 518 519 /* if the value has changed, update the mixer */ 520 if (ar->mixer_state[i].new_linked) { 521 if (ar->mixer_state[i].old_value[0] != ar->mixer_state[i].new_value[0]) { 522 /* linked ctl, so set all ctl values the same */ 523 for (j = 0; j < num_values; j++) 524 mixer_ctl_set_value(ar->mixer_state[i].ctl, j, 525 ar->mixer_state[i].new_value[0]); 526 ar->mixer_state[i].old_value[0] = ar->mixer_state[i].new_value[0]; 527 } 528 } else { 529 for (j = 0; j < num_values; j++) { 530 /* 531 * unlinked ctl, so set each value if necessary. 532 * Note that if the new value is unlinked but the old is 533 * linked, only value 0 is valid, so we always have to 534 * update the mixer for the other values. 535 */ 536 if (ar->mixer_state[i].old_linked || 537 (ar->mixer_state[i].old_value[j] != 538 ar->mixer_state[i].new_value[j])) { 539 mixer_ctl_set_value(ar->mixer_state[i].ctl, j, 540 ar->mixer_state[i].new_value[j]); 541 ar->mixer_state[i].old_value[j] = ar->mixer_state[i].new_value[j]; 542 } 543 } 544 } 545 ar->mixer_state[i].old_linked = ar->mixer_state[i].new_linked; 546 } 547 548 return 0; 549} 550 551/* saves the current state of the mixer, for resetting all controls */ 552static void save_mixer_state(struct audio_route *ar) 553{ 554 unsigned int i; 555 unsigned int j; 556 557 for (i = 0; i < ar->num_mixer_ctls; i++) { 558 for (j = 0; j < ar->mixer_state[i].num_values; j++) { 559 ar->mixer_state[i].reset_value[j] = ar->mixer_state[i].new_value[j]; 560 561 /* if the values are linked, only need to save value 0 */ 562 if (ar->mixer_state[i].new_linked) 563 break; 564 } 565 ar->mixer_state[i].reset_linked = ar->mixer_state[i].new_linked; 566 } 567} 568 569/* Reset the audio routes back to the initial state */ 570void audio_route_reset(struct audio_route *ar) 571{ 572 unsigned int i; 573 unsigned int j; 574 575 /* load all of the saved values */ 576 for (i = 0; i < ar->num_mixer_ctls; i++) { 577 for (j = 0; j < ar->mixer_state[i].num_values; j++) { 578 ar->mixer_state[i].new_value[j] = ar->mixer_state[i].reset_value[j]; 579 580 /* if the values are linked, only need to save value 0 */ 581 if (ar->mixer_state[i].reset_linked) 582 break; 583 } 584 ar->mixer_state[i].new_linked = ar->mixer_state[i].reset_linked; 585 } 586} 587 588/* Apply an audio route path by name */ 589int audio_route_apply_path(struct audio_route *ar, const char *name) 590{ 591 struct mixer_path *path; 592 593 if (!ar) { 594 ALOGE("invalid audio_route"); 595 return -1; 596 } 597 598 path = path_get_by_name(ar, name); 599 if (!path) { 600 ALOGE("unable to find path '%s'", name); 601 return -1; 602 } 603 604 path_apply(ar, path); 605 606 return 0; 607} 608 609struct audio_route *audio_route_init(unsigned int card, const char *xml_path) 610{ 611 struct config_parse_state state; 612 XML_Parser parser; 613 FILE *file; 614 int bytes_read; 615 void *buf; 616 int i; 617 struct audio_route *ar; 618 619 ar = calloc(1, sizeof(struct audio_route)); 620 if (!ar) 621 goto err_calloc; 622 623 ar->mixer = mixer_open(card); 624 if (!ar->mixer) { 625 ALOGE("Unable to open the mixer, aborting."); 626 goto err_mixer_open; 627 } 628 629 ar->mixer_path = NULL; 630 ar->mixer_path_size = 0; 631 ar->num_mixer_paths = 0; 632 633 /* allocate space for and read current mixer settings */ 634 if (alloc_mixer_state(ar) < 0) 635 goto err_mixer_state; 636 637 /* use the default XML path if none is provided */ 638 if (xml_path == NULL) 639 xml_path = MIXER_XML_PATH; 640 641 file = fopen(xml_path, "r"); 642 643 if (!file) { 644 ALOGE("Failed to open %s", xml_path); 645 goto err_fopen; 646 } 647 648 parser = XML_ParserCreate(NULL); 649 if (!parser) { 650 ALOGE("Failed to create XML parser"); 651 goto err_parser_create; 652 } 653 654 memset(&state, 0, sizeof(state)); 655 state.ar = ar; 656 XML_SetUserData(parser, &state); 657 XML_SetElementHandler(parser, start_tag, end_tag); 658 659 for (;;) { 660 buf = XML_GetBuffer(parser, BUF_SIZE); 661 if (buf == NULL) 662 goto err_parse; 663 664 bytes_read = fread(buf, 1, BUF_SIZE, file); 665 if (bytes_read < 0) 666 goto err_parse; 667 668 if (XML_ParseBuffer(parser, bytes_read, 669 bytes_read == 0) == XML_STATUS_ERROR) { 670 ALOGE("Error in mixer xml (%s)", MIXER_XML_PATH); 671 goto err_parse; 672 } 673 674 if (bytes_read == 0) 675 break; 676 } 677 678 /* apply the initial mixer values, and save them so we can reset the 679 mixer to the original values */ 680 audio_route_update_mixer(ar); 681 save_mixer_state(ar); 682 683 XML_ParserFree(parser); 684 fclose(file); 685 return ar; 686 687err_parse: 688 XML_ParserFree(parser); 689err_parser_create: 690 fclose(file); 691err_fopen: 692 free_mixer_state(ar); 693err_mixer_state: 694 mixer_close(ar->mixer); 695err_mixer_open: 696 free(ar); 697 ar = NULL; 698err_calloc: 699 return NULL; 700} 701 702void audio_route_free(struct audio_route *ar) 703{ 704 free_mixer_state(ar); 705 mixer_close(ar->mixer); 706 free(ar); 707} 708