audio: Add API to set audio calibration

 - add API of setting audio calibration only on platform

Bug: 74360112
Test: manual test
Change-Id: Ie0520f31cd1834fcb6faa1205b839336e9233678
Signed-off-by: jasmine cha <chajasmine@android.com>
diff --git a/hal/acdb.h b/hal/acdb.h
index 08026fb..24dcb8f 100644
--- a/hal/acdb.h
+++ b/hal/acdb.h
@@ -36,6 +36,7 @@
 typedef int (*acdb_reload_vocvoltable_t)(int);
 typedef int (*acdb_send_gain_dep_cal_t)(int, int, int, int, int);
 typedef int (*acdb_send_custom_top_t) (void);
+typedef int (*acdb_set_audio_cal_t) (void *, void *, uint32_t);
 
 struct meta_key_list {
     struct listnode list;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index c80f994..b8755b8 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -34,6 +34,8 @@
 #include <sound/devdep_params.h>
 #endif
 
+#include <resolv.h>
+
 #define MIXER_XML_DEFAULT_PATH "mixer_paths.xml"
 #define MIXER_XML_BASE_STRING "mixer_paths"
 #define TOMTOM_8226_SND_CARD_NAME "msm8226-tomtom-snd-card"
@@ -105,6 +107,21 @@
 static struct listnode operator_info_list;
 static struct listnode *operator_specific_device_table[SND_DEVICE_MAX];
 
+#define AUDIO_PARAMETER_KEY_AUD_CALDATA "cal_data"
+
+typedef struct acdb_audio_cal_cfg {
+    uint32_t             persist;
+    uint32_t             snd_dev_id;
+    audio_devices_t      dev_id;
+    int32_t              acdb_dev_id;
+    uint32_t             app_type;
+    uint32_t             topo_id;
+    uint32_t             sampling_rate;
+    uint32_t             cal_type;
+    uint32_t             module_id;
+    uint32_t             param_id;
+} acdb_audio_cal_cfg_t;
+
 /* Audio calibration related functions */
 typedef void (*acdb_send_audio_cal_v3_t)(int, int, int, int, int);
 
@@ -130,6 +147,7 @@
     acdb_deallocate_t          acdb_deallocate;
     acdb_send_audio_cal_t      acdb_send_audio_cal;
     acdb_send_audio_cal_v3_t   acdb_send_audio_cal_v3;
+    acdb_set_audio_cal_t       acdb_set_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
     acdb_send_gain_dep_cal_t   acdb_send_gain_dep_cal;
@@ -741,6 +759,139 @@
 #endif
 }
 
+static int parse_audiocal_cfg(struct str_parms *parms, acdb_audio_cal_cfg_t *cal)
+{
+    int err;
+    char value[64];
+    int ret = 0;
+
+    if (parms == NULL || cal == NULL)
+        return ret;
+
+    err = str_parms_get_str(parms, "cal_persist", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_persist");
+        cal->persist = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x1;
+    }
+    err = str_parms_get_str(parms, "cal_apptype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_apptype");
+        cal->app_type = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x2;
+    }
+    err = str_parms_get_str(parms, "cal_caltype", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_caltype");
+        cal->cal_type = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x4;
+    }
+    err = str_parms_get_str(parms, "cal_samplerate", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_samplerate");
+        cal->sampling_rate = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x8;
+    }
+    err = str_parms_get_str(parms, "cal_devid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_devid");
+        cal->dev_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x10;
+    }
+    err = str_parms_get_str(parms, "cal_snddevid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_snddevid");
+        cal->snd_dev_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x20;
+    }
+    err = str_parms_get_str(parms, "cal_topoid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_topoid");
+        cal->topo_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x40;
+    }
+    err = str_parms_get_str(parms, "cal_moduleid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_moduleid");
+        cal->module_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x80;
+    }
+    err = str_parms_get_str(parms, "cal_paramid", value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(parms, "cal_paramid");
+        cal->param_id = (uint32_t)strtoul(value, NULL, 0);
+        ret = ret | 0x100;
+    }
+    return ret;
+}
+
+static void set_audiocal(void *platform, struct str_parms *parms, char *value, int len)
+{
+    struct platform_data *my_data = (struct platform_data *)platform;
+    acdb_audio_cal_cfg_t cal;
+    uint8_t *dptr = NULL;
+    int32_t dlen = 0;
+    int err ,ret;
+
+    if (value == NULL || platform == NULL || parms == NULL) {
+        ALOGE("[%s] received null pointer, failed", __func__);
+        goto done_key_audcal;
+    }
+
+    memset(&cal, 0, sizeof(acdb_audio_cal_cfg_t));
+    /* parse audio calibration keys */
+    ret = parse_audiocal_cfg(parms, &cal);
+
+    /* handle audio calibration data now */
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, len);
+    if (err >= 0) {
+        str_parms_del(parms, AUDIO_PARAMETER_KEY_AUD_CALDATA);
+        dlen = strlen(value);
+        if (dlen <= 0) {
+            ALOGE("[%s] null data received", __func__);
+            goto done_key_audcal;
+        }
+        /*
+           The base64 encoded string is always larger than the binary data,
+           so b64_pton will always output less data than provided (around 1/3
+           less than the input data). That's why we can allocate input buffer
+           length and then get function work.
+        */
+        dptr = (uint8_t *)calloc(dlen, sizeof(uint8_t));
+        if (dptr == NULL) {
+            ALOGE("[%s] memory allocation failed for %d", __func__, dlen);
+            goto done_key_audcal;
+        }
+        dlen = b64_pton(value, dptr, dlen);
+        if (dlen <= 0) {
+            ALOGE("[%s] data decoding failed %d", __func__, dlen);
+            goto done_key_audcal;
+        }
+
+        if (cal.dev_id) {
+            if (audio_is_input_device(cal.dev_id)) {
+                cal.snd_dev_id = platform_get_input_snd_device(platform, cal.dev_id);
+            } else {
+                cal.snd_dev_id = platform_get_output_snd_device(platform, cal.dev_id);
+            }
+        }
+        cal.acdb_dev_id = platform_get_snd_device_acdb_id(cal.snd_dev_id);
+        ALOGD("Setting audio calibration for snd_device(%d) acdb_id(%d)",
+                cal.snd_dev_id, cal.acdb_dev_id);
+        if (cal.acdb_dev_id == -EINVAL) {
+            ALOGE("[%s] Invalid acdb_device id %d for snd device id %d",
+                       __func__, cal.acdb_dev_id, cal.snd_dev_id);
+            goto done_key_audcal;
+        }
+        if (my_data->acdb_set_audio_cal) {
+            ret = my_data->acdb_set_audio_cal((void *)&cal, (void *)dptr, dlen);
+        }
+    }
+done_key_audcal:
+    if (dptr != NULL)
+        free(dptr);
+}
+
 bool platform_send_gain_dep_cal(void *platform, int level)
 {
     bool ret_val = false;
@@ -1598,6 +1749,12 @@
             ALOGE("%s: Could not find the symbol acdb_get_default_app_type from %s",
                   __func__, LIB_ACDB_LOADER);
 
+        my_data->acdb_set_audio_cal = (acdb_set_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_set_audio_cal_v2");
+        if (!my_data->acdb_set_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_set_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         int result = acdb_init(adev->snd_card);
         if (!result) {
             my_data->acdb_initialized = true;
@@ -3139,8 +3296,9 @@
 int platform_set_parameters(void *platform, struct str_parms *parms)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
-    char value[128];
+    char *value = NULL;
     char *kv_pairs = str_parms_to_str(parms);
+    int len;
     int ret = 0, err;
 
     if (kv_pairs == NULL) {
@@ -3151,8 +3309,16 @@
 
     ALOGV("%s: enter: %s", __func__, kv_pairs);
 
+    len = strlen(kv_pairs);
+    value = (char*)calloc(len+1, sizeof(char));
+    if (value == NULL) {
+        ret = -ENOMEM;
+        ALOGE("[%s] failed to allocate memory",__func__);
+        goto done;
+    }
+
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         str_parms_del(parms, PLATFORM_CONFIG_KEY_SOUNDCARD_NAME);
         my_data->snd_card_name = strdup(value);
@@ -3160,7 +3326,7 @@
     }
 
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_OPERATOR_INFO,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         struct operator_info *info;
         char *str = value;
@@ -3178,19 +3344,24 @@
 
     memset(value, 0, sizeof(value));
     err = str_parms_get_str(parms, PLATFORM_CONFIG_KEY_MAX_MIC_COUNT,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         str_parms_del(parms, PLATFORM_CONFIG_KEY_MAX_MIC_COUNT);
         my_data->max_mic_count = atoi(value);
         ALOGV("%s: max_mic_count %s/%d", __func__, value, my_data->max_mic_count);
     }
 
+    /* handle audio calibration parameters */
+    set_audiocal(platform, parms, value, len);
+
     // to-do: disable setting sidetone gain, will revist this later
     // audio_extn_usb_set_sidetone_gain(parms, value, len);
 done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
     if (kv_pairs != NULL)
         free(kv_pairs);
+    if (value != NULL)
+        free(value);
 
     return ret;
 }