]> granicus.if.org Git - esp-idf/commitdiff
component/bt: transport btif_avk module
authorwangmengyang <wangmengyang@espressif.com>
Wed, 1 Mar 2017 05:49:26 +0000 (13:49 +0800)
committerwangmengyang <wangmengyang@espressif.com>
Wed, 1 Mar 2017 05:49:26 +0000 (13:49 +0800)
components/bt/bluedroid/btc/core/btc_dm.c
components/bt/bluedroid/btc/core/btc_task.c
components/bt/bluedroid/btc/include/btc_task.h
components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c [new file with mode: 0644]
components/bt/bluedroid/btc/profile/std/include/btc_av.h [new file with mode: 0644]
components/bt/bluedroid/btif/btif_avk.c
components/bt/component.mk

index 2bfebe1eebe8752cb794cd8ca90f444bc4db4db0..e1274c46013be1dc9603011c6c7b7bbe70fa5a3e 100644 (file)
@@ -24,8 +24,8 @@ static tBTA_SERVICE_MASK btc_enabled_services = 0;
 /******************************************************************************
 **  Externs
 ******************************************************************************/
-extern bt_status_t btif_av_execute_service(BOOLEAN b_enable);
-extern bt_status_t btif_av_sink_execute_service(BOOLEAN b_enable);
+extern bt_status_t btc_av_execute_service(BOOLEAN b_enable);
+extern bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable);
 
 /******************************************************************************
 **  Functions
@@ -190,10 +190,10 @@ static bt_status_t btc_in_execute_service_request(tBTA_SERVICE_ID service_id,
     /* Check the service_ID and invoke the profile's BT state changed API */
     switch (service_id) {
     case BTA_A2DP_SOURCE_SERVICE_ID:
-        btif_av_execute_service(b_enable);
+        btc_av_execute_service(b_enable);
         break;
     case BTA_A2DP_SINK_SERVICE_ID:
-        btif_av_sink_execute_service(b_enable);
+        btc_av_sink_execute_service(b_enable);
         break;
     default:
         LOG_ERROR("%s: Unknown service being enabled\n", __FUNCTION__);
index 58d1d0e5d3c570e6ca6a6349f1cfa2a13abe3bbb..91b69a0a33e15b98d04d2017972099e0954227fb 100644 (file)
@@ -27,6 +27,7 @@
 #include "btc_blufi_prf.h"
 #include "btc_dm.h"
 #include "btc_profile_queue.h"
+#include "btc_av.h"
 #include "bta_gatt_api.h"
 
 
@@ -46,7 +47,8 @@ static btc_func_t profile_tab[BTC_PID_NUM] = {
     [BTC_PID_SPPLIKE]   = {NULL, NULL},
     [BTC_PID_BLUFI]     = {btc_blufi_call_handler,      btc_blufi_cb_handler    },
     [BTC_PID_DM_SEC]    = {NULL,                        btc_dm_sec_cb_handler   },
-    [BTC_PID_PRF_QUE]   = {btc_profile_queue_handler,   NULL}
+    [BTC_PID_PRF_QUE]   = {btc_profile_queue_handler,   NULL                    },
+    [BTC_PID_A2DP]      = {btc_a2dp_evt_handler,        btc_a2dp_evt_handler    }
 };
 
 /*****************************************************************************
index 5e6dbce2e76ab6f84837cedda8a548dd61b81d85..37d68438b0f881078d92a60c10e92b5021ef991a 100644 (file)
@@ -47,6 +47,7 @@ typedef enum {
     BTC_PID_BLUFI,
     BTC_PID_DM_SEC,
     BTC_PID_PRF_QUE,
+    BTC_PID_A2DP,
     BTC_PID_NUM,
 } btc_pid_t; //btc profile id
 
diff --git a/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c
new file mode 100644 (file)
index 0000000..deb671b
--- /dev/null
@@ -0,0 +1,1279 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// 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:      btc_avk.c
+ *
+ *  Description:   AV implementation
+ *
+ *****************************************************************************/
+
+#include "bt_trace.h"
+#include <string.h>
+
+#include "bt_defs.h"
+#include "esp_bt_defs.h"
+#include "esp_a2dp_api.h"
+#include "allocator.h"
+
+#include "btc_av.h"
+#include "btif_util.h"
+#include "btc_profile_queue.h"
+#include "bta_api.h"
+#include "btif_media.h"
+#include "bta_av_api.h"
+#include "gki.h"
+#include "btu.h"
+#include "bt_utils.h"
+#include "btc_common.h"
+
+/*****************************************************************************
+**  Constants & Macros
+******************************************************************************/
+#define BTC_AV_SERVICE_NAME "Advanced Audio"
+
+#define BTC_TIMEOUT_AV_OPEN_ON_RC_SECS  2
+
+typedef enum {
+    BTC_AV_STATE_IDLE = 0x0,
+    BTC_AV_STATE_OPENING,
+    BTC_AV_STATE_OPENED,
+    BTC_AV_STATE_STARTED,
+    BTC_AV_STATE_CLOSING
+} btc_av_state_t;
+
+/* Should not need dedicated suspend state as actual actions are no
+   different than open state. Suspend flags are needed however to prevent
+   media task from trying to restart stream during remote suspend or while
+   we are in the process of a local suspend */
+
+#define BTC_AV_FLAG_LOCAL_SUSPEND_PENDING 0x1
+#define BTC_AV_FLAG_REMOTE_SUSPEND        0x2
+#define BTC_AV_FLAG_PENDING_START         0x4
+#define BTC_AV_FLAG_PENDING_STOP          0x8
+
+/*****************************************************************************
+**  Local type definitions
+******************************************************************************/
+
+typedef struct {
+    tBTA_AV_HNDL bta_handle;
+    bt_bdaddr_t peer_bda;
+    btif_sm_handle_t sm_handle;
+    UINT8 flags;
+    tBTA_AV_EDR edr;
+    UINT8   peer_sep;  /* sep type of peer device */
+} btc_av_cb_t;
+
+typedef struct {
+    bt_bdaddr_t *target_bda;
+    uint16_t uuid;
+} btc_av_connect_req_t;
+
+/*****************************************************************************
+**  Static variables
+******************************************************************************/
+
+static esp_a2d_cb_t bt_av_sink_callback = NULL;
+
+static btc_av_cb_t btc_av_cb = {0};
+static TIMER_LIST_ENT tle_av_open_on_rc;
+
+// TODO: need protection against race
+#define BTIF_A2D_CB_TO_APP(_event, _param)    do { \
+       if (bt_av_sink_callback) { \
+           bt_av_sink_callback(_event, _param); \
+       } \
+    } while (0)
+
+/* both interface and media task needs to be ready to alloc incoming request */
+#define CHECK_BTAV_INIT() if (btc_av_cb.sm_handle == NULL)\
+{\
+     LOG_WARN("%s: BTAV not initialized\n", __FUNCTION__);\
+     return ESP_ERR_INVALID_STATE;\
+}\
+else\
+{\
+     LOG_INFO("%s\n", __FUNCTION__);\
+}
+
+/* Helper macro to avoid code duplication in the state machine handlers */
+#define CHECK_RC_EVENT(e, d) \
+    case BTA_AV_RC_OPEN_EVT: \
+    case BTA_AV_RC_CLOSE_EVT: \
+    case BTA_AV_REMOTE_CMD_EVT: \
+    case BTA_AV_VENDOR_CMD_EVT: \
+    case BTA_AV_META_MSG_EVT: \
+    case BTA_AV_RC_FEAT_EVT: \
+    case BTA_AV_REMOTE_RSP_EVT: \
+    { \
+         btif_rc_handler(e, d);\
+    }break; \
+
+static BOOLEAN btc_av_state_idle_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btc_av_state_opening_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btc_av_state_opened_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btc_av_state_started_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btc_av_state_closing_handler(btif_sm_event_t event, void *data);
+
+static const btif_sm_handler_t btc_av_state_handlers[] = {
+    btc_av_state_idle_handler,
+    btc_av_state_opening_handler,
+    btc_av_state_opened_handler,
+    btc_av_state_started_handler,
+    btc_av_state_closing_handler
+};
+
+static void btc_av_event_free_data(btif_sm_event_t event, void *p_data);
+
+/*************************************************************************
+** Extern functions
+*************************************************************************/
+extern void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data);
+extern BOOLEAN btif_rc_get_connected_peer(BD_ADDR peer_addr);
+extern void btif_rc_check_handle_pending_play (BD_ADDR peer_addr, BOOLEAN bSendToApp);
+
+extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
+/*****************************************************************************
+** Local helper functions
+******************************************************************************/
+
+static const char *dump_av_sm_state_name(btc_av_state_t state)
+{
+    switch (state) {
+        CASE_RETURN_STR(BTC_AV_STATE_IDLE)
+        CASE_RETURN_STR(BTC_AV_STATE_OPENING)
+        CASE_RETURN_STR(BTC_AV_STATE_OPENED)
+        CASE_RETURN_STR(BTC_AV_STATE_STARTED)
+        CASE_RETURN_STR(BTC_AV_STATE_CLOSING)
+    default: return "UNKNOWN_STATE";
+    }
+}
+
+static const char *dump_av_sm_event_name(btc_av_sm_event_t event)
+{
+    switch ((int)event) {
+        CASE_RETURN_STR(BTA_AV_ENABLE_EVT)
+        CASE_RETURN_STR(BTA_AV_REGISTER_EVT)
+        CASE_RETURN_STR(BTA_AV_OPEN_EVT)
+        CASE_RETURN_STR(BTA_AV_CLOSE_EVT)
+        CASE_RETURN_STR(BTA_AV_START_EVT)
+        CASE_RETURN_STR(BTA_AV_STOP_EVT)
+        CASE_RETURN_STR(BTA_AV_PROTECT_REQ_EVT)
+        CASE_RETURN_STR(BTA_AV_PROTECT_RSP_EVT)
+        CASE_RETURN_STR(BTA_AV_RC_OPEN_EVT)
+        CASE_RETURN_STR(BTA_AV_RC_CLOSE_EVT)
+        CASE_RETURN_STR(BTA_AV_REMOTE_CMD_EVT)
+        CASE_RETURN_STR(BTA_AV_REMOTE_RSP_EVT)
+        CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT)
+        CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT)
+        CASE_RETURN_STR(BTA_AV_RECONFIG_EVT)
+        CASE_RETURN_STR(BTA_AV_SUSPEND_EVT)
+        CASE_RETURN_STR(BTA_AV_PENDING_EVT)
+        CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
+        CASE_RETURN_STR(BTA_AV_REJECT_EVT)
+        CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
+        CASE_RETURN_STR(BTIF_SM_ENTER_EVT)
+        CASE_RETURN_STR(BTIF_SM_EXIT_EVT)
+        CASE_RETURN_STR(BTC_AV_CONNECT_REQ_EVT)
+        CASE_RETURN_STR(BTC_AV_DISCONNECT_REQ_EVT)
+        CASE_RETURN_STR(BTC_AV_START_STREAM_REQ_EVT)
+        CASE_RETURN_STR(BTC_AV_STOP_STREAM_REQ_EVT)
+        CASE_RETURN_STR(BTC_AV_SUSPEND_STREAM_REQ_EVT)
+        CASE_RETURN_STR(BTC_AV_SINK_CONFIG_REQ_EVT)
+    default: return "UNKNOWN_EVENT";
+    }
+}
+
+/****************************************************************************
+**  Local helper functions
+*****************************************************************************/
+/*******************************************************************************
+**
+** Function         btc_initiate_av_open_tmr_hdlr
+**
+** Description      Timer to trigger AV open if the remote headset establishes
+**                  RC connection w/o AV connection. The timer is needed to IOP
+**                  with headsets that do establish AV after RC connection.
+**
+** Returns          void
+**
+*******************************************************************************/
+static void btc_initiate_av_open_tmr_hdlr(TIMER_LIST_ENT *tle)
+{
+    BD_ADDR peer_addr;
+    UNUSED(tle);
+    btc_av_connect_req_t connect_req;
+    UNUSED(tle);
+    /* is there at least one RC connection - There should be */
+    if (btif_rc_get_connected_peer(peer_addr)) {
+        LOG_DEBUG("%s Issuing connect to the remote RC peer", __FUNCTION__);
+        /* In case of AVRCP connection request, we will initiate SRC connection */
+        connect_req.target_bda = (bt_bdaddr_t *)&peer_addr;
+        connect_req.uuid = UUID_SERVCLASS_AUDIO_SOURCE;
+        btif_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_CONNECT_REQ_EVT, (char *)&connect_req);
+    } else {
+        LOG_ERROR("%s No connected RC peers", __FUNCTION__);
+    }
+}
+
+/*****************************************************************************
+**  Static functions
+******************************************************************************/
+static void btc_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr, int disc_rsn)
+{
+    esp_a2d_cb_param_t param;
+    memset(&param, 0, sizeof(esp_a2d_cb_param_t));
+    
+    param.conn_stat.state = state;
+    if (bd_addr) {
+        memcpy(param.conn_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
+    }
+    if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
+        param.conn_stat.disc_rsn = (disc_rsn == 0) ? ESP_A2D_DISC_RSN_NORMAL :
+            ESP_A2D_DISC_RSN_ABNORMAL;
+    }
+    BTIF_A2D_CB_TO_APP(ESP_A2D_CONNECTION_STATE_EVT, &param);
+}
+
+static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_addr)
+{
+    esp_a2d_cb_param_t param;
+    memset(&param, 0, sizeof(esp_a2d_cb_param_t));
+
+    param.audio_stat.state = state;
+    if (bd_addr) {
+        memcpy(param.audio_stat.remote_bda, bd_addr, sizeof(esp_bd_addr_t));
+    }
+    BTIF_A2D_CB_TO_APP(ESP_A2D_AUDIO_STATE_EVT, &param);
+}
+
+/*****************************************************************************
+**
+** Function     btc_av_state_idle_handler
+**
+** Description  State managing disconnected AV link
+**
+** Returns      TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btc_av_state_idle_handler(btif_sm_event_t event, void *p_data)
+{
+    LOG_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+                     dump_av_sm_event_name(event), btc_av_cb.flags);
+
+    switch (event) {
+    case BTIF_SM_ENTER_EVT:
+        /* clear the peer_bda */
+        memset(&btc_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t));
+        btc_av_cb.flags = 0;
+        btc_av_cb.edr = 0;
+        btif_a2dp_on_idle();
+        break;
+
+    case BTIF_SM_EXIT_EVT:
+        break;
+
+    case BTA_AV_ENABLE_EVT:
+        break;
+
+    case BTA_AV_REGISTER_EVT:
+        btc_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl;
+        break;
+
+    case BTA_AV_PENDING_EVT:
+    case BTC_AV_CONNECT_REQ_EVT: {
+        if (event == BTC_AV_CONNECT_REQ_EVT) {
+            memcpy(&btc_av_cb.peer_bda, ((btc_av_connect_req_t *)p_data)->target_bda,
+                   sizeof(bt_bdaddr_t));
+            BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle,
+                       TRUE, BTA_SEC_AUTHENTICATE, ((btc_av_connect_req_t *)p_data)->uuid);
+        } else if (event == BTA_AV_PENDING_EVT) {
+            bdcpy(btc_av_cb.peer_bda.address, ((tBTA_AV *)p_data)->pend.bd_addr);
+            BTA_AvOpen(btc_av_cb.peer_bda.address, btc_av_cb.bta_handle,
+                       TRUE, BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SOURCE);
+        }
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENING);
+    } break;
+
+    case BTA_AV_RC_OPEN_EVT:
+        /* IOP_FIX: Jabra 620 only does RC open without AV open whenever it connects. So
+         * as per the AV WP, an AVRC connection cannot exist without an AV connection. Therefore,
+         * we initiate an AV connection if an RC_OPEN_EVT is received when we are in AV_CLOSED state.
+         * We initiate the AV connection after a small 3s timeout to avoid any collisions from the
+         * headsets, as some headsets initiate the AVRC connection first and then
+         * immediately initiate the AV connection
+         *
+         * TODO: We may need to do this only on an AVRCP Play. FixMe
+         */
+
+        LOG_DEBUG("BTA_AV_RC_OPEN_EVT received w/o AV");
+        memset(&tle_av_open_on_rc, 0, sizeof(tle_av_open_on_rc));
+        tle_av_open_on_rc.param = (UINT32)btc_initiate_av_open_tmr_hdlr;
+        btu_start_timer(&tle_av_open_on_rc, BTU_TTYPE_USER_FUNC,
+                        BTC_TIMEOUT_AV_OPEN_ON_RC_SECS);
+        btif_rc_handler(event, p_data);
+        break;
+
+    case BTA_AV_REMOTE_CMD_EVT:
+    case BTA_AV_VENDOR_CMD_EVT:
+    case BTA_AV_META_MSG_EVT:
+    case BTA_AV_RC_FEAT_EVT:
+    case BTA_AV_REMOTE_RSP_EVT:
+        btif_rc_handler(event, (tBTA_AV *)p_data);
+        break;
+
+    case BTA_AV_RC_CLOSE_EVT:
+        if (tle_av_open_on_rc.in_use) {
+            LOG_DEBUG("BTA_AV_RC_CLOSE_EVT: Stopping AV timer.");
+            btu_stop_timer(&tle_av_open_on_rc);
+        }
+        btif_rc_handler(event, p_data);
+        break;
+
+    default:
+        LOG_WARN("%s : unhandled event:%s\n", __FUNCTION__,
+                           dump_av_sm_event_name(event));
+        return FALSE;
+
+    }
+
+    return TRUE;
+}
+/*****************************************************************************
+**
+** Function        btc_av_state_opening_handler
+**
+** Description     Intermediate state managing events during establishment
+**                 of avdtp channel
+**
+** Returns         TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btc_av_state_opening_handler(btif_sm_event_t event, void *p_data)
+{
+    LOG_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+                     dump_av_sm_event_name(event), btc_av_cb.flags);
+
+    switch (event) {
+    case BTIF_SM_ENTER_EVT:
+        /* inform the application that we are entering connecting state */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btc_av_cb.peer_bda), 0);
+        break;
+
+    case BTIF_SM_EXIT_EVT:
+        break;
+
+    case BTA_AV_REJECT_EVT:
+        LOG_DEBUG(" Received  BTA_AV_REJECT_EVT \n");
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda), 0);
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
+        break;
+
+    case BTA_AV_OPEN_EVT: {
+        tBTA_AV *p_bta_data = (tBTA_AV *)p_data;
+        esp_a2d_connection_state_t state;
+        btif_sm_state_t av_state;
+        LOG_DEBUG("status:%d, edr 0x%x\n", p_bta_data->open.status,
+                         p_bta_data->open.edr);
+
+        if (p_bta_data->open.status == BTA_AV_SUCCESS) {
+            state = ESP_A2D_CONNECTION_STATE_CONNECTED;
+            av_state = BTC_AV_STATE_OPENED;
+            btc_av_cb.edr = p_bta_data->open.edr;
+
+            btc_av_cb.peer_sep = p_bta_data->open.sep;
+            btif_a2dp_set_peer_sep(p_bta_data->open.sep);
+        } else {
+            LOG_WARN("BTA_AV_OPEN_EVT::FAILED status: %d\n",
+                               p_bta_data->open.status );
+            state = ESP_A2D_CONNECTION_STATE_DISCONNECTED;
+            av_state  = BTC_AV_STATE_IDLE;
+        }
+
+        /* inform the application of the event */
+        btc_report_connection_state(state, &(btc_av_cb.peer_bda), 0);
+        /* change state to open/idle based on the status */
+        btif_sm_change_state(btc_av_cb.sm_handle, av_state);
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SNK) {
+            /* if queued PLAY command,  send it now */
+            btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr,
+                                              (p_bta_data->open.status == BTA_AV_SUCCESS));
+        } else if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            /* if queued PLAY command,  send it now */
+            btif_rc_check_handle_pending_play(p_bta_data->open.bd_addr, FALSE);
+            /* Bring up AVRCP connection too */
+            BTA_AvOpenRc(btc_av_cb.bta_handle);
+        }
+        btc_queue_advance();
+    } break;
+
+    case BTC_AV_SINK_CONFIG_REQ_EVT: {
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            esp_a2d_cb_param_t param;
+            memcpy(param.audio_cfg.remote_bda, &btc_av_cb.peer_bda, sizeof(esp_bd_addr_t));
+            memcpy(&param.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t));
+            BTIF_A2D_CB_TO_APP(ESP_A2D_AUDIO_CFG_EVT, &param);
+        }
+    } break;
+
+    case BTC_AV_CONNECT_REQ_EVT:
+        // Check for device, if same device which moved to opening then ignore callback
+        if (memcmp ((bt_bdaddr_t *)p_data, &(btc_av_cb.peer_bda),
+                    sizeof(btc_av_cb.peer_bda)) == 0) {
+            LOG_DEBUG("%s: Same device moved to Opening state,ignore Connect Req\n", __func__);
+            btc_queue_advance();
+            break;
+        } else {
+            LOG_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__);
+            btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data, 0);
+            btc_queue_advance();
+            break;
+        }
+
+    case BTA_AV_PENDING_EVT:
+        // Check for device, if same device which moved to opening then ignore callback
+        if (memcmp (((tBTA_AV *)p_data)->pend.bd_addr, &(btc_av_cb.peer_bda),
+                    sizeof(btc_av_cb.peer_bda)) == 0) {
+            LOG_DEBUG("%s: Same device moved to Opening state,ignore Pending Req\n", __func__);
+            break;
+        } else {
+            LOG_DEBUG("%s: Moved from idle by outgoing Connection request\n", __func__);
+            BTA_AvDisconnect(((tBTA_AV *)p_data)->pend.bd_addr);
+            break;
+        }
+
+        CHECK_RC_EVENT(event, p_data);
+
+    default:
+        LOG_WARN("%s : unhandled event:%s\n", __FUNCTION__,
+                           dump_av_sm_event_name(event));
+        return FALSE;
+
+    }
+    return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function        btc_av_state_closing_handler
+**
+** Description     Intermediate state managing events during closing
+**                 of avdtp channel
+**
+** Returns         TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btc_av_state_closing_handler(btif_sm_event_t event, void *p_data)
+{
+    LOG_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+                     dump_av_sm_event_name(event), btc_av_cb.flags);
+
+    switch (event) {
+    case BTIF_SM_ENTER_EVT:
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            btif_a2dp_set_rx_flush(TRUE);
+        }
+        break;
+
+    case BTA_AV_STOP_EVT:
+    case BTC_AV_STOP_STREAM_REQ_EVT:
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            btif_a2dp_set_rx_flush(TRUE);
+        }
+
+        btif_a2dp_on_stopped(NULL);
+        break;
+
+    case BTIF_SM_EXIT_EVT:
+        break;
+
+    case BTA_AV_CLOSE_EVT: {
+        tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
+        /* inform the application that we are disconnecting */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
+                                     close->disc_rsn);
+
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
+        break;
+    }
+
+    /* Handle the RC_CLOSE event for the cleanup */
+    case BTA_AV_RC_CLOSE_EVT:
+        btif_rc_handler(event, (tBTA_AV *)p_data);
+        break;
+
+    default:
+        LOG_WARN("%s : unhandled event:%s\n", __FUNCTION__,
+                           dump_av_sm_event_name(event));
+        return FALSE;
+    }
+    return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function     btc_av_state_opened_handler
+**
+** Description  Handles AV events while AVDTP is in OPEN state
+**
+** Returns      TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btc_av_state_opened_handler(btif_sm_event_t event, void *p_data)
+{
+    tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+    LOG_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+                     dump_av_sm_event_name(event), btc_av_cb.flags);
+
+    if ( (event == BTA_AV_REMOTE_CMD_EVT) && (btc_av_cb.flags & BTC_AV_FLAG_REMOTE_SUSPEND) &&
+            (p_av->remote_cmd.rc_id == BTA_AV_RC_PLAY) ) {
+        LOG_INFO("%s: Resetting remote suspend flag on RC PLAY\n", __FUNCTION__);
+        btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND;
+    }
+
+    switch (event) {
+    case BTIF_SM_ENTER_EVT:
+        btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_STOP;
+        btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START;
+        break;
+
+    case BTIF_SM_EXIT_EVT:
+        btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START;
+        break;
+
+    case BTC_AV_START_STREAM_REQ_EVT:
+        BTA_AvStart();
+        btc_av_cb.flags |= BTC_AV_FLAG_PENDING_START;
+        break;
+
+    case BTA_AV_START_EVT: {
+        LOG_INFO("BTA_AV_START_EVT status %d, suspending %d, init %d\n",
+                         p_av->start.status, p_av->start.suspending, p_av->start.initiator);
+
+        if ((p_av->start.status == BTA_SUCCESS) && (p_av->start.suspending == TRUE)) {
+            return TRUE;
+        }
+
+        /* remain in open state if status failed */
+        if (p_av->start.status != BTA_AV_SUCCESS) {
+            return FALSE;
+        }
+
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            btif_a2dp_set_rx_flush(FALSE); /*  remove flush state, ready for streaming*/
+        }
+
+        /* change state to started, send acknowledgement if start is pending */
+        if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) {
+
+            /* pending start flag will be cleared when exit current state */
+        }
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_STARTED);
+
+    } break;
+
+    case BTC_AV_DISCONNECT_REQ_EVT:
+        BTA_AvClose(btc_av_cb.bta_handle);
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            BTA_AvCloseRc(btc_av_cb.bta_handle);
+        }
+
+        /* inform the application that we are disconnecting */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
+        break;
+
+    case BTA_AV_CLOSE_EVT: {
+        /* avdtp link is closed */
+        btif_a2dp_on_stopped(NULL);
+
+        tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
+        /* inform the application that we are disconnected */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
+                                     close->disc_rsn);
+
+        /* change state to idle, send acknowledgement if start is pending */
+        if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) {
+            /* pending start flag will be cleared when exit current state */
+        }
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
+        break;
+    }
+
+    case BTA_AV_RECONFIG_EVT:
+        if ((btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) &&
+                (p_av->reconfig.status == BTA_AV_SUCCESS)) {
+            APPL_TRACE_WARNING("reconfig done BTA_AVstart()\n");
+            BTA_AvStart();
+        } else if (btc_av_cb.flags & BTC_AV_FLAG_PENDING_START) {
+            btc_av_cb.flags &= ~BTC_AV_FLAG_PENDING_START;
+        }
+        break;
+
+    case BTC_AV_CONNECT_REQ_EVT:
+        if (memcmp ((bt_bdaddr_t *)p_data, &(btc_av_cb.peer_bda),
+                    sizeof(btc_av_cb.peer_bda)) == 0) {
+            LOG_DEBUG("%s: Ignore BTC_AVCONNECT_REQ_EVT for same device\n", __func__);
+        } else {
+            LOG_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__);
+            btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED,
+                                         (bt_bdaddr_t *)p_data, ESP_A2D_DISC_RSN_NORMAL);
+        }
+        btc_queue_advance();
+        break;
+
+        CHECK_RC_EVENT(event, p_data);
+
+    default:
+        LOG_WARN("%s : unhandled event:%s\n", __FUNCTION__,
+                           dump_av_sm_event_name(event));
+        return FALSE;
+
+    }
+    return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function     btc_av_state_started_handler
+**
+** Description  Handles AV events while A2DP stream is started
+**
+** Returns      TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btc_av_state_started_handler(btif_sm_event_t event, void *p_data)
+{
+    tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+    LOG_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+                     dump_av_sm_event_name(event), btc_av_cb.flags);
+
+    switch (event) {
+    case BTIF_SM_ENTER_EVT:
+
+        /* we are again in started state, clear any remote suspend flags */
+        btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND;
+
+        btc_report_audio_state(ESP_A2D_AUDIO_STATE_STARTED, &(btc_av_cb.peer_bda));
+
+        /* increase the a2dp consumer task priority temporarily when start
+        ** audio playing, to avoid overflow the audio packet queue. */
+        adjust_priority_a2dp(TRUE);
+
+        break;
+
+    case BTIF_SM_EXIT_EVT:
+        /* restore the a2dp consumer task priority when stop audio playing. */
+        adjust_priority_a2dp(FALSE);
+
+        break;
+
+    case BTC_AV_START_STREAM_REQ_EVT:
+        break;
+
+    /* fixme -- use suspend = true always to work around issue with BTA AV */
+    case BTC_AV_STOP_STREAM_REQ_EVT:
+    case BTC_AV_SUSPEND_STREAM_REQ_EVT:
+
+        /* set pending flag to ensure btif task is not trying to restart
+           stream while suspend is in progress */
+        btc_av_cb.flags |= BTC_AV_FLAG_LOCAL_SUSPEND_PENDING;
+
+        /* if we were remotely suspended but suspend locally, local suspend
+           always overrides */
+        btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND;
+
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            btif_a2dp_set_rx_flush(TRUE);
+            btif_a2dp_on_stopped(NULL);
+        }
+
+        BTA_AvStop(TRUE);
+        break;
+
+    case BTC_AV_DISCONNECT_REQ_EVT:
+
+        /* request avdtp to close */
+        BTA_AvClose(btc_av_cb.bta_handle);
+        if (btc_av_cb.peer_sep == AVDT_TSEP_SRC) {
+            BTA_AvCloseRc(btc_av_cb.bta_handle);
+        }
+
+        /* inform the application that we are disconnecting */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btc_av_cb.peer_bda), 0);
+
+        /* wait in closing state until fully closed */
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_CLOSING);
+        break;
+
+    case BTA_AV_SUSPEND_EVT:
+
+        LOG_INFO("BTA_AV_SUSPEND_EVT status %d, init %d\n",
+                         p_av->suspend.status, p_av->suspend.initiator);
+
+        /* a2dp suspended, stop media task until resumed */
+        btif_a2dp_on_suspended(&p_av->suspend);
+
+        /* if not successful, remain in current state */
+        if (p_av->suspend.status != BTA_AV_SUCCESS) {
+            btc_av_cb.flags &= ~BTC_AV_FLAG_LOCAL_SUSPEND_PENDING;
+
+            return FALSE;
+        }
+
+        if (p_av->suspend.initiator != TRUE) {
+            /* remote suspend, notify HAL and await audioflinger to
+               suspend/stop stream */
+
+            /* set remote suspend flag to block media task from restarting
+               stream only if we did not already initiate a local suspend */
+            if ((btc_av_cb.flags & BTC_AV_FLAG_LOCAL_SUSPEND_PENDING) == 0) {
+                btc_av_cb.flags |= BTC_AV_FLAG_REMOTE_SUSPEND;
+            }
+
+            btc_report_audio_state(ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND, &(btc_av_cb.peer_bda));
+        } else {
+            btc_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btc_av_cb.peer_bda));
+        }
+
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENED);
+
+        /* suspend completed and state changed, clear pending status */
+        btc_av_cb.flags &= ~BTC_AV_FLAG_LOCAL_SUSPEND_PENDING;
+        break;
+
+    case BTA_AV_STOP_EVT:
+
+        btc_av_cb.flags |= BTC_AV_FLAG_PENDING_STOP;
+        btif_a2dp_on_stopped(&p_av->suspend);
+
+        btc_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btc_av_cb.peer_bda));
+
+        /* if stop was successful, change state to open */
+        if (p_av->suspend.status == BTA_AV_SUCCESS) {
+            btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_OPENED);
+        }
+
+        break;
+
+    case BTA_AV_CLOSE_EVT: {
+
+        btc_av_cb.flags |= BTC_AV_FLAG_PENDING_STOP;
+
+        /* avdtp link is closed */
+        btif_a2dp_on_stopped(NULL);
+        
+        tBTA_AV_CLOSE *close = (tBTA_AV_CLOSE *)p_data;
+        /* inform the application that we are disconnected */
+        btc_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btc_av_cb.peer_bda),
+                                     close->disc_rsn);
+
+        btif_sm_change_state(btc_av_cb.sm_handle, BTC_AV_STATE_IDLE);
+        break;
+    }
+    
+    CHECK_RC_EVENT(event, p_data);
+
+    default:
+        LOG_WARN("%s : unhandled event:%s\n", __FUNCTION__,
+                           dump_av_sm_event_name(event));
+        return FALSE;
+
+    }
+    return TRUE;
+}
+
+/*****************************************************************************
+**  Local event handlers
+******************************************************************************/
+
+void btc_av_event_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
+{
+    tBTA_AV *av_src = (tBTA_AV *)p_src;
+    tBTA_AV *av_dest = (tBTA_AV *)p_dest;
+
+    // First copy the structure
+    memcpy(p_dest, p_src, sizeof(tBTA_AV));
+
+    switch (msg->act) {
+    case BTA_AV_META_MSG_EVT:
+        if (av_src->meta_msg.p_data && av_src->meta_msg.len) {
+            av_dest->meta_msg.p_data = osi_calloc(av_src->meta_msg.len);
+            assert(av_dest->meta_msg.p_data);
+            memcpy(av_dest->meta_msg.p_data, av_src->meta_msg.p_data, av_src->meta_msg.len);
+        }
+
+        if (av_src->meta_msg.p_msg) {
+            av_dest->meta_msg.p_msg = osi_calloc(sizeof(tAVRC_MSG));
+            assert(av_dest->meta_msg.p_msg);
+            memcpy(av_dest->meta_msg.p_msg, av_src->meta_msg.p_msg, sizeof(tAVRC_MSG));
+
+            if (av_src->meta_msg.p_msg->vendor.p_vendor_data &&
+                    av_src->meta_msg.p_msg->vendor.vendor_len) {
+                av_dest->meta_msg.p_msg->vendor.p_vendor_data = osi_calloc(
+                            av_src->meta_msg.p_msg->vendor.vendor_len);
+                assert(av_dest->meta_msg.p_msg->vendor.p_vendor_data);
+                memcpy(av_dest->meta_msg.p_msg->vendor.p_vendor_data,
+                       av_src->meta_msg.p_msg->vendor.p_vendor_data,
+                       av_src->meta_msg.p_msg->vendor.vendor_len);
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void btc_av_event_free_data(btif_sm_event_t event, void *p_data)
+{
+    switch (event) {
+    case BTA_AV_META_MSG_EVT: {
+        tBTA_AV *av = (tBTA_AV *)p_data;
+        if (av->meta_msg.p_data) {
+            osi_free(av->meta_msg.p_data);
+        }
+
+        if (av->meta_msg.p_msg) {
+            if (av->meta_msg.p_msg->vendor.p_vendor_data) {
+                osi_free(av->meta_msg.p_msg->vendor.p_vendor_data);
+            }
+            osi_free(av->meta_msg.p_msg);
+        }
+    }
+    break;
+
+    default:
+        break;
+    }
+}
+
+static void bte_av_callback(tBTA_AV_EVT event, tBTA_AV *p_data)
+{
+    bt_status_t stat;
+    btc_msg_t msg;
+
+    msg.sig = BTC_SIG_API_CB;
+    msg.pid = BTC_PID_A2DP;
+    msg.act = (uint8_t) event;
+    stat = btc_transfer_context(&msg, (btc_av_args_t *)p_data, sizeof(tBTA_AV), btc_av_event_deep_copy);
+
+    if (stat) {
+        LOG_ERROR("%s transfer failed\n", __func__);
+    }
+}
+
+static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
+{
+    btif_sm_state_t state;
+    UINT8 que_len;
+    tA2D_STATUS a2d_status;
+    tA2D_SBC_CIE sbc_cie;
+
+    if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTIF_MEDIA context */
+        state = btif_sm_get_state(btc_av_cb.sm_handle);
+        if ( (state == BTC_AV_STATE_STARTED) || /* send SBC packets only in Started State */
+                (state == BTC_AV_STATE_OPENED) ) {
+            que_len = btif_media_sink_enque_buf((BT_HDR *)p_data);
+            LOG_DEBUG(" Packets in Que %d\n", que_len);
+        } else {
+            return;
+        }
+    }
+
+    if (event == BTA_AV_MEDIA_SINK_CFG_EVT) {
+        /* send a command to BT Media Task */
+        btif_reset_decoder((UINT8 *)p_data);
+
+       /* currently only supportes SBC */
+        a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
+        if (a2d_status == A2D_SUCCESS) {
+            btc_msg_t msg;
+            btc_av_args_t arg;
+            
+            msg.sig = BTC_SIG_API_CB;
+            msg.pid = BTC_PID_A2DP;
+            msg.act = BTC_AV_SINK_CONFIG_REQ_EVT;
+
+            memset(&arg, 0, sizeof(btc_av_args_t));
+            arg.mcc.type = ESP_A2D_MCT_SBC;
+            memcpy(&(arg.mcc.cie), (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC);
+            btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL);
+        } else {
+            APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+        }
+    }
+}
+/*******************************************************************************
+**
+** Function         btc_av_init
+**
+** Description      Initializes btif AV if not already done
+**
+** Returns          bt_status_t
+**
+*******************************************************************************/
+
+bt_status_t btc_av_init()
+{
+    if (btc_av_cb.sm_handle == NULL) {
+        if (!btif_a2dp_start_media_task()) {
+            return BT_STATUS_FAIL;
+        }
+
+        /* Also initialize the AV state machine */
+        btc_av_cb.sm_handle =
+            btif_sm_init((const btif_sm_handler_t *)btc_av_state_handlers, BTC_AV_STATE_IDLE);
+
+        btif_enable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+        btif_enable_service(BTA_A2DP_SINK_SERVICE_ID);
+#endif
+
+        btif_a2dp_on_init();
+    }
+
+    return BT_STATUS_SUCCESS;
+}
+
+/**
+ *
+ * Function         register A2DP callback
+ *
+ * Description      Initializes the AV interface for sink mode
+ *
+ * Returns          bt_status_t
+ *
+ */
+esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback)
+{
+    // TODO: need protection against race
+    bt_av_sink_callback = callback;
+    return ESP_OK;
+}
+
+/*******************************************************************************
+**
+** Function         init_sink
+**
+** Description      Initializes the AV interface for sink mode
+**
+** Returns          bt_status_t
+**
+*******************************************************************************/
+esp_err_t esp_a2d_sink_init(void)
+{
+    LOG_INFO("%s()\n", __func__);
+
+    bt_status_t status = btc_av_init();
+
+    return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+
+/*******************************************************************************
+**
+** Function         connect
+**
+** Description      Establishes the AV signalling channel with the remote headset
+**
+** Returns          bt_status_t
+**
+*******************************************************************************/
+
+static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
+{
+    btc_av_connect_req_t connect_req;
+    connect_req.target_bda = bd_addr;
+    connect_req.uuid = uuid;
+    LOG_INFO("%s\n", __FUNCTION__);
+
+    btif_sm_dispatch(btc_av_cb.sm_handle, BTC_AV_CONNECT_REQ_EVT, (char *)&connect_req);
+
+    return BT_STATUS_SUCCESS;
+}
+
+esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
+{
+    LOG_INFO("%s\n", __FUNCTION__);
+    CHECK_BTAV_INIT();
+
+    bt_status_t stat;
+    bt_bdaddr_t bd_addr;
+    memcpy(&bd_addr, remote_bda, sizeof(bt_bdaddr_t));
+
+    stat = btc_queue_connect(UUID_SERVCLASS_AUDIO_SINK, &bd_addr, connect_int);
+    return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+
+esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda)
+{
+    bt_status_t stat;
+    btc_av_args_t arg;
+    btc_msg_t msg;
+    
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_A2DP;
+    msg.act = BTC_AV_DISCONNECT_REQ_EVT;
+
+    memset(&arg, 0, sizeof(btc_av_args_t));
+    
+    LOG_INFO("%s\n", __FUNCTION__);
+    CHECK_BTAV_INIT();
+    memcpy(&(arg.disconnect), remote_bda, sizeof(bt_bdaddr_t));
+    /* Switch to BTIF context */
+    stat = btc_transfer_context(&msg, &arg, sizeof(btc_av_args_t), NULL);
+    return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
+}
+/*******************************************************************************
+**
+** Function         cleanup
+**
+** Description      Shuts down the AV interface and does the cleanup
+**
+** Returns          None
+**
+*******************************************************************************/
+static void cleanup(void)
+{
+    LOG_INFO("%s\n", __FUNCTION__);
+
+    btif_a2dp_stop_media_task();
+
+    btif_disable_service(BTA_A2DP_SOURCE_SERVICE_ID);
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+    btif_disable_service(BTA_A2DP_SINK_SERVICE_ID);
+#endif
+
+    /* Also shut down the AV state machine */
+    btif_sm_shutdown(btc_av_cb.sm_handle);
+    btc_av_cb.sm_handle = NULL;
+}
+
+void esp_a2d_sink_deinit(void)
+{
+    LOG_INFO("%s\n", __FUNCTION__);
+
+    if (bt_av_sink_callback) {
+        bt_av_sink_callback = NULL;
+        cleanup();
+    }
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_get_sm_handle
+**
+** Description      Fetches current av SM handle
+**
+** Returns          None
+**
+*******************************************************************************/
+
+btif_sm_handle_t btc_av_get_sm_handle(void)
+{
+    return btc_av_cb.sm_handle;
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_stream_ready
+**
+** Description      Checks whether AV is ready for starting a stream
+**
+** Returns          None
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_stream_ready(void)
+{
+    btif_sm_state_t state = btif_sm_get_state(btc_av_cb.sm_handle);
+
+    LOG_DEBUG("btc_av_stream_ready : sm hdl %d, state %d, flags %x\n",
+                     (int)btc_av_cb.sm_handle, state, btc_av_cb.flags);
+
+    /* also make sure main adapter is enabled */
+    if (btif_is_enabled() == 0) {
+        LOG_INFO("main adapter not enabled");
+        return FALSE;
+    }
+
+    /* check if we are remotely suspended or stop is pending */
+    if (btc_av_cb.flags & (BTC_AV_FLAG_REMOTE_SUSPEND | BTC_AV_FLAG_PENDING_STOP)) {
+        return FALSE;
+    }
+
+    return (state == BTC_AV_STATE_OPENED);
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_stream_started_ready
+**
+** Description      Checks whether AV ready for media start in streaming state
+**
+** Returns          None
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_stream_started_ready(void)
+{
+    btif_sm_state_t state = btif_sm_get_state(btc_av_cb.sm_handle);
+
+    LOG_DEBUG("btc_av_stream_started : sm hdl %d, state %d, flags %x\n",
+                     (int)btc_av_cb.sm_handle, state, btc_av_cb.flags);
+
+    /* disallow media task to start if we have pending actions */
+    if (btc_av_cb.flags & (BTC_AV_FLAG_LOCAL_SUSPEND_PENDING | BTC_AV_FLAG_REMOTE_SUSPEND
+                            | BTC_AV_FLAG_PENDING_STOP)) {
+        return FALSE;
+    }
+
+    return (state == BTC_AV_STATE_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function         btc_dispatch_sm_event
+**
+** Description      Send event to AV statemachine
+**
+** Returns          None
+**
+*******************************************************************************/
+
+/* used to pass events to AV statemachine from other tasks */
+void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len)
+{
+    btc_msg_t msg;
+    msg.sig = BTC_SIG_API_CALL;
+    msg.pid = BTC_PID_A2DP;
+    msg.act = event;
+    btc_transfer_context(&msg, p_data, len, NULL);
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_execute_service
+**
+** Description      Initializes/Shuts down the service
+**
+** Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btc_av_execute_service(BOOLEAN b_enable)
+{
+    if (b_enable) {
+        /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
+         * handle this request in order to allow incoming connections to succeed.
+         * We need to put this back once support for this is added */
+
+        /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
+         * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
+         * be initiated by the app/audioflinger layers */
+        BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_NO_SCO_SSPD)
+                    // | BTA_AV_FEAT_RCTG | BTA_AV_FEAT_METADATA | BTA_AV_FEAT_VENDOR
+                    | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_ADV_CTRL,
+                     bte_av_callback);
+        BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTC_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos);
+    } else {
+        BTA_AvDeregister(btc_av_cb.bta_handle);
+        BTA_AvDisable();
+    }
+    return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_sink_execute_service
+**
+** Description      Initializes/Shuts down the service
+**
+** Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btc_av_sink_execute_service(BOOLEAN b_enable)
+{
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+    BTA_AvEnable_Sink(b_enable);
+#endif
+    return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_is_connected
+**
+** Description      Checks if av has a connected sink
+**
+** Returns          BOOLEAN
+**
+*******************************************************************************/
+BOOLEAN btc_av_is_connected(void)
+{
+    btif_sm_state_t state = btif_sm_get_state(btc_av_cb.sm_handle);
+    return ((state == BTC_AV_STATE_OPENED) || (state ==  BTC_AV_STATE_STARTED));
+}
+
+/*******************************************************************************
+**
+** Function         btc_av_is_peer_edr
+**
+** Description      Check if the connected a2dp device supports
+**                  EDR or not. Only when connected this function
+**                  will accurately provide a true capability of
+**                  remote peer. If not connected it will always be false.
+**
+** Returns          TRUE if remote device is capable of EDR
+**
+*******************************************************************************/
+BOOLEAN btc_av_is_peer_edr(void)
+{
+    BTC_ASSERTC(btc_av_is_connected(), "No active a2dp connection\n", 0);
+
+    if (btc_av_cb.edr) {
+        return TRUE;
+    } else {
+        return FALSE;
+    }
+}
+
+/******************************************************************************
+**
+** Function        btc_av_clear_remote_suspend_flag
+**
+** Description     Clears btif_av_cd.flags if BTC_AV_FLAG_REMOTE_SUSPEND is set
+**
+** Returns          void
+******************************************************************************/
+void btc_av_clear_remote_suspend_flag(void)
+{
+    LOG_DEBUG("%s: flag :%x\n", __func__, btc_av_cb.flags);
+    btc_av_cb.flags &= ~BTC_AV_FLAG_REMOTE_SUSPEND;
+}
+
+void btc_a2dp_evt_handler(btc_msg_t *msg)
+{
+    btif_sm_dispatch(btc_av_cb.sm_handle, msg->act, (void *)(msg->arg));
+    btc_av_event_free_data(msg->act, msg->arg);
+}
diff --git a/components/bt/bluedroid/btc/profile/std/include/btc_av.h b/components/bt/bluedroid/btc/profile/std/include/btc_av.h
new file mode 100644 (file)
index 0000000..a693198
--- /dev/null
@@ -0,0 +1,163 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// 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:      btc_av.h
+ *
+ *  Description:   Main API header file for all BTC AV functions accessed
+ *                 from internal stack.
+ *
+ *******************************************************************************/
+
+#ifndef __BTC_AV_H__
+#define __BTC_AV_H__
+
+#include "esp_a2dp_api.h"
+#include "btc_task.h"
+#include "btif_common.h"
+#include "btif_sm.h"
+#include "bta_av_api.h"
+
+
+/*******************************************************************************
+**  Type definitions for callback functions
+********************************************************************************/
+
+typedef enum {
+    BTC_AV_CONNECT_REQ_EVT = BTA_AV_MAX_EVT,
+    BTC_AV_DISCONNECT_REQ_EVT,
+    BTC_AV_START_STREAM_REQ_EVT,
+    BTC_AV_STOP_STREAM_REQ_EVT,
+    BTC_AV_SUSPEND_STREAM_REQ_EVT,
+    BTC_AV_SINK_CONFIG_REQ_EVT
+} btc_av_sm_event_t;
+
+/* btc_av_args_t */
+typedef union {
+    // BTC_AV_SINK_CONFIG_REQ_EVT
+    esp_a2d_mcc_t mcc;
+    // BTC_AV_DISCONNECT_REQ_EVT
+    bt_bdaddr_t disconnect;
+    // Event set--tBTA_AV_EVT
+    tBTA_AV data;
+} btc_av_args_t;
+
+/*******************************************************************************
+**  BTC AV API
+********************************************************************************/
+
+void btc_a2dp_evt_handler(btc_msg_t *msg);
+
+/*******************************************************************************
+**
+** Function         btc_av_get_sm_handle
+**
+** Description      Fetches current av SM handle
+**
+** Returns          None
+**
+*******************************************************************************/
+
+btif_sm_handle_t btc_av_get_sm_handle(void);
+
+/*******************************************************************************
+**
+** Function         btc_av_stream_ready
+**
+** Description      Checks whether AV is ready for starting a stream
+**
+** Returns          None
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_stream_ready(void);
+
+/*******************************************************************************
+**
+** Function         btc_av_stream_started_ready
+**
+** Description      Checks whether AV ready for media start in streaming state
+**
+** Returns          None
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_stream_started_ready(void);
+
+/*******************************************************************************
+**
+** Function         btc_dispatch_sm_event
+**
+** Description      Send event to AV statemachine
+**
+** Returns          None
+**
+*******************************************************************************/
+
+/* used to pass events to AV statemachine from other tasks */
+void btc_dispatch_sm_event(btc_av_sm_event_t event, void *p_data, int len);
+
+/*******************************************************************************
+**
+** Function         btc_av_init
+**
+** Description      Initializes btif AV if not already done
+**
+** Returns          bt_status_t
+**
+*******************************************************************************/
+
+bt_status_t btc_av_init(void);
+
+/*******************************************************************************
+**
+** Function         btc_av_is_connected
+**
+** Description      Checks if av has a connected sink
+**
+** Returns          BOOLEAN
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_is_connected(void);
+
+
+/*******************************************************************************
+**
+** Function         btc_av_is_peer_edr
+**
+** Description      Check if the connected a2dp device supports
+**                  EDR or not. Only when connected this function
+**                  will accurately provide a true capability of
+**                  remote peer. If not connected it will always be false.
+**
+** Returns          TRUE if remote device is capable of EDR
+**
+*******************************************************************************/
+
+BOOLEAN btc_av_is_peer_edr(void);
+
+/******************************************************************************
+**
+** Function         btc_av_clear_remote_suspend_flag
+**
+** Description      Clears remote suspended flag
+**
+** Returns          Void
+********************************************************************************/
+void btc_av_clear_remote_suspend_flag(void);
+
+#endif /* __BTC_AV_H__ */
index d8cbb2a5852f8b9cd955338f0e8cd493fd40483d..e1df2ca0ba56353a1dad6b11c3cf54a568a572aa 100755 (executable)
@@ -964,13 +964,14 @@ bt_status_t btif_av_init()
  * Returns          bt_status_t
  *
  */
+#if 0
 esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback)
 {
     // TODO: need protection against race
     bt_av_sink_callback = callback;
     return ESP_OK;
 }
-
+#endif /* #if 0 */
 /*******************************************************************************
 **
 ** Function         init_sink
@@ -980,6 +981,7 @@ esp_err_t esp_a2d_register_callback(esp_a2d_cb_t callback)
 ** Returns          bt_status_t
 **
 *******************************************************************************/
+#if 0
 esp_err_t esp_a2d_sink_init(void)
 {
     BTIF_TRACE_EVENT("%s()\n", __func__);
@@ -988,7 +990,7 @@ esp_err_t esp_a2d_sink_init(void)
 
     return (status == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
 }
-
+#endif /* #endif */
 /*******************************************************************************
 **
 ** Function         connect
@@ -1011,6 +1013,7 @@ static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
     return BT_STATUS_SUCCESS;
 }
 
+#if 0
 esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
 {
     BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
@@ -1036,6 +1039,7 @@ esp_err_t esp_a2d_sink_disconnect(esp_bd_addr_t remote_bda)
                                  (char *)(&bd_addr), sizeof(bt_bdaddr_t), NULL);
     return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
 }
+#endif /* #if 0 */
 /*******************************************************************************
 **
 ** Function         cleanup
@@ -1061,6 +1065,7 @@ static void cleanup(void)
     btif_av_cb.sm_handle = NULL;
 }
 
+#if 0
 void esp_a2d_sink_deinit(void)
 {
     BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
@@ -1070,7 +1075,7 @@ void esp_a2d_sink_deinit(void)
         cleanup();
     }
 }
-
+#endif /* #if 0 */
 /*******************************************************************************
 **
 ** Function         btif_av_get_sm_handle
index 5cdcb18df234e4e81a92295c0139d02129b20c6c..eb3440bb7badaa70b44b15cf8a9779b4a9bef9ac 100644 (file)
@@ -67,6 +67,7 @@ COMPONENT_SRCDIRS :=  bluedroid/bta/dm                        \
                        bluedroid/btc/profile/esp/blufi \
                        bluedroid/btc/profile/std/gap           \
                        bluedroid/btc/profile/std/gatt          \
+                       bluedroid/btc/profile/std/a2dp          \
                        bluedroid/btc/profile                   \
                        bluedroid/stack/btm                     \
                        bluedroid/stack/btu                     \