hal: Add XML parser for platform info

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-Id: I86419bf0f48bcf7f0125da58626adab1d23fa50a
diff --git a/hal/Android.mk b/hal/Android.mk
index 037be56..b6e9cad 100644
--- a/hal/Android.mk
+++ b/hal/Android.mk
@@ -116,6 +116,12 @@
 	$(LOCAL_PATH)/audio_extn \
 	$(LOCAL_PATH)/voice_extn
 
+ifneq ($(filter msm8974,$(AUDIO_PLATFORM)),)
+    LOCAL_C_INCLUDES += external/expat/lib
+    LOCAL_SHARED_LIBRARIES += libexpat
+    LOCAL_SRC_FILES += $(AUDIO_PLATFORM)/platform_parser.c
+endif
+
 ifeq ($(strip $(AUDIO_FEATURE_ENABLED_LISTEN)),true)
     LOCAL_CFLAGS += -DAUDIO_LISTEN_ENABLED
     LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-listen
diff --git a/hal/msm8960/platform.c b/hal/msm8960/platform.c
index 298c60d..54bda5a 100644
--- a/hal/msm8960/platform.c
+++ b/hal/msm8960/platform.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -391,6 +391,11 @@
     return device_id;
 }
 
+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 c7896ae..34ff8cc 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -31,6 +31,7 @@
 #include "platform.h"
 #include "audio_extn.h"
 #include "voice_extn.h"
+#include "platform_parser.h"
 
 #define MIXER_XML_PATH "/system/etc/mixer_paths.xml"
 #define MIXER_XML_PATH_AUXPCM "/system/etc/mixer_paths_auxpcm.xml"
@@ -222,7 +223,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] = 14,
@@ -572,6 +573,9 @@
             my_data->acdb_init();
     }
 
+    /* Initialize ACDB ID's */
+    platform_info_init();
+
     /* If platform is apq8084 and baseband is MDM, load CSD Client specific
      * symbols. Voice call is handled by MDM and apps processor talks to
      * MDM through CSD Client
@@ -669,6 +673,22 @@
     return device_id;
 }
 
+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/msm8974/platform_parser.c b/hal/msm8974/platform_parser.c
new file mode 100644
index 0000000..8f86d97
--- /dev/null
+++ b/hal/msm8974/platform_parser.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "platform_parser"
+#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"
+#include "platform_parser.h"
+
+#define PLATFORM_XML_PATH        "/system/etc/platform_info.xml"
+#define BUF_SIZE                    1024
+
+static void process_device(void *userdata, const XML_Char **attr)
+{
+    unsigned int    *snd_device_index = userdata;
+
+    if (strcmp(attr[0], "name") != 0)
+        goto done;
+
+    if (platform_get_snd_device_name(*snd_device_index) == NULL)
+        goto next;
+    if (strcmp(attr[1], platform_get_snd_device_name(*snd_device_index)) != 0) {
+        ALOGE("%s: %s in platform.h at index %d does not match %s, from %s no ACDB ID set!",
+            __func__, platform_get_snd_device_name(*snd_device_index),
+            *snd_device_index, attr[1], PLATFORM_XML_PATH);
+        goto done;
+    }
+
+    if (strcmp(attr[2], "acdb_id") != 0) {
+        ALOGE("%s: Device %s at index %d in %s has no acdb_id, no ACDB ID set!",
+              __func__, attr[1], *snd_device_index, PLATFORM_XML_PATH);
+        goto done;
+    }
+
+    if(platform_set_snd_device_acdb_id(*snd_device_index,
+                                atoi((char *)attr[3])) != 0)
+        goto done;
+
+next:
+     (*snd_device_index)++;
+done:
+    return;
+}
+
+static void start_tag(void *userdata, 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(userdata, attr);
+
+    return;
+}
+
+static void end_tag(void *userdata, const XML_Char *tag_name)
+{
+
+}
+
+int platform_info_init(void)
+{
+    XML_Parser      parser;
+    FILE            *file;
+    int             ret = 0;
+    int             bytes_read;
+    unsigned int    snd_device_index = SND_DEVICE_MIN;
+    void            *buf;
+
+    file = fopen(PLATFORM_XML_PATH, "r");
+    if (!file) {
+        ALOGD("%s: Failed to open %s, using defaults.",
+            __func__, PLATFORM_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_SetUserData(parser, &snd_device_index);
+    XML_SetElementHandler(parser, start_tag, end_tag);
+
+    while (1) {
+        buf = XML_GetBuffer(parser, BUF_SIZE);
+        if (buf == NULL) {
+            ALOGE("%s: XML_GetBuffer failed", __func__);
+            ret = -ENOMEM;
+            goto err_free_parser;
+        }
+
+        bytes_read = fread(buf, 1, BUF_SIZE, 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_XML_PATH);
+            ret = -EINVAL;
+            goto err_free_parser;
+        }
+
+        if (bytes_read == 0)
+            break;
+    }
+
+    if (snd_device_index != SND_DEVICE_MAX) {
+        ALOGE("%s: Only %d/%d ACDB ID's set! Fix %s!",
+            __func__, snd_device_index, SND_DEVICE_MAX, PLATFORM_XML_PATH);
+        ret = -EINVAL;
+    }
+
+err_free_parser:
+    XML_ParserFree(parser);
+err_close_file:
+    fclose(file);
+done:
+    return ret;
+}
diff --git a/hal/msm8974/platform_parser.h b/hal/msm8974/platform_parser.h
new file mode 100644
index 0000000..3e91934
--- /dev/null
+++ b/hal/msm8974/platform_parser.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *     * Neither the name of The Linux Foundation nor the names of its
+ *       contributors may be used to endorse or promote products derived
+ *       from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef AUDIO_PLATFORM_PARSER_H
+#define AUDIO_PLATFORM_PARSER_H
+
+int platform_info_init(void);
+
+#endif // AUDIO_PLATFORM_PARSER_H
diff --git a/hal/platform_api.h b/hal/platform_api.h
index 4096ef0..0b2c435 100644
--- a/hal/platform_api.h
+++ b/hal/platform_api.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
  * Not a contribution.
  *
  * Copyright (C) 2013 The Android Open Source Project
@@ -17,8 +17,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);
@@ -27,6 +27,7 @@
                                       char *device_name);
 void platform_add_backend_name(char *mixer_path, snd_device_t snd_device);
 int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type);
+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_device_post(void *platform,
@@ -57,4 +58,4 @@
 
 bool platform_listen_update_status(snd_device_t snd_device);
 
-#endif // QCOM_AUDIO_PLATFORM_API_H
+#endif // AUDIO_PLATFORM_API_H