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
58 enum {
59 VOL_LISTENER_STATE_UNINITIALIZED,
60 VOL_LISTENER_STATE_INITIALIZED,
61 VOL_LISTENER_STATE_ACTIVE,
62 };
63
64 typedef struct vol_listener_context_s vol_listener_context_t;
65 static const struct effect_interface_s effect_interface;
66
67 /* flag to avoid multiple initialization */
68 static bool initialized = false;
69
70 /* current gain dep cal level that was pushed succesfully */
71 static int current_gain_dep_cal_level = -1;
72
73 enum STREAM_TYPE {
74 MUSIC,
75 RING,
76 ALARM,
77 VOICE_CALL,
78 VC_CALL,
79 NOTIFICATION,
80 MAX_STREAM_TYPES,
81 };
82
83 struct 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 */
97 const 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 */
109 const 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 */
121 const 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 */
133 const 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 */
145 const 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
156 static int total_volume_cal_step = MAX_GAIN_LEVELS;
157
158 // using gain level for non-drc volume curve
159 struct 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
179 static 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
188 pthread_once_t once = PTHREAD_ONCE_INIT;
189 /* flag to indicate if init was success */
190 static int init_status;
191
192 /* current volume level for which gain dep cal level was selected */
193 static float current_vol = 0.0;
194
195 /* HAL interface to send calibration */
196 static bool (*send_gain_dep_cal)(int);
197
198 static int (*get_custom_gain_table)(struct amp_db_and_gain_table *, int);
199
200 /* if dumping allowed */
201 static bool dumping_enabled = false;
202
203 /* list of created effects. */
204 struct listnode vol_effect_list;
205
206 /* lock must be held when modifying or accessing created_effects_list */
207 pthread_mutex_t vol_listner_init_lock;
208
209 /* Treblized modules locations */
210 static const char *primary_audio_hal_path[] =
211 {"/vendor/lib/hw/", "/system/lib/hw/"};
212
213 static bool headset_cal_enabled;
214
215 /*
216 * Local functions
217 */
dump_list_l()218 static 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
valid_dev_in_context(struct vol_listener_context_s * context)242 static 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
check_and_set_gain_dep_cal()257 static 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
clamp16(int32_t sample)367 static inline int16_t clamp16(int32_t sample)
368 {
369 if ((sample>>15) ^ (sample>>31))
370 sample = 0x7FFF ^ (sample>>31);
371 return sample;
372 }
373
vol_effect_command(effect_handle_t self,uint32_t cmd_code,uint32_t cmd_size,void * p_cmd_data,uint32_t * reply_size,void * p_reply_data)374 static 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
574 exit:
575 pthread_mutex_unlock(&vol_listner_init_lock);
576 return status;
577 }
578
579 /* Effect Control Interface Implementation: get_descriptor */
vol_effect_get_descriptor(effect_handle_t self,effect_descriptor_t * descriptor)580 static 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
resolve_audio_hal_path(char * file_name,int mode)595 static 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
init_once()610 static 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(
678 "vendor.audio.volume.listener.dump",
679 property_get_bool("audio.volume.listener.dump", false));
680 headset_cal_enabled = property_get_bool(
681 "vendor.audio.volume.headset.gain.depcal",
682 property_get_bool("audio.volume.headset.gain.depcal", false));
683 init_status = 0;
684 list_init(&vol_effect_list);
685 initialized = true;
686 }
687
lib_init()688 static int lib_init()
689 {
690 pthread_once(&once, init_once);
691 ALOGV("%s Called ", __func__);
692 return init_status;
693 }
694
vol_prc_lib_create(const effect_uuid_t * uuid,int32_t session_id,int32_t io_id __unused,effect_handle_t * p_handle)695 static int vol_prc_lib_create(const effect_uuid_t *uuid,
696 int32_t session_id,
697 int32_t io_id __unused,
698 effect_handle_t *p_handle)
699 {
700 int itt = 0;
701 vol_listener_context_t *context = NULL;
702
703 ALOGV("volume_prc_lib_create .. called ..");
704
705 if (lib_init() != 0) {
706 return init_status;
707 }
708
709 if (p_handle == NULL || uuid == NULL) {
710 ALOGE("%s: %s is NULL", __func__, (p_handle == NULL) ? "p_handle" : "uuid");
711 return -EINVAL;
712 }
713
714 context = (vol_listener_context_t *)calloc(1, sizeof(vol_listener_context_t));
715
716 if (context == NULL) {
717 ALOGE("%s: failed to allocate for context .. oops !!", __func__);
718 return -EINVAL;
719 }
720
721 // check if UUID is supported
722 for (itt = 0; descriptors[itt] != NULL; itt++) {
723 if (memcmp(uuid, &descriptors[itt]->uuid, sizeof(effect_uuid_t)) == 0) {
724 // check if this correct .. very imp
725 context->desc = descriptors[itt];
726 context->stream_type = itt;
727 PRINT_STREAM_TYPE(itt)
728 break;
729 }
730 }
731
732 if (descriptors[itt] == NULL) {
733 ALOGE("%s .. couldnt find passed uuid, something wrong", __func__);
734 free(context);
735 return -EINVAL;
736 }
737
738 ALOGV("%s CREATED_CONTEXT %p", __func__, context);
739
740 context->itfe = &effect_interface;
741 context->state = VOL_LISTENER_STATE_INITIALIZED;
742 context->dev_id = AUDIO_DEVICE_NONE;
743 context->session_id = session_id;
744
745 // Add this to master list
746 pthread_mutex_lock(&vol_listner_init_lock);
747 list_add_tail(&vol_effect_list, &context->effect_list_node);
748
749 if (dumping_enabled) {
750 dump_list_l();
751 }
752
753 pthread_mutex_unlock(&vol_listner_init_lock);
754
755 *p_handle = (effect_handle_t)context;
756 return 0;
757 }
758
vol_prc_lib_release(effect_handle_t handle)759 static int vol_prc_lib_release(effect_handle_t handle)
760 {
761 struct listnode *node, *temp_node_next;
762 vol_listener_context_t *context = NULL;
763 vol_listener_context_t *recv_contex = (vol_listener_context_t *)handle;
764 int status = -EINVAL;
765 bool recompute_flag = false;
766 int active_stream_count = 0;
767
768 ALOGV("%s context %p", __func__, handle);
769
770 if (recv_contex == NULL) {
771 return status;
772 }
773 pthread_mutex_lock(&vol_listner_init_lock);
774
775 // check if the handle/context provided is valid
776 list_for_each_safe(node, temp_node_next, &vol_effect_list) {
777 context = node_to_item(node, struct vol_listener_context_s, effect_list_node);
778 if (context == recv_contex) {
779 ALOGV("--- Found something to remove ---");
780 PRINT_STREAM_TYPE(context->stream_type);
781 if (valid_dev_in_context(context)) {
782 recompute_flag = true;
783 }
784 list_remove(&context->effect_list_node);
785 free(context);
786 status = 0;
787 break;
788 } else {
789 ++active_stream_count;
790 }
791 }
792
793 if (status != 0) {
794 ALOGE("something wrong ... <<<--- Found NOTHING to remove ... ???? --->>>>>");
795 pthread_mutex_unlock(&vol_listner_init_lock);
796 return status;
797 }
798
799 // if there are no active streams, reset cal and volume level
800 if (active_stream_count == 0) {
801 current_gain_dep_cal_level = -1;
802 current_vol = 0.0;
803 }
804
805 if (recompute_flag) {
806 check_and_set_gain_dep_cal();
807 }
808
809 if (dumping_enabled) {
810 dump_list_l();
811 }
812 pthread_mutex_unlock(&vol_listner_init_lock);
813 return status;
814 }
815
vol_prc_lib_get_descriptor(const effect_uuid_t * uuid,effect_descriptor_t * descriptor)816 static int vol_prc_lib_get_descriptor(const effect_uuid_t *uuid,
817 effect_descriptor_t *descriptor)
818 {
819 int i = 0;
820 ALOGV("%s Called ", __func__);
821 if (lib_init() != 0) {
822 return init_status;
823 }
824
825 if (descriptor == NULL || uuid == NULL) {
826 ALOGE("%s: %s is NULL", __func__, (descriptor == NULL) ? "descriptor" : "uuid");
827 return -EINVAL;
828 }
829
830 for (i = 0; descriptors[i] != NULL; i++) {
831 if (memcmp(uuid, &descriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
832 *descriptor = *descriptors[i];
833 return 0;
834 }
835 }
836
837 ALOGE("%s: couldnt found uuid passed, oops", __func__);
838 return -EINVAL;
839 }
840
841
842 /* effect_handle_t interface implementation for volume listener effect */
843 static const struct effect_interface_s effect_interface = {
844 NULL,
845 vol_effect_command,
846 vol_effect_get_descriptor,
847 NULL,
848 };
849
850 __attribute__((visibility("default")))
851 audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
852 .tag = AUDIO_EFFECT_LIBRARY_TAG,
853 .version = EFFECT_LIBRARY_API_VERSION,
854 .name = "Volume Listener Effect Library",
855 .implementor = "Qualcomm Technologies Inc.",
856 .create_effect = vol_prc_lib_create,
857 .release_effect = vol_prc_lib_release,
858 .get_descriptor = vol_prc_lib_get_descriptor,
859 };
860