hal: Add XML parser for platform info

change 1
Add XML parser which parses the platform_info.xml
on the device. That xml contains ACDB ID information
and is populated from the device project folder to
the /etc folder on the device. It is used to overwrite
hardcoded ACDB ID's in platform.c.

change 2
Move platform_parser to root hal directory. Rename
platform_parser to platform_info. Change name of
XML file read from platform_info.xml to
audio_platform_info.xml. The xml now only needs
information for ACDB ID's that you want overwritten.
Names in the XML now match sound device enums in
platform.c.

(cherry-picked from CAF commits
61764e3b8069b819c3da19a6bb38b37ad173bf50, 5588688cbdd065a3572afb032e48a265790dfea2)

Change-Id: Ie5978f609bbe9d60a64e20a0906d6bd7a8c48e1b
diff --git a/hal/Android.mk b/hal/Android.mk
index 30abdf3..c7cb167 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := \
 	audio_hw.c \
 	voice.c \
+	platform_info.c \
 	$(AUDIO_PLATFORM)/platform.c
 
 LOCAL_SHARED_LIBRARIES := \
@@ -29,7 +30,8 @@
 	libtinyalsa \
 	libtinycompress \
 	libaudioroute \
-	libdl
+	libdl \
+	libexpat
 
 LOCAL_C_INCLUDES += \
 	external/tinyalsa/include \
@@ -38,7 +40,8 @@
 	$(call include-path-for, audio-effects) \
 	$(LOCAL_PATH)/$(AUDIO_PLATFORM) \
 	$(LOCAL_PATH)/audio_extn \
-	$(LOCAL_PATH)/voice_extn
+	$(LOCAL_PATH)/voice_extn \
+	external/expat/lib
 
 ifneq ($(filter msm8084,$(TARGET_BOARD_PLATFORM)),)
   LOCAL_SHARED_LIBRARIES += libmdmdetect
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 9efcf26..02935bd 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -405,6 +405,16 @@
     return device_id;
 }
 
+int platform_get_snd_device_index(char *snd_device_index_name)
+{
+    return -ENODEV;
+}
+
+int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
+{
+    return -ENODEV;
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index 3fba99b..8d5ed1a 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -169,7 +169,7 @@
 };
 
 /* ACDB IDs (audio DSP path configuration IDs) for each sound device */
-static const int acdb_device_table[SND_DEVICE_MAX] = {
+static int acdb_device_table[SND_DEVICE_MAX] = {
     [SND_DEVICE_NONE] = -1,
     [SND_DEVICE_OUT_HANDSET] = 7,
     [SND_DEVICE_OUT_SPEAKER] = 15,
@@ -221,6 +221,47 @@
     [SND_DEVICE_IN_VOICE_REC_DMIC_BS_FLUENCE] = 5,
 };
 
+struct snd_device_index {
+    char name[100];
+    unsigned int index;
+};
+
+#define TO_NAME_INDEX(X)   #X, X
+
+/* Used to get index from parsed sting */
+struct snd_device_index snd_device_name_index[SND_DEVICE_MAX] = {
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_REVERSE)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_SPEAKER)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_HDMI)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_SPEAKER_AND_HDMI)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_BT_SCO_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_FULL_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_VCO_HEADPHONES)},
+    {TO_NAME_INDEX(SND_DEVICE_OUT_VOICE_TTY_HCO_HANDSET)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HANDSET_MIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_SPEAKER_MIC_AEC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_SPEAKER_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_HDMI_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_BT_SCO_MIC_WB)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_CAMCORDER_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_FULL_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_VCO_HANDSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_TTY_HCO_HEADSET_MIC)},
+    {TO_NAME_INDEX(SND_DEVICE_IN_VOICE_REC_MIC)},
+};
+
 #define DEEP_BUFFER_PLATFORM_DELAY (29*1000LL)
 #define LOW_LATENCY_PLATFORM_DELAY (13*1000LL)
 
@@ -576,6 +617,10 @@
         else
             my_data->acdb_init();
 #endif
+
+        /* Initialize ACDB ID's */
+        /* Comment - does it make sense to init if acdb handle is NULL */
+        platform_info_init();
     }
 
     /* load csd client */
@@ -642,6 +687,46 @@
     return device_id;
 }
 
+int platform_get_snd_device_index(char *snd_device_index_name)
+{
+    int ret = 0;
+    int i;
+
+    if (snd_device_index_name == NULL) {
+        ALOGE("%s: snd_device_index_name is NULL", __func__);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    for (i=0; i < SND_DEVICE_MAX; i++) {
+        if(strcmp(snd_device_name_index[i].name, snd_device_index_name) == 0) {
+            ret = snd_device_name_index[i].index;
+            goto done;
+        }
+    }
+    ALOGE("%s: Could not find index for snd_device_index_name = %s",
+            __func__, snd_device_index_name);
+    ret = -ENODEV;
+done:
+    return ret;
+}
+
+int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id)
+{
+    int ret = 0;
+
+    if ((snd_device < SND_DEVICE_MIN) || (snd_device >= SND_DEVICE_MAX)) {
+        ALOGE("%s: Invalid snd_device = %d",
+            __func__, snd_device);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    acdb_device_table[snd_device] = acdb_id;
+done:
+    return ret;
+}
+
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device)
 {
     struct platform_data *my_data = (struct platform_data *)platform;
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 81fbc08..7d54381 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef QCOM_AUDIO_PLATFORM_API_H
-#define QCOM_AUDIO_PLATFORM_API_H
+#ifndef AUDIO_PLATFORM_API_H
+#define AUDIO_PLATFORM_API_H
 
 void *platform_init(struct audio_device *adev);
 void platform_deinit(void *platform);
@@ -23,6 +23,8 @@
 void platform_add_backend_name(void *platform, char *mixer_path,
                                                     snd_device_t snd_device);
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
+int platform_get_snd_device_index(char *snd_device_index_name);
+int platform_set_snd_device_acdb_id(snd_device_t snd_device, unsigned int acdb_id);
 int platform_send_audio_calibration(void *platform, snd_device_t snd_device);
 int platform_switch_voice_call_device_pre(void *platform);
 int platform_switch_voice_call_enable_device_config(void *platform,
@@ -54,4 +56,7 @@
 int platform_start_incall_music_usecase(void *platform);
 int platform_stop_incall_music_usecase(void *platform);
 
-#endif // QCOM_AUDIO_PLATFORM_API_H
+/* From platform_info_parser.c */
+int platform_info_init(void);
+
+#endif // AUDIO_PLATFORM_API_H
diff --git a/hal/platform_info.c b/hal/platform_info.c
new file mode 100644
index 0000000..409c396
--- /dev/null
+++ b/hal/platform_info.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+
+#define LOG_TAG "platform_info"
+#define LOG_NDDEBUG 0
+
+#include <errno.h>
+#include <stdio.h>
+#include <expat.h>
+#include <cutils/log.h>
+#include <audio_hw.h>
+#include "platform_api.h"
+#include <platform.h>
+
+#define PLATFORM_INFO_XML_PATH      "/system/etc/audio_platform_info.xml"
+
+static void process_device(const XML_Char **attr)
+{
+    int index;
+
+    if (strcmp(attr[0], "name") != 0) {
+        ALOGE("%s: 'name' not found, no ACDB ID set!", __func__);
+        goto done;
+    }
+
+    index = platform_get_snd_device_index((char *)attr[1]);
+    if (index < 0) {
+        ALOGE("%s: Device %s in %s not found, no ACDB ID set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "acdb_id") != 0) {
+        ALOGE("%s: Device %s in %s has no acdb_id, no ACDB ID set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH);
+        goto done;
+    }
+
+    if(platform_set_snd_device_acdb_id(index, atoi((char *)attr[3])) < 0) {
+        ALOGE("%s: Device %s in %s, ACDB ID %d was not set!",
+              __func__, attr[1], PLATFORM_INFO_XML_PATH, atoi((char *)attr[3]));
+        goto done;
+    }
+
+done:
+    return;
+}
+
+static void start_tag(void *userdata __unused, const XML_Char *tag_name,
+                      const XML_Char **attr)
+{
+    const XML_Char              *attr_name = NULL;
+    const XML_Char              *attr_value = NULL;
+    unsigned int                i;
+
+    if (strcmp(tag_name, "device") == 0)
+        process_device(attr);
+
+    return;
+}
+
+static void end_tag(void *userdata __unused, const XML_Char *tag_name __unused)
+{
+
+}
+
+int platform_info_init(void)
+{
+    XML_Parser      parser;
+    FILE            *file;
+    int             ret = 0;
+    int             bytes_read;
+    void            *buf;
+    static const uint32_t kBufSize = 1024;
+
+    file = fopen(PLATFORM_INFO_XML_PATH, "r");
+    if (!file) {
+        ALOGD("%s: Failed to open %s, using defaults.",
+            __func__, PLATFORM_INFO_XML_PATH);
+        ret = -ENODEV;
+        goto done;
+    }
+
+    parser = XML_ParserCreate(NULL);
+    if (!parser) {
+        ALOGE("%s: Failed to create XML parser!", __func__);
+        ret = -ENODEV;
+        goto err_close_file;
+    }
+
+    XML_SetElementHandler(parser, start_tag, end_tag);
+
+    while (1) {
+        buf = XML_GetBuffer(parser, kBufSize);
+        if (buf == NULL) {
+            ALOGE("%s: XML_GetBuffer failed", __func__);
+            ret = -ENOMEM;
+            goto err_free_parser;
+        }
+
+        bytes_read = fread(buf, 1, kBufSize, file);
+        if (bytes_read < 0) {
+            ALOGE("%s: fread failed, bytes read = %d", __func__, bytes_read);
+             ret = bytes_read;
+            goto err_free_parser;
+        }
+
+        if (XML_ParseBuffer(parser, bytes_read,
+                            bytes_read == 0) == XML_STATUS_ERROR) {
+            ALOGE("%s: XML_ParseBuffer failed, for %s",
+                __func__, PLATFORM_INFO_XML_PATH);
+            ret = -EINVAL;
+            goto err_free_parser;
+        }
+
+        if (bytes_read == 0)
+            break;
+    }
+
+err_free_parser:
+    XML_ParserFree(parser);
+err_close_file:
+    fclose(file);
+done:
+    return ret;
+}