1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "volume_listener" 18//#define LOG_NDEBUG 0 19#include <stdlib.h> 20#include <dlfcn.h> 21#include <math.h> 22#include <pthread.h> 23#include <unistd.h> 24 25#include <cutils/list.h> 26#include <cutils/log.h> 27#include <hardware/audio_effect.h> 28#include <cutils/properties.h> 29#include <platform_api.h> 30 31#define PRIMARY_HAL_FILENAME XSTR(LIB_AUDIO_HAL) 32#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 33 34#define XSTR(x) STR(x) 35#define STR(x) #x 36 37#define VOL_FLAG ( EFFECT_FLAG_TYPE_INSERT | \ 38 EFFECT_FLAG_VOLUME_IND | \ 39 EFFECT_FLAG_DEVICE_IND | \ 40 EFFECT_FLAG_OFFLOAD_SUPPORTED | \ 41 EFFECT_FLAG_NO_PROCESS) 42 43#define PRINT_STREAM_TYPE(i) ALOGV("descriptor found and is of stream type %s ",\ 44 i == MUSIC?"MUSIC": \ 45 i == RING?"RING": \ 46 i == ALARM?"ALARM": \ 47 i == VOICE_CALL?"Voice_call": \ 48 i == VC_CALL ?"VC_call": \ 49 i == NOTIFICATION?"Notification":\ 50 "--INVALID--"); \ 51 52#define MAX_GAIN_LEVELS 5 53 54#define AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION "audio_hw_send_gain_dep_calibration" 55#define AHAL_GAIN_GET_MAPPING_TABLE "audio_hw_get_gain_level_mapping" 56#define DEFAULT_CAL_STEP 0 57 58enum { 59 VOL_LISTENER_STATE_UNINITIALIZED, 60 VOL_LISTENER_STATE_INITIALIZED, 61 VOL_LISTENER_STATE_ACTIVE, 62}; 63 64typedef struct vol_listener_context_s vol_listener_context_t; 65static const struct effect_interface_s effect_interface; 66 67/* flag to avoid multiple initialization */ 68static bool initialized = false; 69 70/* current gain dep cal level that was pushed succesfully */ 71static int current_gain_dep_cal_level = -1; 72 73enum STREAM_TYPE { 74 MUSIC, 75 RING, 76 ALARM, 77 VOICE_CALL, 78 VC_CALL, 79 NOTIFICATION, 80 MAX_STREAM_TYPES, 81}; 82 83struct vol_listener_context_s { 84 const struct effect_interface_s *itfe; 85 struct listnode effect_list_node; 86 effect_config_t config; 87 const effect_descriptor_t *desc; 88 uint32_t stream_type; 89 uint32_t session_id; 90 uint32_t state; 91 uint32_t dev_id; 92 float left_vol; 93 float right_vol; 94}; 95 96/* volume listener, music UUID: 08b8b058-0590-11e5-ac71-0025b32654a0 */ 97const effect_descriptor_t vol_listener_music_descriptor = { 98 { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type 99 { 0x08b8b058, 0x0590, 0x11e5, 0xac71, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid 100 EFFECT_CONTROL_API_VERSION, 101 VOL_FLAG, 102 0, /* TODO */ 103 1, 104 "Volume listener for Music", 105 "Qualcomm Technologies Inc.", 106}; 107 108/* volume listener, ring UUID: 0956df94-0590-11e5-bdbe-0025b32654a0 */ 109const effect_descriptor_t vol_listener_ring_descriptor = { 110 { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type 111 { 0x0956df94, 0x0590, 0x11e5, 0xbdbe, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid 112 EFFECT_CONTROL_API_VERSION, 113 VOL_FLAG, 114 0, /* TODO */ 115 1, 116 "Volume listener for ring", 117 "Qualcomm Technologies Inc", 118}; 119 120/* volume listener, alarm UUID: 09f303e2-0590-11e5-8fdb-0025b32654a0 */ 121const effect_descriptor_t vol_listener_alarm_descriptor = { 122 { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type 123 { 0x09f303e2, 0x0590, 0x11e5, 0x8fdb, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid 124 EFFECT_CONTROL_API_VERSION, 125 VOL_FLAG, 126 0, /* TODO */ 127 1, 128 "Volume listener for alarm", 129 "Qualcomm Technologies Inc", 130}; 131 132/* volume listener, voice call UUID: 0ace5c08-0590-11e5-ae9e-0025b32654a0 */ 133const effect_descriptor_t vol_listener_voice_call_descriptor = { 134 { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type 135 { 0x0ace5c08, 0x0590, 0x11e5, 0xae9e, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid 136 EFFECT_CONTROL_API_VERSION, 137 VOL_FLAG, 138 0, /* TODO */ 139 1, 140 "Volume listener for voice call", 141 "Qualcomm Technologies Inc", 142}; 143 144/* volume listener, notification UUID: 0b776dde-0590-11e5-81ba-0025b32654a0 */ 145const effect_descriptor_t vol_listener_notification_descriptor = { 146 { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // type 147 { 0x0b776dde, 0x0590, 0x11e5, 0x81ba, { 0x00, 0x25, 0xb3, 0x26, 0x54, 0xa0 } }, // uuid 148 EFFECT_CONTROL_API_VERSION, 149 VOL_FLAG, 150 0, /* TODO */ 151 1, 152 "Volume listener for notification", 153 "Qualcomm Technologies Inc", 154}; 155 156static int total_volume_cal_step = MAX_GAIN_LEVELS; 157 158// using gain level for non-drc volume curve 159struct amp_db_and_gain_table volume_curve_gain_mapping_table[MAX_VOLUME_CAL_STEPS] = 160{ 161 /* Level 0 in the calibration database contains default calibration */ 162 { 0.001774, -55, 5 }, 163 { 0.501187, -6, 4 }, 164 { 0.630957, -4, 3 }, 165 { 0.794328, -2, 2 }, 166 { 1.0, 0, 1 }, 167 { 0, 0, -1 }, 168 { 0, 0, -1 }, 169 { 0, 0, -1 }, 170 { 0, 0, -1 }, 171 { 0, 0, -1 }, 172 { 0, 0, -1 }, 173 { 0, 0, -1 }, 174 { 0, 0, -1 }, 175 { 0, 0, -1 }, 176 { 0, 0, -1 } 177}; 178 179static const effect_descriptor_t *descriptors[] = { 180 &vol_listener_music_descriptor, 181 &vol_listener_ring_descriptor, 182 &vol_listener_alarm_descriptor, 183 &vol_listener_voice_call_descriptor, 184 &vol_listener_notification_descriptor, 185 NULL, 186}; 187 188pthread_once_t once = PTHREAD_ONCE_INIT; 189/* flag to indicate if init was success */ 190static int init_status; 191 192/* current volume level for which gain dep cal level was selected */ 193static float current_vol = 0.0; 194 195/* HAL interface to send calibration */ 196static bool (*send_gain_dep_cal)(int); 197 198static int (*get_custom_gain_table)(struct amp_db_and_gain_table *, int); 199 200/* if dumping allowed */ 201static bool dumping_enabled = false; 202 203/* list of created effects. */ 204struct listnode vol_effect_list; 205 206/* lock must be held when modifying or accessing created_effects_list */ 207pthread_mutex_t vol_listner_init_lock; 208 209/* Treblized modules locations */ 210static const char *primary_audio_hal_path[] = 211 {"/vendor/lib/hw/", "/system/lib/hw/"}; 212 213static bool headset_cal_enabled; 214 215/* 216 * Local functions 217 */ 218static void dump_list_l() 219{ 220 struct listnode *node; 221 vol_listener_context_t *context; 222 223 ALOGW("DUMP_START :: ==========="); 224 225 list_for_each(node, &vol_effect_list) { 226 context = node_to_item(node, struct vol_listener_context_s, effect_list_node); 227 // dump stream_type / Device / session_id / left / righ volume 228 ALOGW("%s: streamType [%s] Device [%d] state [%d] sessionID [%d] volume (L/R) [%f / %f] ", 229 __func__, 230 context->stream_type == MUSIC ? "MUSIC" : 231 context->stream_type == RING ? "RING" : 232 context->stream_type == ALARM ? "ALARM" : 233 context->stream_type == VOICE_CALL ? "VOICE_CALL" : 234 context->stream_type == VC_CALL ? "VC_CALL" : 235 context->stream_type == NOTIFICATION ? "NOTIFICATION" : "--INVALID--", 236 context->dev_id, context->state, context->session_id, context->left_vol,context->right_vol); 237 } 238 239 ALOGW("DUMP_END :: ==========="); 240} 241 242static inline bool valid_dev_in_context(struct vol_listener_context_s *context) 243{ 244 if (context->dev_id == AUDIO_DEVICE_OUT_SPEAKER || 245 context->dev_id == AUDIO_DEVICE_OUT_SPEAKER_SAFE) 246 return true; 247 248 if (context->stream_type == VC_CALL && headset_cal_enabled && 249 (context->dev_id == AUDIO_DEVICE_OUT_EARPIECE || 250 context->dev_id == AUDIO_DEVICE_OUT_WIRED_HEADSET || 251 context->dev_id == AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) 252 return true; 253 254 return false; 255} 256 257static void check_and_set_gain_dep_cal() 258{ 259 // iterate through list and make decision to set new gain dep cal level for speaker device 260 // 1. find all usecase active on speaker 261 // 2. find energy sum for each usecase 262 // 3. find the highest of all the active usecase 263 // 4. if new value is different than the current value then load new calibration 264 265 struct listnode *node = NULL; 266 float new_vol = -1.0; 267 int max_level = 0; 268 vol_listener_context_t *context = NULL; 269 if (dumping_enabled) { 270 dump_list_l(); 271 } 272 273 ALOGV("%s ==> Start ...", __func__); 274 275 float sum_energy = 0.0; 276 bool sum_energy_used = false; 277 float temp_vol = 0; 278 // compute energy sum for the active speaker device (pick loudest of both channels) 279 list_for_each(node, &vol_effect_list) { 280 context = node_to_item(node, struct vol_listener_context_s, effect_list_node); 281 if ((context->state == VOL_LISTENER_STATE_ACTIVE) && 282 valid_dev_in_context(context)) { 283 sum_energy_used = true; 284 temp_vol = fmax(context->left_vol, context->right_vol); 285 sum_energy += temp_vol * temp_vol; 286 } 287 } 288 if (sum_energy_used) { 289 new_vol = fmin(sqrt(sum_energy), 1.0); 290 } 291 292 if (new_vol != current_vol) { 293 ALOGV("%s:: Change in decision :: current volume is %f new volume is %f", 294 __func__, current_vol, new_vol); 295 296 if (send_gain_dep_cal != NULL) { 297 // send Gain dep cal level 298 int gain_dep_cal_level = -1; 299 300 if (new_vol >= 1 && total_volume_cal_step > 0) { // max amplitude, use highest DRC level 301 gain_dep_cal_level = volume_curve_gain_mapping_table[total_volume_cal_step - 1].level; 302 } else if (new_vol == -1) { 303 gain_dep_cal_level = DEFAULT_CAL_STEP; 304 } else if (new_vol == 0) { 305 gain_dep_cal_level = volume_curve_gain_mapping_table[0].level; 306 } else { 307 for (max_level = 0; max_level + 1 < total_volume_cal_step; max_level++) { 308 if (new_vol < volume_curve_gain_mapping_table[max_level + 1].amp && 309 new_vol >= volume_curve_gain_mapping_table[max_level].amp) { 310 gain_dep_cal_level = volume_curve_gain_mapping_table[max_level].level; 311 ALOGV("%s: volume(%f), gain dep cal selcetd %d ", 312 __func__, current_vol, gain_dep_cal_level); 313 break; 314 } 315 } 316 } 317 318 // check here if previous gain dep cal level was not same 319 if (gain_dep_cal_level != -1) { 320 if (gain_dep_cal_level != current_gain_dep_cal_level) { 321 // decision made .. send new level now 322 if (!send_gain_dep_cal(gain_dep_cal_level)) { 323 ALOGE("%s: Failed to set gain dep cal level", __func__); 324 } 325 326 if (dumping_enabled) { 327 ALOGW("%s: (old/new) Volume (%f/%f) (old/new) level (%d/%d)", 328 __func__, current_vol, new_vol, current_gain_dep_cal_level, 329 gain_dep_cal_level); 330 } else { 331 ALOGV("%s: Change in Cal::(old/new) Volume (%f/%f) (old/new) level (%d/%d)", 332 __func__, current_vol, new_vol, current_gain_dep_cal_level, 333 gain_dep_cal_level); 334 } 335 336 // Gain level change info send to lower layer that has logic to re-apply on 337 // failure, so change current gain level to reflect new level 338 current_gain_dep_cal_level = gain_dep_cal_level; 339 current_vol = new_vol; 340 } else { 341 if (dumping_enabled) { 342 ALOGW("%s: volume changed but gain dep cal level is still the same", 343 __func__); 344 } else { 345 ALOGV("%s: volume changed but gain dep cal level is still the same", 346 __func__); 347 } 348 } 349 } else { 350 ALOGW("%s: Failed to find gain dep cal level for volume %f", __func__, new_vol); 351 } 352 } else { 353 ALOGE("%s: not able to send calibration, NULL function pointer", 354 __func__); 355 } 356 } else { 357 ALOGV("%s:: volume not changed, stick to same config ..... ", __func__); 358 } 359 360 ALOGV("check_and_set_gain_dep_cal ==> End "); 361} 362 363/* 364 * Effect Control Interface Implementation 365 */ 366 367static inline int16_t clamp16(int32_t sample) 368{ 369 if ((sample>>15) ^ (sample>>31)) 370 sample = 0x7FFF ^ (sample>>31); 371 return sample; 372} 373 374static int vol_effect_command(effect_handle_t self, 375 uint32_t cmd_code, uint32_t cmd_size, 376 void *p_cmd_data, uint32_t *reply_size, 377 void *p_reply_data) 378{ 379 vol_listener_context_t *context = (vol_listener_context_t *)self; 380 int status = 0; 381 382 ALOGV("%s Called ", __func__); 383 pthread_mutex_lock(&vol_listner_init_lock); 384 385 if (context == NULL || context->state == VOL_LISTENER_STATE_UNINITIALIZED) { 386 ALOGE("%s: %s is NULL", __func__, (context == NULL) ? 387 "context" : "context->state"); 388 status = -EINVAL; 389 goto exit; 390 } 391 392 switch (cmd_code) { 393 case EFFECT_CMD_INIT: 394 ALOGV("%s :: cmd called EFFECT_CMD_INIT", __func__); 395 if (p_reply_data == NULL || *reply_size != sizeof(int)) { 396 ALOGE("%s: EFFECT_CMD_INIT: %s, sending -EINVAL", __func__, 397 (p_reply_data == NULL) ? "p_reply_data is NULL" : 398 "*reply_size != sizeof(int)"); 399 android_errorWriteLog(0x534e4554, "32669549"); 400 status = -EINVAL; 401 goto exit; 402 } 403 *(int *)p_reply_data = 0; 404 break; 405 406 case EFFECT_CMD_SET_CONFIG: 407 ALOGV("%s :: cmd called EFFECT_CMD_SET_CONFIG", __func__); 408 if (p_cmd_data == NULL || cmd_size != sizeof(effect_config_t) 409 || p_reply_data == NULL || reply_size == NULL || *reply_size != sizeof(int)) { 410 android_errorWriteLog(0x534e4554, "32669549"); 411 status = -EINVAL; 412 goto exit; 413 } 414 context->config = *(effect_config_t *)p_cmd_data; 415 *(int *)p_reply_data = 0; 416 break; 417 418 case EFFECT_CMD_GET_CONFIG: 419 ALOGV("%s :: cmd called EFFECT_CMD_GET_CONFIG", __func__); 420 break; 421 422 case EFFECT_CMD_RESET: 423 ALOGV("%s :: cmd called EFFECT_CMD_RESET", __func__); 424 break; 425 426 case EFFECT_CMD_SET_AUDIO_MODE: 427 ALOGV("%s :: cmd called EFFECT_CMD_SET_AUDIO_MODE", __func__); 428 break; 429 430 case EFFECT_CMD_OFFLOAD: 431 ALOGV("%s :: cmd called EFFECT_CMD_OFFLOAD", __func__); 432 if (p_reply_data == NULL || *reply_size != sizeof(int)) { 433 ALOGE("%s: EFFECT_CMD_OFFLOAD: %s, sending -EINVAL", __func__, 434 (p_reply_data == NULL) ? "p_reply_data is NULL" : 435 "*reply_size != sizeof(int)"); 436 android_errorWriteLog(0x534e4554, "32669549"); 437 status = -EINVAL; 438 goto exit; 439 } 440 *(int *)p_reply_data = 0; 441 break; 442 443 case EFFECT_CMD_ENABLE: 444 ALOGV("%s :: cmd called EFFECT_CMD_ENABLE", __func__); 445 if (p_reply_data == NULL || *reply_size != sizeof(int)) { 446 ALOGE("%s: EFFECT_CMD_ENABLE: %s, sending -EINVAL", __func__, 447 (p_reply_data == NULL) ? "p_reply_data is NULL" : 448 "*reply_size != sizeof(int)"); 449 status = -EINVAL; 450 goto exit; 451 } 452 453 if (context->state != VOL_LISTENER_STATE_INITIALIZED) { 454 ALOGE("%s: EFFECT_CMD_ENABLE : state not INITIALIZED", __func__); 455 status = -ENOSYS; 456 goto exit; 457 } 458 459 context->state = VOL_LISTENER_STATE_ACTIVE; 460 *(int *)p_reply_data = 0; 461 462 // After changing the state and if device is speaker 463 // recalculate gain dep cal level 464 if (valid_dev_in_context(context)) { 465 check_and_set_gain_dep_cal(); 466 } 467 468 break; 469 470 case EFFECT_CMD_DISABLE: 471 ALOGV("%s :: cmd called EFFECT_CMD_DISABLE", __func__); 472 if (p_reply_data == NULL || *reply_size != sizeof(int)) { 473 ALOGE("%s: EFFECT_CMD_DISABLE: %s, sending -EINVAL", __func__, 474 (p_reply_data == NULL) ? "p_reply_data is NULL" : 475 "*reply_size != sizeof(int)"); 476 status = -EINVAL; 477 goto exit; 478 } 479 480 if (context->state != VOL_LISTENER_STATE_ACTIVE) { 481 ALOGE("%s: EFFECT_CMD_ENABLE : state not ACTIVE", __func__); 482 status = -ENOSYS; 483 goto exit; 484 } 485 486 context->state = VOL_LISTENER_STATE_INITIALIZED; 487 *(int *)p_reply_data = 0; 488 489 // After changing the state and if device is speaker 490 // recalculate gain dep cal level 491 if (valid_dev_in_context(context)) { 492 check_and_set_gain_dep_cal(); 493 } 494 495 break; 496 497 case EFFECT_CMD_GET_PARAM: 498 ALOGV("%s :: cmd called EFFECT_CMD_GET_PARAM", __func__); 499 break; 500 501 case EFFECT_CMD_SET_PARAM: 502 ALOGV("%s :: cmd called EFFECT_CMD_SET_PARAM", __func__); 503 break; 504 505 case EFFECT_CMD_SET_DEVICE: 506 { 507 uint32_t new_device; 508 bool recompute_gain_dep_cal_Level = false; 509 ALOGV("cmd called EFFECT_CMD_SET_DEVICE "); 510 511 if (p_cmd_data == NULL) { 512 ALOGE("%s: EFFECT_CMD_SET_DEVICE: cmd data NULL", __func__); 513 status = -EINVAL; 514 goto exit; 515 } 516 517 new_device = *(uint32_t *)p_cmd_data; 518 ALOGV("%s :: EFFECT_CMD_SET_DEVICE: (current/new) device (0x%x / 0x%x)", 519 __func__, context->dev_id, new_device); 520 521 // check if old or new device is speaker 522 if (valid_dev_in_context(context) || 523 new_device == AUDIO_DEVICE_OUT_SPEAKER || 524 new_device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) { 525 recompute_gain_dep_cal_Level = true; 526 } 527 528 context->dev_id = new_device; 529 530 if (recompute_gain_dep_cal_Level) { 531 check_and_set_gain_dep_cal(); 532 } 533 } 534 break; 535 536 case EFFECT_CMD_SET_VOLUME: 537 { 538 float left_vol = 0, right_vol = 0; 539 bool recompute_gain_dep_cal_Level = false; 540 541 ALOGV("cmd called EFFECT_CMD_SET_VOLUME"); 542 if (p_cmd_data == NULL || cmd_size != 2 * sizeof(uint32_t)) { 543 ALOGE("%s: EFFECT_CMD_SET_VOLUME: %s", __func__, (p_cmd_data == NULL) ? 544 "p_cmd_data is NULL" : "cmd_size issue"); 545 status = -EINVAL; 546 goto exit; 547 } 548 549 if (valid_dev_in_context(context)) { 550 recompute_gain_dep_cal_Level = true; 551 } 552 553 left_vol = (float)(*(uint32_t *)p_cmd_data) / (1 << 24); 554 right_vol = (float)(*((uint32_t *)p_cmd_data + 1)) / (1 << 24); 555 ALOGV("Current Volume (%f / %f ) new Volume (%f / %f)", context->left_vol, 556 context->right_vol, left_vol, right_vol); 557 558 context->left_vol = left_vol; 559 context->right_vol = right_vol; 560 561 // recompute gan dep cal level only if volume changed on speaker device 562 if (recompute_gain_dep_cal_Level) { 563 check_and_set_gain_dep_cal(); 564 } 565 } 566 break; 567 568 default: 569 ALOGW("volume_listener_command invalid command %d", cmd_code); 570 status = -ENOSYS; 571 break; 572 } 573 574exit: 575 pthread_mutex_unlock(&vol_listner_init_lock); 576 return status; 577} 578 579/* Effect Control Interface Implementation: get_descriptor */ 580static int vol_effect_get_descriptor(effect_handle_t self, 581 effect_descriptor_t *descriptor) 582{ 583 vol_listener_context_t *context = (vol_listener_context_t *)self; 584 ALOGV("%s Called ", __func__); 585 586 if (descriptor == NULL) { 587 ALOGE("%s: descriptor is NULL", __func__); 588 return -EINVAL; 589 } 590 591 *descriptor = *context->desc; 592 return 0; 593} 594 595static bool resolve_audio_hal_path(char *file_name, int mode) { 596 char file_path[PATH_MAX]; 597 unsigned long i; 598 599 for (i = 0; i < ARRAY_SIZE(primary_audio_hal_path); i++) { 600 snprintf(file_path, PATH_MAX, "%s/%s", 601 primary_audio_hal_path[i], file_name); 602 if (F_OK == access(file_path, mode)) { 603 strcpy(file_name, file_path); 604 return true; 605 } 606 } 607 return false; 608} 609 610static void init_once() 611{ 612 int max_table_ent = 0; 613 void *hal_lib_pointer = NULL; 614 char primary_hal_path[PATH_MAX] = {0}; 615 616 if (initialized) { 617 ALOGV("%s : already init .. do nothing", __func__); 618 return; 619 } 620 621 ALOGD("%s Called ", __func__); 622 send_gain_dep_cal = NULL; 623 get_custom_gain_table = NULL; 624 625 pthread_mutex_init(&vol_listner_init_lock, NULL); 626 627 strcpy(primary_hal_path, PRIMARY_HAL_FILENAME); 628 // get hal function pointer 629 if (resolve_audio_hal_path(primary_hal_path, R_OK)) { 630 hal_lib_pointer = dlopen(primary_hal_path, RTLD_NOW); 631 } else { 632 ALOGE("%s: not able to acces lib: %s", __func__, PRIMARY_HAL_FILENAME); 633 } 634 635 if (!hal_lib_pointer) { 636 ALOGE("%s: DLOPEN failed for %s", __func__, primary_hal_path); 637 } else { 638 ALOGV("%s: DLOPEN of %s Succes .. next get HAL entry function", __func__, primary_hal_path); 639 send_gain_dep_cal = (bool (*)(int))dlsym(hal_lib_pointer, AHAL_GAIN_DEPENDENT_INTERFACE_FUNCTION); 640 if (send_gain_dep_cal == NULL) { 641 ALOGE("Couldnt able to get the function symbol"); 642 } 643 get_custom_gain_table = (int (*) (struct amp_db_and_gain_table *, int))dlsym(hal_lib_pointer, AHAL_GAIN_GET_MAPPING_TABLE); 644 if (get_custom_gain_table == NULL) { 645 ALOGE("Couldnt able to get the function AHAL_GAIN_GET_MAPPING_TABLE symbol"); 646 } else { 647 max_table_ent = get_custom_gain_table(volume_curve_gain_mapping_table, MAX_VOLUME_CAL_STEPS); 648 // if number of entries is 0 use default 649 // if number of entries > MAX_VOLUME_CAL_STEPS (this should never happen) then in this case 650 // use only default number of steps but this will result in unexpected behaviour 651 652 if (max_table_ent > 0 && max_table_ent <= MAX_VOLUME_CAL_STEPS) { 653 if (max_table_ent < total_volume_cal_step) { 654 for (int i = max_table_ent; i < total_volume_cal_step; i++ ) { 655 volume_curve_gain_mapping_table[i].amp = 0; 656 volume_curve_gain_mapping_table[i].db = 0; 657 volume_curve_gain_mapping_table[i].level = -1; 658 } 659 } 660 total_volume_cal_step = max_table_ent; 661 ALOGD("%s: using custome volume table", __func__); 662 } else { 663 ALOGD("%s: using default volume table", __func__); 664 } 665 666 if (dumping_enabled) { 667 ALOGD("%s: dumping table here .. size of table received %d", 668 __func__, max_table_ent); 669 for (int i = 0; i < MAX_VOLUME_CAL_STEPS ; i++) 670 ALOGD("[%d] %f %f %d", i, volume_curve_gain_mapping_table[i].amp, 671 volume_curve_gain_mapping_table[i].db, 672 volume_curve_gain_mapping_table[i].level); 673 } 674 } 675 } 676 677 dumping_enabled = property_get_bool("audio.volume.listener.dump", false); 678 headset_cal_enabled = property_get_bool("audio.volume.headset.gain.depcal", false); 679 init_status = 0; 680 list_init(&vol_effect_list); 681 initialized = true; 682} 683 684static int lib_init() 685{ 686 pthread_once(&once, init_once); 687 ALOGV("%s Called ", __func__); 688 return init_status; 689} 690 691static int vol_prc_lib_create(const effect_uuid_t *uuid, 692 int32_t session_id, 693 int32_t io_id __unused, 694 effect_handle_t *p_handle) 695{ 696 int itt = 0; 697 vol_listener_context_t *context = NULL; 698 699 ALOGV("volume_prc_lib_create .. called .."); 700 701 if (lib_init() != 0) { 702 return init_status; 703 } 704 705 if (p_handle == NULL || uuid == NULL) { 706 ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid"); 707 return -EINVAL; 708 } 709 710 context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t)); 711 712 if (context == NULL) { 713 ALOGE("%s: failed to allocate for context .. oops !!", __func__); 714 return -EINVAL; 715 } 716 717 // check if UUID is supported 718 for (itt = 0; descriptors[itt] != NULL; itt++) { 719 if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) { 720 // check if this correct .. very imp 721 context->desc = descriptors[itt]; 722 context->stream_type = itt; 723 PRINT_STREAM_TYPE(itt) 724 break; 725 } 726 } 727 728 if (descriptors[itt] == NULL) { 729 ALOGE("%s .. couldnt find passed uuid, something wrong", __func__); 730 free(context); 731 return -EINVAL; 732 } 733 734 ALOGV("%s CREATED_CONTEXT %p", __func__, context); 735 736 context->itfe = &effect_interface; 737 context->state = VOL_LISTENER_STATE_INITIALIZED; 738 context->dev_id = AUDIO_DEVICE_NONE; 739 context->session_id = session_id; 740 741 // Add this to master list 742 pthread_mutex_lock(&vol_listner_init_lock); 743 list_add_tail(&vol_effect_list, &context->effect_list_node); 744 745 if (dumping_enabled) { 746 dump_list_l(); 747 } 748 749 pthread_mutex_unlock(&vol_listner_init_lock); 750 751 *p_handle = (effect_handle_t)context; 752 return 0; 753} 754 755static int vol_prc_lib_release(effect_handle_t handle) 756{ 757 struct listnode *node, *temp_node_next; 758 vol_listener_context_t *context = NULL; 759 vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle; 760 int status = -EINVAL; 761 bool recompute_flag = false; 762 int active_stream_count = 0; 763 uint32_t session_id; 764 uint32_t stream_type; 765 effect_uuid_t uuid; 766 767 ALOGV("%s context %p", __func__, handle); 768 769 if (recv_contex == NULL) { 770 return status; 771 } 772 pthread_mutex_lock(&vol_listner_init_lock); 773 session_id = recv_contex->session_id; 774 stream_type = recv_contex->stream_type; 775 776 if (recv_contex->desc == NULL) { 777 ALOGE("%s: Got NULL descriptor, session %u, stream type %u", 778 __func__, session_id, stream_type); 779 dump_list_l(); 780 pthread_mutex_unlock(&vol_listner_init_lock); 781 return status; 782 } 783 uuid = recv_contex->desc->uuid; 784 785 // check if the handle/context provided is valid 786 list_for_each_safe(node, temp_node_next, &vol_effect_list) { 787 context = node_to_item(node, struct vol_listener_context_s, effect_list_node); 788 if ((memcmp(&(context->desc->uuid), &uuid, sizeof(effect_uuid_t)) == 0) 789 && (context->session_id == session_id) 790 && (context->stream_type == stream_type)) { 791 ALOGV("--- Found something to remove ---"); 792 list_remove(node); 793 PRINT_STREAM_TYPE(context->stream_type); 794 if (valid_dev_in_context(context)) { 795 recompute_flag = true; 796 } 797 free(context); 798 status = 0; 799 } else { 800 ++active_stream_count; 801 } 802 } 803 804 if (status != 0) { 805 ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>"); 806 pthread_mutex_unlock(&vol_listner_init_lock); 807 return status; 808 } 809 810 // if there are no active streams, reset cal and volume level 811 if (active_stream_count == 0) { 812 current_gain_dep_cal_level = -1; 813 current_vol = 0.0; 814 } 815 816 if (recompute_flag) { 817 check_and_set_gain_dep_cal(); 818 } 819 820 if (dumping_enabled) { 821 dump_list_l(); 822 } 823 pthread_mutex_unlock(&vol_listner_init_lock); 824 return status; 825} 826 827static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid, 828 effect_descriptor_t *descriptor) 829{ 830 int i = 0; 831 ALOGV("%s Called ", __func__); 832 if (lib_init() != 0) { 833 return init_status; 834 } 835 836 if (descriptor == NULL || uuid == NULL) { 837 ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid"); 838 return -EINVAL; 839 } 840 841 for (i = 0; descriptors[i] != NULL; i++) { 842 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) { 843 *descriptor = *descriptors[i]; 844 return 0; 845 } 846 } 847 848 ALOGE("%s: couldnt found uuid passed, oops", __func__); 849 return -EINVAL; 850} 851 852 853/* effect_handle_t interface implementation for volume listener effect */ 854static const struct effect_interface_s effect_interface = { 855 NULL, 856 vol_effect_command, 857 vol_effect_get_descriptor, 858 NULL, 859}; 860 861__attribute__((visibility("default"))) 862audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = { 863 .tag = AUDIO_EFFECT_LIBRARY_TAG, 864 .version = EFFECT_LIBRARY_API_VERSION, 865 .name = "Volume Listener Effect Library", 866 .implementor = "Qualcomm Technologies Inc.", 867 .create_effect = vol_prc_lib_create, 868 .release_effect = vol_prc_lib_release, 869 .get_descriptor = vol_prc_lib_get_descriptor, 870}; 871