OSDN Git Service

A2DP: New BT IPC library
authorpramod kotreshappa <kpramod@codeaurora.org>
Thu, 31 Mar 2016 21:34:20 +0000 (14:34 -0700)
committerLinux Build Service Account <lnxbuild@localhost>
Wed, 24 Aug 2016 14:10:06 +0000 (08:10 -0600)
New BT IPC library that facilitates communication betwee audio hal
and BT host. Bthost-ipc library will be loaded by audio primary
hal in Split A2dp scenario to communicate with bt host and in
legacy non-split a2dp scenario this library will be loaded by
a2dp hal.

Change-Id: Ie11d88b27662c97a00ac9d484ae9c621293949e8

audio_a2dp_hw/Android.mk
audio_a2dp_hw/audio_a2dp_hw.c
audio_a2dp_hw/audio_a2dp_hw.h
audio_a2dp_hw/bthost_ipc.c [new file with mode: 0644]
audio_a2dp_hw/bthost_ipc.h [new file with mode: 0644]

index 0dc1388..7581068 100644 (file)
@@ -12,6 +12,7 @@ LOCAL_C_INCLUDES += \
        $(LOCAL_PATH)/../ \
        $(LOCAL_PATH)/../utils/include
 
+LOCAL_CFLAGS = -DBT_HOST_IPC_ENABLED
 LOCAL_MODULE := audio.a2dp.default
 LOCAL_MODULE_RELATIVE_PATH := hw
 
@@ -25,3 +26,24 @@ LOCAL_CONLYFLAGS += $(bluetooth_CONLYFLAGS)
 LOCAL_CPPFLAGS += $(bluetooth_CPPFLAGS)
 
 include $(BUILD_SHARED_LIBRARY)
+
+##########################################
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := bthost_ipc.c
+
+LOCAL_C_INCLUDES += \
+       $(LOCAL_PATH)/bthost_ipc.h \
+       $(LOCAL_PATH)/../ \
+       $(LOCAL_PATH)/../utils/include
+
+LOCAL_CFLAGS = $(bdroid_CFLAGS)
+
+LOCAL_MODULE := libbthost_if
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_STATIC_LIBRARIES := libosi
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
index f9b6e0e..37baeb8 100644 (file)
@@ -1,5 +1,9 @@
 /******************************************************************************
+ *  Copyright (C) 2016, The Linux Foundation. All rights reserved.
  *
+ *  Not a Contribution
+ ******************************************************************************/
+/*****************************************************************************
  *  Copyright (C) 2009-2012 Broadcom Corporation
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
 #include "osi/include/osi.h"
 #include "osi/include/socket_utils/sockets.h"
 
+#include <dlfcn.h>
+
+#ifdef BT_HOST_IPC_ENABLED
+#include "bthost_ipc.h"
+#endif
+
 #ifdef BT_AUDIO_SYSTRACE_LOG
 #include <cutils/trace.h>
 #endif
@@ -88,20 +98,10 @@ static int number =0;
 #define ERROR(fmt, ...)     LOG_ERROR(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
 
 #define ASSERTC(cond, msg, val) if (!(cond)) {ERROR("### ASSERT : %s line %d %s (%d) ###", __FILE__, __LINE__, msg, val);}
-
+//#define BT_HOST_IPC_PATH "/system/lib/hw/bthost-ipc.so"
 /*****************************************************************************
 **  Local type definitions
 ******************************************************************************/
-
-typedef enum {
-    AUDIO_A2DP_STATE_STARTING,
-    AUDIO_A2DP_STATE_STARTED,
-    AUDIO_A2DP_STATE_STOPPING,
-    AUDIO_A2DP_STATE_STOPPED,
-    AUDIO_A2DP_STATE_SUSPENDED, /* need explicit set param call to resume (suspend=false) */
-    AUDIO_A2DP_STATE_STANDBY    /* allows write to autoresume */
-} a2dp_state_t;
-
 struct a2dp_stream_in;
 struct a2dp_stream_out;
 
@@ -111,23 +111,6 @@ struct a2dp_audio_device {
     struct a2dp_stream_out *output;
 };
 
-struct a2dp_config {
-    uint32_t                rate;
-    uint32_t                channel_flags;
-    int                     format;
-};
-
-/* move ctrl_fd outside output stream and keep open until HAL unloaded ? */
-
-struct a2dp_stream_common {
-    pthread_mutex_t         lock;
-    int                     ctrl_fd;
-    int                     audio_fd;
-    size_t                  buffer_sz;
-    struct a2dp_config      cfg;
-    a2dp_state_t            state;
-};
-
 struct a2dp_stream_out {
     struct audio_stream_out stream;
     struct a2dp_stream_common common;
@@ -143,7 +126,10 @@ struct a2dp_stream_in {
 /*****************************************************************************
 **  Static variables
 ******************************************************************************/
-
+#ifdef BT_HOST_IPC_ENABLED
+static void *lib_handle = NULL;
+bt_host_ipc_interface_t *ipc_if = NULL;
+#endif
 /*****************************************************************************
 **  Static functions
 ******************************************************************************/
@@ -159,8 +145,9 @@ static size_t out_get_buffer_size(const struct audio_stream *stream);
 ******************************************************************************/
 /* Function used only in debug mode */
 static const char* dump_a2dp_ctrl_event(char event) __attribute__ ((unused));
+#ifndef BT_HOST_IPC_ENABLED
 static void a2dp_open_ctrl_path(struct a2dp_stream_common *common);
-
+#endif
 /*****************************************************************************
 **   Miscellaneous helper functions
 ******************************************************************************/
@@ -175,6 +162,8 @@ static const char* dump_a2dp_ctrl_event(char event)
         CASE_RETURN_STR(A2DP_CTRL_CMD_STOP)
         CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
         CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_STREAM_STARTED)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_SUPPORTED)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED)
         default:
             return "UNKNOWN MSG ID";
     }
@@ -209,6 +198,7 @@ static void ts_error_log(char *tag, int val, int buff_size, struct a2dp_config c
     }
 }
 
+#ifndef BT_HOST_IPC_ENABLED
 /* logs timestamp with microsec precision
    pprev is optional in case a dedicated diff is required */
 static void ts_log(char *tag, int val, struct timespec *pprev_opt)
@@ -253,6 +243,8 @@ static const char* dump_a2dp_hal_state(int event)
             return "UNKNOWN STATE ID";
     }
 }
+
+//#ifndef BT_HOST_IPC_ENABLED
 /*****************************************************************************
 **
 **   bluedroid stack adaptation
@@ -597,7 +589,6 @@ static int start_audio_datapath(struct a2dp_stream_common *common)
 #else
     common->state = AUDIO_A2DP_STATE_STARTED;
 #endif
-
     return 0;
 
 error:
@@ -668,7 +659,7 @@ static int check_a2dp_stream_started(struct a2dp_stream_out *out)
    }
    return 0;
 }
-
+#endif
 
 /*****************************************************************************
 **
@@ -699,7 +690,11 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
     if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) ||
         (out->common.state == AUDIO_A2DP_STATE_STANDBY))
     {
+#ifdef BT_HOST_IPC_ENABLED
+        if (ipc_if->start_audio_datapath(&out->common) < 0)
+#else
         if (start_audio_datapath(&out->common) < 0)
+#endif
         {
             goto error;
         }
@@ -728,7 +723,11 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
     }
     #endif
 
+#ifdef BT_HOST_IPC_ENABLED
+    sent = ipc_if->skt_write(out->common.audio_fd, buffer,  bytes);
+#else
     sent = skt_write(out->common.audio_fd, buffer,  bytes);
+#endif
     pthread_mutex_lock(&out->common.lock);
 
     #ifdef BT_AUDIO_SYSTRACE_LOG
@@ -738,8 +737,13 @@ static ssize_t out_write(struct audio_stream_out *stream, const void* buffer,
     }
     #endif
 
-    if (sent == -1) {
+    if (sent == -1)
+    {
+#ifdef BT_HOST_IPC_ENABLED
+        ipc_if->skt_disconnect(out->common.audio_fd);
+#else
         skt_disconnect(out->common.audio_fd);
+#endif
         out->common.audio_fd = AUDIO_SKT_DISCONNECTED;
         if ((out->common.state != AUDIO_A2DP_STATE_SUSPENDED) &&
                 (out->common.state != AUDIO_A2DP_STATE_STOPPING)) {
@@ -842,7 +846,11 @@ static int out_standby(struct audio_stream *stream)
     pthread_mutex_lock(&out->common.lock);
     // Do nothing in SUSPENDED state.
     if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED)
-        retVal = suspend_audio_datapath(&out->common, true);
+#ifdef BT_HOST_IPC_ENABLED
+        retVal =  ipc_if->suspend_audio_datapath(&out->common, true);
+#else
+        retVal =  suspend_audio_datapath(&out->common, true);
+#endif
     out->frames_rendered = 0; // rendered is reset, presented is not
     pthread_mutex_unlock (&out->common.lock);
 
@@ -890,7 +898,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
             else if ((out->common.state == AUDIO_A2DP_STATE_STOPPED) ||
                 (out->common.state == AUDIO_A2DP_STATE_STANDBY))
             {
+#ifdef BT_HOST_IPC_ENABLED
+                if (ipc_if->start_audio_datapath(&out->common) < 0)
+#else
                 if (start_audio_datapath(&out->common) < 0)
+#endif
                 {
                     INFO("stream start failed");
                     status = -1;
@@ -909,7 +921,11 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
             INFO("out_set_parameters, value: false");
             pthread_mutex_lock(&out->common.lock);
             if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED)
+#ifdef BT_HOST_IPC_ENABLED
+                status = ipc_if->suspend_audio_datapath(&out->common, true);
+#else
                 status = suspend_audio_datapath(&out->common, true);
+#endif
             else
             {
                 ERROR("stream alreday suspended");
@@ -936,16 +952,28 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
     {
         pthread_mutex_lock(&out->common.lock);
         if (out->common.state == AUDIO_A2DP_STATE_STARTED)
+#ifdef BT_HOST_IPC_ENABLED
+            status = ipc_if->suspend_audio_datapath(&out->common, false);
+#else
             status = suspend_audio_datapath(&out->common, false);
+#endif
         else
         {
-                if (check_a2dp_stream_started(out) == 0)
-                   /*Btif and A2dp HAL state can be out of sync
-                    *check state of btif and suspend audio.
-                    *Happens when remote initiates start.*/
-                    status = suspend_audio_datapath(&out->common, false);
-                else
-                    out->common.state = AUDIO_A2DP_STATE_SUSPENDED;
+#ifdef BT_HOST_IPC_ENABLED
+            if (ipc_if->check_a2dp_stream_started(&out->common) == 0)
+#else
+            if (check_a2dp_stream_started(out) == 0)
+#endif
+               /*Btif and A2dp HAL state can be out of sync
+                *check state of btif and suspend audio.
+                *Happens when remote initiates start.*/
+#ifdef BT_HOST_IPC_ENABLED
+                status = ipc_if->suspend_audio_datapath(&out->common, false);
+#else
+                status = suspend_audio_datapath(&out->common, false);
+#endif
+            else
+                out->common.state = AUDIO_A2DP_STATE_SUSPENDED;
         }
         pthread_mutex_unlock(&out->common.lock);
     }
@@ -1192,7 +1220,11 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
     if ((in->common.state == AUDIO_A2DP_STATE_STOPPED) ||
         (in->common.state == AUDIO_A2DP_STATE_STANDBY))
     {
+#ifdef BT_HOST_IPC_ENABLED
+        if (ipc_if->start_audio_datapath(&in->common) < 0)
+#else
         if (start_audio_datapath(&in->common) < 0)
+#endif
         {
             goto error;
         }
@@ -1204,11 +1236,20 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
     }
 
     pthread_mutex_unlock(&in->common.lock);
+#ifdef BT_HOST_IPC_ENABLED
+    read = ipc_if->skt_read(in->common.audio_fd, buffer, bytes);
+#else
     read = skt_read(in->common.audio_fd, buffer, bytes);
+#endif
     pthread_mutex_lock(&in->common.lock);
+
     if (read == -1)
     {
+#ifdef BT_HOST_IPC_ENABLED
+        ipc_if->skt_disconnect(in->common.audio_fd);
+#else
         skt_disconnect(in->common.audio_fd);
+#endif
         in->common.audio_fd = AUDIO_SKT_DISCONNECTED;
         if ((in->common.state != AUDIO_A2DP_STATE_SUSPENDED) &&
                 (in->common.state != AUDIO_A2DP_STATE_STOPPING)) {
@@ -1293,6 +1334,25 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
     number++;
     #endif
 
+#ifdef BT_HOST_IPC_ENABLED
+    lib_handle = dlopen("libbthost_if.so", RTLD_NOW);
+    if (!lib_handle)
+    {
+        INFO("Failed to load bthost-ipc library %s",dlerror());
+        ret = -1;
+        goto err_open;
+    }
+    else
+    {
+        ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE");
+        if (!ipc_if)
+        {
+            ERROR("Failed to load BT IPC library symbol");
+            ret  = -1;
+            goto err_open;
+        }
+    }
+#endif
     out->stream.common.get_sample_rate = out_get_sample_rate;
     out->stream.common.set_sample_rate = out_set_sample_rate;
     out->stream.common.get_buffer_size = out_get_buffer_size;
@@ -1313,8 +1373,11 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
 
 
     /* initialize a2dp specifics */
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->a2dp_stream_common_init(&out->common);
+#else
     a2dp_stream_common_init(&out->common);
-
+#endif
     out->common.cfg.channel_flags = AUDIO_STREAM_DEFAULT_CHANNEL_FLAG;
     out->common.cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
     out->common.cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
@@ -1329,7 +1392,11 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
     *stream_out = &out->stream;
     a2dp_dev->output = out;
 
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->a2dp_open_ctrl_path(&out->common);
+#else
     a2dp_open_ctrl_path(&out->common);
+#endif
     if (out->common.ctrl_fd == AUDIO_SKT_DISCONNECTED)
     {
         ERROR("ctrl socket failed to connect (%s)", strerror(errno));
@@ -1337,6 +1404,13 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
         goto err_open;
     }
 
+#ifdef BT_HOST_IPC_ENABLED
+    if (ipc_if->a2dp_command(&out->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) {
+#else
+    if (a2dp_command(&out->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) {
+#endif
+        DEBUG("Streaming mode set successfully");
+    }
     INFO("success");
     /* Delay to ensure Headset is in proper state when START is initiated
        from DUT immediately after the connection due to ongoing music playback. */
@@ -1361,17 +1435,26 @@ static void adev_close_output_stream(struct audio_hw_device *dev,
 
     pthread_mutex_lock(&out->common.lock);
     if ((out->common.state == AUDIO_A2DP_STATE_STARTED) ||
-            (out->common.state == AUDIO_A2DP_STATE_STOPPING)) {
+            (out->common.state == AUDIO_A2DP_STATE_STOPPING))
+#ifdef BT_HOST_IPC_ENABLED
+        ipc_if->stop_audio_datapath(&out->common);
+#else
         stop_audio_datapath(&out->common);
-    }
+#endif
 
     #ifdef BT_AUDIO_SAMPLE_LOG
     ALOGV("close file output");
     fclose (outputpcmsamplefile);
     #endif
 
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->skt_disconnect(out->common.ctrl_fd);
+#else
     skt_disconnect(out->common.ctrl_fd);
+#endif
     out->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    if (lib_handle)
+        dlclose(lib_handle);
     free(stream);
     a2dp_dev->output = NULL;
     pthread_mutex_unlock(&out->common.lock);
@@ -1504,6 +1587,25 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
     if (!in)
         return -ENOMEM;
 
+#ifdef BT_HOST_IPC_ENABLED
+    lib_handle = dlopen("libbthost_if.so", RTLD_NOW);
+    if (!lib_handle)
+    {
+        INFO("Failed to load bthost-ipc library %s",dlerror());
+        ret = -1;
+        goto err_open;
+    }
+    else
+    {
+        ipc_if = (bt_host_ipc_interface_t*) dlsym(lib_handle,"BTHOST_IPC_INTERFACE");
+        if (!ipc_if)
+        {
+            ERROR("Failed to load BT IPC library symbol");
+            ret =  -1;
+            goto err_open;
+        }
+    }
+#endif
     in->stream.common.get_sample_rate = in_get_sample_rate;
     in->stream.common.set_sample_rate = in_set_sample_rate;
     in->stream.common.get_buffer_size = in_get_buffer_size;
@@ -1521,12 +1623,19 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
     in->stream.get_input_frames_lost = in_get_input_frames_lost;
 
     /* initialize a2dp specifics */
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->a2dp_stream_common_init(&in->common);
+#else
     a2dp_stream_common_init(&in->common);
-
+#endif
     *stream_in = &in->stream;
     a2dp_dev->input = in;
 
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->a2dp_open_ctrl_path(&in->common);
+#else
     a2dp_open_ctrl_path(&in->common);
+#endif
     if (in->common.ctrl_fd == AUDIO_SKT_DISCONNECTED)
     {
         ERROR("ctrl socket failed to connect (%s)", strerror(errno));
@@ -1534,7 +1643,18 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
         goto err_open;
     }
 
+#ifdef BT_HOST_IPC_ENABLED
+    if (ipc_if->a2dp_command(&in->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) {
+#else
+    if (a2dp_command(&in->common, A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED) == 0) {
+#endif
+        DEBUG("Streaming mode set successfully");
+    }
+#ifdef BT_HOST_IPC_ENABLED
+    if (ipc_if->a2dp_read_audio_config(&in->common) < 0) {
+#else
     if (a2dp_read_audio_config(&in->common) < 0) {
+#endif
         ERROR("a2dp_read_audio_config failed (%s)", strerror(errno));
         ret = -1;
         goto err_open;
@@ -1561,13 +1681,22 @@ static void adev_close_input_stream(struct audio_hw_device *dev,
     INFO("closing input (state %d)", state);
 
     if ((state == AUDIO_A2DP_STATE_STARTED) || (state == AUDIO_A2DP_STATE_STOPPING))
+#ifdef BT_HOST_IPC_ENABLED
+        ipc_if->stop_audio_datapath(&in->common);
+#else
         stop_audio_datapath(&in->common);
+#endif
 
+#ifdef BT_HOST_IPC_ENABLED
+    ipc_if->skt_disconnect(in->common.ctrl_fd);
+#else
     skt_disconnect(in->common.ctrl_fd);
+#endif
     in->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
     free(stream);
     a2dp_dev->input = NULL;
-
+    if (lib_handle)
+        dlclose(lib_handle);
     DEBUG("done");
 }
 
index 1a639e9..8c30b76 100644 (file)
@@ -1,4 +1,9 @@
 /******************************************************************************
+ *  Copyright (C) 2016, The Linux Foundation. All rights reserved.
+ *
+ *  Not a Contribution
+ *****************************************************************************/
+/******************************************************************************
  *
  *  Copyright (C) 2009-2012 Broadcom Corporation
  *
@@ -26,7 +31,7 @@
 
 #ifndef AUDIO_A2DP_HW_H
 #define AUDIO_A2DP_HW_H
-
+#include <pthread.h>
 /*****************************************************************************
 **  Constants & Macros
 ******************************************************************************/
@@ -91,6 +96,8 @@ typedef enum {
     A2DP_CTRL_CMD_SUSPEND,
     A2DP_CTRL_GET_AUDIO_CONFIG,
     A2DP_CTRL_CMD_OFFLOAD_START,
+    A2DP_CTRL_CMD_OFFLOAD_SUPPORTED,
+    A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED,
 } tA2DP_CTRL_CMD;
 
 typedef enum {
@@ -101,6 +108,32 @@ typedef enum {
 } tA2DP_CTRL_ACK;
 
 
+typedef enum {
+    AUDIO_A2DP_STATE_STARTING,
+    AUDIO_A2DP_STATE_STARTED,
+    AUDIO_A2DP_STATE_STOPPING,
+    AUDIO_A2DP_STATE_STOPPED,
+    AUDIO_A2DP_STATE_SUSPENDED, /* need explicit set param call to resume (suspend=false) */
+    AUDIO_A2DP_STATE_STANDBY    /* allows write to autoresume */
+} a2dp_state_t;
+
+struct a2dp_config {
+    uint32_t                rate;
+    uint32_t                channel_flags;
+    int                     format;
+};
+
+/* move ctrl_fd outside output stream and keep open until HAL unloaded ? */
+
+struct a2dp_stream_common {
+    pthread_mutex_t         lock;
+    int                     ctrl_fd;
+    int                     audio_fd;
+    size_t                  buffer_sz;
+    struct a2dp_config      cfg;
+    a2dp_state_t            state;
+    uint8_t                 codec_cfg[20];
+};
 /*****************************************************************************
 **  Type definitions for callback functions
 ******************************************************************************/
diff --git a/audio_a2dp_hw/bthost_ipc.c b/audio_a2dp_hw/bthost_ipc.c
new file mode 100644 (file)
index 0000000..ef20733
--- /dev/null
@@ -0,0 +1,997 @@
+/******************************************************************************
+ *  Copyright (C) 2016, The Linux Foundation. All rights reserved.
+ *
+ *  Not a Contribution
+ *****************************************************************************/
+/*****************************************************************************
+ *  Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+/*      bthost_ipc.c
+ *
+ *  Description:   Implements IPC interface between HAL and BT host
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <system/audio.h>
+#include <hardware/audio.h>
+
+#include <hardware/hardware.h>
+#include "bthost_ipc.h"
+#include "bt_utils.h"
+#include "osi/include/hash_map.h"
+#include "osi/include/hash_map_utils.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "osi/include/socket_utils/sockets.h"
+
+
+#define LOG_TAG "bthost_ipc"
+#include "osi/include/log.h"
+
+
+static int bt_split_a2dp_enabled = 0;
+/*****************************************************************************
+**  Constants & Macros
+******************************************************************************/
+#define STREAM_START_MAX_RETRY_COUNT 50
+#define CTRL_CHAN_RETRY_COUNT 3
+#define USEC_PER_SEC 1000000L
+#define SOCK_SEND_TIMEOUT_MS 2000  /* Timeout for sending */
+#define SOCK_RECV_TIMEOUT_MS 5000  /* Timeout for receiving */
+
+// set WRITE_POLL_MS to 0 for blocking sockets, nonzero for polled non-blocking sockets
+#define WRITE_POLL_MS 20
+
+#define CASE_RETURN_STR(const) case const: return #const;
+
+#define FNLOG()             LOG_VERBOSE(LOG_TAG, "%s", __FUNCTION__);
+#define DEBUG(fmt, ...)     LOG_VERBOSE(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+#define INFO(fmt, ...)      LOG_INFO(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+#define WARN(fmt, ...)      LOG_WARN(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+#define ERROR(fmt, ...)     LOG_ERROR(LOG_TAG, "%s: " fmt,__FUNCTION__, ## __VA_ARGS__)
+
+#define ASSERTC(cond, msg, val) if (!(cond)) {ERROR("### ASSERT : %s line %d %s (%d) ###", __FILE__, __LINE__, msg, val);}
+
+/*****************************************************************************
+**  Local type definitions
+******************************************************************************/
+
+struct a2dp_stream_common audio_stream;
+
+/*****************************************************************************
+**  Static functions
+******************************************************************************/
+
+audio_sbc_encoder_config sbc_codec;
+audio_aptx_encoder_config aptx_codec;
+/*****************************************************************************
+**  Externs
+******************************************************************************/
+
+/*****************************************************************************
+**  Functions
+******************************************************************************/
+void a2dp_open_ctrl_path(struct a2dp_stream_common *common);
+/*****************************************************************************
+**   Miscellaneous helper functions
+******************************************************************************/
+static const char* dump_a2dp_ctrl_event(char event)
+{
+    switch(event)
+    {
+        CASE_RETURN_STR(A2DP_CTRL_CMD_NONE)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_READY)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_START)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_STOP)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_SUPPORTED)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_NOT_SUPPORTED)
+        CASE_RETURN_STR(A2DP_CTRL_CMD_CHECK_STREAM_STARTED)
+        CASE_RETURN_STR(A2DP_CTRL_GET_CODEC_CONFIG)
+        CASE_RETURN_STR(A2DP_CTRL_GET_MULTICAST_STATUS)
+        default:
+            return "UNKNOWN MSG ID";
+    }
+}
+
+/* logs timestamp with microsec precision
+   pprev is optional in case a dedicated diff is required */
+static void ts_log(char *tag, int val, struct timespec *pprev_opt)
+{
+    struct timespec now;
+    static struct timespec prev = {0,0};
+    unsigned long long now_us;
+    unsigned long long diff_us;
+    UNUSED(tag);
+    UNUSED(val);
+
+    clock_gettime(CLOCK_MONOTONIC, &now);
+
+    now_us = now.tv_sec*USEC_PER_SEC + now.tv_nsec/1000;
+
+    if (pprev_opt)
+    {
+        diff_us = (now.tv_sec - prev.tv_sec) * USEC_PER_SEC + (now.tv_nsec - prev.tv_nsec)/1000;
+        *pprev_opt = now;
+        DEBUG("[%s] ts %08lld, *diff %08lld, val %d", tag, now_us, diff_us, val);
+    }
+    else
+    {
+        diff_us = (now.tv_sec - prev.tv_sec) * USEC_PER_SEC + (now.tv_nsec - prev.tv_nsec)/1000;
+        prev = now;
+        DEBUG("[%s] ts %08lld, diff %08lld, val %d", tag, now_us, diff_us, val);
+    }
+}
+
+
+static const char* dump_a2dp_hal_state(int event)
+{
+    switch(event)
+    {
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_STARTING)
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_STARTED)
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_STOPPING)
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_STOPPED)
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_SUSPENDED)
+        CASE_RETURN_STR(AUDIO_A2DP_STATE_STANDBY)
+        default:
+            return "UNKNOWN STATE ID";
+    }
+}
+static void* a2dp_codec_parser(uint8_t *codec_cfg, audio_format_t *codec_type)
+{
+    char byte,len;
+    uint8_t *p_cfg = codec_cfg;
+    INFO("%s",__func__);
+    if (codec_cfg[CODEC_OFFSET] == CODEC_TYPE_SBC)
+    {
+        memset(&sbc_codec,0,sizeof(audio_sbc_encoder_config));
+        p_cfg++;//skip dev idx
+        len = *p_cfg++;
+        p_cfg++;//skip media type
+        len--;
+        p_cfg++;
+        len--;
+        byte = *p_cfg++;
+        len--;
+        switch (byte & A2D_SBC_FREQ_MASK)
+        {
+            case A2D_SBC_SAMP_FREQ_48:
+                 sbc_codec.sampling_rate = 48000;
+                 break;
+            case A2D_SBC_SAMP_FREQ_44:
+                 sbc_codec.sampling_rate = 44100;
+                 break;
+            case A2D_SBC_SAMP_FREQ_32:
+                 sbc_codec.sampling_rate = 3200;
+                 break;
+            case A2D_SBC_SAMP_FREQ_16:
+                 sbc_codec.sampling_rate = 16000;
+                 break;
+            default:
+                 ERROR("Unkown sampling rate");
+        }
+
+        switch (byte & A2D_SBC_CHN_MASK)
+        {
+            case A2D_SBC_CH_MD_JOINT:
+                 sbc_codec.channels = 3;
+                 break;
+            case A2D_SBC_CH_MD_STEREO:
+                 sbc_codec.channels = 2;
+                 break;
+            case A2D_SBC_CH_MD_DUAL:
+                 sbc_codec.channels = 1;
+                 break;
+            case A2D_SBC_CH_MD_MONO:
+                 sbc_codec.channels = 0;
+                 break;
+            default:
+                 ERROR("Unknow channel mode");
+        }
+        byte = *p_cfg++;
+        len--;
+        switch (byte & A2D_SBC_BLK_MASK)
+        {
+            case A2D_SBC_BLOCKS_16:
+                sbc_codec.blk_len = 16;
+                break;
+            case A2D_SBC_BLOCKS_12:
+                sbc_codec.blk_len = 12;
+                break;
+            case A2D_SBC_BLOCKS_8:
+                sbc_codec.blk_len = 8;
+                break;
+            case A2D_SBC_BLOCKS_4:
+                sbc_codec.blk_len = 4;
+                break;
+            default:
+                ERROR("Unknown block length");
+        }
+
+        switch (byte & A2D_SBC_SUBBAND_MASK)
+        {
+            case A2D_SBC_SUBBAND_8:
+                sbc_codec.subband = 8;
+                break;
+            case A2D_SBC_SUBBAND_4:
+                sbc_codec.subband = 4;
+                break;
+            default:
+                ERROR("Unknown subband");
+        }
+        switch (byte & A2D_SBC_ALLOC_MASK)
+        {
+            case A2D_SBC_ALLOC_MD_L:
+                sbc_codec.alloc = 1;
+                break;
+            case A2D_SBC_ALLOC_MD_S:
+                sbc_codec.alloc = 2;
+            default:
+                ERROR("Unknown alloc method");
+        }
+        sbc_codec.min_bitpool = *p_cfg++;
+        len--;
+        sbc_codec.max_bitpool = *p_cfg++;
+        len--;
+        if (len == 0)
+            INFO("Copied codec config");
+
+        p_cfg += 2; //skip mtu
+        sbc_codec.bitrate = *p_cfg++;
+        sbc_codec.bitrate |= (*p_cfg++ << 8);
+        sbc_codec.bitrate |= (*p_cfg++ << 16);
+        sbc_codec.bitrate |= (*p_cfg++ << 24);
+        *codec_type = AUDIO_FORMAT_SBC;
+        INFO("Done copying full codec config");
+        return ((void *)(&sbc_codec));
+    }
+    else if (codec_cfg[CODEC_OFFSET] == NON_A2DP_CODEC_TYPE)
+    {
+        if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX &&
+            codec_cfg[CODEC_ID_OFFSET] == APTX_CODEC_ID)
+        {
+            INFO("AptX-classic codec");
+            *codec_type = AUDIO_FORMAT_APTX;
+        }
+        if (codec_cfg[VENDOR_ID_OFFSET] == VENDOR_APTX_HD &&
+            codec_cfg[CODEC_ID_OFFSET] == APTX_HD_CODEC_ID)
+        {
+            INFO("AptX-HD codec");
+            *codec_type = AUDIO_FORMAT_APTX_HD;
+        }
+        memset(&aptx_codec,0,sizeof(audio_aptx_encoder_config));
+        p_cfg++; //skip dev_idx
+        len = *p_cfg++;//LOSC
+        p_cfg++; // Skip media type
+        len--;
+        p_cfg++; //codec_type
+        len--;
+        p_cfg+=4;//skip vendor id
+        len -= 4;
+        p_cfg += 2; //skip codec id
+        len -= 2;
+        byte = *p_cfg++;
+        len--;
+        switch (byte & A2D_APTX_SAMP_FREQ_MASK)
+        {
+            case A2D_APTX_SAMP_FREQ_48:
+                 aptx_codec.sampling_rate = 48000;
+                 break;
+            case A2D_APTX_SAMP_FREQ_44:
+                 aptx_codec.sampling_rate = 44100;
+                 break;
+            default:
+                 ERROR("Unknown sampling rate");
+        }
+        switch (byte & A2D_APTX_CHAN_MASK)
+        {
+            case A2D_APTX_CHAN_STEREO:
+                 aptx_codec.channels = 2;
+                 break;
+            case A2D_APTX_CHAN_MONO:
+                 aptx_codec.channels = 1;
+                 break;
+            default:
+                 ERROR("Unknown channel mode");
+        }
+        if (*codec_type == AUDIO_FORMAT_APTX_HD)
+            len -= 4;//ignore 4 bytes not used
+        if (len == 0)
+            INFO("Codec config copied");
+
+        p_cfg += 2; //skip mtu
+
+        aptx_codec.bitrate = *p_cfg++;
+        aptx_codec.bitrate |= (*p_cfg++ << 8);
+        aptx_codec.bitrate |= (*p_cfg++ << 16);
+        aptx_codec.bitrate |= (*p_cfg++ << 24);
+        return ((void *)&aptx_codec);
+    }
+    return NULL;
+}
+/*****************************************************************************
+**
+**   bluedroid stack adaptation
+**
+*****************************************************************************/
+
+static int skt_connect(char *path, size_t buffer_sz)
+{
+    int ret;
+    int skt_fd;
+    int len;
+
+    INFO("connect to %s (sz %zu)", path, buffer_sz);
+
+    skt_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+
+    if(osi_socket_local_client_connect(skt_fd, path,
+            ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM) < 0)
+    {
+        ERROR("failed to connect (%s)", strerror(errno));
+        close(skt_fd);
+        return -1;
+    }
+
+    len = buffer_sz;
+    ret = setsockopt(skt_fd, SOL_SOCKET, SO_SNDBUF, (char*)&len, (int)sizeof(len));
+    if (ret < 0)
+        ERROR("setsockopt failed (%s)", strerror(errno));
+
+    ret = setsockopt(skt_fd, SOL_SOCKET, SO_RCVBUF, (char*)&len, (int)sizeof(len));
+    if (ret < 0)
+        ERROR("setsockopt failed (%s)", strerror(errno));
+
+    /* Socket send/receive timeout value */
+    struct timeval tv;
+    tv.tv_sec = SOCK_SEND_TIMEOUT_MS / 1000;
+    tv.tv_usec = (SOCK_SEND_TIMEOUT_MS % 1000) * 1000;
+
+    ret = setsockopt(skt_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+    if (ret < 0)
+        ERROR("setsockopt failed (%s)", strerror(errno));
+
+    tv.tv_sec = SOCK_RECV_TIMEOUT_MS / 1000;
+    tv.tv_usec = (SOCK_RECV_TIMEOUT_MS % 1000) * 1000;
+
+    ret = setsockopt(skt_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+    if (ret < 0)
+        ERROR("setsockopt failed (%s)", strerror(errno));
+
+    INFO("connected to stack fd = %d", skt_fd);
+
+    return skt_fd;
+}
+
+static int skt_read(int fd, void *p, size_t len)
+{
+    ssize_t read;
+
+    FNLOG();
+
+    ts_log("skt_read recv", len, NULL);
+
+    OSI_NO_INTR(read = recv(fd, p, len, MSG_NOSIGNAL));
+    if (read == -1)
+        ERROR("read failed with errno=%d\n", errno);
+
+    return (int)read;
+}
+
+static int skt_write(int fd, const void *p, size_t len)
+{
+    ssize_t sent;
+
+    FNLOG();
+
+    ts_log("skt_write", len, NULL);
+
+    if (WRITE_POLL_MS == 0) {
+        // do not poll, use blocking send
+        OSI_NO_INTR(sent = send(fd, p, len, MSG_NOSIGNAL));
+        if (sent == -1)
+            ERROR("write failed with error(%s)", strerror(errno));
+
+        return (int)sent;
+    }
+
+    // use non-blocking send, poll
+    int ms_timeout = SOCK_SEND_TIMEOUT_MS;
+    size_t count = 0;
+    while (count < len) {
+        OSI_NO_INTR(sent = send(fd, p, len - count, MSG_NOSIGNAL | MSG_DONTWAIT));
+        if (sent == -1) {
+            if (errno != EAGAIN && errno != EWOULDBLOCK) {
+                ERROR("write failed with error(%s)", strerror(errno));
+                return -1;
+            }
+            if (ms_timeout >= WRITE_POLL_MS) {
+                usleep(WRITE_POLL_MS * 1000);
+                ms_timeout -= WRITE_POLL_MS;
+                continue;
+            }
+            WARN("write timeout exceeded, sent %zu bytes", count);
+            return -1;
+        }
+        count += sent;
+        p = (const uint8_t *)p + sent;
+    }
+    return (int)count;
+}
+
+static int skt_disconnect(int fd)
+{
+    INFO("fd %d", fd);
+
+    if (fd != AUDIO_SKT_DISCONNECTED)
+    {
+        shutdown(fd, SHUT_RDWR);
+        close(fd);
+    }
+    return 0;
+}
+
+
+
+/*****************************************************************************
+**
+**  AUDIO CONTROL PATH
+**
+*****************************************************************************/
+
+int a2dp_ctrl_receive(struct a2dp_stream_common *common, void* buffer, int length)
+{
+    ssize_t ret;
+    int i;
+
+    for (i = 0;; i++) {
+        OSI_NO_INTR(ret = recv(common->ctrl_fd, buffer, length, MSG_NOSIGNAL));
+        if (ret > 0) {
+            break;
+        }
+        if (ret == 0) {
+            ERROR("ack failed: peer closed");
+            break;
+        }
+        if (errno != EWOULDBLOCK && errno != EAGAIN) {
+            ERROR("ack failed: error(%s)", strerror(errno));
+            break;
+        }
+        if (i == (CTRL_CHAN_RETRY_COUNT - 1)) {
+            ERROR("ack failed: max retry count");
+            break;
+        }
+        INFO("ack failed (%s), retrying", strerror(errno));
+    }
+    if (ret <= 0) {
+        skt_disconnect(common->ctrl_fd);
+        common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    }
+    return ret;
+}
+
+int a2dp_command(struct a2dp_stream_common *common, char cmd)
+{
+    char ack;
+
+    INFO("A2DP COMMAND %s", dump_a2dp_ctrl_event(cmd));
+
+    if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED) {
+        INFO("recovering from previous error");
+        a2dp_open_ctrl_path(common);
+        if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED) {
+            ERROR("failure to open ctrl path");
+            return -1;
+        }
+    }
+
+    /* send command */
+    ssize_t sent;
+    OSI_NO_INTR(sent = send(common->ctrl_fd, &cmd, 1, MSG_NOSIGNAL));
+    if (sent == -1)
+    {
+        ERROR("cmd failed (%s)", strerror(errno));
+        skt_disconnect(common->ctrl_fd);
+        common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+        return -1;
+    }
+
+    /* wait for ack byte */
+    if (a2dp_ctrl_receive(common, &ack, 1) < 0) {
+        ERROR("A2DP COMMAND %s: no ACK", dump_a2dp_ctrl_event(cmd));
+        return -1;
+    }
+
+    INFO("A2DP COMMAND %s DONE STATUS %d", dump_a2dp_ctrl_event(cmd), ack);
+
+    if (ack == A2DP_CTRL_ACK_INCALL_FAILURE)
+        return ack;
+    if (ack != A2DP_CTRL_ACK_SUCCESS) {
+        ERROR("A2DP COMMAND %s error %d", dump_a2dp_ctrl_event(cmd), ack);
+        return -1;
+    }
+    return 0;
+}
+
+int check_a2dp_ready(struct a2dp_stream_common *common)
+{
+    INFO("state %s", dump_a2dp_hal_state(common->state));
+    if (a2dp_command(common, A2DP_CTRL_CMD_CHECK_READY) < 0)
+    {
+        ERROR("check a2dp ready failed");
+        return -1;
+    }
+    return 0;
+}
+
+int a2dp_read_audio_config(struct a2dp_stream_common *common)
+{
+    uint32_t sample_rate;
+    uint8_t channel_count;
+
+    if (a2dp_command(common, A2DP_CTRL_GET_AUDIO_CONFIG) < 0)
+    {
+        ERROR("check a2dp ready failed");
+        return -1;
+    }
+
+    if (a2dp_ctrl_receive(common, &sample_rate, 4) < 0)
+        return -1;
+    if (a2dp_ctrl_receive(common, &channel_count, 1) < 0)
+        return -1;
+
+    common->cfg.channel_flags = (channel_count == 1 ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO);
+    common->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
+    common->cfg.rate = sample_rate;
+
+    INFO("got config %d %d", common->cfg.format, common->cfg.rate);
+
+    return 0;
+}
+
+int a2dp_read_codec_config(struct a2dp_stream_common *common,uint8_t idx)
+{
+    char cmd[2],ack;
+    int i,len = 0;
+    uint8_t *p_codec_cfg = common->codec_cfg;
+    cmd[0] = A2DP_CTRL_GET_CODEC_CONFIG;
+    cmd[1] = idx;
+    INFO("%s",__func__);
+    memset(p_codec_cfg,0,20);
+    INFO("%s",__func__);
+
+    if (send(common->ctrl_fd, cmd, 2,  MSG_NOSIGNAL) == -1)
+    {
+        ERROR("cmd failed (%s)", strerror(errno));
+        skt_disconnect(common->ctrl_fd);
+        common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+        return -1;
+    }
+
+    if (a2dp_ctrl_receive(common, &ack, 1) < 0)
+        return -1;
+
+    if (ack != A2DP_CTRL_ACK_SUCCESS)
+    {
+        ERROR("%s: Failed to get ack",__func__);
+        return -1;
+    }
+    if (a2dp_ctrl_receive(common, &len, 1) < 0)
+        return -1;
+    if (a2dp_ctrl_receive(common, p_codec_cfg, len) < 0)
+        return -1;
+
+    INFO("got codec config");
+    p_codec_cfg = common->codec_cfg;
+
+    for (i=0;i<len;i++)
+         INFO("code_config[%d] = %d ", i,*p_codec_cfg++);
+
+    return 0;
+}
+
+int a2dp_get_multicast_status(struct a2dp_stream_common *common, uint8_t *mcast_status,
+                               uint8_t *num_dev)
+{
+    char cmd = A2DP_CTRL_GET_MULTICAST_STATUS;
+    INFO("%s",__func__);
+    if (a2dp_command(common,A2DP_CTRL_GET_MULTICAST_STATUS) < 0)
+    {
+        ERROR("check a2dp ready failed");
+        return -1;
+    }
+    INFO("a2dp_get_multicast_status acked fd = %d",common->ctrl_fd);
+    if (a2dp_ctrl_receive(common, mcast_status, 1) < 0)
+        return -1;
+    if (a2dp_ctrl_receive(common, num_dev, 1) < 0)
+        return -1;
+    INFO("%s: multicast status = %d, num_dev = %d",__func__,*mcast_status,*num_dev);
+    return 0;
+}
+
+void a2dp_open_ctrl_path(struct a2dp_stream_common *common)
+{
+    int i;
+
+    /* retry logic to catch any timing variations on control channel */
+    for (i = 0; i < CTRL_CHAN_RETRY_COUNT; i++)
+    {
+        /* connect control channel if not already connected */
+        if ((common->ctrl_fd = skt_connect(A2DP_CTRL_PATH, common->buffer_sz)) > 0)
+        {
+            /* success, now check if stack is ready */
+            if (check_a2dp_ready(common) == 0)
+                break;
+
+            ERROR("error : a2dp not ready, wait 250 ms and retry");
+            usleep(250000);
+            skt_disconnect(common->ctrl_fd);
+            common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+        }
+
+        /* ctrl channel not ready, wait a bit */
+        usleep(250000);
+    }
+}
+
+/*****************************************************************************
+**
+** AUDIO DATA PATH
+**
+*****************************************************************************/
+
+void a2dp_stream_common_init(struct a2dp_stream_common *common)
+{
+    pthread_mutexattr_t lock_attr;
+
+    FNLOG();
+
+    pthread_mutexattr_init(&lock_attr);
+    pthread_mutexattr_settype(&lock_attr, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&common->lock, &lock_attr);
+
+    common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    common->audio_fd = AUDIO_SKT_DISCONNECTED;
+    common->state = AUDIO_A2DP_STATE_STOPPED;
+
+    /* manages max capacity of socket pipe */
+    common->buffer_sz = AUDIO_STREAM_OUTPUT_BUFFER_SZ;
+    bt_split_a2dp_enabled = false;
+}
+
+int start_audio_datapath(struct a2dp_stream_common *common)
+{
+    INFO("state %d", common->state);
+
+    #ifdef BT_AUDIO_SYSTRACE_LOG
+    char trace_buf[512];
+    #endif
+
+    INFO("state %s", dump_a2dp_hal_state(common->state));
+
+    int oldstate = common->state;
+    common->state = AUDIO_A2DP_STATE_STARTING;
+
+    int a2dp_status = a2dp_command(common, A2DP_CTRL_CMD_START);
+    #ifdef BT_AUDIO_SYSTRACE_LOG
+    snprintf(trace_buf, 32, "start_audio_data_path:");
+    if (PERF_SYSTRACE)
+    {
+        ATRACE_BEGIN(trace_buf);
+    }
+    #endif
+
+
+    #ifdef BT_AUDIO_SYSTRACE_LOG
+    if (PERF_SYSTRACE)
+    {
+        ATRACE_END();
+    }
+    #endif
+    if (a2dp_status < 0)
+    {
+        ERROR("%s Audiopath start failed (status %d)", __func__, a2dp_status);
+        goto error;
+    }
+    else if (a2dp_status == A2DP_CTRL_ACK_INCALL_FAILURE)
+    {
+        ERROR("%s Audiopath start failed - in call, move to suspended", __func__);
+        goto error;
+    }
+    if (!bt_split_a2dp_enabled)
+    {
+        /* connect socket if not yet connected */
+        if (common->audio_fd == AUDIO_SKT_DISCONNECTED)
+        {
+            common->audio_fd = skt_connect(A2DP_DATA_PATH, common->buffer_sz);
+            if (common->audio_fd < 0)
+            {
+                common->state = oldstate;
+                goto error;
+            }
+
+            common->state = AUDIO_A2DP_STATE_STARTED;
+        }
+    }
+    else
+    {
+        common->state = AUDIO_A2DP_STATE_STARTED;
+    }
+
+    return 0;
+error:
+    common->state = oldstate;
+    return -1;
+}
+
+int stop_audio_datapath(struct a2dp_stream_common *common)
+{
+    int oldstate = common->state;
+
+    INFO("state %s", dump_a2dp_hal_state(common->state));
+
+    if (common->ctrl_fd == AUDIO_SKT_DISCONNECTED)
+         return -1;
+
+    /* prevent any stray output writes from autostarting the stream
+       while stopping audiopath */
+    common->state = AUDIO_A2DP_STATE_STOPPING;
+
+    if (a2dp_command(common, A2DP_CTRL_CMD_STOP) < 0)
+    {
+        ERROR("audiopath stop failed");
+        common->state = oldstate;
+        return -1;
+    }
+
+    common->state = AUDIO_A2DP_STATE_STOPPED;
+
+    if (!bt_split_a2dp_enabled)
+    {
+        /* disconnect audio path */
+        skt_disconnect(common->audio_fd);
+        common->audio_fd = AUDIO_SKT_DISCONNECTED;
+    }
+
+    return 0;
+}
+
+int suspend_audio_datapath(struct a2dp_stream_common *common, bool standby)
+{
+    INFO("state %s", dump_a2dp_hal_state(common->state));
+
+
+    if (common->state == AUDIO_A2DP_STATE_STOPPING)
+        return -1;
+
+    if (a2dp_command(common, A2DP_CTRL_CMD_SUSPEND) < 0)
+        return -1;
+
+    if (standby)
+        common->state = AUDIO_A2DP_STATE_STANDBY;
+    else
+        common->state = AUDIO_A2DP_STATE_SUSPENDED;
+
+    if (!bt_split_a2dp_enabled)
+    {
+        /* disconnect audio path */
+        skt_disconnect(common->audio_fd);
+
+        common->audio_fd = AUDIO_SKT_DISCONNECTED;
+    }
+
+    return 0;
+}
+
+int check_a2dp_stream_started(struct a2dp_stream_common *common)
+{
+   if (a2dp_command(common, A2DP_CTRL_CMD_CHECK_STREAM_STARTED) < 0)
+   {
+       INFO("Btif not in stream state");
+       return -1;
+   }
+   return 0;
+}
+
+int audio_open_ctrl_path()
+{
+    INFO("%s",__func__);
+    a2dp_open_ctrl_path(&audio_stream);
+    if (audio_stream.ctrl_fd != AUDIO_SKT_DISCONNECTED)
+    {
+        INFO("control path opend successfull");
+        return 0;
+    }
+    else
+        INFO("control path opend successfull");
+    return -1;
+}
+
+int audio_start_stream()
+{
+    int i;
+    INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state));
+
+    if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED)
+    {
+        INFO("stream suspended");
+        return -1;
+    }
+
+    for (i = 0; i < STREAM_START_MAX_RETRY_COUNT; i++)
+    {
+        if (start_audio_datapath(&audio_stream) == 0)
+        {
+            INFO("a2dp stream started successfully");
+            break;
+        }
+        if (audio_stream.ctrl_fd == AUDIO_SKT_DISCONNECTED)
+        {
+            INFO("control path is disconnected");
+            break;
+        }
+        INFO("%s: a2dp stream not started,wait 100mse & retry");
+        usleep(100000);
+    }
+    if (audio_stream.state != AUDIO_A2DP_STATE_STARTED)
+    {
+        ERROR("Failed to start a2dp stream");
+        return -1;
+    }
+    return 0;
+}
+
+int audio_stream_open()
+{
+    INFO("%s",__func__);
+    a2dp_stream_common_init(&audio_stream);
+    a2dp_open_ctrl_path(&audio_stream);
+    bt_split_a2dp_enabled = true;
+    if (audio_stream.ctrl_fd != AUDIO_SKT_DISCONNECTED)
+    {
+        INFO("control path open successful");
+        /*Delay to ensure Headset is in proper state when START is initiated
+        from DUT immediately after the connection due to ongoing music playback. */
+        usleep(250000);
+        a2dp_command(&audio_stream,A2DP_CTRL_CMD_OFFLOAD_SUPPORTED);
+        return 0;
+    }
+    else
+        INFO("control path open failed");
+
+    return -1;
+}
+
+int audio_stream_close()
+{
+    INFO("%s",__func__);
+
+    if (audio_stream.state == AUDIO_A2DP_STATE_STARTED ||
+        audio_stream.state == AUDIO_A2DP_STATE_STOPPING)
+    {
+        INFO("%s: Suspending audio stream",__func__);
+        suspend_audio_datapath(&audio_stream,true);
+    }
+
+    skt_disconnect(audio_stream.ctrl_fd);
+    audio_stream.ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    return 0;
+}
+int audio_stop_stream()
+{
+    INFO("%s",__func__);
+    if (suspend_audio_datapath(&audio_stream, true) == 0)
+    {
+        INFO("audio start stream successful");
+        return 0;
+    }
+    audio_stream.state = AUDIO_A2DP_STATE_STOPPED;
+    return -1; 
+}
+
+int audio_suspend_stream()
+{
+    INFO("%s",__func__);
+    if (suspend_audio_datapath(&audio_stream, false) == 0)
+    {
+        INFO("audio start stream successful");
+        return 0;
+    }
+    return -1;
+}
+
+void audio_handoff_triggered()
+{
+    INFO("%s state = %s",__func__,dump_a2dp_hal_state(audio_stream.state));
+    if (audio_stream.state != AUDIO_A2DP_STATE_STOPPED ||
+        audio_stream.state != AUDIO_A2DP_STATE_STOPPING)
+    {
+        audio_stream.state = AUDIO_A2DP_STATE_STOPPED;
+    }
+}
+
+void clear_a2dpsuspend_flag()
+{
+    INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state));
+    if (audio_stream.state == AUDIO_A2DP_STATE_SUSPENDED)
+        audio_stream.state = AUDIO_A2DP_STATE_STOPPED;
+}
+
+void * audio_get_codec_config(uint8_t *multicast_status, uint8_t *num_dev,
+                              audio_format_t *codec_type)
+{
+    char *p_common_cfg = &audio_stream.codec_cfg[0];
+    int i;
+
+    INFO("%s: state = %s",__func__,dump_a2dp_hal_state(audio_stream.state));
+
+    a2dp_get_multicast_status(&audio_stream, multicast_status,num_dev);
+
+    DEBUG("got multicast status = %d dev = %d",*multicast_status,*num_dev);
+    if (a2dp_read_codec_config(&audio_stream, 0) == 0)
+    {
+        return (a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type));
+    }
+    return NULL;
+}
+
+void* audio_get_next_codec_config(uint8_t idx, audio_format_t *codec_type)
+{
+    INFO("%s",__func__);
+    if (a2dp_read_codec_config(&audio_stream,idx) == 0)
+    {
+        return a2dp_codec_parser(&audio_stream.codec_cfg[0], codec_type);
+    }
+    return NULL;
+}
+//Entry point for dynamic lib
+const bt_host_ipc_interface_t BTHOST_IPC_INTERFACE = {
+    sizeof(bt_host_ipc_interface_t),
+    a2dp_open_ctrl_path,
+    a2dp_stream_common_init,
+    start_audio_datapath,
+    suspend_audio_datapath,
+    stop_audio_datapath,
+    check_a2dp_stream_started,
+    check_a2dp_ready,
+    a2dp_read_audio_config,
+    skt_read,
+    skt_write,
+    skt_disconnect,
+    a2dp_command,
+    audio_stream_open,
+    audio_stream_close,
+    audio_start_stream,
+    audio_stop_stream,
+    audio_suspend_stream,
+    audio_get_codec_config,
+    audio_handoff_triggered,
+    clear_a2dpsuspend_flag,
+    audio_get_next_codec_config
+};
diff --git a/audio_a2dp_hw/bthost_ipc.h b/audio_a2dp_hw/bthost_ipc.h
new file mode 100644 (file)
index 0000000..5ee0a71
--- /dev/null
@@ -0,0 +1,155 @@
+/******************************************************************************
+ *  Copyright (C) 2016, The Linux Foundation. All rights reserved.
+ *
+ *  Not a Contribution
+ *****************************************************************************/
+/*****************************************************************************
+ *  Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/*****************************************************************************
+ *
+ *  Filename:      audio_a2dp_hw.h
+ *
+ *  Description:
+ *
+ *****************************************************************************/
+#ifndef BT_HOST_IPC_H
+#define BT_HOST_IPC_H
+#include "audio_a2dp_hw.h"
+#include <system/audio.h>
+/*****************************************************************************
+**  Constants & Macros
+******************************************************************************/
+
+#define BT_AUDIO_HARDWARE_INTERFACE "libbthost"
+#define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl"
+#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data"
+
+typedef enum {
+    A2DP_CTRL_GET_CODEC_CONFIG = 11,
+    A2DP_CTRL_GET_MULTICAST_STATUS,
+} tA2DP_CTRL_EXT_CMD;
+
+/*
+codec specific definitions
+*/
+#define CODEC_TYPE_SBC 0x00
+#define CODEC_TYPE_AAC 0x02
+#define NON_A2DP_CODEC_TYPE 0xFF
+#define CODEC_OFFSET 3
+#define VENDOR_ID_OFFSET 4
+#define CODEC_ID_OFFSET (VENDOR_ID_OFFSET + 4)
+
+#ifndef VENDOR_APTX
+#define VENDOR_APTX 0x4F
+#endif
+#ifndef VENDOR_APTX_HD
+#define VENDOR_APTX_HD 0xD7
+#endif
+#ifndef VENDOR_APTX_LL
+#define VENDOR_APTX_LL 0x0A
+#endif
+#ifndef APTX_CODEC_ID
+#define APTX_CODEC_ID 0x01
+#endif
+#ifndef APTX_HD_CODEC_ID
+#define APTX_HD_CODEC_ID 0x24
+#endif
+
+#define A2D_SBC_FREQ_MASK 0xF0
+#define A2D_SBC_CHN_MASK  0x0F
+#define A2D_SBC_BLK_MASK  0xF0
+#define A2D_SBC_SUBBAND_MASK 0x0C
+#define A2D_SBC_ALLOC_MASK 0x03
+#define A2D_SBC_SAMP_FREQ_16     0x80    /* b7:16  kHz */
+#define A2D_SBC_SAMP_FREQ_32     0x40    /* b6:32  kHz */
+#define A2D_SBC_SAMP_FREQ_44     0x20    /* b5:44.1kHz */
+#define A2D_SBC_SAMP_FREQ_48     0x10    /* b4:48  kHz */
+#define A2D_SBC_CH_MD_MONO       0x08    /* b3: mono */
+#define A2D_SBC_CH_MD_DUAL       0x04    /* b2: dual */
+#define A2D_SBC_CH_MD_STEREO     0x02    /* b1: stereo */
+#define A2D_SBC_CH_MD_JOINT      0x01    /* b0: joint stereo */
+#define A2D_SBC_BLOCKS_4         0x80    /* 4 blocks */
+#define A2D_SBC_BLOCKS_8         0x40    /* 8 blocks */
+#define A2D_SBC_BLOCKS_12        0x20    /* 12blocks */
+#define A2D_SBC_BLOCKS_16        0x10    /* 16blocks */
+#define A2D_SBC_SUBBAND_4        0x08    /* b3: 4 */
+#define A2D_SBC_SUBBAND_8        0x04    /* b2: 8 */
+#define A2D_SBC_ALLOC_MD_S       0x02    /* b1: SNR */
+#define A2D_SBC_ALLOC_MD_L       0x01    /* b0: loundess */
+
+/* APTX bitmask helper */
+#define A2D_APTX_SAMP_FREQ_MASK  0xF0
+#define A2D_APTX_SAMP_FREQ_48    0x10
+#define A2D_APTX_SAMP_FREQ_44    0x20
+#define A2D_APTX_CHAN_MASK       0x0F
+#define A2D_APTX_CHAN_STEREO     0x02
+#define A2D_APTX_CHAN_MONO       0x01
+
+typedef struct {
+    uint8_t  codec_type;
+    uint8_t  dev_idx;
+    uint16_t sampling_rate; /*44.1khz,48khz*/
+    uint8_t  chn;           /*0(Mono),1(Dual),2(Stereo),3(JS)*/
+    uint8_t  blk_len;       /*4,8,12,16 */
+    uint8_t  subband;       /*4,8*/
+    uint8_t  alloc;         /*0(Loudness),1(SNR)*/
+    uint8_t  min_bitpool;   /* 2 */
+    uint8_t  max_bitpool;   /*53(44.1khz),51 (48khz) */
+    uint16_t mtu;
+    uint32_t bitrate;
+}tA2DP_SBC_CODEC;
+
+typedef struct {
+    uint8_t  codec_type;
+    uint8_t  dev_idx;
+    uint32_t vendor_id;
+    uint16_t codec_id;
+    uint16_t sampling_rate;
+    uint8_t  chnl;
+    uint8_t  cp;
+    uint16_t mtu;
+    uint32_t bitrate;
+}tA2DP_APTX_CODEC;
+
+typedef struct {
+    /** Set to sizeof(bt_host_ipc_interface_t) */
+    size_t          size;
+    void (*a2dp_open_ctrl_path)(struct a2dp_stream_common *common);
+    void (*a2dp_stream_common_init)(struct a2dp_stream_common *common);
+    int (*start_audio_datapath)(struct a2dp_stream_common *common);
+    int (*suspend_audio_datapath)(struct a2dp_stream_common *common, bool standby);
+    int (*stop_audio_datapath)(struct a2dp_stream_common *common);
+    int (*check_a2dp_stream_started)(struct a2dp_stream_common *common);
+    int (*check_a2dp_ready)(struct a2dp_stream_common *common);
+    int (*a2dp_read_audio_config)(struct a2dp_stream_common *common);
+    int (*skt_read)(int fd,void *buf, size_t bytes);
+    int (*skt_write)(int fd,const void *buf, size_t bytes);
+    void (*skt_disconnect)(int fd);
+    int (*a2dp_command)(struct a2dp_stream_common *common,char cmd);
+    int (*audio_stream_open)(void);
+    int (*audio_stream_close)(void);
+    int (*audio_start_stream)(void);
+    int (*audio_stop_stream)(void);
+    int (*audio_suspend_stream)(void);
+    void* (*audio_get_codec_config)(uint8_t *mcast, uint8_t *num_dev, audio_format_t *codec_type);
+    void (*audio_handoff_triggered)(void);
+    void (*clear_a2dpsuspend_flag)(void);
+    void*(*audio_get_next_codec_config)(uint8_t idx, audio_format_t *codec_type);
+
+} bt_host_ipc_interface_t;
+#endif