diff --git a/components/bt/bluedroid/btc/core/btc_dm.c b/components/bt/bluedroid/btc/core/btc_dm.c index 2bfebe1ee..e1274c460 100644 --- a/components/bt/bluedroid/btc/core/btc_dm.c +++ b/components/bt/bluedroid/btc/core/btc_dm.c @@ -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__); diff --git a/components/bt/bluedroid/btc/core/btc_task.c b/components/bt/bluedroid/btc/core/btc_task.c index 58d1d0e5d..91b69a0a3 100644 --- a/components/bt/bluedroid/btc/core/btc_task.c +++ b/components/bt/bluedroid/btc/core/btc_task.c @@ -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 } }; /***************************************************************************** diff --git a/components/bt/bluedroid/btc/include/btc_task.h b/components/bt/bluedroid/btc/include/btc_task.h index 5e6dbce2e..37d68438b 100644 --- a/components/bt/bluedroid/btc/include/btc_task.h +++ b/components/bt/bluedroid/btc/include/btc_task.h @@ -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 index 000000000..deb671b97 --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/a2dp/btc_avk.c @@ -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 + +#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(¶m, 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, ¶m); +} + +static void btc_report_audio_state(esp_a2d_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + esp_a2d_cb_param_t param; + memset(¶m, 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, ¶m); +} + +/***************************************************************************** +** +** 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(¶m.audio_cfg.mcc, p_data, sizeof(esp_a2d_mcc_t)); + BTIF_A2D_CB_TO_APP(ESP_A2D_AUDIO_CFG_EVT, ¶m); + } + } 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 index 000000000..a693198ab --- /dev/null +++ b/components/bt/bluedroid/btc/profile/std/include/btc_av.h @@ -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__ */ diff --git a/components/bt/bluedroid/btif/btif_avk.c b/components/bt/bluedroid/btif/btif_avk.c index d8cbb2a58..e1df2ca0b 100755 --- a/components/bt/bluedroid/btif/btif_avk.c +++ b/components/bt/bluedroid/btif/btif_avk.c @@ -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 diff --git a/components/bt/component.mk b/components/bt/component.mk index 5cdcb18df..eb3440bb7 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -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 \