hal: Add API to set & get audio calibration

added base64 encode and decode utility functions
Add API to set & get audio calibration.
Use audiocal for set & get key.

Change-Id: I2ea3362a75bd99018f404d8cf0771efa0a2aabf7
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 02add40..38f1384 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -358,4 +358,7 @@
 #ifndef AUDIO_FORMAT_E_AC3_JOC
 #define AUDIO_FORMAT_E_AC3_JOC  0x19000000UL
 #endif
+
+int b64decode(char *inp, int ilen, uint8_t* outp);
+int b64encode(uint8_t *inp, int ilen, char* outp);
 #endif /* AUDIO_EXTN_H */
diff --git a/hal/audio_extn/utils.c b/hal/audio_extn/utils.c
index 8bd98a0..a9b9a0d 100644
--- a/hal/audio_extn/utils.c
+++ b/hal/audio_extn/utils.c
@@ -48,6 +48,9 @@
 #define STRING_TO_ENUM(string) { #string, string }
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
+#define BASE_TABLE_SIZE 64
+#define MAX_BASEINDEX_LEN 256
+
 struct string_to_enum {
     const char *name;
     uint32_t value;
@@ -97,6 +100,15 @@
 #endif
 };
 
+static char bTable[BASE_TABLE_SIZE] = {
+            'A','B','C','D','E','F','G','H','I','J','K','L',
+            'M','N','O','P','Q','R','S','T','U','V','W','X',
+            'Y','Z','a','b','c','d','e','f','g','h','i','j',
+            'k','l','m','n','o','p','q','r','s','t','u','v',
+            'w','x','y','z','0','1','2','3','4','5','6','7',
+            '8','9','+','/'
+};
+
 static uint32_t string_to_enum(const struct string_to_enum *table, size_t size,
                                const char *name)
 {
@@ -567,3 +579,145 @@
     }
 }
 
+// Base64 Encode and Decode
+// Not all features supported. This must be used only with following conditions.
+// Decode Modes: Support with and without padding
+//         CRLF not handling. So no CRLF in string to decode.
+// Encode Modes: Supports only padding
+int b64decode(char *inp, int ilen, uint8_t* outp)
+{
+    int i, j, k, ii, num;
+    int rem, pcnt;
+    uint32_t res=0;
+    uint8_t getIndex[MAX_BASEINDEX_LEN];
+    uint8_t tmp, cflag;
+
+    if(inp == NULL || outp == NULL || ilen <= 0) {
+        ALOGE("[%s] received NULL pointer or zero length",__func__);
+        return -1;
+    }
+
+    memset(getIndex, MAX_BASEINDEX_LEN-1, sizeof(getIndex));
+    for(i=0;i<BASE_TABLE_SIZE;i++) {
+        getIndex[(uint8_t)bTable[i]] = (uint8_t)i;
+    }
+    getIndex[(uint8_t)'=']=0;
+
+    j=0;k=0;
+    num = ilen/4;
+    rem = ilen%4;
+    if(rem==0)
+        num = num-1;
+    cflag=0;
+    for(i=0; i<num; i++) {
+        res=0;
+        for(ii=0;ii<4;ii++) {
+            res = res << 6;
+            tmp = getIndex[(uint8_t)inp[j++]];
+            res = res | tmp;
+            cflag = cflag | tmp;
+        }
+        outp[k++] = (res >> 16)&0xFF;
+        outp[k++] = (res >> 8)&0xFF;
+        outp[k++] = res & 0xFF;
+    }
+
+    // Handle last bytes special
+    pcnt=0;
+    if(rem == 0) {
+        //With padding or full data
+        res = 0;
+        for(ii=0;ii<4;ii++) {
+            if(inp[j] == '=')
+                pcnt++;
+            res = res << 6;
+            tmp = getIndex[(uint8_t)inp[j++]];
+            res = res | tmp;
+            cflag = cflag | tmp;
+        }
+        outp[k++] = res >> 16;
+        if(pcnt == 2)
+            goto done;
+        outp[k++] = (res>>8)&0xFF;
+        if(pcnt == 1)
+            goto done;
+        outp[k++] = res&0xFF;
+    } else {
+        //without padding
+        res = 0;
+        for(i=0;i<rem;i++) {
+            res = res << 6;
+            tmp = getIndex[(uint8_t)inp[j++]];
+            res = res | tmp;
+            cflag = cflag | tmp;
+        }
+        for(i=rem;i<4;i++) {
+            res = res << 6;
+            pcnt++;
+        }
+        outp[k++] = res >> 16;
+        if(pcnt == 2)
+            goto done;
+        outp[k++] = (res>>8)&0xFF;
+        if(pcnt == 1)
+            goto done;
+        outp[k++] = res&0xFF;
+    }
+done:
+    if(cflag == 0xFF) {
+        ALOGE("[%s] base64 decode failed. Invalid character found %s",
+            __func__, inp);
+        return 0;
+    }
+    return k;
+}
+
+int b64encode(uint8_t *inp, int ilen, char* outp)
+{
+    int i,j,k, num;
+    int rem=0;
+    uint32_t res=0;
+
+    if(inp == NULL || outp == NULL || ilen<=0) {
+        ALOGE("[%s] received NULL pointer or zero input length",__func__);
+        return -1;
+    }
+
+    num = ilen/3;
+    rem = ilen%3;
+    j=0;k=0;
+    for(i=0; i<num; i++) {
+        //prepare index
+        res = inp[j++]<<16;
+        res = res | inp[j++]<<8;
+        res = res | inp[j++];
+        //get output map from index
+        outp[k++] = (char) bTable[(res>>18)&0x3F];
+        outp[k++] = (char) bTable[(res>>12)&0x3F];
+        outp[k++] = (char) bTable[(res>>6)&0x3F];
+        outp[k++] = (char) bTable[res&0x3F];
+    }
+
+    switch(rem) {
+        case 1:
+            res = inp[j++]<<16;
+            outp[k++] = (char) bTable[res>>18];
+            outp[k++] = (char) bTable[(res>>12)&0x3F];
+            //outp[k++] = '=';
+            //outp[k++] = '=';
+            break;
+        case 2:
+            res = inp[j++]<<16;
+            res = res | inp[j++]<<8;
+            outp[k++] = (char) bTable[res>>18];
+            outp[k++] = (char) bTable[(res>>12)&0x3F];
+            outp[k++] = (char) bTable[(res>>6)&0x3F];
+            //outp[k++] = '=';
+            break;
+        default:
+            break;
+    }
+done:
+    outp[k] = '\0';
+    return k;
+}
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index e0d8a39..4424b06 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -91,10 +91,16 @@
 #define SAMPLE_RATE_8KHZ  8000
 #define SAMPLE_RATE_16KHZ 16000
 
+#define MAX_SET_CAL_BYTE_SIZE 65536
+
 #define AUDIO_PARAMETER_KEY_FLUENCE_TYPE  "fluence"
 #define AUDIO_PARAMETER_KEY_SLOWTALK      "st_enable"
 #define AUDIO_PARAMETER_KEY_HD_VOICE      "hd_voice"
 #define AUDIO_PARAMETER_KEY_VOLUME_BOOST  "volume_boost"
+#define AUDIO_PARAMETER_KEY_AUD_CALDATA   "cal_data"
+#define AUDIO_PARAMETER_KEY_AUD_CALRESULT "cal_result"
+
+
 /* Query external audio device connection status */
 #define AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE "ext_audio_device"
 
@@ -120,6 +126,19 @@
     int length;
 };
 
+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_deallocate_t)();
 typedef int  (*acdb_init_t)(const char *, char *);
@@ -129,6 +148,8 @@
 typedef int  (*acdb_get_default_app_type_t)(void);
 typedef int (*acdb_loader_get_calibration_t)(char *attr, int size, void *data);
 acdb_loader_get_calibration_t acdb_loader_get_calibration;
+typedef int (*acdb_set_audio_cal_t) (void *, void *, uint32_t);
+typedef int (*acdb_get_audio_cal_t) (void *, void *, uint32_t*);
 
 struct platform_data {
     struct audio_device *adev;
@@ -152,6 +173,8 @@
     acdb_init_t                acdb_init;
     acdb_deallocate_t          acdb_deallocate;
     acdb_send_audio_cal_t      acdb_send_audio_cal;
+    acdb_set_audio_cal_t       acdb_set_audio_cal;
+    acdb_get_audio_cal_t       acdb_get_audio_cal;
     acdb_send_voice_cal_t      acdb_send_voice_cal;
     acdb_reload_vocvoltable_t  acdb_reload_vocvoltable;
     acdb_get_default_app_type_t acdb_get_default_app_type;
@@ -1058,6 +1081,18 @@
             ALOGE("%s: Could not find the symbol acdb_send_audio_cal 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);
+
+        my_data->acdb_get_audio_cal = (acdb_get_audio_cal_t)dlsym(my_data->acdb_handle,
+                                                    "acdb_loader_get_audio_cal_v2");
+        if (!my_data->acdb_get_audio_cal)
+            ALOGE("%s: Could not find the symbol acdb_get_audio_cal_v2 from %s",
+                  __func__, LIB_ACDB_LOADER);
+
         my_data->acdb_send_voice_cal = (acdb_send_voice_cal_t)dlsym(my_data->acdb_handle,
                                                     "acdb_loader_send_voice_cal");
         if (!my_data->acdb_send_voice_cal)
@@ -2275,18 +2310,150 @@
     return ret;
 }
 
+static int parse_audiocal_cfg(struct str_parms *parms, acdb_audio_cal_cfg_t *cal)
+{
+    int err;
+    unsigned int val;
+    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={0};
+    uint8_t *dptr = NULL;
+    int32_t dlen;
+    int err, ret;
+    if(value == NULL || platform == NULL || parms == NULL) {
+        ALOGE("[%s] received null pointer, failed",__func__);
+        goto done_key_audcal;
+    }
+
+    /* 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;
+        }
+        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 = b64decode(value, strlen(value), dptr);
+        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);
+}
+
 int platform_set_parameters(void *platform, struct str_parms *parms)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     char *str;
-    char value[256] = {0};
-    int val;
+    char *value=NULL;
+    int val, len;
     int ret = 0, err;
     char *kv_pairs = str_parms_to_str(parms);
 
     ALOGV_IF(kv_pairs != NULL, "%s: enter: %s", __func__, kv_pairs);
 
-    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SLOWTALK, value, sizeof(value));
+    len = strlen(kv_pairs);
+    value = (char*)calloc(len, sizeof(char));
+    if(value == NULL) {
+        ret = -ENOMEM;
+        ALOGE("[%s] failed to allocate memory",__func__);
+        goto done;
+    }
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_SLOWTALK, value, len);
     if (err >= 0) {
         bool state = false;
         if (!strncmp("true", value, sizeof("true"))) {
@@ -2299,7 +2466,7 @@
             ALOGE("%s: Failed to set slow talk err: %d", __func__, ret);
     }
 
-    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HD_VOICE, value, sizeof(value));
+    err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_HD_VOICE, value, len);
     if (err >= 0) {
         bool state = false;
         if (!strncmp("true", value, sizeof("true"))) {
@@ -2317,7 +2484,7 @@
     }
 
     err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         str_parms_del(parms, AUDIO_PARAMETER_KEY_VOLUME_BOOST);
 
@@ -2335,7 +2502,7 @@
     }
 
     err = str_parms_get_str(parms, AUDIO_PARAMETER_KEY_EXT_AUDIO_DEVICE,
-                            value, sizeof(value));
+                            value, len);
     if (err >= 0) {
         char *event_name, *status_str;
         bool status = false;
@@ -2351,8 +2518,14 @@
         update_external_device_status(my_data, event_name, status);
     }
 
+    /* handle audio calibration parameters */
+    set_audiocal(platform, parms, value, len);
+
+done:
     ALOGV("%s: exit with code(%d)", __func__, ret);
     free(kv_pairs);
+    if(value != NULL)
+        free(value);
     return ret;
 }
 
@@ -2456,13 +2629,106 @@
     return ret;
 }
 
+static void get_audiocal(void *platform, void *keys, void *pReply) {
+    struct platform_data *my_data = (struct platform_data *)platform;
+    struct str_parms *query = (struct str_parms *)keys;
+    struct str_parms *reply=(struct str_parms *)pReply;
+    acdb_audio_cal_cfg_t cal={0};
+    uint8_t *dptr = NULL;
+    char value[512] = {0};
+    char *rparms=NULL;
+    int ret=0, err;
+    uint32_t param_len;
+
+    if(query==NULL || platform==NULL || reply==NULL) {
+        ALOGE("[%s] received null pointer",__func__);
+        ret=-EINVAL;
+        goto done;
+    }
+    /* parse audiocal configuration keys */
+    ret = parse_audiocal_cfg(query, &cal);
+    if(ret == 0) {
+        /* No calibration keys found */
+        goto done;
+    }
+    err = str_parms_get_str(query, AUDIO_PARAMETER_KEY_AUD_CALDATA, value, sizeof(value));
+    if (err >= 0) {
+        str_parms_del(query, AUDIO_PARAMETER_KEY_AUD_CALDATA);
+    } else {
+        goto done;
+    }
+
+    if(cal.dev_id & AUDIO_DEVICE_BIT_IN) {
+        cal.snd_dev_id = platform_get_input_snd_device(platform, cal.dev_id);
+    } else if(cal.dev_id) {
+        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);
+    if (cal.acdb_dev_id < 0) {
+        ALOGE("%s: Failed. Could not find acdb id for snd device(%d)",
+              __func__, cal.snd_dev_id);
+        ret = -EINVAL;
+        goto done_key_audcal;
+    }
+    ALOGD("[%s] Getting audio calibration for snd_device(%d) acdb_id(%d)",
+           __func__, cal.snd_dev_id, cal.acdb_dev_id);
+
+    param_len = MAX_SET_CAL_BYTE_SIZE;
+    dptr = (uint8_t*)calloc(param_len, sizeof(uint8_t));
+    if(dptr == NULL) {
+        ALOGE("[%s] Memory allocation failed for length %d",__func__,param_len);
+        ret = -ENOMEM;
+        goto done_key_audcal;
+    }
+    if (my_data->acdb_get_audio_cal != NULL) {
+        ret = my_data->acdb_get_audio_cal((void*)&cal, (void*)dptr, &param_len);
+        if (ret == 0) {
+            int dlen;
+            if(param_len == 0 || param_len == MAX_SET_CAL_BYTE_SIZE) {
+                ret = -EINVAL;
+                goto done_key_audcal;
+            }
+            /* Allocate memory for encoding */
+            rparms = (char*)calloc((param_len*2), sizeof(char));
+            if(rparms == NULL) {
+                ALOGE("[%s] Memory allocation failed for size %d",
+                            __func__, param_len*2);
+                ret = -ENOMEM;
+                goto done_key_audcal;
+            }
+            if(cal.persist==0 && cal.module_id && cal.param_id) {
+                err = b64encode(dptr+12, param_len-12, rparms);
+            } else {
+                err = b64encode(dptr, param_len, rparms);
+            }
+            if(err < 0) {
+                ALOGE("[%s] failed to convert data to string", __func__);
+                ret = -EINVAL;
+                goto done_key_audcal;
+            }
+            str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUD_CALRESULT, ret);
+            str_parms_add_str(reply, AUDIO_PARAMETER_KEY_AUD_CALDATA, rparms);
+        }
+    }
+done_key_audcal:
+    if(ret != 0) {
+        str_parms_add_int(reply, AUDIO_PARAMETER_KEY_AUD_CALRESULT, ret);
+        str_parms_add_str(reply, AUDIO_PARAMETER_KEY_AUD_CALDATA, "");
+    }
+done:
+    if(dptr != NULL)
+        free(dptr);
+    if(rparms != NULL)
+        free(rparms);
+}
+
 void platform_get_parameters(void *platform,
                             struct str_parms *query,
                             struct str_parms *reply)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
     char *str = NULL;
-    char value[256] = {0};
+    char value[512] = {0};
     int ret;
     char *kv_pairs = NULL;
 
@@ -2492,6 +2758,10 @@
         str_parms_add_str(reply, AUDIO_PARAMETER_KEY_VOLUME_BOOST, value);
     }
 
+    /* Handle audio calibration keys */
+    get_audiocal(platform, query, reply);
+
+done:
     kv_pairs = str_parms_to_str(reply);
     ALOGV_IF(kv_pairs != NULL, "%s: exit: returns - %s", __func__, kv_pairs);
     free(kv_pairs);