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