--- /dev/null
- APPL_TRACE_ERROR("bta_av_str_stopped:audio_open_cnt=%d, p_data %x",
+/******************************************************************************
+ *
+ * Copyright (C) 2004-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains action functions for advanced audio/video stream
+ * state machine. these functions are shared by both audio and video
+ * streams.
+ *
+ ******************************************************************************/
+
+#include "bt_target.h"
+#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE)
+
+// #include <assert.h>
+#include "bt_trace.h"
+#include <string.h>
+
+// #include <cutils/properties.h>
+
+#include "bta_av_int.h"
+#include "avdt_api.h"
+#include "utl.h"
+#include "l2c_api.h"
+#include "l2cdefs.h"
+#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
+#include "bta_ar_api.h"
+#endif
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+/* the delay time in milliseconds to start service discovery on AVRCP */
+#ifndef BTA_AV_RC_DISC_TIME_VAL
+#define BTA_AV_RC_DISC_TIME_VAL 3500
+#endif
+
+/* the timer in milliseconds to guard against link busy and AVDT_CloseReq failed to be sent */
+#ifndef BTA_AV_CLOSE_REQ_TIME_VAL
+#define BTA_AV_CLOSE_REQ_TIME_VAL 4000
+#endif
+
+/* number to retry on reconfigure failure - some headsets requirs this number to be more than 1 */
+#ifndef BTA_AV_RECONFIG_RETRY
+#define BTA_AV_RECONFIG_RETRY 6
+#endif
+
+static void bta_av_st_rc_timer(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data);
+
+/* state machine states */
+enum
+{
+ BTA_AV_INIT_SST,
+ BTA_AV_INCOMING_SST,
+ BTA_AV_OPENING_SST,
+ BTA_AV_OPEN_SST,
+ BTA_AV_RCFG_SST,
+ BTA_AV_CLOSING_SST
+};
+
+
+/* the call out functions for audio stream */
+/* const tBTA_AV_CO_FUNCTS bta_av_a2d_cos =
+{
+ bta_av_co_audio_init,
+ bta_av_co_audio_disc_res,
+ bta_av_co_audio_getconfig,
+ bta_av_co_audio_setconfig,
+ bta_av_co_audio_open,
+ bta_av_co_audio_close,
+ bta_av_co_audio_start,
+ bta_av_co_audio_stop,
+ bta_av_co_audio_src_data_path,
+ bta_av_co_audio_delay
+};
+*/
+tBTA_AV_CO_FUNCTS *p_bta_av_a2d_cos = NULL;
+
+
+
+/* ssm action functions for audio stream */
+const tBTA_AV_SACT bta_av_a2d_action[] =
+{
+ bta_av_do_disc_a2d, /* BTA_AV_DO_DISC */
+ bta_av_cleanup, /* BTA_AV_CLEANUP */
+ bta_av_free_sdb, /* BTA_AV_FREE_SDB */
+ bta_av_config_ind, /* BTA_AV_CONFIG_IND */
+ bta_av_disconnect_req, /* BTA_AV_DISCONNECT_REQ */
+ bta_av_security_req, /* BTA_AV_SECURITY_REQ */
+ bta_av_security_rsp, /* BTA_AV_SECURITY_RSP */
+ bta_av_setconfig_rsp, /* BTA_AV_SETCONFIG_RSP */
+ bta_av_st_rc_timer, /* BTA_AV_ST_RC_TIMER */
+ bta_av_str_opened, /* BTA_AV_STR_OPENED */
+ bta_av_security_ind, /* BTA_AV_SECURITY_IND */
+ bta_av_security_cfm, /* BTA_AV_SECURITY_CFM */
+ bta_av_do_close, /* BTA_AV_DO_CLOSE */
+ bta_av_connect_req, /* BTA_AV_CONNECT_REQ */
+ bta_av_sdp_failed, /* BTA_AV_SDP_FAILED */
+ bta_av_disc_results, /* BTA_AV_DISC_RESULTS */
+ bta_av_disc_res_as_acp, /* BTA_AV_DISC_RES_AS_ACP */
+ bta_av_open_failed, /* BTA_AV_OPEN_FAILED */
+ bta_av_getcap_results, /* BTA_AV_GETCAP_RESULTS */
+ bta_av_setconfig_rej, /* BTA_AV_SETCONFIG_REJ */
+ bta_av_discover_req, /* BTA_AV_DISCOVER_REQ */
+ bta_av_conn_failed, /* BTA_AV_CONN_FAILED */
+ bta_av_do_start, /* BTA_AV_DO_START */
+ bta_av_str_stopped, /* BTA_AV_STR_STOPPED */
+ bta_av_reconfig, /* BTA_AV_RECONFIG */
+ bta_av_data_path, /* BTA_AV_DATA_PATH */
+ bta_av_start_ok, /* BTA_AV_START_OK */
+ bta_av_start_failed, /* BTA_AV_START_FAILED */
+ bta_av_str_closed, /* BTA_AV_STR_CLOSED */
+ bta_av_clr_cong, /* BTA_AV_CLR_CONG */
+ bta_av_suspend_cfm, /* BTA_AV_SUSPEND_CFM */
+ bta_av_rcfg_str_ok, /* BTA_AV_RCFG_STR_OK */
+ bta_av_rcfg_failed, /* BTA_AV_RCFG_FAILED */
+ bta_av_rcfg_connect, /* BTA_AV_RCFG_CONNECT */
+ bta_av_rcfg_discntd, /* BTA_AV_RCFG_DISCNTD */
+ bta_av_suspend_cont, /* BTA_AV_SUSPEND_CONT */
+ bta_av_rcfg_cfm, /* BTA_AV_RCFG_CFM */
+ bta_av_rcfg_open, /* BTA_AV_RCFG_OPEN */
+ bta_av_security_rej, /* BTA_AV_SECURITY_REJ */
+ bta_av_open_rc, /* BTA_AV_OPEN_RC */
+ bta_av_chk_2nd_start, /* BTA_AV_CHK_2ND_START */
+ bta_av_save_caps, /* BTA_AV_SAVE_CAPS */
+ bta_av_set_use_rc, /* BTA_AV_SET_USE_RC */
+ bta_av_cco_close, /* BTA_AV_CCO_CLOSE */
+ bta_av_switch_role, /* BTA_AV_SWITCH_ROLE */
+ bta_av_role_res, /* BTA_AV_ROLE_RES */
+ bta_av_delay_co, /* BTA_AV_DELAY_CO */
+ bta_av_open_at_inc, /* BTA_AV_OPEN_AT_INC */
+ NULL
+};
+
+/* these tables translate AVDT events to SSM events */
+static const UINT16 bta_av_stream_evt_ok[] = {
+ BTA_AV_STR_DISC_OK_EVT, /* AVDT_DISCOVER_CFM_EVT */
+ BTA_AV_STR_GETCAP_OK_EVT, /* AVDT_GETCAP_CFM_EVT */
+ BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_CFM_EVT */
+ BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_IND_EVT */
+ BTA_AV_STR_CONFIG_IND_EVT, /* AVDT_CONFIG_IND_EVT */
+ BTA_AV_STR_START_OK_EVT, /* AVDT_START_CFM_EVT */
+ BTA_AV_STR_START_OK_EVT, /* AVDT_START_IND_EVT */
+ BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_CFM_EVT */
+ BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_IND_EVT */
+ BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_CFM_EVT */
+ BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_IND_EVT */
+ BTA_AV_STR_RECONFIG_CFM_EVT, /* AVDT_RECONFIG_CFM_EVT */
+ 0, /* AVDT_RECONFIG_IND_EVT */
+ BTA_AV_STR_SECURITY_CFM_EVT, /* AVDT_SECURITY_CFM_EVT */
+ BTA_AV_STR_SECURITY_IND_EVT, /* AVDT_SECURITY_IND_EVT */
+ BTA_AV_STR_WRITE_CFM_EVT, /* AVDT_WRITE_CFM_EVT */
+ BTA_AV_AVDT_CONNECT_EVT, /* AVDT_CONNECT_IND_EVT */
+ BTA_AV_AVDT_DISCONNECT_EVT, /* AVDT_DISCONNECT_IND_EVT */
+#if (AVDT_REPORTING == TRUE)
+ BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_CONN_EVT */
+ BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_DISCONN_EVT */
+#endif
+ BTA_AV_AVDT_DELAY_RPT_EVT, /* AVDT_DELAY_REPORT_EVT */
+ 0 /* AVDT_DELAY_REPORT_CFM_EVT */
+};
+
+static const UINT16 bta_av_stream_evt_fail[] = {
+ BTA_AV_STR_DISC_FAIL_EVT, /* AVDT_DISCOVER_CFM_EVT */
+ BTA_AV_STR_GETCAP_FAIL_EVT, /* AVDT_GETCAP_CFM_EVT */
+ BTA_AV_STR_OPEN_FAIL_EVT, /* AVDT_OPEN_CFM_EVT */
+ BTA_AV_STR_OPEN_OK_EVT, /* AVDT_OPEN_IND_EVT */
+ BTA_AV_STR_CONFIG_IND_EVT, /* AVDT_CONFIG_IND_EVT */
+ BTA_AV_STR_START_FAIL_EVT, /* AVDT_START_CFM_EVT */
+ BTA_AV_STR_START_OK_EVT, /* AVDT_START_IND_EVT */
+ BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_CFM_EVT */
+ BTA_AV_STR_SUSPEND_CFM_EVT, /* AVDT_SUSPEND_IND_EVT */
+ BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_CFM_EVT */
+ BTA_AV_STR_CLOSE_EVT, /* AVDT_CLOSE_IND_EVT */
+ BTA_AV_STR_RECONFIG_CFM_EVT, /* AVDT_RECONFIG_CFM_EVT */
+ 0, /* AVDT_RECONFIG_IND_EVT */
+ BTA_AV_STR_SECURITY_CFM_EVT, /* AVDT_SECURITY_CFM_EVT */
+ BTA_AV_STR_SECURITY_IND_EVT, /* AVDT_SECURITY_IND_EVT */
+ BTA_AV_STR_WRITE_CFM_EVT, /* AVDT_WRITE_CFM_EVT */
+ BTA_AV_AVDT_CONNECT_EVT, /* AVDT_CONNECT_IND_EVT */
+ BTA_AV_AVDT_DISCONNECT_EVT, /* AVDT_DISCONNECT_IND_EVT */
+#if (AVDT_REPORTING == TRUE)
+ BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_CONN_EVT */
+ BTA_AV_AVDT_RPT_CONN_EVT, /* AVDT_REPORT_DISCONN_EVT */
+#endif
+ BTA_AV_AVDT_DELAY_RPT_EVT, /* AVDT_DELAY_REPORT_EVT */
+ 0 /* AVDT_DELAY_REPORT_CFM_EVT */
+};
+
+void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt);
+static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+static void bta_av_stream1_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+#if BTA_AV_NUM_STRS > 2
+static void bta_av_stream2_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+#endif
+#if BTA_AV_NUM_STRS > 3
+static void bta_av_stream3_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+#endif
+#if BTA_AV_NUM_STRS > 4
+static void bta_av_stream4_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+#endif
+#if BTA_AV_NUM_STRS > 5
+static void bta_av_stream5_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data);
+#endif
+/* the array of callback functions to receive events from AVDT control channel */
+tAVDT_CTRL_CBACK * const bta_av_dt_cback[] =
+{
+ bta_av_stream0_cback
+ ,bta_av_stream1_cback
+#if BTA_AV_NUM_STRS > 2
+ ,bta_av_stream2_cback
+#endif
+#if BTA_AV_NUM_STRS > 3
+ ,bta_av_stream3_cback
+#endif
+#if BTA_AV_NUM_STRS > 4
+ ,bta_av_stream4_cback
+#endif
+#if BTA_AV_NUM_STRS > 5
+ ,bta_av_stream5_cback
+#endif
+};
+/***********************************************
+**
+** Function bta_get_scb_handle
+**
+** Description gives the registered AVDT handle.by checking with sep_type.
+**
+**
+** Returns void
+***********************************************/
+static UINT8 bta_av_get_scb_handle(tBTA_AV_SCB *p_scb, UINT8 local_sep)
+{
+ UINT8 xx =0;
+ for (xx = 0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ if ((p_scb->seps[xx].tsep == local_sep) &&
+ (p_scb->seps[xx].codec_type == p_scb->codec_type))
+ return (p_scb->seps[xx].av_handle);
+ }
+ APPL_TRACE_DEBUG(" bta_av_get_scb_handle appropiate sep_type not found")
+ return 0; /* return invalid handle */
+}
+
+/***********************************************
+**
+** Function bta_av_get_scb_sep_type
+**
+** Description gives the sep type by cross-checking with AVDT handle
+**
+**
+** Returns void
+***********************************************/
+static UINT8 bta_av_get_scb_sep_type(tBTA_AV_SCB *p_scb, UINT8 tavdt_handle)
+{
+ UINT8 xx =0;
+ for (xx = 0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ if (p_scb->seps[xx].av_handle == tavdt_handle)
+ return (p_scb->seps[xx].tsep);
+ }
+ APPL_TRACE_DEBUG(" bta_av_get_scb_sep_type appropiate handle not found")
+ return 3; /* return invalid sep type */
+}
+
+/*******************************************************************************
+**
+** Function bta_av_save_addr
+**
+** Description copy the bd_addr and maybe reset the supported flags
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_save_addr(tBTA_AV_SCB *p_scb, const BD_ADDR b)
+{
+ APPL_TRACE_DEBUG("bta_av_save_addr r:%d, s:%d",
+ p_scb->recfg_sup, p_scb->suspend_sup);
+ if(bdcmp(p_scb->peer_addr, b) != 0)
+ {
+ APPL_TRACE_ERROR("reset flags");
+ /* a new addr, reset the supported flags */
+ p_scb->recfg_sup = TRUE;
+ p_scb->suspend_sup = TRUE;
+ }
+
+ /* do this copy anyway, just in case the first addr matches
+ * the control block one by accident */
+ bdcpy(p_scb->peer_addr, b);
+}
+
+/*******************************************************************************
+**
+** Function notify_start_failed
+**
+** Description notify up-layer AV start failed
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void notify_start_failed(tBTA_AV_SCB *p_scb)
+{
+ tBTA_AV_START start;
+ /* if start failed, clear role */
+ p_scb->role &= ~BTA_AV_ROLE_START_INT;
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_FAIL;
+ start.initiator = TRUE;
+ start.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_st_rc_timer
+**
+** Description start the AVRC timer if no RC connection & CT is supported &
+** RC is used or
+** as ACP (we do not really know if we want AVRC)
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_st_rc_timer(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_st_rc_timer rc_handle:%d, use_rc: %d",
+ p_scb->rc_handle, p_scb->use_rc);
+ /* for outgoing RC connection as INT/CT */
+ if( (p_scb->rc_handle == BTA_AV_RC_HANDLE_NONE) &&
+ /*(bta_av_cb.features & BTA_AV_FEAT_RCCT) &&*/
+ (p_scb->use_rc == TRUE || (p_scb->role & BTA_AV_ROLE_AD_ACP)) )
+ {
+ if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) == 0)
+ bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL);
+ else
+ p_scb->wait |= BTA_AV_WAIT_CHECK_RC;
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function bta_av_next_getcap
+**
+** Description The function gets the capabilities of the next available
+** stream found in the discovery results.
+**
+** Returns TRUE if we sent request to AVDT, FALSE otherwise.
+**
+*******************************************************************************/
+static BOOLEAN bta_av_next_getcap(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ int i;
+ tAVDT_GETCAP_REQ *p_req;
+ BOOLEAN sent_cmd = FALSE;
+ UINT16 uuid_int = p_scb->uuid_int;
+ UINT8 sep_requested = 0;
+
+ if(uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ sep_requested = AVDT_TSEP_SNK;
+ else if(uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ sep_requested = AVDT_TSEP_SRC;
+
+ for (i = p_scb->sep_info_idx; i < p_scb->num_seps; i++)
+ {
+ /* steam not in use, is a sink, and is the right media type (audio/video) */
+ if ((p_scb->sep_info[i].in_use == FALSE) &&
+ (p_scb->sep_info[i].tsep == sep_requested) &&
+ (p_scb->sep_info[i].media_type == p_scb->media_type))
+ {
+ p_scb->sep_info_idx = i;
+
+ /* we got a stream; get its capabilities */
+ if (p_scb->p_cap == NULL)
+ {
+ p_scb->p_cap = (tAVDT_CFG *) GKI_getbuf(sizeof(tAVDT_CFG));
+ }
+ if (p_scb->p_cap == NULL)
+ {
+ i = p_scb->num_seps;
+ break;
+ }
+ if (p_scb->avdt_version >= AVDT_VERSION_SYNC)
+ {
+ p_req = AVDT_GetAllCapReq;
+ }
+ else
+ {
+ p_req = AVDT_GetCapReq;
+ }
+ (*p_req)(p_scb->peer_addr,
+ p_scb->sep_info[i].seid,
+ p_scb->p_cap, bta_av_dt_cback[p_scb->hdi]);
+ sent_cmd = TRUE;
+ break;
+ }
+ }
+
+ /* if no streams available then stream open fails */
+ if (!sent_cmd)
+ {
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_GETCAP_FAIL_EVT, p_data);
+ }
+
+ return sent_cmd;
+
+}
+
+/*******************************************************************************
+**
+** Function bta_av_proc_stream_evt
+**
+** Description Utility function to compose stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_proc_stream_evt(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data, int index)
+{
+ tBTA_AV_STR_MSG *p_msg;
+ UINT16 sec_len = 0;
+ tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[index];
+ int xx;
+
+ if (p_data)
+ {
+ if (event == AVDT_SECURITY_IND_EVT)
+ {
+ sec_len = (p_data->security_ind.len < BTA_AV_SECURITY_MAX_LEN) ?
+ p_data->security_ind.len : BTA_AV_SECURITY_MAX_LEN;
+ }
+ else if (event == AVDT_SECURITY_CFM_EVT && p_data->hdr.err_code == 0)
+ {
+ sec_len = (p_data->security_cfm.len < BTA_AV_SECURITY_MAX_LEN) ?
+ p_data->security_cfm.len : BTA_AV_SECURITY_MAX_LEN;
+ }
+ }
+
+ if (p_scb && (p_msg = (tBTA_AV_STR_MSG *) GKI_getbuf((UINT16) (sizeof(tBTA_AV_STR_MSG) + sec_len))) != NULL)
+ {
+
+ /* copy event data, bd addr, and handle to event message buffer */
+ p_msg->hdr.offset = 0;
+
+ if (bd_addr != NULL)
+ {
+ bdcpy(p_msg->bd_addr, bd_addr);
+ APPL_TRACE_DEBUG(" bd_addr:%02x-%02x-%02x-%02x-%02x-%02x",
+ bd_addr[0], bd_addr[1],
+ bd_addr[2], bd_addr[3],
+ bd_addr[4], bd_addr[5]);
+ }
+
+ if (p_data != NULL)
+ {
+ memcpy(&p_msg->msg, p_data, sizeof (tAVDT_CTRL));
+ /* copy config params to event message buffer */
+ switch (event)
+ {
+ case AVDT_RECONFIG_CFM_EVT:
+ APPL_TRACE_DEBUG("reconfig cfm event codec info = 0x%06x-%06x-%06x-%02x",
+ (p_msg->msg.reconfig_cfm.p_cfg->codec_info[0]<<16)+(p_msg->msg.reconfig_cfm.p_cfg->codec_info[1]<<8)+p_msg->msg.reconfig_cfm.p_cfg->codec_info[2],
+ (p_msg->msg.reconfig_cfm.p_cfg->codec_info[3]<<16)+(p_msg->msg.reconfig_cfm.p_cfg->codec_info[4]<<8)+p_msg->msg.reconfig_cfm.p_cfg->codec_info[5],
+ (p_msg->msg.reconfig_cfm.p_cfg->codec_info[6]<<16)+(p_msg->msg.reconfig_cfm.p_cfg->codec_info[7]<<8)+p_msg->msg.reconfig_cfm.p_cfg->codec_info[8],
+ p_msg->msg.reconfig_cfm.p_cfg->codec_info[9]);
+ break;
+
+
+
+ case AVDT_CONFIG_IND_EVT:
+ /* We might have 2 SEP signallings(A2DP + VDP) with one peer device on one L2CAP.
+ * If we already have a signalling connection with the bd_addr and the streaming
+ * SST is at INIT state, change it to INCOMING state to handle the signalling
+ * from the 2nd SEP. */
+ if ((bta_av_find_lcb(bd_addr, BTA_AV_LCB_FIND) != NULL) && (bta_av_is_scb_init(p_scb)))
+ {
+ bta_av_set_scb_sst_incoming (p_scb);
+
+ /* When ACP_CONNECT_EVT was received, we put first available scb to incoming state.
+ * Later when we receive AVDT_CONFIG_IND_EVT, we use a new p_scb and set its state to
+ * incoming which we do it above.
+ * We also have to set the old p_scb state to init to be used later */
+ for (xx = 0; xx < BTA_AV_NUM_STRS; xx++)
+ {
+ if ((bta_av_cb.p_scb[xx]) && (xx != index))
+ {
+ if (bta_av_cb.p_scb[xx]->state == BTA_AV_INCOMING_SST)
+ {
+ bta_av_cb.p_scb[xx]->state = BTA_AV_INIT_SST;
+ bta_av_cb.p_scb[xx]->coll_mask = 0;
+ break;
+ }
+ }
+ }
+ }
+
+ memcpy(&p_msg->cfg, p_data->config_ind.p_cfg, sizeof(tAVDT_CFG));
+ break;
+
+ case AVDT_SECURITY_IND_EVT:
+ p_msg->msg.security_ind.p_data = (UINT8 *) (p_msg + 1);
+ memcpy(p_msg->msg.security_ind.p_data, p_data->security_ind.p_data, sec_len);
+ break;
+
+ case AVDT_SECURITY_CFM_EVT:
+ p_msg->msg.security_cfm.p_data = (UINT8 *) (p_msg + 1);
+ if (p_data->hdr.err_code == 0)
+ {
+ memcpy(p_msg->msg.security_cfm.p_data, p_data->security_cfm.p_data, sec_len);
+ }
+ break;
+ case AVDT_SUSPEND_IND_EVT:
+ p_msg->msg.hdr.err_code = 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ p_msg->msg.hdr.err_code = 0;
+
+ /* look up application event */
+ if ((p_data == NULL) || (p_data->hdr.err_code == 0))
+ {
+ p_msg->hdr.event = bta_av_stream_evt_ok[event];
+ }
+ else
+ {
+ p_msg->hdr.event = bta_av_stream_evt_fail[event];
+ }
+
+ p_msg->initiator = FALSE;
+ if (event == AVDT_SUSPEND_CFM_EVT)
+ p_msg->initiator = TRUE;
+
+ APPL_TRACE_VERBOSE("hndl:x%x", p_scb->hndl);
+ p_msg->hdr.layer_specific = p_scb->hndl;
+ p_msg->handle = handle;
+ p_msg->avdt_event = event;
+ bta_sys_sendmsg(p_msg);
+ }
+
+/* coverity[var_deref_model] */
+/* false-positive: bta_av_conn_cback only processes AVDT_CONNECT_IND_EVT and AVDT_DISCONNECT_IND_EVT event
+ * these 2 events always have associated p_data */
+ if (p_data)
+ {
+ bta_av_conn_cback(handle, bd_addr, event, p_data);
+ }
+ else
+ {
+ APPL_TRACE_ERROR("%s: p_data is null", __func__);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_stream_data_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_stream_data_cback(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt)
+{
+ int index = 0;
+ tBTA_AV_SCB *p_scb ;
+ APPL_TRACE_DEBUG("bta_av_stream_data_cback avdt_handle: %d pkt_len=0x%x ofst = 0x%x", handle,p_pkt->len,p_pkt->offset);
+ APPL_TRACE_DEBUG(" Number of frames 0x%x",*((UINT8*)(p_pkt + 1) + p_pkt->offset));
+ APPL_TRACE_DEBUG("Sequence Number 0x%x",p_pkt->layer_specific);
+ /* Get SCB and correct sep type*/
+ for(index = 0; index < BTA_AV_NUM_STRS;index ++ )
+ {
+ p_scb = bta_av_cb.p_scb[index];
+ if((p_scb->avdt_handle == handle)&&(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK))
+ break;
+ }
+ if(index == BTA_AV_NUM_STRS) /* cannot find correct handler */
+ {
+ GKI_freebuf(p_pkt);
+ return;
+ }
+ p_pkt->event = BTA_AV_MEDIA_DATA_EVT;
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_DATA_EVT, (tBTA_AV_MEDIA*)p_pkt);
+ GKI_freebuf(p_pkt); /* a copy of packet had been delivered, we free this buffer */
+}
+
+/*******************************************************************************
+**
+** Function bta_av_stream0_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_stream0_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_VERBOSE("bta_av_stream0_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 0);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_stream1_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_stream1_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_EVENT("bta_av_stream1_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 1);
+}
+
+#if BTA_AV_NUM_STRS > 2
+/*******************************************************************************
+**
+** Function bta_av_stream2_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_stream2_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_EVENT("bta_av_stream2_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 2);
+}
+#endif
+
+#if BTA_AV_NUM_STRS > 3
+/*******************************************************************************
+**
+** Function bta_av_stream3_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_stream3_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_EVENT("bta_av_stream3_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 3);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_av_stream4_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+#if BTA_AV_NUM_STRS > 4
+static void bta_av_stream4_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_EVENT("bta_av_stream4_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 4);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_av_stream5_cback
+**
+** Description This is the AVDTP callback function for stream events.
+**
+** Returns void
+**
+*******************************************************************************/
+#if BTA_AV_NUM_STRS > 5
+static void bta_av_stream5_cback(UINT8 handle, BD_ADDR bd_addr, UINT8 event, tAVDT_CTRL *p_data)
+{
+ APPL_TRACE_EVENT("bta_av_stream5_cback avdt_handle: %d event=0x%x", handle, event);
+ bta_av_proc_stream_evt(handle, bd_addr, event, p_data, 5);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_av_a2d_sdp_cback
+**
+** Description A2DP service discovery callback.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_av_a2d_sdp_cback(BOOLEAN found, tA2D_Service *p_service)
+{
+ tBTA_AV_SDP_RES *p_msg;
+ tBTA_AV_SCB *p_scb;
+
+ if ((p_msg = (tBTA_AV_SDP_RES *) GKI_getbuf(sizeof(tBTA_AV_SDP_RES))) != NULL)
+ {
+ p_msg->hdr.event = (found) ? BTA_AV_SDP_DISC_OK_EVT : BTA_AV_SDP_DISC_FAIL_EVT;
+
+ p_scb = bta_av_hndl_to_scb(bta_av_cb.handle);
+ if (p_scb)
+ {
+ if (found && (p_service != NULL))
+ p_scb->avdt_version = p_service->avdt_version;
+ else
+ p_scb->avdt_version = 0x00;
+
+ p_msg->hdr.layer_specific = bta_av_cb.handle;
+ bta_sys_sendmsg(p_msg);
+ }
+ else
+ {
+ APPL_TRACE_ERROR ("bta_av_a2d_sdp_cback, no scb found for handle(0x%x)", bta_av_cb.handle);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_adjust_seps_idx
+**
+** Description adjust the sep_idx
+**
+** Returns
+**
+*******************************************************************************/
+static void bta_av_adjust_seps_idx(tBTA_AV_SCB *p_scb, UINT8 avdt_handle)
+{
+ int xx;
+ APPL_TRACE_DEBUG("bta_av_adjust_seps_idx codec_type: %d", p_scb->codec_type);
+ for(xx=0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ APPL_TRACE_DEBUG("av_handle: %d codec_type: %d",
+ p_scb->seps[xx].av_handle, p_scb->seps[xx].codec_type);
+ if((p_scb->seps[xx].av_handle && p_scb->codec_type == p_scb->seps[xx].codec_type)
+ && (p_scb->seps[xx].av_handle == avdt_handle))
+ {
+ p_scb->sep_idx = xx;
+ p_scb->avdt_handle = p_scb->seps[xx].av_handle;
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_switch_role
+**
+** Description Switch role was not started and a timer was started.
+** another attempt to switch role now - still opening.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_switch_role (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_RS_RES switch_res = BTA_AV_RS_NONE;
+ tBTA_AV_API_OPEN *p_buf = &p_scb->q_info.open;
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_switch_role wait:x%x", p_scb->wait);
+ if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_START)
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RETRY;
+
+ /* clear the masks set when the timer is started */
+ p_scb->wait &= ~(BTA_AV_WAIT_ROLE_SW_RES_OPEN|BTA_AV_WAIT_ROLE_SW_RES_START);
+
+ if (p_scb->q_tag == BTA_AV_Q_TAG_OPEN)
+ {
+ if (bta_av_switch_if_needed(p_scb) || !bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT))
+ {
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN;
+ }
+ else
+ {
+ /* this should not happen in theory. Just in case...
+ * continue to do_disc_a2d */
+ switch_res = BTA_AV_RS_DONE;
+ }
+ }
+ else
+ {
+ /* report failure on OPEN */
+ switch_res = BTA_AV_RS_FAIL;
+ }
+
+ if (switch_res != BTA_AV_RS_NONE)
+ {
+ if (bta_av_cb.rs_idx == (p_scb->hdi + 1))
+ {
+ bta_av_cb.rs_idx = 0;
+ }
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_RETRY;
+ p_scb->q_tag = 0;
+ p_buf->switch_res = switch_res;
+ bta_av_do_disc_a2d(p_scb, (tBTA_AV_DATA *)p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_role_res
+**
+** Description Handle the role changed event
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_role_res (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ BOOLEAN initiator = FALSE;
+ tBTA_AV_START start;
+ tBTA_AV_OPEN av_open;
+
+ APPL_TRACE_DEBUG("bta_av_role_res q_tag:%d, wait:x%x, role:x%x", p_scb->q_tag, p_scb->wait, p_scb->role);
+ if (p_scb->role & BTA_AV_ROLE_START_INT)
+ initiator = TRUE;
+
+ if (p_scb->q_tag == BTA_AV_Q_TAG_START)
+ {
+ if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_STARTED)
+ {
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+ if (p_data->role_res.hci_status != HCI_SUCCESS)
+ {
+ p_scb->role &= ~BTA_AV_ROLE_START_INT;
+ bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+ /* start failed because of role switch. */
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_FAIL_ROLE;
+ start.hndl = p_scb->hndl;
+ start.initiator = initiator;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+ }
+ else
+ {
+ bta_av_start_ok(p_scb, p_data);
+ }
+ }
+ else if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_START)
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_FAILED;
+ }
+ else if (p_scb->q_tag == BTA_AV_Q_TAG_OPEN)
+ {
+ if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_RES_OPEN)
+ {
+ p_scb->role &= ~BTA_AV_ROLE_START_INT;
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+
+ if (p_data->role_res.hci_status != HCI_SUCCESS)
+ {
+ /* Open failed because of role switch. */
+ bdcpy(av_open.bd_addr, p_scb->peer_addr);
+ av_open.chnl = p_scb->chnl;
+ av_open.hndl = p_scb->hndl;
+ start.status = BTA_AV_FAIL_ROLE;
+ if(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ av_open.sep = AVDT_TSEP_SNK;
+ else if(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ av_open.sep = AVDT_TSEP_SRC;
+ (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *)&av_open);
+ }
+ else
+ {
+ /* Continue av open process */
+ p_scb->q_info.open.switch_res = BTA_AV_RS_DONE;
+ bta_av_do_disc_a2d (p_scb, (tBTA_AV_DATA *)&(p_scb->q_info.open));
+ }
+ }
+ else
+ {
+ APPL_TRACE_WARNING ("Unexpected role switch event: q_tag = %d wait = %d", p_scb->q_tag, p_scb->wait);
+ }
+ }
+
+ APPL_TRACE_DEBUG("wait:x%x, role:x%x", p_scb->wait, p_scb->role);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_delay_co
+**
+** Description Call the delay call-out function to report the delay report
+** from SNK
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_delay_co (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ p_scb->p_cos->delay(p_scb->hndl, p_data->str_msg.msg.delay_rpt_cmd.delay);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_do_disc_a2d
+**
+** Description Do service discovery for A2DP.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_do_disc_a2d (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ BOOLEAN ok_continue = FALSE;
+ tA2D_SDP_DB_PARAMS db_params;
+ UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
+ ATTR_ID_PROTOCOL_DESC_LIST,
+ ATTR_ID_BT_PROFILE_DESC_LIST};
+ UINT16 sdp_uuid = 0; /* UUID for which SDP has to be done */
+
+ APPL_TRACE_DEBUG("bta_av_do_disc_a2d use_rc: %d rs:%d, oc:%d",
+ p_data->api_open.use_rc, p_data->api_open.switch_res, bta_av_cb.audio_open_cnt);
+
+ memcpy (&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN));
+
+ switch(p_data->api_open.switch_res)
+ {
+ case BTA_AV_RS_NONE:
+ if (bta_av_switch_if_needed(p_scb) || !bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT))
+ {
+ /* waiting for role switch result. save the api to control block */
+ memcpy(&p_scb->q_info.open, &p_data->api_open, sizeof(tBTA_AV_API_OPEN));
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN;
+ p_scb->q_tag = BTA_AV_Q_TAG_OPEN;
+ }
+ else
+ {
+ ok_continue = TRUE;
+ }
+ break;
+
+ case BTA_AV_RS_FAIL:
+ /* report a new failure event */
+ p_scb->open_status = BTA_AV_FAIL_ROLE;
+ bta_av_ssm_execute(p_scb, BTA_AV_SDP_DISC_FAIL_EVT, NULL);
+ break;
+
+ case BTA_AV_RS_OK:
+ p_data = (tBTA_AV_DATA *)&p_scb->q_info.open;
+ /* continue to open if link role is ok */
+ if (bta_av_link_role_ok(p_scb, A2D_SET_MULTL_BIT))
+ {
+ ok_continue = TRUE;
+ }
+ else
+ {
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_OPEN;
+ }
+ break;
+
+ case BTA_AV_RS_DONE:
+ ok_continue = TRUE;
+ break;
+ }
+
+ APPL_TRACE_DEBUG("ok_continue: %d wait:x%x, q_tag: %d", ok_continue, p_scb->wait, p_scb->q_tag);
+ if (!ok_continue)
+ return;
+
+ /* clear the role switch bits */
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+
+ if (p_scb->wait & BTA_AV_WAIT_CHECK_RC)
+ {
+ p_scb->wait &= ~BTA_AV_WAIT_CHECK_RC;
+ bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL);
+ }
+
+ if (bta_av_cb.features & BTA_AV_FEAT_MASTER)
+ {
+ L2CA_SetDesireRole(L2CAP_ROLE_DISALLOW_SWITCH);
+
+ if (bta_av_cb.audio_open_cnt == 1)
+ {
+ /* there's already an A2DP connection. do not allow switch */
+ bta_sys_clear_default_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH);
+ }
+ }
+ /* store peer addr other parameters */
+ bta_av_save_addr(p_scb, p_data->api_open.bd_addr);
+ p_scb->sec_mask = p_data->api_open.sec_mask;
+ p_scb->use_rc = p_data->api_open.use_rc;
+
+ bta_sys_app_open(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);
+
+ /* allocate discovery database */
+ if (p_scb->p_disc_db == NULL)
+ {
+ p_scb->p_disc_db = (tSDP_DISCOVERY_DB *) GKI_getbuf(BTA_AV_DISC_BUF_SIZE);
+ }
+
+ /* only one A2D find service is active at a time */
+ bta_av_cb.handle = p_scb->hndl;
+
+ if(p_scb->p_disc_db)
+ {
+ /* set up parameters */
+ db_params.db_len = BTA_AV_DISC_BUF_SIZE;
+ db_params.num_attr = 3;
+ db_params.p_db = p_scb->p_disc_db;
+ db_params.p_attrs = attr_list;
+ p_scb->uuid_int = p_data->api_open.uuid;
+ if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ sdp_uuid = UUID_SERVCLASS_AUDIO_SOURCE;
+ else if (p_scb->uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ sdp_uuid = UUID_SERVCLASS_AUDIO_SINK;
+
+ APPL_TRACE_DEBUG("uuid_int 0x%x, Doing SDP For 0x%x", p_scb->uuid_int, sdp_uuid);
+ if(A2D_FindService(sdp_uuid, p_scb->peer_addr, &db_params,
+ bta_av_a2d_sdp_cback) == A2D_SUCCESS)
+ {
+ return;
+ }
+ }
+
+ /* when the code reaches here, either the DB is NULL
+ * or A2D_FindService is not successful */
+ bta_av_a2d_sdp_cback(FALSE, NULL);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_cleanup
+**
+** Description cleanup AV stream control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_cleanup(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_CONN_CHG msg;
+ int xx;
+ UINT8 role = BTA_AV_ROLE_AD_INT;
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_cleanup");
+
+ /* free any buffers */
+ utl_freebuf((void **) &p_scb->p_cap);
+ utl_freebuf((void **) &p_scb->p_disc_db);
+ p_scb->avdt_version = 0;
+
+ /* initialize some control block variables */
+ p_scb->open_status = BTA_AV_SUCCESS;
+
+ /* if de-registering shut everything down */
+ msg.hdr.layer_specific = p_scb->hndl;
+ p_scb->started = FALSE;
+ p_scb->cong = FALSE;
+ p_scb->role = role;
+ p_scb->cur_psc_mask = 0;
+ p_scb->wait = 0;
+ p_scb->num_disc_snks = 0;
+ bta_sys_stop_timer(&p_scb->timer);
+ if (p_scb->deregistring)
+ {
+ /* remove stream */
+ for(xx=0; xx<BTA_AV_MAX_SEPS; xx++)
+ {
+ if(p_scb->seps[xx].av_handle)
+ AVDT_RemoveStream(p_scb->seps[xx].av_handle);
+ p_scb->seps[xx].av_handle = 0;
+ }
+
+ bta_av_dereg_comp((tBTA_AV_DATA *) &msg);
+ }
+ else
+ {
+ /* report stream closed to main SM */
+ msg.is_up = FALSE;
+ bdcpy(msg.peer_addr, p_scb->peer_addr);
+ bta_av_conn_chg((tBTA_AV_DATA *) &msg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_free_sdb
+**
+** Description Free service discovery db buffer.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_free_sdb(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+ utl_freebuf((void **) &p_scb->p_disc_db);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_config_ind
+**
+** Description Handle a stream configuration indication from the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_config_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_CI_SETCONFIG setconfig;
+ tAVDT_SEP_INFO *p_info;
+ tAVDT_CFG *p_evt_cfg = &p_data->str_msg.cfg;
+ UINT8 psc_mask = (p_evt_cfg->psc_mask | p_scb->cfg.psc_mask);
+ UINT8 local_sep; /* sep type of local handle on which connection was received */
+ tBTA_AV_STR_MSG *p_msg = (tBTA_AV_STR_MSG *)p_data;
+ UNUSED(p_data);
+
+ local_sep = bta_av_get_scb_sep_type(p_scb, p_msg->handle);
+ p_scb->avdt_label = p_data->str_msg.msg.hdr.label;
+ memcpy(p_scb->cfg.codec_info, p_evt_cfg->codec_info, AVDT_CODEC_SIZE);
+ p_scb->codec_type = p_evt_cfg->codec_info[BTA_AV_CODEC_TYPE_IDX];
+ bta_av_save_addr(p_scb, p_data->str_msg.bd_addr);
+
+ /* Clear collision mask */
+ p_scb->coll_mask = 0;
+ bta_sys_stop_timer(&bta_av_cb.acp_sig_tmr);
+
+ /* if no codec parameters in configuration, fail */
+ if ((p_evt_cfg->num_codec == 0) ||
+ /* or the peer requests for a service we do not support */
+ ((psc_mask != p_scb->cfg.psc_mask) &&
+ (psc_mask != (p_scb->cfg.psc_mask&~AVDT_PSC_DELAY_RPT))) )
+ {
+ setconfig.hndl = p_scb->hndl; /* we may not need this */
+ setconfig.err_code = AVDT_ERR_UNSUP_CFG;
+ bta_av_ssm_execute(p_scb, BTA_AV_CI_SETCONFIG_FAIL_EVT, (tBTA_AV_DATA *) &setconfig);
+ }
+ else
+ {
+ p_info = &p_scb->sep_info[0];
+ p_info->in_use = 0;
+ p_info->media_type = p_scb->media_type;
+ p_info->seid = p_data->str_msg.msg.config_ind.int_seid;
+
+ /* Sep type of Peer will be oppsite role to our local sep */
+ if (local_sep == AVDT_TSEP_SRC)
+ p_info->tsep = AVDT_TSEP_SNK;
+ else if (local_sep == AVDT_TSEP_SNK)
+ p_info->tsep = AVDT_TSEP_SRC;
+
+ p_scb->role |= BTA_AV_ROLE_AD_ACP;
+ p_scb->cur_psc_mask = p_evt_cfg->psc_mask;
+ if (bta_av_cb.features & BTA_AV_FEAT_RCTG)
+ p_scb->use_rc = TRUE;
+ else
+ p_scb->use_rc = FALSE;
+
+ p_scb->num_seps = 1;
+ p_scb->sep_info_idx = 0;
+ APPL_TRACE_DEBUG("bta_av_config_ind: SEID: %d use_rc: %d cur_psc_mask:0x%x", p_info->seid, p_scb->use_rc, p_scb->cur_psc_mask);
+ /* in case of A2DP SINK this is the first time peer data is being sent to co functions */
+ if (local_sep == AVDT_TSEP_SNK)
+ {
+ p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type,
+ p_evt_cfg->codec_info,
+ p_info->seid,
+ p_scb->peer_addr,
+ p_evt_cfg->num_protect,
+ p_evt_cfg->protect_info,
+ AVDT_TSEP_SNK,
+ p_msg->handle);
+ }
+ else
+ {
+ p_scb->p_cos->setcfg(p_scb->hndl, p_scb->codec_type,
+ p_evt_cfg->codec_info,
+ p_info->seid,
+ p_scb->peer_addr,
+ p_evt_cfg->num_protect,
+ p_evt_cfg->protect_info,
+ AVDT_TSEP_SRC,
+ p_msg->handle);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_disconnect_req
+**
+** Description Disconnect AVDTP connection.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_disconnect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_RCB *p_rcb;
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_disconnect_req conn_lcb: 0x%x", bta_av_cb.conn_lcb);
+
+ bta_sys_stop_timer(&bta_av_cb.sig_tmr);
+ bta_sys_stop_timer(&p_scb->timer);
+ if(bta_av_cb.conn_lcb)
+ {
+ p_rcb = bta_av_get_rcb_by_shdl((UINT8)(p_scb->hdi + 1));
+ if (p_rcb)
+ bta_av_del_rc(p_rcb);
+ AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+ }
+ else
+ {
+ bta_av_ssm_execute(p_scb, BTA_AV_AVDT_DISCONNECT_EVT, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_security_req
+**
+** Description Send an AVDTP security request.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_security_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ if (bta_av_cb.features & BTA_AV_FEAT_PROTECT)
+ {
+ AVDT_SecurityReq(p_scb->avdt_handle, p_data->api_protect_req.p_data,
+ p_data->api_protect_req.len);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_security_rsp
+**
+** Description Send an AVDTP security response.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_security_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ if (bta_av_cb.features & BTA_AV_FEAT_PROTECT)
+ {
+ AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->api_protect_rsp.error_code,
+ p_data->api_protect_rsp.p_data, p_data->api_protect_rsp.len);
+ }
+ else
+ {
+ AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_NSC,
+ NULL, 0);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_setconfig_rsp
+**
+** Description setconfig is OK
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_setconfig_rsp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 num = p_data->ci_setconfig.num_seid + 1;
+ UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle;
+ UINT8 *p_seid = p_data->ci_setconfig.p_seid;
+ int i;
+ UINT8 local_sep;
+
+ /* we like this codec_type. find the sep_idx */
+ local_sep = bta_av_get_scb_sep_type(p_scb,avdt_handle);
+ bta_av_adjust_seps_idx(p_scb, avdt_handle);
+ APPL_TRACE_DEBUG("bta_av_setconfig_rsp: sep_idx: %d cur_psc_mask:0x%x", p_scb->sep_idx, p_scb->cur_psc_mask);
+
+ if ((AVDT_TSEP_SNK == local_sep) && (p_data->ci_setconfig.err_code == AVDT_SUCCESS) &&
+ (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL))
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
+ (tBTA_AV_MEDIA*)p_scb->cfg.codec_info);
+
+
+ AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, p_data->ci_setconfig.err_code,
+ p_data->ci_setconfig.category);
+
+ bta_sys_stop_timer(&bta_av_cb.sig_tmr);
+
+ if(p_data->ci_setconfig.err_code == AVDT_SUCCESS)
+ {
+ p_scb->wait = BTA_AV_WAIT_ACP_CAPS_ON;
+ if(p_data->ci_setconfig.recfg_needed)
+ p_scb->role |= BTA_AV_ROLE_SUSPEND_OPT;
+ APPL_TRACE_DEBUG("bta_av_setconfig_rsp recfg_needed:%d role:x%x num:%d",
+ p_data->ci_setconfig.recfg_needed, p_scb->role, num);
+ /* callout module tells BTA the number of "good" SEPs and their SEIDs.
+ * getcap on these SEID */
+ p_scb->num_seps = num;
+
+ if (p_scb->cur_psc_mask & AVDT_PSC_DELAY_RPT)
+ p_scb->avdt_version = AVDT_VERSION_SYNC;
+
+
+ if (p_scb->codec_type == BTA_AV_CODEC_SBC || num > 1)
+ {
+ /* if SBC is used by the SNK as INT, discover req is not sent in bta_av_config_ind.
+ * call disc_res now */
+ /* this is called in A2DP SRC path only, In case of SINK we don't need it */
+ if (local_sep == AVDT_TSEP_SRC)
+ p_scb->p_cos->disc_res(p_scb->hndl, num, num, 0, p_scb->peer_addr,
+ UUID_SERVCLASS_AUDIO_SOURCE);
+ }
+ else
+ {
+ /* we do not know the peer device and it is using non-SBC codec
+ * we need to know all the SEPs on SNK */
+ bta_av_discover_req(p_scb, NULL);
+ return;
+ }
+
+ for (i = 1; i < num; i++)
+ {
+ APPL_TRACE_DEBUG("sep_info[%d] SEID: %d", i, p_seid[i-1]);
+ /* initialize the sep_info[] to get capabilities */
+ p_scb->sep_info[i].in_use = FALSE;
+ p_scb->sep_info[i].tsep = AVDT_TSEP_SNK;
+ p_scb->sep_info[i].media_type = p_scb->media_type;
+ p_scb->sep_info[i].seid = p_seid[i-1];
+ }
+
+ /* only in case of local sep as SRC we need to look for other SEPs, In case of SINK we don't */
+ if (local_sep == AVDT_TSEP_SRC)
+ {
+ /* Make sure UUID has been initialized... */
+ if (p_scb->uuid_int == 0)
+ p_scb->uuid_int = p_scb->open_api.uuid;
+ bta_av_next_getcap(p_scb, p_data);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_str_opened
+**
+** Description Stream opened OK (incoming/outgoing).
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_CONN_CHG msg;
+ tBTA_AV_OPEN open;
+ UINT8 *p;
+ UINT16 mtu;
+
+ msg.hdr.layer_specific = p_scb->hndl;
+ msg.is_up = TRUE;
+ bdcpy(msg.peer_addr, p_scb->peer_addr);
+ p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle);
+ bta_av_conn_chg((tBTA_AV_DATA *) &msg);
+ /* set the congestion flag, so AV would not send media packets by accident */
+ p_scb->cong = TRUE;
+
+
+ p_scb->stream_mtu = p_data->str_msg.msg.open_ind.peer_mtu - AVDT_MEDIA_HDR_SIZE;
+ mtu = bta_av_chk_mtu(p_scb, p_scb->stream_mtu);
+ APPL_TRACE_DEBUG("bta_av_str_opened l2c_cid: 0x%x stream_mtu: %d mtu: %d",
+ p_scb->l2c_cid, p_scb->stream_mtu, mtu);
+ if(mtu == 0 || mtu > p_scb->stream_mtu)
+ mtu = p_scb->stream_mtu;
+
+ /* Set the media channel as medium priority */
+ L2CA_SetTxPriority(p_scb->l2c_cid, L2CAP_CHNL_PRIORITY_MEDIUM);
+ L2CA_SetChnlFlushability (p_scb->l2c_cid, TRUE);
+
+ bta_sys_conn_open(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);
+ memset(&p_scb->q_info, 0, sizeof(tBTA_AV_Q_INFO));
+
+ p_scb->l2c_bufs = 0;
+ p_scb->p_cos->open(p_scb->hndl,
+ p_scb->codec_type, p_scb->cfg.codec_info, mtu);
+
+ {
+ /* TODO check if other audio channel is open.
+ * If yes, check if reconfig is needed
+ * Rigt now we do not do this kind of checking.
+ * BTA-AV is INT for 2nd audio connection.
+ * The application needs to make sure the current codec_info is proper.
+ * If one audio connection is open and another SNK attempts to connect to AV,
+ * the connection will be rejected.
+ */
+ /* check if other audio channel is started. If yes, start */
+ bdcpy(open.bd_addr, p_scb->peer_addr);
+ open.chnl = p_scb->chnl;
+ open.hndl = p_scb->hndl;
+ open.status = BTA_AV_SUCCESS;
+ open.starting = bta_av_chk_start(p_scb);
+ open.edr = 0;
+ if( NULL != (p = BTM_ReadRemoteFeatures(p_scb->peer_addr)))
+ {
+ if(HCI_EDR_ACL_2MPS_SUPPORTED(p))
+ open.edr |= BTA_AV_EDR_2MBPS;
+ if(HCI_EDR_ACL_3MPS_SUPPORTED(p))
+ open.edr |= BTA_AV_EDR_3MBPS;
+ }
+#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
+ bta_ar_avdt_conn(BTA_ID_AV, open.bd_addr);
+#endif
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ open.sep = AVDT_TSEP_SRC;
+
+ (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open);
+ if(open.starting)
+ {
+ bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
+ }
+ }
+
+#if 0 /* TODO: implement the property enable/disable */
+ // This code is used to pass PTS TC for AVDTP ABORT
+ char value[PROPERTY_VALUE_MAX] = {0};
+ if ((property_get("bluetooth.pts.force_a2dp_abort", value, "false"))
+ && (!strcmp(value, "true")))
+ {
+ APPL_TRACE_ERROR ("%s: Calling AVDT_AbortReq", __func__);
+ AVDT_AbortReq(p_scb->avdt_handle);
+ }
+#endif /* #if 0*/
+}
+
+/*******************************************************************************
+**
+** Function bta_av_security_ind
+**
+** Description Handle an AVDTP security indication.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_security_ind (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_PROTECT_REQ protect_req;
+
+ p_scb->avdt_label = p_data->str_msg.msg.hdr.label;
+
+ if (bta_av_cb.features & BTA_AV_FEAT_PROTECT)
+ {
+ protect_req.chnl = p_scb->chnl;
+ protect_req.hndl = p_scb->hndl;
+ /*
+ APPL_TRACE_EVENT("sec ind handle: x%x", protect_req.hndl);
+ */
+ protect_req.p_data = p_data->str_msg.msg.security_ind.p_data;
+ protect_req.len = p_data->str_msg.msg.security_ind.len;
+
+ (*bta_av_cb.p_cback)(BTA_AV_PROTECT_REQ_EVT, (tBTA_AV *) &protect_req);
+ }
+ /* app doesn't support security indication; respond with failure */
+ else
+ {
+ AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_NSC, NULL, 0);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_security_cfm
+**
+** Description Handle an AVDTP security confirm.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_security_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_PROTECT_RSP protect_rsp;
+
+ if (bta_av_cb.features & BTA_AV_FEAT_PROTECT)
+ {
+ protect_rsp.chnl = p_scb->chnl;
+ protect_rsp.hndl = p_scb->hndl;
+ protect_rsp.p_data = p_data->str_msg.msg.security_cfm.p_data;
+ protect_rsp.len = p_data->str_msg.msg.security_cfm.len;
+ protect_rsp.err_code= p_data->str_msg.msg.hdr.err_code;
+
+ (*bta_av_cb.p_cback)(BTA_AV_PROTECT_RSP_EVT, (tBTA_AV *) &protect_rsp);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_do_close
+**
+** Description Close stream.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_do_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ /* stop stream if started */
+ if (p_scb->co_started)
+ {
+ bta_av_str_stopped(p_scb, NULL);
+ }
+ bta_sys_stop_timer(&bta_av_cb.sig_tmr);
+
+ /* close stream */
+ p_scb->started = FALSE;
+
+ /* drop the buffers queued in L2CAP */
+ L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+
+ AVDT_CloseReq(p_scb->avdt_handle);
+ /* just in case that the link is congested, link is flow controled by peer or
+ * for whatever reason the the close request can not be sent in time.
+ * when this timer expires, AVDT_DisconnectReq will be called to disconnect the link
+ */
+ bta_sys_start_timer(&p_scb->timer,
+ (UINT16)BTA_AV_API_CLOSE_EVT,
+ BTA_AV_CLOSE_REQ_TIME_VAL);
+
+}
+
+/*******************************************************************************
+**
+** Function bta_av_connect_req
+**
+** Description Connect AVDTP connection.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_connect_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ utl_freebuf((void **) &p_scb->p_disc_db);
+
+ if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR)
+ {
+ /* SNK initiated L2C connection while SRC was doing SDP. */
+ /* Wait until timeout to check if SNK starts signalling. */
+ APPL_TRACE_EVENT("bta_av_connect_req: coll_mask = 0x%2X", p_scb->coll_mask);
+ return;
+ }
+
+ AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_sdp_failed
+**
+** Description Service discovery failed.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_sdp_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ if (!p_scb->open_status)
+ p_scb->open_status = BTA_AV_FAIL_SDP;
+
+ utl_freebuf((void **) &p_scb->p_disc_db);
+ bta_av_str_closed(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_disc_results
+**
+** Description Handle the AVDTP discover results. Search through the
+** results and find the first available stream, and get
+** its capabilities.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_disc_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 num_snks = 0, num_srcs =0, i;
+ /* our uuid in case we initiate connection */
+ UINT16 uuid_int = p_scb->uuid_int;
+
+ APPL_TRACE_DEBUG(" initiator UUID 0x%x", uuid_int);
+ /* store number of stream endpoints returned */
+ p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps;
+
+ for (i = 0; i < p_scb->num_seps; i++)
+ {
+ /* steam not in use, is a sink, and is audio */
+ if ((p_scb->sep_info[i].in_use == FALSE) &&
+ (p_scb->sep_info[i].media_type == p_scb->media_type))
+ {
+ if((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) &&
+ (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE))
+ num_snks++;
+
+ if((p_scb->sep_info[i].tsep == AVDT_TSEP_SRC) &&
+ (uuid_int == UUID_SERVCLASS_AUDIO_SINK))
+ num_srcs++;
+
+ }
+ }
+
+ p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, num_srcs, p_scb->peer_addr,
+ uuid_int);
+ p_scb->num_disc_snks = num_snks;
+ p_scb->num_disc_srcs = num_srcs;
+
+ /* if we got any */
+ if (p_scb->num_seps > 0)
+ {
+ /* initialize index into discovery results */
+ p_scb->sep_info_idx = 0;
+
+ /* get the capabilities of the first available stream */
+ bta_av_next_getcap(p_scb, p_data);
+ }
+ /* else we got discover response but with no streams; we're done */
+ else
+ {
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_disc_res_as_acp
+**
+** Description Handle the AVDTP discover results. Search through the
+** results and find the first available stream, and get
+** its capabilities.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_disc_res_as_acp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 num_snks = 0, i;
+
+ /* store number of stream endpoints returned */
+ p_scb->num_seps = p_data->str_msg.msg.discover_cfm.num_seps;
+
+
+
+ for (i = 0; i < p_scb->num_seps; i++)
+ {
+ /* steam is a sink, and is audio */
+ if ((p_scb->sep_info[i].tsep == AVDT_TSEP_SNK) &&
+ (p_scb->sep_info[i].media_type == p_scb->media_type))
+ {
+ p_scb->sep_info[i].in_use = FALSE;
+ num_snks++;
+ }
+ }
+ p_scb->p_cos->disc_res(p_scb->hndl, p_scb->num_seps, num_snks, 0, p_scb->peer_addr,
+ UUID_SERVCLASS_AUDIO_SOURCE);
+ p_scb->num_disc_snks = num_snks;
+ p_scb->num_disc_srcs = 0;
+
+ /* if we got any */
+ if (p_scb->num_seps > 0)
+ {
+ /* initialize index into discovery results */
+ p_scb->sep_info_idx = 0;
+
+ /* get the capabilities of the first available stream */
+ bta_av_next_getcap(p_scb, p_data);
+ }
+ /* else we got discover response but with no streams; we're done */
+ else
+ {
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_save_caps
+**
+** Description report the SNK SEP capabilities to application
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_save_caps(tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tAVDT_CFG cfg;
+ tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx];
+ UINT8 old_wait = p_scb->wait;
+ BOOLEAN getcap_done = FALSE;
+
+ APPL_TRACE_DEBUG("bta_av_save_caps num_seps:%d sep_info_idx:%d wait:x%x",
+ p_scb->num_seps, p_scb->sep_info_idx, p_scb->wait);
+ memcpy(&cfg, p_scb->p_cap, sizeof(tAVDT_CFG));
+ /* let application know the capability of the SNK */
+ p_scb->p_cos->getcfg(p_scb->hndl, cfg.codec_info[BTA_AV_CODEC_TYPE_IDX],
+ cfg.codec_info, &p_scb->sep_info_idx, p_info->seid,
+ &cfg.num_protect, cfg.protect_info);
+
+ p_scb->sep_info_idx++;
+ if(p_scb->num_seps > p_scb->sep_info_idx)
+ {
+ /* Some devices have seps at the end of the discover list, which is not */
+ /* matching media type(video not audio). */
+ /* In this case, we are done with getcap without sending another */
+ /* request to AVDT. */
+ if (!bta_av_next_getcap(p_scb, p_data))
+ getcap_done = TRUE;
+ }
+ else
+ getcap_done = TRUE;
+
+ if (getcap_done)
+ {
+ /* we are done getting capabilities. restore the p_cb->sep_info_idx */
+ p_scb->sep_info_idx = 0;
+ p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON|BTA_AV_WAIT_ACP_CAPS_STARTED);
+ if (old_wait & BTA_AV_WAIT_ACP_CAPS_STARTED)
+ {
+ bta_av_start_ok (p_scb, NULL);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_set_use_rc
+**
+** Description set to use AVRC for this stream control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_set_use_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ p_scb->use_rc = TRUE;
+}
+
+/*******************************************************************************
+**
+** Function bta_av_cco_close
+**
+** Description call close call-out function.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_cco_close (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT16 mtu;
+ UNUSED(p_data);
+
+ mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU);
+
+ p_scb->p_cos->close(p_scb->hndl, p_scb->codec_type, mtu);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_open_failed
+**
+** Description Failed to open an AVDT stream
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_open_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+
+ BOOLEAN is_av_opened = FALSE;
+ tBTA_AV_SCB * p_opened_scb = NULL;
+ UINT8 idx;
+ tBTA_AV_OPEN open;
+
+ APPL_TRACE_DEBUG("bta_av_open_failed");
+ p_scb->open_status = BTA_AV_FAIL_STREAM;
+ bta_av_cco_close(p_scb, p_data);
+
+ /* check whether there is already an opened audio or video connection with the same device */
+ for (idx = 0; (idx < BTA_AV_NUM_STRS) && (is_av_opened == FALSE); idx++ )
+ {
+ p_opened_scb = bta_av_cb.p_scb[idx];
+ if (p_opened_scb && (p_opened_scb->state == BTA_AV_OPEN_SST) && (!bdcmp(p_opened_scb->peer_addr,p_scb->peer_addr )) )
+ is_av_opened = TRUE;
+
+ }
+
+ /* if there is already an active AV connnection with the same bd_addr,
+ don't send disconnect req, just report the open event with BTA_AV_FAIL_GET_CAP status */
+ if (is_av_opened == TRUE)
+ {
+ bdcpy(open.bd_addr, p_scb->peer_addr);
+ open.chnl = p_scb->chnl;
+ open.hndl = p_scb->hndl;
+ open.status = BTA_AV_FAIL_GET_CAP;
+ open.starting = bta_av_chk_start(p_scb);
+ open.edr = 0;
+ /* set the state back to initial state */
+ bta_av_set_scb_sst_init(p_scb);
+
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ open.sep = AVDT_TSEP_SRC;
+
+ (*bta_av_cb.p_cback)(BTA_AV_OPEN_EVT, (tBTA_AV *) &open);
+
+ }
+ else
+ {
+ AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function bta_av_getcap_results
+**
+** Description Handle the AVDTP get capabilities results. Check the codec
+** type and see if it matches ours. If it does not, get the
+** capabilities of the next stream, if any.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_getcap_results (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tAVDT_CFG cfg;
+ UINT8 media_type;
+ tAVDT_SEP_INFO *p_info = &p_scb->sep_info[p_scb->sep_info_idx];
+ UINT16 uuid_int; /* UUID for which connection was initiatied */
+
+ memcpy(&cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
+ cfg.num_codec = 1;
+ cfg.num_protect = p_scb->p_cap->num_protect;
+ memcpy(cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE);
+ memcpy(cfg.protect_info, p_scb->p_cap->protect_info, AVDT_PROTECT_SIZE);
+ media_type = p_scb->p_cap->codec_info[BTA_AV_MEDIA_TYPE_IDX] >> 4;
+
+ APPL_TRACE_DEBUG("num_codec %d", p_scb->p_cap->num_codec);
+ APPL_TRACE_DEBUG("media type x%x, x%x", media_type, p_scb->media_type);
+#if AVDT_MULTIPLEXING == TRUE
+ APPL_TRACE_DEBUG("mux x%x, x%x", cfg.mux_mask, p_scb->p_cap->mux_mask);
+#endif
+
+ /* if codec present and we get a codec configuration */
+ if ((p_scb->p_cap->num_codec != 0) &&
+ (media_type == p_scb->media_type) &&
+ (p_scb->p_cos->getcfg(p_scb->hndl, p_scb->p_cap->codec_info[BTA_AV_CODEC_TYPE_IDX],
+ cfg.codec_info, &p_scb->sep_info_idx, p_info->seid,
+ &cfg.num_protect, cfg.protect_info) == 0))
+ {
+#if AVDT_MULTIPLEXING == TRUE
+ cfg.mux_mask &= p_scb->p_cap->mux_mask;
+ APPL_TRACE_DEBUG("mux_mask used x%x", cfg.mux_mask);
+#endif
+ /* save copy of codec type and configuration */
+ p_scb->codec_type = cfg.codec_info[BTA_AV_CODEC_TYPE_IDX];
+ memcpy(&p_scb->cfg, &cfg, sizeof(tAVDT_CFG));
+
+ uuid_int = p_scb->uuid_int;
+ APPL_TRACE_DEBUG(" initiator UUID = 0x%x ", uuid_int);
+ if (uuid_int == UUID_SERVCLASS_AUDIO_SOURCE)
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
+ else if (uuid_int == UUID_SERVCLASS_AUDIO_SINK)
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SNK));
+
+ /* use only the services peer supports */
+ cfg.psc_mask &= p_scb->p_cap->psc_mask;
+ p_scb->cur_psc_mask = cfg.psc_mask;
+
+ if ((uuid_int == UUID_SERVCLASS_AUDIO_SINK) &&
+ (p_scb->seps[p_scb->sep_idx].p_app_data_cback != NULL))
+ {
+ APPL_TRACE_DEBUG(" Configure Deoder for Sink Connection ");
+ p_scb->seps[p_scb->sep_idx].p_app_data_cback(BTA_AV_MEDIA_SINK_CFG_EVT,
+ (tBTA_AV_MEDIA*)p_scb->cfg.codec_info);
+ }
+
+ /* open the stream */
+ AVDT_OpenReq(p_scb->seps[p_scb->sep_idx].av_handle, p_scb->peer_addr,
+ p_scb->sep_info[p_scb->sep_info_idx].seid, &cfg);
+
+ if (!bta_av_is_rcfg_sst(p_scb))
+ {
+ /* free capabilities buffer */
+ utl_freebuf((void **) &p_scb->p_cap);
+ }
+ }
+ else
+ {
+ /* try the next stream, if any */
+ p_scb->sep_info_idx++;
+ bta_av_next_getcap(p_scb, p_data);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function bta_av_setconfig_rej
+**
+** Description Send AVDTP set config reject.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_setconfig_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_REJECT reject;
+ UINT8 avdt_handle = p_data->ci_setconfig.avdt_handle;
+
+ bta_av_adjust_seps_idx(p_scb, avdt_handle);
+ APPL_TRACE_DEBUG("bta_av_setconfig_rej: sep_idx: %d",p_scb->sep_idx);
+ AVDT_ConfigRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_UNSUP_CFG, 0);
+
+ bdcpy(reject.bd_addr, p_data->str_msg.bd_addr);
+ reject.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_REJECT_EVT, (tBTA_AV *) &reject);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_discover_req
+**
+** Description Send an AVDTP discover request to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_discover_req (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ /* send avdtp discover request */
+
+ AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS, bta_av_dt_cback[p_scb->hdi]);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_conn_failed
+**
+** Description AVDTP connection failed.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_conn_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ p_scb->open_status = BTA_AV_FAIL_STREAM;
+ bta_av_str_closed(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_do_start
+**
+** Description Start stream.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_do_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 policy = HCI_ENABLE_SNIFF_MODE;
+ UINT8 cur_role;
+
+ APPL_TRACE_DEBUG("bta_av_do_start sco_occupied:%d, role:x%x, started:%d", bta_av_cb.sco_occupied, p_scb->role, p_scb->started);
+ if (bta_av_cb.sco_occupied)
+ {
+ bta_av_start_failed(p_scb, p_data);
+ return;
+ }
+
+ /* disallow role switch during streaming, only if we are the master role
+ * i.e. allow role switch, if we are slave.
+ * It would not hurt us, if the peer device wants us to be master */
+ if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) &&
+ (cur_role == BTM_ROLE_MASTER) )
+ {
+ policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ }
+
+ bta_sys_clear_policy(BTA_ID_AV, policy, p_scb->peer_addr);
+
+ if ((p_scb->started == FALSE) && ((p_scb->role & BTA_AV_ROLE_START_INT) == 0))
+ {
+ p_scb->role |= BTA_AV_ROLE_START_INT;
+ bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+
+ AVDT_StartReq(&p_scb->avdt_handle, 1);
+ }
+ else if (p_scb->started)
+ {
+ p_scb->role |= BTA_AV_ROLE_START_INT;
+ if ( p_scb->wait == 0 ) {
+ if (p_scb->role & BTA_AV_ROLE_SUSPEND) {
+ notify_start_failed(p_scb);
+ } else {
+ bta_av_start_ok(p_scb, NULL);
+ }
+ }
+ }
+ APPL_TRACE_DEBUG("started %d role:x%x", p_scb->started, p_scb->role);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_str_stopped
+**
+** Description Stream stopped.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_str_stopped (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_SUSPEND suspend_rsp;
+ UINT8 start = p_scb->started;
+ BOOLEAN sus_evt = TRUE;
+ BT_HDR *p_buf;
+ UINT8 policy = HCI_ENABLE_SNIFF_MODE;
+
++ APPL_TRACE_ERROR("bta_av_str_stopped:audio_open_cnt=%d, p_data %p",
+ bta_av_cb.audio_open_cnt, p_data);
+
+ bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+ if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1)
+ policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr);
+
+ if (p_scb->co_started)
+ {
+ bta_av_stream_chg(p_scb, FALSE);
+ p_scb->co_started = FALSE;
+
+ p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type);
+ L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO);
+ }
+
+ /* if q_info.a2d_list is not empty, drop it now */
+ if (BTA_AV_CHNL_AUDIO == p_scb->chnl) {
+ while (!list_is_empty(p_scb->a2d_list))
+ {
+ p_buf = (BT_HDR *)list_front(p_scb->a2d_list);
+ list_remove(p_scb->a2d_list, p_buf);
+ GKI_freebuf(p_buf);
+ }
+
+ /* drop the audio buffers queued in L2CAP */
+ if (p_data && p_data->api_stop.flush)
+ L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+ }
+
+ suspend_rsp.chnl = p_scb->chnl;
+ suspend_rsp.hndl = p_scb->hndl;
+
+ if (p_data && p_data->api_stop.suspend)
+ {
+ APPL_TRACE_DEBUG("suspending: %d, sup:%d", start, p_scb->suspend_sup);
+ if ((start) && (p_scb->suspend_sup))
+ {
+ sus_evt = FALSE;
+ p_scb->l2c_bufs = 0;
+ AVDT_SuspendReq(&p_scb->avdt_handle, 1);
+ }
+
+ /* send SUSPEND_EVT event only if not in reconfiguring state and sus_evt is TRUE*/
+ if ((sus_evt)&&(p_scb->state != BTA_AV_RCFG_SST))
+ {
+ suspend_rsp.status = BTA_AV_SUCCESS;
+ suspend_rsp.initiator = TRUE;
+ (*bta_av_cb.p_cback)(BTA_AV_SUSPEND_EVT, (tBTA_AV *) &suspend_rsp);
+ }
+ }
+ else
+ {
+ suspend_rsp.status = BTA_AV_SUCCESS;
+ suspend_rsp.initiator = TRUE;
+ APPL_TRACE_EVENT("bta_av_str_stopped status %d", suspend_rsp.status);
+
+ /* send STOP_EVT event only if not in reconfiguring state */
+ if (p_scb->state != BTA_AV_RCFG_SST)
+ {
+ (*bta_av_cb.p_cback)(BTA_AV_STOP_EVT, (tBTA_AV *) &suspend_rsp);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_reconfig
+**
+** Description process the reconfigure request.
+** save the parameter in control block and
+** suspend, reconfigure or close the stream
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_reconfig (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tAVDT_CFG *p_cfg;
+ tBTA_AV_API_STOP stop;
+ tBTA_AV_RECONFIG evt;
+ tBTA_AV_API_RCFG *p_rcfg = &p_data->api_reconfig;
+
+ APPL_TRACE_DEBUG("bta_av_reconfig r:%d, s:%d idx: %d (o:%d)",
+ p_scb->recfg_sup, p_scb->suspend_sup,
+ p_scb->rcfg_idx, p_scb->sep_info_idx);
+
+ p_scb->num_recfg = 0;
+ /* store the new configuration in control block */
+ if (p_scb->p_cap == NULL)
+ {
+ p_scb->p_cap = (tAVDT_CFG *) GKI_getbuf(sizeof(tAVDT_CFG));
+ }
+ if((p_cfg = p_scb->p_cap) == NULL)
+ {
+ /* report failure */
+ evt.status = BTA_AV_FAIL_RESOURCES;
+ evt.chnl = p_scb->chnl;
+ evt.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt);
+
+ /* this event is not possible in this state.
+ * use it to bring the SSM back to open state */
+ bta_av_ssm_execute(p_scb, BTA_AV_SDP_DISC_OK_EVT, NULL);
+ return;
+ }
+
+ /*if(bta_av_cb.features & BTA_AV_FEAT_RCCT)*/
+ bta_sys_stop_timer(&p_scb->timer);
+
+ memcpy(p_cfg, &p_scb->cfg, sizeof(tAVDT_CFG));
+ p_cfg->num_protect = p_rcfg->num_protect;
+ memcpy(p_cfg->codec_info, p_rcfg->codec_info, AVDT_CODEC_SIZE);
+ memcpy(p_cfg->protect_info, p_rcfg->p_protect_info, p_rcfg->num_protect);
+ p_scb->rcfg_idx = p_rcfg->sep_info_idx;
+ p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
+
+ /* if the requested index differs from the current one, we can only close/open */
+ if ((p_scb->rcfg_idx == p_scb->sep_info_idx) &&
+ (p_rcfg->suspend)&& (p_scb->recfg_sup) && (p_scb->suspend_sup))
+ {
+ if(p_scb->started)
+ {
+ stop.flush = FALSE;
+ stop.suspend = TRUE;
+ bta_av_str_stopped(p_scb, (tBTA_AV_DATA *)&stop);
+ }
+ else
+ {
+ APPL_TRACE_DEBUG("Reconfig");
+ AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap);
+ p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
+ }
+ }
+ else
+ {
+ /* close the stream */
+ APPL_TRACE_DEBUG("close/open num_protect: %d", p_cfg->num_protect);
+ if(p_scb->started)
+ {
+ bta_av_str_stopped(p_scb, NULL);
+ p_scb->started = FALSE;
+
+ /* drop the buffers queued in L2CAP */
+ L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+
+ AVDT_CloseReq(p_scb->avdt_handle);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_data_path
+**
+** Description Handle stream data path.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_data_path (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ BT_HDR *p_buf = NULL;
+ UINT32 data_len;
+ UINT32 timestamp;
+ BOOLEAN new_buf = FALSE;
+ UINT8 m_pt = 0x60 | p_scb->codec_type;
+ tAVDT_DATA_OPT_MASK opt;
+ UNUSED(p_data);
+
+ if (p_scb->cong)
+ {
+ return;
+ }
+
+ /*
+ APPL_TRACE_ERROR("q: %d", p_scb->l2c_bufs);
+ */
+ //Always get the current number of bufs que'd up
+ p_scb->l2c_bufs = (UINT8)L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_GET);
+
+ if (!list_is_empty(p_scb->a2d_list)) {
+ p_buf = (BT_HDR *)list_front(p_scb->a2d_list);
+ list_remove(p_scb->a2d_list, p_buf);
+ /* use q_info.a2d data, read the timestamp */
+ timestamp = *(UINT32 *)(p_buf + 1);
+ }
+ else
+ {
+ new_buf = TRUE;
+ /* a2d_list empty, call co_data, dup data to other channels */
+ p_buf = (BT_HDR *)p_scb->p_cos->data(p_scb->codec_type, &data_len,
+ ×tamp);
+
+ if (p_buf)
+ {
+ /* use the offset area for the time stamp */
+ *(UINT32 *)(p_buf + 1) = timestamp;
+
+ /* dup the data to other channels */
+ bta_av_dup_audio_buf(p_scb, p_buf);
+ }
+ }
+
+ if(p_buf)
+ {
+ if(p_scb->l2c_bufs < (BTA_AV_QUEUE_DATA_CHK_NUM))
+ {
+ /* there's a buffer, just queue it to L2CAP */
+ /* There's no need to increment it here, it is always read from L2CAP see above */
+ /* p_scb->l2c_bufs++; */
+ /*
+ APPL_TRACE_ERROR("qw: %d", p_scb->l2c_bufs);
+ */
+
+ /* opt is a bit mask, it could have several options set */
+ opt = AVDT_DATA_OPT_NONE;
+ if (p_scb->no_rtp_hdr)
+ {
+ opt |= AVDT_DATA_OPT_NO_RTP;
+ }
+
+ AVDT_WriteReqOpt(p_scb->avdt_handle, p_buf, timestamp, m_pt, opt);
+ p_scb->cong = TRUE;
+ }
+ else
+ {
+ /* there's a buffer, but L2CAP does not seem to be moving data */
+ if(new_buf)
+ {
+ /* just got this buffer from co_data,
+ * put it in queue */
+ list_append(p_scb->a2d_list, p_buf);
+ }
+ else
+ {
+ /* just dequeue it from the a2d_list */
+ if (list_length(p_scb->a2d_list) < 3) {
+ /* put it back to the queue */
+ list_prepend(p_scb->a2d_list, p_buf);
+ }
+ else
+ {
+ /* too many buffers in a2d_list, drop it. */
+ bta_av_co_audio_drop(p_scb->hndl);
+ GKI_freebuf(p_buf);
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_start_ok
+**
+** Description Stream started.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_start_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_START start;
+ tBTA_AV_API_STOP stop;
+ BOOLEAN initiator = FALSE;
+ BOOLEAN suspend = FALSE;
+ UINT16 flush_to;
+ UINT8 new_role = p_scb->role;
+ BT_HDR hdr;
+ UINT8 policy = HCI_ENABLE_SNIFF_MODE;
+ UINT8 cur_role;
+
+ APPL_TRACE_DEBUG("bta_av_start_ok wait:x%x, role:x%x", p_scb->wait, p_scb->role);
+
+ p_scb->started = TRUE;
+ if (p_scb->sco_suspend)
+ {
+ p_scb->sco_suspend = FALSE;
+ }
+
+ if (new_role & BTA_AV_ROLE_START_INT)
+ initiator = TRUE;
+
+ /* for A2DP SINK we do not send get_caps */
+ if ((p_scb->avdt_handle == p_scb->seps[p_scb->sep_idx].av_handle)
+ &&(p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK))
+ {
+ p_scb->wait &= ~(BTA_AV_WAIT_ACP_CAPS_ON);
+ APPL_TRACE_DEBUG(" Local SEP type is SNK new wait is 0x%x",p_scb->wait);
+ }
+ if (p_scb->wait & BTA_AV_WAIT_ROLE_SW_FAILED)
+ {
+ /* role switch has failed */
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_FAILED;
+ p_data = (tBTA_AV_DATA *)&hdr;
+ hdr.offset = BTA_AV_RS_FAIL;
+ }
+ APPL_TRACE_DEBUG("wait:x%x", p_scb->wait);
+
+ if (p_data && (p_data->hdr.offset != BTA_AV_RS_NONE))
+ {
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+ if (p_data->hdr.offset == BTA_AV_RS_FAIL)
+ {
+ bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_FAIL_ROLE;
+ start.hndl = p_scb->hndl;
+ start.initiator = initiator;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+ return;
+ }
+ }
+
+ if (!bta_av_link_role_ok(p_scb, A2D_SET_ONE_BIT))
+ p_scb->q_tag = BTA_AV_Q_TAG_START;
+ else
+ {
+ /* The wait flag may be set here while we are already master on the link */
+ /* this could happen if a role switch complete event occurred during reconfig */
+ /* if we are now master on the link, there is no need to wait for the role switch, */
+ /* complete anymore so we can clear the wait for role switch flag */
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+ }
+
+ if (p_scb->wait & (BTA_AV_WAIT_ROLE_SW_RES_OPEN|BTA_AV_WAIT_ROLE_SW_RES_START))
+ {
+ p_scb->wait |= BTA_AV_WAIT_ROLE_SW_STARTED;
+ p_scb->q_tag = BTA_AV_Q_TAG_START;
+ }
+
+ if (p_scb->wait)
+ {
+ APPL_TRACE_ERROR("wait:x%x q_tag:%d- not started", p_scb->wait, p_scb->q_tag);
+ /* Clear first bit of p_scb->wait and not to return from this point else
+ * HAL layer gets blocked. And if there is delay in Get Capability response as
+ * first bit of p_scb->wait is cleared hence it ensures bt_av_start_ok is not called
+ * again from bta_av_save_caps.
+ */
+ p_scb->wait &= ~BTA_AV_WAIT_ACP_CAPS_ON;
+ }
+
+ /* tell role manager to check M/S role */
+ bta_sys_conn_open(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);
+
+ bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+
+ if(p_scb->media_type == AVDT_MEDIA_AUDIO)
+ {
+ /* in normal logic, conns should be bta_av_cb.audio_count - 1,
+ * However, bta_av_stream_chg is not called to increase bta_av_cb.audio_count yet.
+ * If the code were to be re-arranged for some reasons, this number may need to be changed
+ */
+ p_scb->co_started = bta_av_cb.audio_open_cnt;
+ flush_to = p_bta_av_cfg->p_audio_flush_to[p_scb->co_started - 1];
+ }
+ else
+ {
+ flush_to = p_bta_av_cfg->video_flush_to;
+ }
+ L2CA_SetFlushTimeout(p_scb->peer_addr, flush_to );
+
+ /* clear the congestion flag */
+ p_scb->cong = FALSE;
+
+ if (new_role & BTA_AV_ROLE_START_INT)
+ {
+ new_role &= ~BTA_AV_ROLE_START_INT;
+ }
+ else if ((new_role & BTA_AV_ROLE_AD_ACP) && (new_role & BTA_AV_ROLE_SUSPEND_OPT))
+ {
+ suspend = TRUE;
+ }
+
+ if (!suspend)
+ {
+ p_scb->q_tag = BTA_AV_Q_TAG_STREAM;
+ bta_av_stream_chg(p_scb, TRUE);
+ }
+
+ {
+ /* If sink starts stream, disable sniff mode here */
+ if (!initiator)
+ {
+ /* If souce is the master role, disable role switch during streaming.
+ * Otherwise allow role switch, if source is slave.
+ * Because it would not hurt source, if the peer device wants source to be master */
+ if ((BTM_GetRole (p_scb->peer_addr, &cur_role) == BTM_SUCCESS) &&
+ (cur_role == BTM_ROLE_MASTER) )
+ {
+ policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ }
+
+ bta_sys_clear_policy(BTA_ID_AV, policy, p_scb->peer_addr);
+ }
+
+ p_scb->role = new_role;
+ p_scb->role &= ~BTA_AV_ROLE_AD_ACP;
+ p_scb->role &= ~BTA_AV_ROLE_SUSPEND_OPT;
+
+ p_scb->no_rtp_hdr = FALSE;
+ p_scb->p_cos->start(p_scb->hndl, p_scb->codec_type, p_scb->cfg.codec_info, &p_scb->no_rtp_hdr);
+ p_scb->co_started = TRUE;
+
+ APPL_TRACE_DEBUG("bta_av_start_ok suspending: %d, role:x%x, init %d",
+ suspend, p_scb->role, initiator);
+
+ start.suspending = suspend;
+ start.initiator = initiator;
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_SUCCESS;
+ start.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+
+ if(suspend)
+ {
+ p_scb->role |= BTA_AV_ROLE_SUSPEND;
+ p_scb->cong = TRUE; /* do not allow the media data to go through */
+ /* do not duplicate the media packets to this channel */
+ p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type);
+ p_scb->co_started = FALSE;
+ stop.flush = FALSE;
+ stop.suspend = TRUE;
+ bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA *)&stop);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_start_failed
+**
+** Description Stream start failed.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_start_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ if(p_scb->started == FALSE && p_scb->co_started == FALSE)
+ {
+ bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+ notify_start_failed(p_scb);
+ }
+
+ bta_sys_set_policy(BTA_ID_AV, (HCI_ENABLE_SNIFF_MODE|HCI_ENABLE_MASTER_SLAVE_SWITCH), p_scb->peer_addr);
+ p_scb->sco_suspend = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_av_str_closed
+**
+** Description Stream closed.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_str_closed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV data;
+ tBTA_AV_EVT event;
+ UINT16 mtu;
+ UINT8 policy = HCI_ENABLE_SNIFF_MODE;
+
+ if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1)
+ policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr);
+ if (bta_av_cb.audio_open_cnt <= 1)
+ {
+ /* last connection - restore the allow switch flag */
+ L2CA_SetDesireRole(L2CAP_ROLE_ALLOW_SWITCH);
+ }
+
+ if (p_scb->open_status)
+ {
+ /* must be failure when opening the stream */
+ bdcpy(data.open.bd_addr, p_scb->peer_addr);
+ data.open.status = p_scb->open_status;
+ data.open.chnl = p_scb->chnl;
+ data.open.hndl = p_scb->hndl;
+
+ if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC )
+ data.open.sep = AVDT_TSEP_SNK;
+ else if (p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SNK )
+ data.open.sep = AVDT_TSEP_SRC;
+
+ event = BTA_AV_OPEN_EVT;
+ p_scb->open_status = BTA_AV_SUCCESS;
+
+ bta_sys_conn_close(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);
+ bta_av_cleanup(p_scb, p_data);
+ (*bta_av_cb.p_cback)(event, &data);
+ }
+ else
+ {
+ /* do stop if we were started */
+ if (p_scb->co_started)
+ {
+ bta_av_str_stopped(p_scb, NULL);
+ }
+
+ /* Update common mtu shared by remaining connectons */
+ mtu = bta_av_chk_mtu(p_scb, BTA_AV_MAX_A2DP_MTU);
+
+ {
+ p_scb->p_cos->close(p_scb->hndl, p_scb->codec_type, mtu);
+ data.close.chnl = p_scb->chnl;
+ data.close.hndl = p_scb->hndl;
+ event = BTA_AV_CLOSE_EVT;
+
+ bta_sys_conn_close(BTA_ID_AV, p_scb->app_id, p_scb->peer_addr);
+ bta_av_cleanup(p_scb, p_data);
+ (*bta_av_cb.p_cback)(event, &data);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_clr_cong
+**
+** Description Clear stream congestion flag.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_clr_cong (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ if(p_scb->co_started)
+ p_scb->cong = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_av_suspend_cfm
+**
+** Description process the suspend response
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_suspend_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_SUSPEND suspend_rsp;
+ UINT8 err_code = p_data->str_msg.msg.hdr.err_code;
+ UINT8 policy = HCI_ENABLE_SNIFF_MODE;
+
+ APPL_TRACE_DEBUG ("bta_av_suspend_cfm:audio_open_cnt = %d, err_code = %d",
+ bta_av_cb.audio_open_cnt, err_code);
+
+ if (p_scb->started == FALSE)
+ {
+ /* handle the condition where there is a collision of SUSPEND req from either side
+ ** Second SUSPEND req could be rejected. Do not treat this as a failure
+ */
+ APPL_TRACE_WARNING("bta_av_suspend_cfm: already suspended, ignore, err_code %d",
+ err_code);
+ return;
+ }
+
+ suspend_rsp.status = BTA_AV_SUCCESS;
+ if (err_code && (err_code != AVDT_ERR_BAD_STATE))
+ {
+ /* Disable suspend feature only with explicit rejection(not with timeout) */
+ if (err_code != AVDT_ERR_TIMEOUT)
+ {
+ p_scb->suspend_sup = FALSE;
+ }
+ suspend_rsp.status = BTA_AV_FAIL;
+
+ APPL_TRACE_ERROR ("bta_av_suspend_cfm: suspend failed, closing connection");
+
+ /* SUSPEND failed. Close connection. */
+ bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, NULL);
+ }
+ else
+ {
+ /* only set started to FALSE when suspend is successful */
+ p_scb->started = FALSE;
+ }
+
+ if (p_scb->role & BTA_AV_ROLE_SUSPEND)
+ {
+ p_scb->role &= ~BTA_AV_ROLE_SUSPEND;
+ p_scb->cong = FALSE;
+ }
+
+ bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
+ if ((bta_av_cb.features & BTA_AV_FEAT_MASTER) == 0 || bta_av_cb.audio_open_cnt == 1)
+ policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH;
+ bta_sys_set_policy(BTA_ID_AV, policy, p_scb->peer_addr);
+
+ /* in case that we received suspend_ind, we may need to call co_stop here */
+ if(p_scb->co_started)
+ {
+ bta_av_stream_chg(p_scb, FALSE);
+
+ {
+ p_scb->co_started = FALSE;
+ p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type);
+ }
+ L2CA_SetFlushTimeout(p_scb->peer_addr, L2CAP_DEFAULT_FLUSH_TO);
+ }
+
+ {
+ suspend_rsp.chnl = p_scb->chnl;
+ suspend_rsp.hndl = p_scb->hndl;
+ suspend_rsp.initiator = p_data->str_msg.initiator;
+ (*bta_av_cb.p_cback)(BTA_AV_SUSPEND_EVT, (tBTA_AV *) &suspend_rsp);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_str_ok
+**
+** Description report reconfigure successful
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_str_ok (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_RECONFIG evt;
+ UNUSED(p_data);
+
+ p_scb->l2c_cid = AVDT_GetL2CapChannel(p_scb->avdt_handle);
+ APPL_TRACE_DEBUG("bta_av_rcfg_str_ok: l2c_cid: %d", p_scb->l2c_cid);
+
+ /* rc listen */
+ bta_av_st_rc_timer(p_scb, NULL);
+ utl_freebuf((void **)&p_scb->p_cap);
+
+ /* No need to keep the role bits once reconfig is done. */
+ p_scb->role &= ~BTA_AV_ROLE_AD_ACP;
+ p_scb->role &= ~BTA_AV_ROLE_SUSPEND_OPT;
+ p_scb->role &= ~BTA_AV_ROLE_START_INT;
+
+ {
+ /* reconfigure success */
+ evt.status = BTA_AV_SUCCESS;
+ evt.chnl = p_scb->chnl;
+ evt.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_failed
+**
+** Description process reconfigure failed
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_failed (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_RECONFIG evt;
+
+ APPL_TRACE_DEBUG("bta_av_rcfg_failed num_recfg: %d, conn_lcb:0x%x",
+ p_scb->num_recfg, bta_av_cb.conn_lcb);
+ if(p_scb->num_recfg > BTA_AV_RECONFIG_RETRY)
+ {
+ bta_av_cco_close(p_scb, p_data);
+ /* report failure */
+ evt.status = BTA_AV_FAIL_STREAM;
+ evt.chnl = p_scb->chnl;
+ evt.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt);
+ /* go to closing state */
+ bta_av_ssm_execute(p_scb, BTA_AV_API_CLOSE_EVT, NULL);
+ }
+ else
+ {
+ /* open failed. try again */
+ p_scb->num_recfg++;
+ if(bta_av_cb.conn_lcb)
+ {
+ AVDT_DisconnectReq(p_scb->peer_addr, bta_av_dt_cback[p_scb->hdi]);
+ }
+ else
+ {
+ bta_av_connect_req(p_scb, NULL);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_connect
+**
+** Description stream closed. reconnect the stream
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_connect (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ p_scb->cong = FALSE;
+ p_scb->num_recfg++;
+ APPL_TRACE_DEBUG("bta_av_rcfg_connect num_recfg: %d", p_scb->num_recfg);
+ if(p_scb->num_recfg > BTA_AV_RECONFIG_RETRY)
+ {
+ /* let bta_av_rcfg_failed report fail */
+ bta_av_rcfg_failed(p_scb, NULL);
+ }
+ else
+ AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_discntd
+**
+** Description AVDT disconnected. reconnect the stream
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_discntd (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_RECONFIG evt;
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_rcfg_discntd num_recfg: %d", p_scb->num_recfg);
+ p_scb->num_recfg++;
+ if(p_scb->num_recfg > BTA_AV_RECONFIG_RETRY)
+ {
+ /* report failure */
+ evt.status = BTA_AV_FAIL_STREAM;
+ evt.chnl = p_scb->chnl;
+ evt.hndl = p_scb->hndl;
+ (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt);
+ /* report close event & go to init state */
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, NULL);
+ }
+ else
+ AVDT_ConnectReq(p_scb->peer_addr, p_scb->sec_mask, bta_av_dt_cback[p_scb->hdi]);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_suspend_cont
+**
+** Description received the suspend response.
+** continue to reconfigure the stream
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_suspend_cont (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 err_code = p_data->str_msg.msg.hdr.err_code;
+ tBTA_AV_RECONFIG evt;
+
+ p_scb->started = FALSE;
+ p_scb->cong = FALSE;
+ if (err_code)
+ {
+ if (AVDT_ERR_CONNECT == err_code)
+ {
+ /* report failure */
+ evt.status = BTA_AV_FAIL;
+ (*bta_av_cb.p_cback)(BTA_AV_RECONFIG_EVT, (tBTA_AV *)&evt);
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_DISC_FAIL_EVT, NULL);
+ }
+ else
+ {
+ APPL_TRACE_ERROR("suspend rejected, try close");
+ /* Disable suspend feature only with explicit rejection(not with timeout) */
+ if (err_code != AVDT_ERR_TIMEOUT)
+ {
+ p_scb->suspend_sup = FALSE;
+ }
+ /* drop the buffers queued in L2CAP */
+ L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+
+ AVDT_CloseReq(p_scb->avdt_handle);
+ }
+ }
+ else
+ {
+ APPL_TRACE_DEBUG("bta_av_suspend_cont calling AVDT_ReconfigReq");
+ /* reconfig the stream */
+
+ AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap);
+ p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_cfm
+**
+** Description if reconfigure is successful, report the event
+** otherwise, close the stream.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_cfm (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UINT8 err_code = p_data->str_msg.msg.hdr.err_code;
+
+ /*
+ APPL_TRACE_DEBUG("bta_av_rcfg_cfm");
+ */
+ if (err_code)
+ {
+ APPL_TRACE_ERROR("reconfig rejected, try close");
+ /* Disable reconfiguration feature only with explicit rejection(not with timeout) */
+ if (err_code != AVDT_ERR_TIMEOUT)
+ {
+ p_scb->recfg_sup = FALSE;
+ }
+ /* started flag is FALSE when reconfigure command is sent */
+ /* drop the buffers queued in L2CAP */
+ L2CA_FlushChannel (p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);
+ AVDT_CloseReq(p_scb->avdt_handle);
+ }
+ else
+ {
+ /* update the codec info after rcfg cfm */
+ memcpy(p_scb->cfg.codec_info,p_data->str_msg.msg.reconfig_cfm.p_cfg->codec_info,AVDT_CODEC_SIZE);
+ /* take the SSM back to OPEN state */
+ bta_av_ssm_execute(p_scb, BTA_AV_STR_OPEN_OK_EVT, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_rcfg_open
+**
+** Description AVDT is connected. open the stream with the new configuration
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_rcfg_open (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG("bta_av_rcfg_open, num_disc_snks = %d", p_scb->num_disc_snks);
+
+ if (p_scb->num_disc_snks == 0)
+ {
+ /* Need to update call-out module so that it will be ready for discover */
+ p_scb->p_cos->stop(p_scb->hndl, p_scb->codec_type);
+
+ /* send avdtp discover request */
+ AVDT_DiscoverReq(p_scb->peer_addr, p_scb->sep_info, BTA_AV_NUM_SEPS, bta_av_dt_cback[p_scb->hdi]);
+ }
+ else
+ {
+ p_scb->codec_type = p_scb->p_cap->codec_info[BTA_AV_CODEC_TYPE_IDX];
+ memcpy(p_scb->cfg.codec_info, p_scb->p_cap->codec_info, AVDT_CODEC_SIZE);
+ /* we may choose to use a different SEP at reconfig.
+ * adjust the sep_idx now */
+ bta_av_adjust_seps_idx(p_scb, bta_av_get_scb_handle(p_scb, AVDT_TSEP_SRC));
+
+ /* open the stream with the new config */
+ p_scb->sep_info_idx = p_scb->rcfg_idx;
+ AVDT_OpenReq(p_scb->avdt_handle, p_scb->peer_addr,
+ p_scb->sep_info[p_scb->sep_info_idx].seid, p_scb->p_cap);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function bta_av_security_rej
+**
+** Description Send an AVDTP security reject.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_security_rej (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ AVDT_SecurityRsp(p_scb->avdt_handle, p_scb->avdt_label, AVDT_ERR_BAD_STATE,
+ NULL, 0);
+}
+
+/*******************************************************************************
+**
+** Function bta_av_chk_2nd_start
+**
+** Description check if this is 2nd stream and if it needs to be started.
+** This function needs to be kept very similar to bta_av_chk_start
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_chk_2nd_start (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_SCB *p_scbi;
+ int i;
+ BOOLEAN new_started = FALSE;
+ UNUSED(p_data);
+
+ if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2))
+ {
+ /* more than one audio channel is connected */
+ if (!(p_scb->role & BTA_AV_ROLE_SUSPEND_OPT))
+ {
+ /* this channel does not need to be reconfigured.
+ * if there is other channel streaming, start the stream now */
+ for(i=0; i<BTA_AV_NUM_STRS; i++)
+ {
+ p_scbi = bta_av_cb.p_scb[i];
+ if(p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started)
+ {
+ if (!new_started)
+ {
+ /* start the new stream */
+ new_started = TRUE;
+ bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
+ }
+ /* may need to update the flush timeout of this already started stream */
+ if (p_scbi->co_started != bta_av_cb.audio_open_cnt)
+ {
+ p_scbi->co_started = bta_av_cb.audio_open_cnt;
+ L2CA_SetFlushTimeout(p_scbi->peer_addr, p_bta_av_cfg->p_audio_flush_to[p_scbi->co_started - 1] );
+ }
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_open_rc
+**
+** Description Send a message to main SM to open RC channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_open_rc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_START start;
+
+ APPL_TRACE_DEBUG("bta_av_open_rc use_rc: %d, wait: x%x role:x%x", p_scb->use_rc, p_scb->wait, p_scb->role);
+ if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) && (p_scb->q_tag == BTA_AV_Q_TAG_START))
+ {
+ /* waiting for role switch for some reason & the timer expires */
+ if (!bta_av_link_role_ok(p_scb, A2D_SET_ONE_BIT))
+ {
+ APPL_TRACE_ERROR ("failed to start streaming for role management reasons!!");
+ bta_sys_stop_timer(&p_scb->timer);
+ start.chnl = p_scb->chnl;
+ start.status = BTA_AV_FAIL_ROLE;
+ start.initiator = TRUE;
+ start.hndl = p_scb->hndl;
+ p_scb->wait &= ~BTA_AV_WAIT_ROLE_SW_BITS;
+ bta_av_cb.rs_idx = 0;
+ (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV *) &start);
+ }
+ else
+ {
+ /* role switch is done. continue to start streaming */
+ bta_av_cb.rs_idx = 0;
+ p_data->hdr.offset = BTA_AV_RS_OK;
+ bta_av_start_ok (p_scb, p_data);
+ }
+ return;
+ }
+
+ if(p_scb->use_rc == TRUE || (p_scb->role & BTA_AV_ROLE_AD_ACP) )
+ {
+ if(bta_av_cb.disc)
+ {
+ /* AVRC discover db is in use */
+ if(p_scb->rc_handle == BTA_AV_RC_HANDLE_NONE)
+ {
+ /* AVRC channel is not connected. delay a little bit */
+ if ((p_scb->wait & BTA_AV_WAIT_ROLE_SW_BITS) == 0)
+ bta_sys_start_timer(&p_scb->timer, BTA_AV_AVRC_TIMER_EVT, BTA_AV_RC_DISC_TIME_VAL);
+ else
+ p_scb->wait |= BTA_AV_WAIT_CHECK_RC;
+ }
+ }
+ else
+ {
+ /* use main SM for AVRC SDP activities */
+ bta_av_rc_disc((UINT8)(p_scb->hdi + 1));
+ }
+ }
+ else
+ {
+ if(BTA_AV_RC_HANDLE_NONE != p_scb->rc_handle)
+ {
+ /* the open API said that this handle does not want a RC connection.
+ * disconnect it now */
+ AVRC_Close(p_scb->rc_handle);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_av_open_at_inc
+**
+** Description This function is called if API open is called by application
+** while state-machine is at incoming state.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_av_open_at_inc (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data)
+{
+ tBTA_AV_API_OPEN *p_buf;
+
+ memcpy (&(p_scb->open_api), &(p_data->api_open), sizeof(tBTA_AV_API_OPEN));
+
+ if (p_scb->coll_mask & BTA_AV_COLL_INC_TMR)
+ {
+ p_scb->coll_mask |= BTA_AV_COLL_API_CALLED;
+
+ /* API open will be handled at timeout if SNK did not start signalling. */
+ /* API open will be ignored if SNK starts signalling. */
+ }
+ else
+ {
+ /* SNK did not start signalling, API was called N seconds timeout. */
+ /* We need to switch to INIT state and start opening connection. */
+ p_scb->coll_mask = 0;
+ bta_av_set_scb_sst_init (p_scb);
+
+ if ((p_buf = (tBTA_AV_API_OPEN *) GKI_getbuf(sizeof(tBTA_AV_API_OPEN))) != NULL)
+ {
+ memcpy(p_buf, &(p_scb->open_api), sizeof(tBTA_AV_API_OPEN));
+ bta_sys_sendmsg(p_buf);
+ }
+ }
+}
+
+#endif /* BTA_AV_INCLUDED */
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+
+/*****************************************************************************
+ *
+ * Filename: btif_av.c
+ *
+ * Description: Bluedroid 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"
+
+#define LOG_TAG "bt_btif_av"
+
+#include "btif_av.h"
+#include "btif_util.h"
+#include "btif_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"
+
+/*****************************************************************************
+** Constants & Macros
+******************************************************************************/
+#define BTIF_AV_SERVICE_NAME "Advanced Audio"
+
+#define BTIF_TIMEOUT_AV_OPEN_ON_RC_SECS 2
+
+typedef enum {
+ BTIF_AV_STATE_IDLE = 0x0,
+ BTIF_AV_STATE_OPENING,
+ BTIF_AV_STATE_OPENED,
+ BTIF_AV_STATE_STARTED,
+ BTIF_AV_STATE_CLOSING
+} btif_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 BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING 0x1
+#define BTIF_AV_FLAG_REMOTE_SUSPEND 0x2
+#define BTIF_AV_FLAG_PENDING_START 0x4
+#define BTIF_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 */
+} btif_av_cb_t;
+
+typedef struct {
+ bt_bdaddr_t *target_bda;
+ uint16_t uuid;
+} btif_av_connect_req_t;
+
+/*****************************************************************************
+** Static variables
+******************************************************************************/
+
+static esp_profile_cb_t bt_av_sink_callback = NULL;
+
+static btif_av_cb_t btif_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 (btif_av_cb.sm_handle == NULL)\
+{\
+ BTIF_TRACE_WARNING("%s: BTAV not initialized\n", __FUNCTION__);\
+ return ESP_ERR_INVALID_STATE;\
+}\
+else\
+{\
+ BTIF_TRACE_EVENT("%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: \
+ { \
+ }break; \
+
+static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *data);
+static BOOLEAN btif_av_state_closing_handler(btif_sm_event_t event, void *data);
+
+static const btif_sm_handler_t btif_av_state_handlers[] = {
+ btif_av_state_idle_handler,
+ btif_av_state_opening_handler,
+ btif_av_state_opened_handler,
+ btif_av_state_started_handler,
+ btif_av_state_closing_handler
+};
+
+static void btif_av_event_free_data(btif_sm_event_t event, void *p_data);
+
+/*************************************************************************
+** Extern functions
+*************************************************************************/
+
+extern tBTA_AV_CO_FUNCTS bta_av_a2d_cos;
+/*****************************************************************************
+** Local helper functions
+******************************************************************************/
+
+const char *dump_av_sm_state_name(btif_av_state_t state)
+{
+ switch (state) {
+ CASE_RETURN_STR(BTIF_AV_STATE_IDLE)
+ CASE_RETURN_STR(BTIF_AV_STATE_OPENING)
+ CASE_RETURN_STR(BTIF_AV_STATE_OPENED)
+ CASE_RETURN_STR(BTIF_AV_STATE_STARTED)
+ CASE_RETURN_STR(BTIF_AV_STATE_CLOSING)
+ default: return "UNKNOWN_STATE";
+ }
+}
+
+const char *dump_av_sm_event_name(btif_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(BTIF_AV_CONNECT_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_DISCONNECT_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_START_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_STOP_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_SUSPEND_STREAM_REQ_EVT)
+ CASE_RETURN_STR(BTIF_AV_SINK_CONFIG_REQ_EVT)
+ default: return "UNKNOWN_EVENT";
+ }
+}
+
+/****************************************************************************
+** Local helper functions
+*****************************************************************************/
+
+/*****************************************************************************
+** Static functions
+******************************************************************************/
+static void btif_report_connection_state(esp_a2d_connection_state_t state, bt_bdaddr_t *bd_addr)
+{
+ 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));
+ }
+ BTIF_A2D_CB_TO_APP(ESP_A2D_CONNECTION_STATE_EVT, ¶m);
+}
+
+static void btif_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 btif_av_state_idle_handler
+**
+** Description State managing disconnected AV link
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ /* clear the peer_bda */
+ memset(&btif_av_cb.peer_bda, 0, sizeof(bt_bdaddr_t));
+ btif_av_cb.flags = 0;
+ btif_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:
+ btif_av_cb.bta_handle = ((tBTA_AV *)p_data)->registr.hndl;
+ break;
+
+ case BTA_AV_PENDING_EVT:
+ case BTIF_AV_CONNECT_REQ_EVT: {
+ if (event == BTIF_AV_CONNECT_REQ_EVT) {
+ memcpy(&btif_av_cb.peer_bda, ((btif_av_connect_req_t *)p_data)->target_bda,
+ sizeof(bt_bdaddr_t));
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_AUTHENTICATE, ((btif_av_connect_req_t *)p_data)->uuid);
+ } else if (event == BTA_AV_PENDING_EVT) {
+ bdcpy(btif_av_cb.peer_bda.address, ((tBTA_AV *)p_data)->pend.bd_addr);
+ BTA_AvOpen(btif_av_cb.peer_bda.address, btif_av_cb.bta_handle,
+ TRUE, BTA_SEC_AUTHENTICATE, UUID_SERVCLASS_AUDIO_SOURCE);
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENING);
+ } break;
+
+ case BTA_AV_RC_OPEN_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:
+ case BTA_AV_RC_CLOSE_EVT:
+ BTIF_TRACE_WARNING("%s : unhandled RC event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ break;
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+
+ return TRUE;
+}
+/*****************************************************************************
+**
+** Function btif_av_state_opening_handler
+**
+** Description Intermediate state managing events during establishment
+** of avdtp channel
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_opening_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ /* inform the application that we are entering connecting state */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_CONNECTING, &(btif_av_cb.peer_bda));
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ break;
+
+ case BTA_AV_REJECT_EVT:
+ BTIF_TRACE_DEBUG(" Received BTA_AV_REJECT_EVT \n");
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_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;
+ BTIF_TRACE_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 = BTIF_AV_STATE_OPENED;
+ btif_av_cb.edr = p_bta_data->open.edr;
+
+ btif_av_cb.peer_sep = p_bta_data->open.sep;
+ btif_a2dp_set_peer_sep(p_bta_data->open.sep);
+ } else {
+ BTIF_TRACE_WARNING("BTA_AV_OPEN_EVT::FAILED status: %d\n",
+ p_bta_data->open.status );
+ state = ESP_A2D_CONNECTION_STATE_DISCONNECTED;
+ av_state = BTIF_AV_STATE_IDLE;
+ }
+
+ /* inform the application of the event */
+ btif_report_connection_state(state, &(btif_av_cb.peer_bda));
+ /* change state to open/idle based on the status */
+ btif_sm_change_state(btif_av_cb.sm_handle, av_state);
+#if (BTIF_AV_SRC_INCLUDED == TRUE)
+ if (btif_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 (btif_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(btif_av_cb.bta_handle);
+ }
+#endif
+ btif_queue_advance();
+ } break;
+
+ case BTIF_AV_SINK_CONFIG_REQ_EVT: {
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ esp_a2d_cb_param_t param;
+ memcpy(param.audio_cfg.remote_bda, &btif_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 BTIF_AV_CONNECT_REQ_EVT:
+ // Check for device, if same device which moved to opening then ignore callback
+ if (memcmp ((bt_bdaddr_t *)p_data, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Connect Req\n", __func__);
+ btif_queue_advance();
+ break;
+ } else {
+ BTIF_TRACE_DEBUG("%s: Moved from idle by Incoming Connection request\n", __func__);
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, (bt_bdaddr_t *)p_data);
+ btif_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, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Same device moved to Opening state,ignore Pending Req\n", __func__);
+ break;
+ } else {
+ BTIF_TRACE_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:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function btif_av_state_closing_handler
+**
+** Description Intermediate state managing events during closing
+** of avdtp channel
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_closing_handler(btif_sm_event_t event, void *p_data)
+{
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(TRUE);
+ }
+ break;
+
+ case BTA_AV_STOP_EVT:
+ case BTIF_AV_STOP_STREAM_REQ_EVT:
+ if (btif_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:
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ /* Handle the RC_CLOSE event for the cleanup */
+ case BTA_AV_RC_CLOSE_EVT:
+ BTIF_TRACE_WARNING("%s : unhandled RC event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ break;
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/*****************************************************************************
+**
+** Function btif_av_state_opened_handler
+**
+** Description Handles AV events while AVDTP is in OPEN state
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_opened_handler(btif_sm_event_t event, void *p_data)
+{
+ tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ if ( (event == BTA_AV_REMOTE_CMD_EVT) && (btif_av_cb.flags & BTIF_AV_FLAG_REMOTE_SUSPEND) &&
+ (p_av->remote_cmd.rc_id == BTA_AV_RC_PLAY) ) {
+ BTIF_TRACE_EVENT("%s: Resetting remote suspend flag on RC PLAY\n", __FUNCTION__);
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+ }
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_STOP;
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTIF_SM_EXIT_EVT:
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTIF_AV_START_STREAM_REQ_EVT:
+ BTA_AvStart();
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_START;
+ break;
+
+ case BTA_AV_START_EVT: {
+ BTIF_TRACE_EVENT("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 (btif_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 (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+
+ /* pending start flag will be cleared when exit current state */
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_STARTED);
+
+ } break;
+
+ case BTIF_AV_DISCONNECT_REQ_EVT:
+ BTA_AvClose(btif_av_cb.bta_handle);
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btif_av_cb.peer_bda));
+ break;
+
+ case BTA_AV_CLOSE_EVT:
+ /* avdtp link is closed */
+ btif_a2dp_on_stopped(NULL);
+
+ /* inform the application that we are disconnected */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ /* change state to idle, send acknowledgement if start is pending */
+ if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ /* pending start flag will be cleared when exit current state */
+ }
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ case BTA_AV_RECONFIG_EVT:
+ if ((btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) &&
+ (p_av->reconfig.status == BTA_AV_SUCCESS)) {
+ APPL_TRACE_WARNING("reconfig done BTA_AVstart()\n");
+ BTA_AvStart();
+ } else if (btif_av_cb.flags & BTIF_AV_FLAG_PENDING_START) {
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_PENDING_START;
+ }
+ break;
+
+ case BTIF_AV_CONNECT_REQ_EVT:
+ if (memcmp ((bt_bdaddr_t *)p_data, &(btif_av_cb.peer_bda),
+ sizeof(btif_av_cb.peer_bda)) == 0) {
+ BTIF_TRACE_DEBUG("%s: Ignore BTIF_AV_CONNECT_REQ_EVT for same device\n", __func__);
+ } else {
+ BTIF_TRACE_DEBUG("%s: Moved to opened by Other Incoming Conn req\n", __func__);
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED,
+ (bt_bdaddr_t *)p_data);
+ }
+ btif_queue_advance();
+ break;
+
+ CHECK_RC_EVENT(event, p_data);
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function btif_av_state_started_handler
+**
+** Description Handles AV events while A2DP stream is started
+**
+** Returns TRUE if event was processed, FALSE otherwise
+**
+*******************************************************************************/
+
+static BOOLEAN btif_av_state_started_handler(btif_sm_event_t event, void *p_data)
+{
+ tBTA_AV *p_av = (tBTA_AV *)p_data;
+
+ BTIF_TRACE_DEBUG("%s event:%s flags %x\n", __FUNCTION__,
+ dump_av_sm_event_name(event), btif_av_cb.flags);
+
+ switch (event) {
+ case BTIF_SM_ENTER_EVT:
+
+ /* we are again in started state, clear any remote suspend flags */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STARTED, &(btif_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 BTIF_AV_START_STREAM_REQ_EVT:
+ break;
+
+ /* fixme -- use suspend = true always to work around issue with BTA AV */
+ case BTIF_AV_STOP_STREAM_REQ_EVT:
+ case BTIF_AV_SUSPEND_STREAM_REQ_EVT:
+
+ /* set pending flag to ensure btif task is not trying to restart
+ stream while suspend is in progress */
+ btif_av_cb.flags |= BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+
+ /* if we were remotely suspended but suspend locally, local suspend
+ always overrides */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+
+ if (btif_av_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_a2dp_set_rx_flush(TRUE);
+ btif_a2dp_on_stopped(NULL);
+ }
+
+ BTA_AvStop(TRUE);
+ break;
+
+ case BTIF_AV_DISCONNECT_REQ_EVT:
+
+ /* request avdtp to close */
+ BTA_AvClose(btif_av_cb.bta_handle);
+
+ /* inform the application that we are disconnecting */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTING, &(btif_av_cb.peer_bda));
+
+ /* wait in closing state until fully closed */
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_CLOSING);
+ break;
+
+ case BTA_AV_SUSPEND_EVT:
+
+ BTIF_TRACE_EVENT("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) {
+ btif_av_cb.flags &= ~BTIF_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 ((btif_av_cb.flags & BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING) == 0) {
+ btif_av_cb.flags |= BTIF_AV_FLAG_REMOTE_SUSPEND;
+ }
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_REMOTE_SUSPEND, &(btif_av_cb.peer_bda));
+ } else {
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btif_av_cb.peer_bda));
+ }
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
+
+ /* suspend completed and state changed, clear pending status */
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING;
+ break;
+
+ case BTA_AV_STOP_EVT:
+
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
+ btif_a2dp_on_stopped(&p_av->suspend);
+
+ btif_report_audio_state(ESP_A2D_AUDIO_STATE_STOPPED, &(btif_av_cb.peer_bda));
+
+ /* if stop was successful, change state to open */
+ if (p_av->suspend.status == BTA_AV_SUCCESS) {
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_OPENED);
+ }
+
+ break;
+
+ case BTA_AV_CLOSE_EVT:
+
+ btif_av_cb.flags |= BTIF_AV_FLAG_PENDING_STOP;
+
+ /* avdtp link is closed */
+ btif_a2dp_on_stopped(NULL);
+
+ /* inform the application that we are disconnected */
+ btif_report_connection_state(ESP_A2D_CONNECTION_STATE_DISCONNECTED, &(btif_av_cb.peer_bda));
+
+ btif_sm_change_state(btif_av_cb.sm_handle, BTIF_AV_STATE_IDLE);
+ break;
+
+ CHECK_RC_EVENT(event, p_data);
+
+ default:
+ BTIF_TRACE_WARNING("%s : unhandled event:%s\n", __FUNCTION__,
+ dump_av_sm_event_name(event));
+ return FALSE;
+
+ }
+ return TRUE;
+}
+
+/*****************************************************************************
+** Local event handlers
+******************************************************************************/
+
+static void btif_av_handle_event(UINT16 event, char *p_param)
+{
+ btif_sm_dispatch(btif_av_cb.sm_handle, event, (void *)p_param);
+ btif_av_event_free_data(event, p_param);
+}
+
+void btif_av_event_deep_copy(UINT16 event, char *p_dest, char *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 (event) {
+ 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 btif_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)
+{
+ btif_transfer_context(btif_av_handle_event, event,
+ (char *)p_data, sizeof(tBTA_AV), btif_av_event_deep_copy);
+}
+
+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;
+ esp_a2d_mcc_t mcc;
+
+ if (event == BTA_AV_MEDIA_DATA_EVT) { /* Switch to BTIF_MEDIA context */
+ state = btif_sm_get_state(btif_av_cb.sm_handle);
+ if ( (state == BTIF_AV_STATE_STARTED) || /* send SBC packets only in Started State */
+ (state == BTIF_AV_STATE_OPENED) ) {
+ que_len = btif_media_sink_enque_buf((BT_HDR *)p_data);
+ BTIF_TRACE_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) {
+ mcc.type = ESP_A2D_MCT_SBC;
+ memcpy(&mcc.cie, (uint8_t *)p_data + 3, ESP_A2D_CIE_LEN_SBC);
+ /* Switch to BTIF context */
+ btif_transfer_context(btif_av_handle_event, BTIF_AV_SINK_CONFIG_REQ_EVT,
+ (char *)&mcc, sizeof(mcc), NULL);
+ } else {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ }
+ }
+}
+/*******************************************************************************
+**
+** Function btif_av_init
+**
+** Description Initializes btif AV if not already done
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+
+bt_status_t btif_av_init()
+{
+ if (btif_av_cb.sm_handle == NULL) {
+ if (!btif_a2dp_start_media_task()) {
+ return BT_STATUS_FAIL;
+ }
+
+ /* Also initialize the AV state machine */
+ btif_av_cb.sm_handle =
+ btif_sm_init((const btif_sm_handler_t *)btif_av_state_handlers, BTIF_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_profile_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)
+{
+ BTIF_TRACE_EVENT("%s()\n", __func__);
+
+ bt_status_t status = btif_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)
+{
+ btif_av_connect_req_t connect_req;
+ connect_req.target_bda = bd_addr;
+ connect_req.uuid = uuid;
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+
+ btif_sm_dispatch(btif_av_cb.sm_handle, BTIF_AV_CONNECT_REQ_EVT, (char *)&connect_req);
+
+ return BT_STATUS_SUCCESS;
+}
+
+esp_err_t esp_a2d_sink_connect(esp_bd_addr_t remote_bda)
+{
+ BTIF_TRACE_EVENT("%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 = btif_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;
+ bt_bdaddr_t bd_addr;
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+ CHECK_BTAV_INIT();
+ memcpy(&bd_addr, remote_bda, sizeof(bt_bdaddr_t));
+ /* Switch to BTIF context */
+ stat = btif_transfer_context(btif_av_handle_event, BTIF_AV_DISCONNECT_REQ_EVT,
+ (char *)(&bd_addr), sizeof(bt_bdaddr_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)
+{
+ BTIF_TRACE_EVENT("%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(btif_av_cb.sm_handle);
+ btif_av_cb.sm_handle = NULL;
+}
+
+void esp_a2d_sink_deinit(void)
+{
+ BTIF_TRACE_EVENT("%s\n", __FUNCTION__);
+
+ if (bt_av_sink_callback) {
+ bt_av_sink_callback = NULL;
+ cleanup();
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_av_get_sm_handle
+**
+** Description Fetches current av SM handle
+**
+** Returns None
+**
+*******************************************************************************/
+
+btif_sm_handle_t btif_av_get_sm_handle(void)
+{
+ return btif_av_cb.sm_handle;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_stream_ready
+**
+** Description Checks whether AV is ready for starting a stream
+**
+** Returns None
+**
+*******************************************************************************/
+
+BOOLEAN btif_av_stream_ready(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+
+ BTIF_TRACE_DEBUG("btif_av_stream_ready : sm hdl %d, state %d, flags %x\n",
+ (int)btif_av_cb.sm_handle, state, btif_av_cb.flags);
+
+ /* also make sure main adapter is enabled */
+ if (btif_is_enabled() == 0) {
+ BTIF_TRACE_EVENT("main adapter not enabled");
+ return FALSE;
+ }
+
+ /* check if we are remotely suspended or stop is pending */
+ if (btif_av_cb.flags & (BTIF_AV_FLAG_REMOTE_SUSPEND | BTIF_AV_FLAG_PENDING_STOP)) {
+ return FALSE;
+ }
+
+ return (state == BTIF_AV_STATE_OPENED);
+}
+
+/*******************************************************************************
+**
+** Function btif_av_stream_started_ready
+**
+** Description Checks whether AV ready for media start in streaming state
+**
+** Returns None
+**
+*******************************************************************************/
+
+BOOLEAN btif_av_stream_started_ready(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+
+ BTIF_TRACE_DEBUG("btif_av_stream_started : sm hdl %d, state %d, flags %x\n",
+ (int)btif_av_cb.sm_handle, state, btif_av_cb.flags);
+
+ /* disallow media task to start if we have pending actions */
+ if (btif_av_cb.flags & (BTIF_AV_FLAG_LOCAL_SUSPEND_PENDING | BTIF_AV_FLAG_REMOTE_SUSPEND
+ | BTIF_AV_FLAG_PENDING_STOP)) {
+ return FALSE;
+ }
+
+ return (state == BTIF_AV_STATE_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function btif_dispatch_sm_event
+**
+** Description Send event to AV statemachine
+**
+** Returns None
+**
+*******************************************************************************/
+
+/* used to pass events to AV statemachine from other tasks */
+void btif_dispatch_sm_event(btif_av_sm_event_t event, void *p_data, int len)
+{
+ /* Switch to BTIF context */
+ btif_transfer_context(btif_av_handle_event, event,
+ (char *)p_data, len, NULL);
+}
+
+/*******************************************************************************
+**
+** Function btif_av_execute_service
+**
+** Description Initializes/Shuts down the service
+**
+** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btif_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),
+ bte_av_callback);
+ BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 0, bte_av_media_callback, &bta_av_a2d_cos);
+ } else {
+ BTA_AvDeregister(btif_av_cb.bta_handle);
+ BTA_AvDisable();
+ }
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_sink_execute_service
+**
+** Description Initializes/Shuts down the service
+**
+** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
+**
+*******************************************************************************/
+bt_status_t btif_av_sink_execute_service(BOOLEAN b_enable)
+{
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ BTA_AvEnable_Sink(b_enable);
+#endif
+ return BT_STATUS_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function btif_av_is_connected
+**
+** Description Checks if av has a connected sink
+**
+** Returns BOOLEAN
+**
+*******************************************************************************/
+BOOLEAN btif_av_is_connected(void)
+{
+ btif_sm_state_t state = btif_sm_get_state(btif_av_cb.sm_handle);
+ return ((state == BTIF_AV_STATE_OPENED) || (state == BTIF_AV_STATE_STARTED));
+}
+
+/*******************************************************************************
+**
+** Function btif_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 btif_av_is_peer_edr(void)
+{
+ ASSERTC(btif_av_is_connected(), "No active a2dp connection\n", 0);
+
+ if (btif_av_cb.edr) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/******************************************************************************
+**
+** Function btif_av_clear_remote_suspend_flag
+**
+** Description Clears btif_av_cd.flags if BTIF_AV_FLAG_REMOTE_SUSPEND is set
+**
+** Returns void
+******************************************************************************/
+void btif_av_clear_remote_suspend_flag(void)
+{
+ BTIF_TRACE_DEBUG("%s: flag :%x\n", __func__, btif_av_cb.flags);
+ btif_av_cb.flags &= ~BTIF_AV_FLAG_REMOTE_SUSPEND;
+}
--- /dev/null
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ **
+ ** Name: btif_media_task.c
+ **
+ ** Description: This is the multimedia module for the BTIF system. It
+ ** contains task implementations AV, HS and HF profiles
+ ** audio & video processing
+ **
+ ******************************************************************************/
+
+#define LOG_TAG "bt_btif_media"
+
+#include "bt_trace.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "bt_target.h"
+#include "fixed_queue.h"
+#include "gki.h"
+#include "bta_api.h"
+#include "btu.h"
+#include "bta_sys.h"
+#include "bta_sys_int.h"
+
+#include "bta_av_api.h"
+#include "a2d_api.h"
+#include "a2d_sbc.h"
+#include "a2d_int.h"
+#include "bta_av_sbc.h"
+#include "bta_av_ci.h"
+#include "l2c_api.h"
+
+#include "btif_av_co.h"
+#include "btif_media.h"
+
+#include "alarm.h"
+#include "bt_trace.h"
+#include "thread.h"
+
+#include "bt_defs.h"
+#include "btif_av.h"
+#include "btif_sm.h"
+#include "btif_util.h"
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+#include "oi_codec_sbc.h"
+#include "oi_status.h"
+#endif
+#include "stdio.h"
+
+#include "btif_media.h"
+#include "allocator.h"
+#include "bt_utils.h"
+#include "esp_a2dp_api.h"
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+OI_CODEC_SBC_DECODER_CONTEXT context;
+OI_UINT32 contextData[CODEC_DATA_WORDS(2, SBC_CODEC_FAST_FILTER_BUFFERS)];
+OI_INT16 pcmData[15 * SBC_MAX_SAMPLES_PER_FRAME * SBC_MAX_CHANNELS];
+#endif
+
+
+/*****************************************************************************
+ ** Constants
+ *****************************************************************************/
+
+#ifndef AUDIO_CHANNEL_OUT_MONO
+#define AUDIO_CHANNEL_OUT_MONO 0x01
+#endif
+
+#ifndef AUDIO_CHANNEL_OUT_STEREO
+#define AUDIO_CHANNEL_OUT_STEREO 0x03
+#endif
+
+/* BTIF media cmd event definition : BTIF_MEDIA_TASK_CMD */
+enum {
+ BTIF_MEDIA_START_AA_TX = 1,
+ BTIF_MEDIA_STOP_AA_TX,
+ BTIF_MEDIA_AA_RX_RDY,
+ BTIF_MEDIA_UIPC_RX_RDY,
+ BTIF_MEDIA_SBC_ENC_INIT,
+ BTIF_MEDIA_SBC_ENC_UPDATE,
+ BTIF_MEDIA_SBC_DEC_INIT,
+ BTIF_MEDIA_VIDEO_DEC_INIT,
+ BTIF_MEDIA_FLUSH_AA_TX,
+ BTIF_MEDIA_FLUSH_AA_RX,
+ BTIF_MEDIA_AUDIO_FEEDING_INIT,
+ BTIF_MEDIA_AUDIO_RECEIVING_INIT,
+ BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE,
+ BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK
+};
+
+enum {
+ MEDIA_TASK_STATE_OFF = 0,
+ MEDIA_TASK_STATE_ON = 1,
+ MEDIA_TASK_STATE_SHUTTING_DOWN = 2
+};
+
+enum {
+ SIG_MEDIA_TASK_INIT = 0xf0,
+ SIG_MEDIA_TASK_CLEAN_UP = 0xf1,
+ SIG_MEDIA_TASK_AVK_ALARM_TO = 0xf2,
+ SIG_MEDIA_TASK_AA_ALARM_TO = 0xf3,
+ SIG_MEDIA_TASK_CMD_READY = 0xf4
+};
+
+/* Macro to multiply the media task tick */
+#ifndef BTIF_MEDIA_NUM_TICK
+#define BTIF_MEDIA_NUM_TICK 1
+#endif
+
+/* Media task tick in milliseconds, must be set to multiple of
+ (1000/TICKS_PER_SEC) (10) */
+
+#define BTIF_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
+#define A2DP_DATA_READ_POLL_MS (BTIF_MEDIA_TIME_TICK / 2)
+#define BTIF_SINK_MEDIA_TIME_TICK (20 * BTIF_MEDIA_NUM_TICK)
+
+
+/* buffer pool */
+#define BTIF_MEDIA_AA_POOL_ID GKI_POOL_ID_3
+#define BTIF_MEDIA_AA_BUF_SIZE GKI_BUF3_SIZE
+
+/* offset */
+#if (BTA_AV_CO_CP_SCMS_T == TRUE)
+#define BTIF_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE + 1)
+#else
+#define BTIF_MEDIA_AA_SBC_OFFSET (AVDT_MEDIA_OFFSET + BTA_AV_SBC_HDR_SIZE)
+#endif
+
+/* Define the bitrate step when trying to match bitpool value */
+#ifndef BTIF_MEDIA_BITRATE_STEP
+#define BTIF_MEDIA_BITRATE_STEP 5
+#endif
+
+/* Middle quality quality setting @ 44.1 khz */
+#define DEFAULT_SBC_BITRATE 328
+
+#ifndef BTIF_A2DP_NON_EDR_MAX_RATE
+#define BTIF_A2DP_NON_EDR_MAX_RATE 229
+#endif
+
+#define USEC_PER_SEC 1000000L
+#define TPUT_STATS_INTERVAL_US (3000*1000)
+
+/*
+ * CONGESTION COMPENSATION CTRL ::
+ *
+ * Thus setting controls how many buffers we will hold in media task
+ * during temp link congestion. Together with the stack buffer queues
+ * it controls much temporary a2dp link congestion we can
+ * compensate for. It however also depends on the default run level of sinks
+ * jitterbuffers. Depending on type of sink this would vary.
+ * Ideally the (SRC) max tx buffer capacity should equal the sinks
+ * jitterbuffer runlevel including any intermediate buffers on the way
+ * towards the sinks codec.
+ */
+
+/* fixme -- define this in pcm time instead of buffer count */
+
+/* The typical runlevel of the tx queue size is ~1 buffer
+ but due to link flow control or thread preemption in lower
+ layers we might need to temporarily buffer up data */
+
+/* 18 frames is equivalent to 6.89*18*2.9 ~= 360 ms @ 44.1 khz, 20 ms mediatick */
+#define MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ 18
+
+#ifndef MAX_PCM_FRAME_NUM_PER_TICK
+#define MAX_PCM_FRAME_NUM_PER_TICK 14
+#endif
+
+/* In case of A2DP SINK, we will delay start by 5 AVDTP Packets*/
+#define MAX_A2DP_DELAYED_START_FRAME_COUNT 5
+#define PACKET_PLAYED_PER_TICK_48 8
+#define PACKET_PLAYED_PER_TICK_44 7
+#define PACKET_PLAYED_PER_TICK_32 5
+#define PACKET_PLAYED_PER_TICK_16 3
+
+typedef struct {
+ UINT16 num_frames_to_be_processed;
+ UINT16 len;
+ UINT16 offset;
+ UINT16 layer_specific;
+} tBT_SBC_HDR;
+
+typedef struct {
+ UINT32 aa_frame_counter;
+ INT32 aa_feed_counter;
+ INT32 aa_feed_residue;
+ UINT32 counter;
+ UINT32 bytes_per_tick; /* pcm bytes read each media task tick */
+} tBTIF_AV_MEDIA_FEEDINGS_PCM_STATE;
+
+typedef union {
+ tBTIF_AV_MEDIA_FEEDINGS_PCM_STATE pcm;
+} tBTIF_AV_MEDIA_FEEDINGS_STATE;
+
+typedef struct {
+#if (BTA_AV_INCLUDED == TRUE)
+ BUFFER_Q RxSbcQ;
+ BOOLEAN is_tx_timer;
+ BOOLEAN is_rx_timer;
+ UINT8 busy_level;
+ void *av_sm_hdl;
+ UINT8 a2dp_cmd_pending; /* we can have max one command pending */
+ BOOLEAN rx_flush; /* discards any incoming data when true */
+ UINT8 peer_sep;
+ BOOLEAN data_channel_open;
+ UINT8 frames_to_process;
+
+ UINT32 sample_rate;
+ UINT8 channel_count;
+ osi_alarm_t *decode_alarm;
+#endif
+
+} tBTIF_MEDIA_CB;
+
+typedef struct {
+ long long rx;
+ long long rx_tot;
+ long long tx;
+ long long tx_tot;
+ long long ts_prev_us;
+} t_stat;
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+extern OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context,
+ const OI_BYTE **frameData,
+ unsigned long *frameBytes,
+ OI_INT16 *pcmData,
+ unsigned long *pcmBytes);
+extern OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context,
+ unsigned long *decoderData,
+ unsigned long decoderDataBytes,
+ OI_UINT8 maxChannels,
+ OI_UINT8 pcmStride,
+ OI_BOOL enhanced);
+#endif
+static void btif_media_flush_q(BUFFER_Q *p_q);
+static void btif_media_task_aa_handle_stop_decoding(void );
+static void btif_media_task_aa_rx_flush(void);
+
+static const char *dump_media_event(UINT16 event);
+static void btif_media_thread_handle_cmd(fixed_queue_t *queue);
+
+/* Handle incoming media packets A2DP SINK streaming*/
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR *p_msg);
+#endif
+
+#if (BTA_AV_INCLUDED == TRUE)
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg);
+static void btif_media_task_aa_handle_clear_track(void);
+#endif
+static void btif_media_task_aa_handle_start_decoding(void);
+#endif
+BOOLEAN btif_media_task_clear_track(void);
+static void btif_media_task_handler(void *arg);
+
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context);
+static void btif_media_thread_init(UNUSED_ATTR void *context);
+static void btif_media_thread_cleanup(UNUSED_ATTR void *context);
+extern BOOLEAN btif_hf_is_call_idle();
+
+static tBTIF_MEDIA_CB btif_media_cb;
+static int media_task_running = MEDIA_TASK_STATE_OFF;
+
+static fixed_queue_t *btif_media_cmd_msg_queue = NULL;
+static xTaskHandle xBtifMediaTaskHandle = NULL;
+static QueueHandle_t xBtifMediaQueue = NULL;
+
+static esp_a2d_data_cb_t bt_av_sink_data_callback = NULL;
+
+esp_err_t esp_a2d_register_data_callback(esp_a2d_data_cb_t cb)
+{
+ // TODO: need protection against race
+ bt_av_sink_data_callback = cb;
++ return ESP_OK;
+}
+
+// TODO: need protection against race
+#define BTIF_A2D_DATA_CB_TO_APP(data, len) do { \
+ if (bt_av_sink_data_callback) { \
+ bt_av_sink_data_callback(data, len); \
+ } \
+ } while (0)
+
+/*****************************************************************************
+ ** temporary hacked functions. TODO: port these functions or remove them?
+ *****************************************************************************/
+BOOLEAN btif_hf_is_call_idle(void)
+{
+ return FALSE;
+}
+
+/*****************************************************************************
+ ** Misc helper functions
+ *****************************************************************************/
+
+UNUSED_ATTR static const char *dump_media_event(UINT16 event)
+{
+ switch (event) {
+ CASE_RETURN_STR(BTIF_MEDIA_START_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_STOP_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_AA_RX_RDY)
+ CASE_RETURN_STR(BTIF_MEDIA_UIPC_RX_RDY)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_ENC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_ENC_UPDATE)
+ CASE_RETURN_STR(BTIF_MEDIA_SBC_DEC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_VIDEO_DEC_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_FLUSH_AA_TX)
+ CASE_RETURN_STR(BTIF_MEDIA_FLUSH_AA_RX)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_FEEDING_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_RECEIVING_INIT)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE)
+ CASE_RETURN_STR(BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK)
+
+ default:
+ return "UNKNOWN MEDIA EVENT";
+ }
+}
+
+/*****************************************************************************
+ ** A2DP CTRL PATH
+ *****************************************************************************/
+#if 0
+// TODO: consider the necessity to add an API based on this function
+static void btif_audiopath_detached(void)
+{
+ APPL_TRACE_EVENT("## AUDIO PATH DETACHED ##");
+
+ /* send stop request only if we are actively streaming and haven't received
+ a stop request. Potentially audioflinger detached abnormally */
+ if (btif_media_cb.is_tx_timer) {
+ /* post stop event and wait for audio path to stop */
+ btif_dispatch_sm_event(BTIF_AV_STOP_STREAM_REQ_EVT, NULL, 0);
+ }
+}
+#endif
+
+/*****************************************************************************
+ ** BTIF ADAPTATION
+ *****************************************************************************/
+
+static void btif_media_task_post(uint32_t sig)
+{
+ BtTaskEvt_t *evt = (BtTaskEvt_t *)osi_malloc(sizeof(BtTaskEvt_t));
+ if (evt == NULL) {
+ return;
+ }
+
+ evt->sig = sig;
+ evt->par = 0;
+
+ if (xQueueSend(xBtifMediaQueue, &evt, 10 / portTICK_RATE_MS) != pdTRUE) {
+ APPL_TRACE_ERROR("xBtifMediaQueue failed\n");
+ }
+}
+
+static void btif_media_task_handler(void *arg)
+{
+ BtTaskEvt_t *e;
+ for (;;) {
+ if (pdTRUE == xQueueReceive(xBtifMediaQueue, &e, (portTickType)portMAX_DELAY)) {
+ // LOG_ERROR("med evt %d\n", e->sig);
+ switch (e->sig) {
+ case SIG_MEDIA_TASK_AVK_ALARM_TO:
+ btif_media_task_avk_handle_timer(NULL);
+ break;
+ case SIG_MEDIA_TASK_CMD_READY:
+ fixed_queue_process(btif_media_cmd_msg_queue);
+ break;
+ case SIG_MEDIA_TASK_INIT:
+ btif_media_thread_init(NULL);
+ break;
+ case SIG_MEDIA_TASK_CLEAN_UP:
+ btif_media_thread_cleanup(NULL);
+ break;
+ default:
+ APPL_TRACE_ERROR("media task unhandled evt: 0x%x\n", e->sig);
+ }
+ }
+ osi_free(e);
+ }
+}
+
+bool btif_a2dp_start_media_task(void)
+{
+ if (media_task_running != MEDIA_TASK_STATE_OFF) {
+ APPL_TRACE_ERROR("warning : media task already running");
+ return false;
+ }
+
+ APPL_TRACE_EVENT("## A2DP START MEDIA THREAD ##");
+
+ btif_media_cmd_msg_queue = fixed_queue_new(SIZE_MAX);
+ if (btif_media_cmd_msg_queue == NULL) {
+ goto error_exit;
+ }
+
+ xBtifMediaQueue = xQueueCreate(60, sizeof(void *));
+ if (xBtifMediaQueue == 0) {
+ goto error_exit;
+ }
+ xTaskCreate(btif_media_task_handler, "BtifMediaT\n", 2048, NULL, configMAX_PRIORITIES - 1, &xBtifMediaTaskHandle);
+ if (xBtifMediaTaskHandle == NULL) {
+ goto error_exit;
+ }
+ fixed_queue_register_dequeue(btif_media_cmd_msg_queue, btif_media_thread_handle_cmd);
+ btif_media_task_post(SIG_MEDIA_TASK_INIT);
+
+ APPL_TRACE_EVENT("## A2DP MEDIA THREAD STARTED ##\n");
+
+ return true;
+
+error_exit:;
+ APPL_TRACE_ERROR("%s unable to start up media thread\n", __func__);
+
+ if (xBtifMediaTaskHandle != NULL) {
+ vTaskDelete(xBtifMediaTaskHandle);
+ xBtifMediaTaskHandle = NULL;
+ }
+
+ if (xBtifMediaQueue != 0) {
+ vQueueDelete(xBtifMediaQueue);
+ xBtifMediaQueue = 0;
+ }
+ fixed_queue_free(btif_media_cmd_msg_queue, NULL);
+ btif_media_cmd_msg_queue = NULL;
+ return false;
+}
+
+void btif_a2dp_stop_media_task(void)
+{
+ APPL_TRACE_EVENT("## A2DP STOP MEDIA THREAD ##\n");
+
+ // Exit thread
+ btif_media_task_post(SIG_MEDIA_TASK_CLEAN_UP);
+ // TODO: wait until CLEAN up is done, then do task delete
+ vTaskDelete(xBtifMediaTaskHandle);
+ xBtifMediaTaskHandle = NULL;
+ vQueueDelete(xBtifMediaQueue);
+ xBtifMediaQueue = NULL;
+
+ fixed_queue_free(btif_media_cmd_msg_queue, NULL);
+ btif_media_cmd_msg_queue = NULL;
+}
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_init
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_init(void)
+{
+ //tput_mon(1, 0, 1);
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_setup_codec
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_setup_codec(void)
+{
+ tBTIF_AV_MEDIA_FEEDINGS media_feeding;
+ tBTIF_STATUS status;
+
+ APPL_TRACE_EVENT("## A2DP SETUP CODEC ##\n");
+
+ GKI_disable();
+
+ /* for now hardcode 44.1 khz 16 bit stereo PCM format */
+ media_feeding.cfg.pcm.sampling_freq = 44100;
+ media_feeding.cfg.pcm.bit_per_sample = 16;
+ media_feeding.cfg.pcm.num_channel = 2;
+ media_feeding.format = BTIF_AV_CODEC_PCM;
+
+ bta_av_co_audio_set_codec(&media_feeding, &status);
+
+ GKI_enable();
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_idle
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_idle(void)
+{
+ APPL_TRACE_EVENT("## ON A2DP IDLE ##\n");
+
+ bta_av_co_init();
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ btif_media_task_clear_track();
+ APPL_TRACE_DEBUG("Stopped BT track");
+ }
+#endif
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_clear_track
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_clear_track(void)
+{
+ BT_HDR *p_buf;
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR)))) {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+ return TRUE;
+}
+
+/*****************************************************************************
+**
+** Function btif_reset_decoder
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_reset_decoder(UINT8 *p_av)
+{
+ APPL_TRACE_EVENT("btif_reset_decoder");
+ APPL_TRACE_DEBUG("btif_reset_decoder p_codec_info[%x:%x:%x:%x:%x:%x]\n",
+ p_av[1], p_av[2], p_av[3],
+ p_av[4], p_av[5], p_av[6]);
+
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf;
+ if (NULL == (p_buf = GKI_getbuf(sizeof(tBTIF_MEDIA_SINK_CFG_UPDATE)))) {
+ APPL_TRACE_ERROR("btif_reset_decoder No Buffer ");
+ return;
+ }
+
+ memcpy(p_buf->codec_info, p_av, AVDT_CODEC_SIZE);
+ p_buf->hdr.event = BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+}
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_stopped
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_stopped(tBTA_AV_SUSPEND *p_av)
+{
+ APPL_TRACE_EVENT("## ON A2DP STOPPED ##\n");
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) { /* Handling for A2DP SINK cases*/
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ btif_media_cb.data_channel_open = FALSE;
+ return;
+ }
+}
+
+
+/*****************************************************************************
+**
+** Function btif_a2dp_on_suspended
+**
+** Description
+**
+** Returns
+**
+*******************************************************************************/
+
+void btif_a2dp_on_suspended(tBTA_AV_SUSPEND *p_av)
+{
+ APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##\n");
+ if (btif_media_cb.peer_sep == AVDT_TSEP_SRC) {
+ btif_media_cb.rx_flush = TRUE;
+ btif_media_task_aa_rx_flush_req();
+ btif_media_task_aa_handle_stop_decoding();
+ return;
+ }
+}
+
+/* when true media task discards any rx frames */
+void btif_a2dp_set_rx_flush(BOOLEAN enable)
+{
+ APPL_TRACE_EVENT("## DROP RX %d ##\n", enable);
+ btif_media_cb.rx_flush = enable;
+}
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context)
+{
+ UINT8 count;
+ tBT_SBC_HDR *p_msg;
+ int num_sbc_frames;
+ int num_frames_to_process;
+
+ count = btif_media_cb.RxSbcQ._count;
+ if (0 == count) {
+ APPL_TRACE_DEBUG(" QUE EMPTY ");
+ } else {
+ if (btif_media_cb.rx_flush == TRUE) {
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+ return;
+ }
+
+ num_frames_to_process = btif_media_cb.frames_to_process;
+ APPL_TRACE_DEBUG(" Process Frames + ");
+ do {
+ p_msg = (tBT_SBC_HDR *)GKI_getfirst(&(btif_media_cb.RxSbcQ));
+ if (p_msg == NULL) {
+ return;
+ }
+ num_sbc_frames = p_msg->num_frames_to_be_processed; /* num of frames in Que Packets */
+ APPL_TRACE_DEBUG(" Frames left in topmost packet %d\n", num_sbc_frames);
+ APPL_TRACE_DEBUG(" Remaining frames to process in tick %d\n", num_frames_to_process);
+ APPL_TRACE_DEBUG(" Num of Packets in Que %d\n", btif_media_cb.RxSbcQ._count);
+
+ if ( num_sbc_frames > num_frames_to_process) { /* Que Packet has more frames*/
+ p_msg->num_frames_to_be_processed = num_frames_to_process;
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg->num_frames_to_be_processed = num_sbc_frames - num_frames_to_process;
+ num_frames_to_process = 0;
+ break;
+ } else { /* Que packet has less frames */
+ btif_media_task_handle_inc_media(p_msg);
+ p_msg = (tBT_SBC_HDR *)GKI_dequeue(&(btif_media_cb.RxSbcQ));
+ if ( p_msg == NULL ) {
+ APPL_TRACE_ERROR("Insufficient data in que ");
+ break;
+ }
+ num_frames_to_process = num_frames_to_process - p_msg->num_frames_to_be_processed;
+ GKI_freebuf(p_msg);
+ }
+ } while (num_frames_to_process > 0);
+
+ APPL_TRACE_DEBUG(" Process Frames - ");
+ }
+}
+#else
+static void btif_media_task_avk_handle_timer(UNUSED_ATTR void *context) {}
+#endif
+
+static void btif_media_thread_init(UNUSED_ATTR void *context)
+{
+ memset(&btif_media_cb, 0, sizeof(btif_media_cb));
+ LOG_ERROR("med thread init\n");
+#if (UIPC_INCLUDED == TRUE)
+ UIPC_Init(NULL);
+
+#if (BTA_AV_INCLUDED == TRUE)
+ UIPC_Open(UIPC_CH_ID_AV_CTRL , btif_a2dp_ctrl_cb);
+#endif ( BTA_AV_INCLUDED == TRUE)
+#endif /* UIPC_INCLUDED == TRUE */
+ btif_media_cb.av_sm_hdl = btif_av_get_sm_handle();
+ raise_priority_a2dp(TASK_HIGH_MEDIA);
+ media_task_running = MEDIA_TASK_STATE_ON;
+}
+
+static void btif_media_thread_cleanup(UNUSED_ATTR void *context)
+{
+ /* make sure no channels are restarted while shutting down */
+ media_task_running = MEDIA_TASK_STATE_SHUTTING_DOWN;
+
+ btif_media_cb.data_channel_open = FALSE;
+ /* Clear media task flag */
+ media_task_running = MEDIA_TASK_STATE_OFF;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_flush_q
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_flush_q(BUFFER_Q *p_q)
+{
+ while (!GKI_queue_is_empty(p_q)) {
+ GKI_freebuf(GKI_dequeue(p_q));
+ }
+}
+
+static void btif_media_thread_handle_cmd(fixed_queue_t *queue)
+{
+ BT_HDR *p_msg;
+ while (!fixed_queue_is_empty(queue)) {
+ p_msg = (BT_HDR *)fixed_queue_dequeue(queue);
+ LOG_VERBOSE("btif_media_thread_handle_cmd : %d %s\n", p_msg->event,
+ dump_media_event(p_msg->event));
+
+ switch (p_msg->event) {
+#if (BTA_AV_INCLUDED == TRUE)
+ case BTIF_MEDIA_AUDIO_SINK_CFG_UPDATE:
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_media_task_aa_handle_decoder_reset(p_msg);
+#endif
+ break;
+ case BTIF_MEDIA_AUDIO_SINK_CLEAR_TRACK:
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+ btif_media_task_aa_handle_clear_track();
+#endif
+ break;
+ case BTIF_MEDIA_FLUSH_AA_RX:
+ btif_media_task_aa_rx_flush();
+ break;
+#endif
+ default:
+ APPL_TRACE_ERROR("ERROR in %s unknown event %d\n", __func__, p_msg->event);
+ }
+ GKI_freebuf(p_msg);
+ LOG_VERBOSE("%s: %s DONE\n", __func__, dump_media_event(p_msg->event));
+ }
+}
+
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+/*******************************************************************************
+ **
+ ** Function btif_media_task_handle_inc_media
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_handle_inc_media(tBT_SBC_HDR *p_msg)
+{
+ UINT8 *sbc_start_frame = ((UINT8 *)(p_msg + 1) + p_msg->offset + 1);
+ int count;
+ UINT32 pcmBytes, availPcmBytes;
+ OI_INT16 *pcmDataPointer = pcmData; /*Will be overwritten on next packet receipt*/
+ OI_STATUS status;
+ int num_sbc_frames = p_msg->num_frames_to_be_processed;
+ UINT32 sbc_frame_len = p_msg->len - 1;
+ availPcmBytes = 2 * sizeof(pcmData);
+
+ if ((btif_media_cb.peer_sep == AVDT_TSEP_SNK) || (btif_media_cb.rx_flush)) {
+ APPL_TRACE_DEBUG(" State Changed happened in this tick ");
+ return;
+ }
+
+ // ignore data if no one is listening
+ if (!btif_media_cb.data_channel_open) {
+ return;
+ }
+
+ APPL_TRACE_DEBUG("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
+ // LOG_ERROR("Number of sbc frames %d, frame_len %d\n", num_sbc_frames, sbc_frame_len);
+ for (count = 0; count < num_sbc_frames && sbc_frame_len != 0; count ++) {
+ pcmBytes = availPcmBytes;
+ status = OI_CODEC_SBC_DecodeFrame(&context, (const OI_BYTE **)&sbc_start_frame,
+ (OI_UINT32 *)&sbc_frame_len,
+ (OI_INT16 *)pcmDataPointer,
+ (OI_UINT32 *)&pcmBytes);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR("Decoding failure: %d\n", status);
+ break;
+ }
+ availPcmBytes -= pcmBytes;
+ pcmDataPointer += pcmBytes / 2;
+ p_msg->offset += (p_msg->len - 1) - sbc_frame_len;
+ p_msg->len = sbc_frame_len + 1;
+ }
+ // LOG_ERROR("pre-send: %d\n", availPcmBytes);
+
+ // UIPC_Send(UIPC_CH_ID_AV_AUDIO, 0, (UINT8 *)pcmData, (2 * sizeof(pcmData) - availPcmBytes));
+ BTIF_A2D_DATA_CB_TO_APP((uint8_t *)pcmData, (2 * sizeof(pcmData) - availPcmBytes));
+
+}
+#endif
+
+#if (BTA_AV_INCLUDED == TRUE)
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush_req
+ **
+ ** Description
+ **
+ ** Returns TRUE is success
+ **
+ *******************************************************************************/
+BOOLEAN btif_media_task_aa_rx_flush_req(void)
+{
+ BT_HDR *p_buf;
+
+ if (GKI_queue_is_empty(&(btif_media_cb.RxSbcQ)) == TRUE) { /* Que is already empty */
+ return TRUE;
+ }
+
+ if (NULL == (p_buf = GKI_getbuf(sizeof(BT_HDR)))) {
+ return FALSE;
+ }
+
+ p_buf->event = BTIF_MEDIA_FLUSH_AA_RX;
+
+ fixed_queue_enqueue(btif_media_cmd_msg_queue, p_buf);
+ btif_media_task_post(SIG_MEDIA_TASK_CMD_READY);
+ return TRUE;
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_rx_flush
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_rx_flush(void)
+{
+ /* Flush all enqueued GKI SBC buffers (encoded) */
+ APPL_TRACE_DEBUG("btif_media_task_aa_rx_flush");
+
+ btif_media_flush_q(&(btif_media_cb.RxSbcQ));
+}
+
+int btif_a2dp_get_track_frequency(UINT8 frequency)
+{
+ int freq = 48000;
+ switch (frequency) {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ freq = 16000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ freq = 32000;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ freq = 44100;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ freq = 48000;
+ break;
+ }
+ return freq;
+}
+
+int btif_a2dp_get_track_channel_count(UINT8 channeltype)
+{
+ int count = 1;
+ switch (channeltype) {
+ case A2D_SBC_IE_CH_MD_MONO:
+ count = 1;
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ case A2D_SBC_IE_CH_MD_STEREO:
+ case A2D_SBC_IE_CH_MD_JOINT:
+ count = 2;
+ break;
+ }
+ return count;
+}
+
+void btif_a2dp_set_peer_sep(UINT8 sep)
+{
+ btif_media_cb.peer_sep = sep;
+}
+
+static void btif_decode_alarm_cb(UNUSED_ATTR void *context)
+{
+ btif_media_task_post(SIG_MEDIA_TASK_AVK_ALARM_TO);
+}
+
+static void btif_media_task_aa_handle_stop_decoding(void)
+{
+ osi_alarm_free(btif_media_cb.decode_alarm);
+ btif_media_cb.decode_alarm = NULL;
+}
+
+static void btif_media_task_aa_handle_start_decoding(void)
+{
+ if (btif_media_cb.decode_alarm) {
+ return;
+ }
+
+ btif_media_cb.decode_alarm = osi_alarm_new("dec_timer\n", btif_decode_alarm_cb, NULL, BTIF_SINK_MEDIA_TIME_TICK, true);
+ if (!btif_media_cb.decode_alarm) {
+ APPL_TRACE_ERROR("%s unable to allocate decode alarm.\n", __func__);
+ return;
+ }
+ osi_alarm_set(btif_media_cb.decode_alarm, BTIF_SINK_MEDIA_TIME_TICK);
+}
+
+#if (BTA_AV_SINK_INCLUDED == TRUE)
+
+static void btif_media_task_aa_handle_clear_track (void)
+{
+ APPL_TRACE_DEBUG("btif_media_task_aa_handle_clear_track");
+}
+
+/*******************************************************************************
+ **
+ ** Function btif_media_task_aa_handle_decoder_reset
+ **
+ ** Description
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+static void btif_media_task_aa_handle_decoder_reset(BT_HDR *p_msg)
+{
+ tBTIF_MEDIA_SINK_CFG_UPDATE *p_buf = (tBTIF_MEDIA_SINK_CFG_UPDATE *) p_msg;
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+ OI_STATUS status;
+ UINT32 freq_multiple = 48 * 20; /* frequency multiple for 20ms of data , initialize with 48K*/
+ UINT32 num_blocks = 16;
+ UINT32 num_subbands = 8;
+
+ APPL_TRACE_EVENT("btif_media_task_aa_handle_decoder_reset p_codec_info[%x:%x:%x:%x:%x:%x]\n",
+ p_buf->codec_info[1], p_buf->codec_info[2], p_buf->codec_info[3],
+ p_buf->codec_info[4], p_buf->codec_info[5], p_buf->codec_info[6]);
+
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_buf->codec_info, FALSE);
+ if (a2d_status != A2D_SUCCESS) {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ return;
+ }
+
+ btif_media_cb.sample_rate = btif_a2dp_get_track_frequency(sbc_cie.samp_freq);
+ btif_media_cb.channel_count = btif_a2dp_get_track_channel_count(sbc_cie.ch_mode);
+
+ btif_media_cb.rx_flush = FALSE;
+ APPL_TRACE_EVENT("Reset to sink role");
+ status = OI_CODEC_SBC_DecoderReset(&context, contextData, sizeof(contextData), 2, 2, FALSE);
+ if (!OI_SUCCESS(status)) {
+ APPL_TRACE_ERROR("OI_CODEC_SBC_DecoderReset failed with error code %d\n", status);
+ }
+
+ btif_media_cb.data_channel_open = TRUE;
+
+ switch (sbc_cie.samp_freq) {
+ case A2D_SBC_IE_SAMP_FREQ_16:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq);
+ freq_multiple = 16 * 20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_32:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq);
+ freq_multiple = 32 * 20;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_44:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (44100)\n", sbc_cie.samp_freq);
+ freq_multiple = 441 * 2;
+ break;
+ case A2D_SBC_IE_SAMP_FREQ_48:
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq);
+ freq_multiple = 48 * 20;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Frequency ");
+ break;
+ }
+
+ switch (sbc_cie.ch_mode) {
+ case A2D_SBC_IE_CH_MD_MONO:
+ APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_DUAL:
+ APPL_TRACE_DEBUG("\tch_mode:%d (DUAL)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_STEREO:
+ APPL_TRACE_DEBUG("\tch_mode:%d (STEREO)\n", sbc_cie.ch_mode);
+ break;
+ case A2D_SBC_IE_CH_MD_JOINT:
+ APPL_TRACE_DEBUG("\tch_mode:%d (JOINT)\n", sbc_cie.ch_mode);
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Mode ");
+ break;
+ }
+
+ switch (sbc_cie.block_len) {
+ case A2D_SBC_IE_BLOCKS_4:
+ APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len);
+ num_blocks = 4;
+ break;
+ case A2D_SBC_IE_BLOCKS_8:
+ APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len);
+ num_blocks = 8;
+ break;
+ case A2D_SBC_IE_BLOCKS_12:
+ APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len);
+ num_blocks = 12;
+ break;
+ case A2D_SBC_IE_BLOCKS_16:
+ APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len);
+ num_blocks = 16;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown BlockLen ");
+ break;
+ }
+
+ switch (sbc_cie.num_subbands) {
+ case A2D_SBC_IE_SUBBAND_4:
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands);
+ num_subbands = 4;
+ break;
+ case A2D_SBC_IE_SUBBAND_8:
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands);
+ num_subbands = 8;
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown SubBands ");
+ break;
+ }
+
+ switch (sbc_cie.alloc_mthd) {
+ case A2D_SBC_IE_ALLOC_MD_S:
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd);
+ break;
+ case A2D_SBC_IE_ALLOC_MD_L:
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (Loudness)\n", sbc_cie.alloc_mthd);
+ break;
+ default:
+ APPL_TRACE_DEBUG(" Unknown Allocation Method");
+ break;
+ }
+
+ APPL_TRACE_EVENT("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
+
+ btif_media_cb.frames_to_process = ((freq_multiple) / (num_blocks * num_subbands)) + 1;
+ APPL_TRACE_EVENT(" Frames to be processed in 20 ms %d\n", btif_media_cb.frames_to_process);
+}
+#endif
+
+/*******************************************************************************
+ **
+ ** Function btif_media_sink_enque_buf
+ **
+ ** Description This function is called by the av_co to fill A2DP Sink Queue
+ **
+ **
+ ** Returns size of the queue
+ *******************************************************************************/
+UINT8 btif_media_sink_enque_buf(BT_HDR *p_pkt)
+{
+ tBT_SBC_HDR *p_msg;
+
+ if (btif_media_cb.rx_flush == TRUE) { /* Flush enabled, do not enque*/
+ return GKI_queue_length(&btif_media_cb.RxSbcQ);
+ }
+ if (GKI_queue_length(&btif_media_cb.RxSbcQ) == MAX_OUTPUT_A2DP_FRAME_QUEUE_SZ) {
+ GKI_freebuf(GKI_dequeue(&(btif_media_cb.RxSbcQ)));
+ }
+
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf + ");
+
+ /* allocate and Queue this buffer */
+ if ((p_msg = (tBT_SBC_HDR *) GKI_getbuf(sizeof(tBT_SBC_HDR) +
+ p_pkt->offset + p_pkt->len)) != NULL) {
+ memcpy(p_msg, p_pkt, (sizeof(BT_HDR) + p_pkt->offset + p_pkt->len));
+ p_msg->num_frames_to_be_processed = (*((UINT8 *)(p_msg + 1) + p_msg->offset)) & 0x0f;
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf %d + \n", p_msg->num_frames_to_be_processed);
+ // LOG_ERROR("sink enq %d\n", p_msg->num_frames_to_be_processed);
+ GKI_enqueue(&(btif_media_cb.RxSbcQ), p_msg);
+ if (GKI_queue_length(&btif_media_cb.RxSbcQ) == MAX_A2DP_DELAYED_START_FRAME_COUNT) {
+ BTIF_TRACE_DEBUG(" Initiate Decoding ");
+ btif_media_task_aa_handle_start_decoding();
+ }
+ } else {
+ /* let caller deal with a failed allocation */
+ BTIF_TRACE_VERBOSE("btif_media_sink_enque_buf No Buffer left - ");
+ }
+ return GKI_queue_length(&btif_media_cb.RxSbcQ);
+}
+
+
+/*******************************************************************************
+ **
+ ** Function btif_media_aa_readbuf
+ **
+ ** Description This function is called by the av_co to get the next buffer to send
+ **
+ **
+ ** Returns void
+ *******************************************************************************/
+BT_HDR *btif_media_aa_readbuf(void)
+{
+ return NULL;
+}
+
+#endif /* BTA_AV_INCLUDED == TRUE */
+/*******************************************************************************
+ **
+ ** Function dump_codec_info
+ **
+ ** Description Decode and display codec_info (for debug)
+ **
+ ** Returns void
+ **
+ *******************************************************************************/
+void dump_codec_info(unsigned char *p_codec)
+{
+ tA2D_STATUS a2d_status;
+ tA2D_SBC_CIE sbc_cie;
+
+ a2d_status = A2D_ParsSbcInfo(&sbc_cie, p_codec, FALSE);
+ if (a2d_status != A2D_SUCCESS) {
+ APPL_TRACE_ERROR("ERROR dump_codec_info A2D_ParsSbcInfo fail:%d\n", a2d_status);
+ return;
+ }
+
+ APPL_TRACE_DEBUG("dump_codec_info");
+
+ if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_16) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (16000)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_32) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (32000)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_44) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (44.100)\n", sbc_cie.samp_freq);
+ } else if (sbc_cie.samp_freq == A2D_SBC_IE_SAMP_FREQ_48) {
+ APPL_TRACE_DEBUG("\tsamp_freq:%d (48000)\n", sbc_cie.samp_freq);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD samp_freq:%d\n", sbc_cie.samp_freq);
+ }
+
+ if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_MONO) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Mono)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_DUAL) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Dual)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_STEREO) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Stereo)\n", sbc_cie.ch_mode);
+ } else if (sbc_cie.ch_mode == A2D_SBC_IE_CH_MD_JOINT) {
+ APPL_TRACE_DEBUG("\tch_mode:%d (Joint)\n", sbc_cie.ch_mode);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD ch_mode:%d\n", sbc_cie.ch_mode);
+ }
+
+ if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_4) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (4)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_8) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (8)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_12) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (12)\n", sbc_cie.block_len);
+ } else if (sbc_cie.block_len == A2D_SBC_IE_BLOCKS_16) {
+ APPL_TRACE_DEBUG("\tblock_len:%d (16)\n", sbc_cie.block_len);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD block_len:%d\n", sbc_cie.block_len);
+ }
+
+ if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_4) {
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (4)\n", sbc_cie.num_subbands);
+ } else if (sbc_cie.num_subbands == A2D_SBC_IE_SUBBAND_8) {
+ APPL_TRACE_DEBUG("\tnum_subbands:%d (8)\n", sbc_cie.num_subbands);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD num_subbands:%d\n", sbc_cie.num_subbands);
+ }
+
+ if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_S) {
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (SNR)\n", sbc_cie.alloc_mthd);
+ } else if (sbc_cie.alloc_mthd == A2D_SBC_IE_ALLOC_MD_L) {
+ APPL_TRACE_DEBUG("\talloc_mthd:%d (Loundess)\n", sbc_cie.alloc_mthd);
+ } else {
+ APPL_TRACE_DEBUG("\tBAD alloc_mthd:%d\n", sbc_cie.alloc_mthd);
+ }
+
+ APPL_TRACE_DEBUG("\tBit pool Min:%d Max:%d\n", sbc_cie.min_bitpool, sbc_cie.max_bitpool);
+
+}
--- /dev/null
- connect_queue = list_new(osi_free);
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/*******************************************************************************
+ *
+ * Filename: btif_profile_queue.c
+ *
+ * Description: Bluetooth remote device connection queuing implementation.
+ *
+ ******************************************************************************/
+
+// #include <assert.h>
+#include "bt_trace.h"
+#include <string.h>
+// #include <hardware/bluetooth.h>
+#include "bt_defs.h"
+#include <string.h>
+
+#define LOG_TAG "bt_btif_queue"
+#include "btif_common.h"
+#include "btif_profile_queue.h"
+#include "gki.h"
+#include "list.h"
+#include "allocator.h"
+#include "stack_manager.h"
+
+/*******************************************************************************
+** Local type definitions
+*******************************************************************************/
+
+typedef enum {
+ BTIF_QUEUE_CONNECT_EVT,
+ BTIF_QUEUE_ADVANCE_EVT,
+} btif_queue_event_t;
+
+typedef struct {
+ bt_bdaddr_t bda;
+ uint16_t uuid;
+ bool busy;
+ btif_connect_cb_t connect_cb;
+} connect_node_t;
+
+/*******************************************************************************
+** Static variables
+*******************************************************************************/
+
+static list_t *connect_queue;
+
+static const size_t MAX_REASONABLE_REQUESTS = 10;
+
+/*******************************************************************************
+** Queue helper functions
+*******************************************************************************/
+
+static void queue_int_add(connect_node_t *p_param)
+{
+ if (!connect_queue) {
++ connect_queue = list_new(osi_free_func);
+ assert(connect_queue != NULL);
+ }
+
+ // Sanity check to make sure we're not leaking connection requests
+ assert(list_length(connect_queue) < MAX_REASONABLE_REQUESTS);
+
+ for (const list_node_t *node = list_begin(connect_queue); node != list_end(connect_queue); node = list_next(node)) {
+ if (((connect_node_t *)list_node(node))->uuid == p_param->uuid) {
+ LOG_INFO("%s dropping duplicate connect request for uuid: %04x", __func__, p_param->uuid);
+ return;
+ }
+ }
+
+ connect_node_t *p_node = osi_malloc(sizeof(connect_node_t));
+ assert(p_node != NULL);
+ memcpy(p_node, p_param, sizeof(connect_node_t));
+ list_append(connect_queue, p_node);
+}
+
+static void queue_int_advance()
+{
+ if (connect_queue && !list_is_empty(connect_queue)) {
+ list_remove(connect_queue, list_front(connect_queue));
+ }
+}
+
+static void queue_int_handle_evt(UINT16 event, char *p_param)
+{
+ switch (event) {
+ case BTIF_QUEUE_CONNECT_EVT:
+ queue_int_add((connect_node_t *)p_param);
+ break;
+
+ case BTIF_QUEUE_ADVANCE_EVT:
+ queue_int_advance();
+ break;
+ }
+
+ // if (stack_manager_get_interface()->get_stack_is_running())
+ if (stack_manager_is_stack_running()) {
+ btif_queue_connect_next();
+ }
+}
+
+/*******************************************************************************
+**
+** Function btif_queue_connect
+**
+** Description Add a new connection to the queue and trigger the next
+** scheduled connection.
+**
+** Returns BT_STATUS_SUCCESS if successful
+**
+*******************************************************************************/
+bt_status_t btif_queue_connect(uint16_t uuid, const bt_bdaddr_t *bda, btif_connect_cb_t connect_cb)
+{
+ connect_node_t node;
+ memset(&node, 0, sizeof(connect_node_t));
+ memcpy(&node.bda, bda, sizeof(bt_bdaddr_t));
+ node.uuid = uuid;
+ node.connect_cb = connect_cb;
+
+ return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT,
+ (char *)&node, sizeof(connect_node_t), NULL);
+}
+
+/*******************************************************************************
+**
+** Function btif_queue_advance
+**
+** Description Clear the queue's busy status and advance to the next
+** scheduled connection.
+**
+** Returns void
+**
+*******************************************************************************/
+void btif_queue_advance()
+{
+ btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_ADVANCE_EVT,
+ NULL, 0, NULL);
+}
+
+// This function dispatches the next pending connect request. It is called from
+// stack_manager when the stack comes up.
+bt_status_t btif_queue_connect_next(void)
+{
+ if (!connect_queue || list_is_empty(connect_queue)) {
+ return BT_STATUS_FAIL;
+ }
+
+ connect_node_t *p_head = list_front(connect_queue);
+
+ // If the queue is currently busy, we return success anyway,
+ // since the connection has been queued...
+ if (p_head->busy) {
+ return BT_STATUS_SUCCESS;
+ }
+
+ p_head->busy = true;
+ return p_head->connect_cb(&p_head->bda, p_head->uuid);
+}
+
+
+/*******************************************************************************
+**
+** Function btif_queue_release
+**
+** Description Free up all the queue nodes and set the queue head to NULL
+**
+** Returns void
+**
+*******************************************************************************/
+void btif_queue_release()
+{
+ list_free(connect_queue);
+ connect_queue = NULL;
+}
** Returns A pointer to the buffer, or NULL if none available
**
*******************************************************************************/
- void *GKI_getbuf (UINT16 size)
+ void *GKI_getbuf_func(UINT16 size)
{
BUFFER_HDR_T *header = osi_malloc(size + BUFFER_HDR_SIZE);
- header->status = BUF_STATUS_UNLINKED;
- header->p_next = NULL;
- header->Type = 0;
- header->size = size;
-
- return header + 1;
+ assert(header != NULL);
+ if (header != NULL) {
+ header->status = BUF_STATUS_UNLINKED;
+ header->p_next = NULL;
+ header->Type = 0;
+ header->size = size;
+
+ return header + 1;
+ } else {
+ return NULL;
+ }
}
-
/*******************************************************************************
**
- ** Function GKI_getpoolbuf
+ ** Function GKI_getpoolbuf_func
**
** Description Called by an application to get a free buffer from
** a specific buffer pool.
SIG_PRF_WORK = 0xfd,
SIG_BTU_START_UP = 0xfe,
SIG_BTU_WORK = 0xff,
- SIG_BTIF_WORK = 0xff
+ // SIG_BTIF_WORK = 0xff
};
- #define HCI_HOST_TASK_STACK_SIZE 1024
- #define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 3)
+ #define HCI_HOST_TASK_STACK_SIZE 1500
+ #define HCI_HOST_TASK_PRIO (configMAX_PRIORITIES - 2)
#define HCI_HOST_TASK_NAME "hciHostT"
- #define HCI_HOST_QUEUE_NUM 30
+ #define HCI_HOST_QUEUE_NUM 40
- #define HCI_H4_TASK_STACK_SIZE 1024
+ #define HCI_H4_TASK_STACK_SIZE 1500
#define HCI_H4_TASK_PRIO (configMAX_PRIORITIES - 3)
#define HCI_H4_TASK_NAME "hciH4T"
- #define HCI_H4_QUEUE_NUM 30
+ #define HCI_H4_QUEUE_NUM 60
#define BTU_TASK_STACK_SIZE 4096
- #define BTU_TASK_PRIO (configMAX_PRIORITIES - 1)
+ #define BTU_TASK_PRIO (configMAX_PRIORITIES - 4)
#define BTU_TASK_NAME "btuT"
#define BTU_QUEUE_NUM 50
--- /dev/null
- AVCT_TRACE_WARNING("%x: aloc:%d, lcb:0x%x/0x%x, ccb:0x%x/0x%x",
+/******************************************************************************
+ *
+ * Copyright (C) 2003-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains the link control state machine and functions which
+ * operate on the link control block.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bt_types.h"
+#include "bt_target.h"
+#include "bt_utils.h"
+#include "avct_api.h"
+#include "avct_int.h"
+#include "gki.h"
+
+/*****************************************************************************
+** state machine constants and types
+*****************************************************************************/
+
+#if BT_TRACE_VERBOSE == TRUE
+
+/* verbose state strings for trace */
+const char * const avct_lcb_st_str[] = {
+ "LCB_IDLE_ST",
+ "LCB_OPENING_ST",
+ "LCB_OPEN_ST",
+ "LCB_CLOSING_ST"
+};
+
+/* verbose event strings for trace */
+const char * const avct_lcb_evt_str[] = {
+ "UL_BIND_EVT",
+ "UL_UNBIND_EVT",
+ "UL_MSG_EVT",
+ "INT_CLOSE_EVT",
+ "LL_OPEN_EVT",
+ "LL_CLOSE_EVT",
+ "LL_MSG_EVT",
+ "LL_CONG_EVT"
+};
+
+#endif
+
+/* lcb state machine states */
+enum {
+ AVCT_LCB_IDLE_ST,
+ AVCT_LCB_OPENING_ST,
+ AVCT_LCB_OPEN_ST,
+ AVCT_LCB_CLOSING_ST
+};
+
+/* state machine action enumeration list */
+enum {
+ AVCT_LCB_CHNL_OPEN,
+ AVCT_LCB_CHNL_DISC,
+ AVCT_LCB_SEND_MSG,
+ AVCT_LCB_OPEN_IND,
+ AVCT_LCB_OPEN_FAIL,
+ AVCT_LCB_CLOSE_IND,
+ AVCT_LCB_CLOSE_CFM,
+ AVCT_LCB_MSG_IND,
+ AVCT_LCB_CONG_IND,
+ AVCT_LCB_BIND_CONN,
+ AVCT_LCB_BIND_FAIL,
+ AVCT_LCB_UNBIND_DISC,
+ AVCT_LCB_CHK_DISC,
+ AVCT_LCB_DISCARD_MSG,
+ AVCT_LCB_DEALLOC,
+ AVCT_LCB_FREE_MSG_IND,
+ AVCT_LCB_NUM_ACTIONS
+};
+
+#define AVCT_LCB_IGNORE AVCT_LCB_NUM_ACTIONS
+
+/* type for action functions */
+typedef void (*tAVCT_LCB_ACTION)(tAVCT_LCB *p_ccb, tAVCT_LCB_EVT *p_data);
+
+/* action function list */
+const tAVCT_LCB_ACTION avct_lcb_action[] = {
+ avct_lcb_chnl_open,
+ avct_lcb_chnl_disc,
+ avct_lcb_send_msg,
+ avct_lcb_open_ind,
+ avct_lcb_open_fail,
+ avct_lcb_close_ind,
+ avct_lcb_close_cfm,
+ avct_lcb_msg_ind,
+ avct_lcb_cong_ind,
+ avct_lcb_bind_conn,
+ avct_lcb_bind_fail,
+ avct_lcb_unbind_disc,
+ avct_lcb_chk_disc,
+ avct_lcb_discard_msg,
+ avct_lcb_dealloc,
+ avct_lcb_free_msg_ind
+};
+
+/* state table information */
+#define AVCT_LCB_ACTIONS 2 /* number of actions */
+#define AVCT_LCB_NEXT_STATE 2 /* position of next state */
+#define AVCT_LCB_NUM_COLS 3 /* number of columns in state tables */
+
+/* state table for idle state */
+const UINT8 avct_lcb_st_idle[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_CHNL_OPEN, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}
+};
+
+/* state table for opening state */
+const UINT8 avct_lcb_st_opening[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_OPEN_FAIL, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}
+};
+
+/* state table for open state */
+const UINT8 avct_lcb_st_open[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_BIND_CONN, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_CHK_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_SEND_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}
+};
+
+/* state table for closing state */
+const UINT8 avct_lcb_st_closing[][AVCT_LCB_NUM_COLS] = {
+/* Event Action 1 Action 2 Next state */
+/* UL_BIND_EVT */ {AVCT_LCB_BIND_FAIL, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* UL_UNBIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_CFM, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST},
+/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST},
+/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}
+};
+
+/* type for state table */
+typedef const UINT8 (*tAVCT_LCB_ST_TBL)[AVCT_LCB_NUM_COLS];
+
+/* state table */
+const tAVCT_LCB_ST_TBL avct_lcb_st_tbl[] = {
+ avct_lcb_st_idle,
+ avct_lcb_st_opening,
+ avct_lcb_st_open,
+ avct_lcb_st_closing
+};
+
+/*******************************************************************************
+**
+** Function avct_lcb_event
+**
+** Description State machine event handling function for lcb
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_LCB_ST_TBL state_table;
+ UINT8 action;
+ int i;
+
+#if BT_TRACE_VERBOSE == TRUE
+ AVCT_TRACE_EVENT("LCB lcb=%d event=%s state=%s", p_lcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_lcb->state]);
+#else
+ AVCT_TRACE_EVENT("LCB lcb=%d event=%d state=%d", p_lcb->allocated, event, p_lcb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = avct_lcb_st_tbl[p_lcb->state];
+
+ /* set next state */
+ p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE];
+
+ /* execute action functions */
+ for (i = 0; i < AVCT_LCB_ACTIONS; i++)
+ {
+ if ((action = state_table[event][i]) != AVCT_LCB_IGNORE)
+ {
+ (*avct_lcb_action[action])(p_lcb, p_data);
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_bcb_event
+**
+** Description State machine event handling function for lcb
+**
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+#if (AVCT_BROWSE_INCLUDED == TRUE)
+void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_LCB_ST_TBL state_table;
+ UINT8 action;
+ int i;
+
+#if BT_TRACE_VERBOSE == TRUE
+ AVCT_TRACE_EVENT("BCB lcb=%d event=%s state=%s", p_bcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_bcb->state]);
+#else
+ AVCT_TRACE_EVENT("BCB lcb=%d event=%d state=%d", p_bcb->allocated, event, p_bcb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = avct_lcb_st_tbl[p_bcb->state];
+
+ /* set next state */
+ p_bcb->state = state_table[event][AVCT_LCB_NEXT_STATE];
+
+ /* execute action functions */
+ for (i = 0; i < AVCT_LCB_ACTIONS; i++)
+ {
+ if ((action = state_table[event][i]) != AVCT_LCB_IGNORE)
+ {
+ (*avct_bcb_action[action])(p_bcb, p_data);
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function avct_lcb_by_bd
+**
+** Description This lookup function finds the lcb for a BD address.
+**
+**
+** Returns pointer to the lcb, or NULL if none found.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ /* if allocated lcb has matching lcb */
+ if (p_lcb->allocated && (!memcmp(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN)))
+ {
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* if no lcb found */
+ p_lcb = NULL;
+
+ AVCT_TRACE_DEBUG("No lcb for addr %02x-%02x-%02x-%02x-%02x-%02x",
+ bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
+ }
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_alloc
+**
+** Description Allocate a link control block.
+**
+**
+** Returns pointer to the lcb, or NULL if none could be allocated.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ if (!p_lcb->allocated)
+ {
+ p_lcb->allocated = (UINT8)(i + 1);
+ memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN);
+ AVCT_TRACE_DEBUG("avct_lcb_alloc %d", p_lcb->allocated);
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* out of lcbs */
+ p_lcb = NULL;
+ AVCT_TRACE_WARNING("Out of lcbs");
+ }
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_dealloc
+**
+** Description Deallocate a link control block.
+**
+**
+** Returns void.
+**
+*******************************************************************************/
+void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ BOOLEAN found = FALSE;
+ int i;
+ UNUSED(p_data);
+
+ AVCT_TRACE_DEBUG("avct_lcb_dealloc %d", p_lcb->allocated);
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ /* if ccb allocated and */
+ if (p_ccb->allocated)
+ {
+ if (p_ccb->p_lcb == p_lcb)
+ {
+ AVCT_TRACE_DEBUG("avct_lcb_dealloc used by ccb: %d", i);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ AVCT_TRACE_DEBUG("avct_lcb_dealloc now");
+
+ /* clear reassembled msg buffer if in use */
+ if (p_lcb->p_rx_msg != NULL)
+ {
+ GKI_freebuf(p_lcb->p_rx_msg);
+ }
+ memset(p_lcb, 0, sizeof(tAVCT_LCB));
+ }
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_by_lcid
+**
+** Description Find the LCB associated with the L2CAP LCID
+**
+**
+** Returns pointer to the lcb, or NULL if none found.
+**
+*******************************************************************************/
+tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid)
+{
+ tAVCT_LCB *p_lcb = &avct_cb.lcb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++)
+ {
+ if (p_lcb->allocated && ((p_lcb->ch_lcid == lcid) || (p_lcb->conflict_lcid == lcid)))
+ {
+ break;
+ }
+ }
+
+ if (i == AVCT_NUM_LINKS)
+ {
+ /* out of lcbs */
+ p_lcb = NULL;
+ AVCT_TRACE_WARNING("No lcb for lcid %x", lcid);
+ }
+
+ return p_lcb;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_has_pid
+**
+** Description See if any ccbs on this lcb have a particular pid.
+**
+**
+** Returns Pointer to CCB if PID found, NULL otherwise.
+**
+*******************************************************************************/
+tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb->cc.pid == pid))
+ {
+ return p_ccb;
+ }
+ }
+ return NULL;
+}
+
+/*******************************************************************************
+**
+** Function avct_lcb_last_ccb
+**
+** Description See if given ccb is only one on the lcb.
+**
+**
+** Returns TRUE if ccb is last, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last)
+{
+ tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
+ int i;
+
+ AVCT_TRACE_WARNING("avct_lcb_last_ccb");
+ for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++)
+ {
++ AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
+ i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
+ if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+
--- /dev/null
- AVDT_TRACE_WARNING("p_scb->p_pkt=%x, p_scb->p_ccb=%x, IsQueueEmpty=%x, p_scb->frag_off=%x\n",
+/******************************************************************************
+ *
+ * Copyright (C) 2002-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains API of the audio/video distribution transport
+ * protocol.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bt_types.h"
+#include "bt_target.h"
+#include "avdt_api.h"
+#include "avdtc_api.h"
+#include "avdt_int.h"
+#include "l2c_api.h"
+#include "btm_api.h"
+#include "btu.h"
+
+
+/* Control block for AVDT */
+#if AVDT_DYNAMIC_MEMORY == FALSE
+tAVDT_CB avdt_cb;
+#endif
+
+
+/*******************************************************************************
+**
+** Function avdt_process_timeout
+**
+** Description This function is called by BTU when an AVDTP timer
+** expires. The function sends a timer event to the
+** appropriate CCB or SCB state machine.
+**
+** This function is for use internal to the stack only.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void avdt_process_timeout(TIMER_LIST_ENT *p_tle)
+{
+ UINT8 event = 0;
+ UINT8 err_code = AVDT_ERR_TIMEOUT;
+
+ switch (p_tle->event)
+ {
+ case BTU_TTYPE_AVDT_CCB_RET:
+ event = AVDT_CCB_RET_TOUT_EVT + AVDT_CCB_MKR;
+ break;
+
+ case BTU_TTYPE_AVDT_CCB_RSP:
+ event = AVDT_CCB_RSP_TOUT_EVT + AVDT_CCB_MKR;
+ break;
+
+ case BTU_TTYPE_AVDT_CCB_IDLE:
+ event = AVDT_CCB_IDLE_TOUT_EVT + AVDT_CCB_MKR;
+ break;
+
+ case BTU_TTYPE_AVDT_SCB_TC:
+ event = AVDT_SCB_TC_TOUT_EVT;
+ break;
+
+ default:
+ break;
+ }
+
+ if (event & AVDT_CCB_MKR)
+ {
+ avdt_ccb_event((tAVDT_CCB *) p_tle->param, (UINT8) (event & ~AVDT_CCB_MKR),
+ (tAVDT_CCB_EVT *) &err_code);
+ }
+ else
+ {
+ avdt_scb_event((tAVDT_SCB *) p_tle->param, event, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function AVDT_Register
+**
+** Description This is the system level registration function for the
+** AVDTP protocol. This function initializes AVDTP and
+** prepares the protocol stack for its use. This function
+** must be called once by the system or platform using AVDTP
+** before the other functions of the API an be used.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void AVDT_Register(tAVDT_REG *p_reg, tAVDT_CTRL_CBACK *p_cback)
+{
+ /* register PSM with L2CAP */
+ L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO *) &avdt_l2c_appl);
+
+ /* set security level */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);
+
+ /* do not use security on the media channel */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);
+
+#if AVDT_REPORTING == TRUE
+ /* do not use security on the reporting channel */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
+ AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
+#endif
+
+ /* initialize AVDTP data structures */
+ avdt_scb_init();
+ avdt_ccb_init();
+ avdt_ad_init();
+
+ /* copy registration struct */
+ memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG));
+ avdt_cb.p_conn_cback = p_cback;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_Deregister
+**
+** Description This function is called to deregister use AVDTP protocol.
+** It is called when AVDTP is no longer being used by any
+** application in the system. Before this function can be
+** called, all streams must be removed with AVDT_RemoveStream().
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void AVDT_Deregister(void)
+{
+ /* deregister PSM with L2CAP */
+ L2CA_Deregister(AVDT_PSM);
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SINK_Activate
+**
+** Description Activate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made false in case of activation. A2DP SRC
+** will receive in_use as false and can open A2DP Sink
+** connection
+**
+** Returns void.
+**
+*******************************************************************************/
+void AVDT_SINK_Activate()
+{
+ tAVDT_SCB *p_scb = &avdt_cb.scb[0];
+ int i;
+ AVDT_TRACE_DEBUG("AVDT_SINK_Activate");
+ /* for all allocated scbs */
+ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++)
+ {
+ if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK))
+ {
+ AVDT_TRACE_DEBUG("AVDT_SINK_Activate found scb");
+ p_scb->sink_activated = TRUE;
+ /* update in_use */
+ p_scb->in_use = FALSE;
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SINK_Deactivate
+**
+** Description Deactivate SEP of A2DP Sink. In Use parameter is adjusted.
+** In Use will be made TRUE in case of activation. A2DP SRC
+** will receive in_use as true and will not open A2DP Sink
+** connection
+**
+** Returns void.
+**
+*******************************************************************************/
+void AVDT_SINK_Deactivate()
+{
+ tAVDT_SCB *p_scb = &avdt_cb.scb[0];
+ int i;
+ AVDT_TRACE_DEBUG("AVDT_SINK_Deactivate");
+ /* for all allocated scbs */
+ for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++)
+ {
+ if ((p_scb->allocated) && (p_scb->cs.tsep == AVDT_TSEP_SNK))
+ {
+ AVDT_TRACE_DEBUG("AVDT_SINK_Deactivate, found scb");
+ p_scb->sink_activated = FALSE;
+ /* update in_use */
+ p_scb->in_use = TRUE;
+ break;
+ }
+ }
+}
+
+void AVDT_AbortReq(UINT8 handle)
+{
+ AVDT_TRACE_ERROR("%s\n", __func__);
+
+ tAVDT_SCB *p_scb = avdt_scb_by_hdl(handle);
+ if (p_scb != NULL)
+ {
+ avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL);
+ } else {
+ AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream\n", __func__);
+ }
+}
+
+/*******************************************************************************
+**
+** Function AVDT_CreateStream
+**
+** Description Create a stream endpoint. After a stream endpoint is
+** created an application can initiate a connection between
+** this endpoint and an endpoint on a peer device. In
+** addition, a peer device can discover, get the capabilities,
+** and connect to this endpoint.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_CreateStream(UINT8 *p_handle, tAVDT_CS *p_cs)
+{
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB *p_scb;
+
+ /* Verify parameters; if invalid, return failure */
+ if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) || (p_cs->p_ctrl_cback == NULL))
+ {
+ result = AVDT_BAD_PARAMS;
+ }
+ /* Allocate scb; if no scbs, return failure */
+ else if ((p_scb = avdt_scb_alloc(p_cs)) == NULL)
+ {
+ result = AVDT_NO_RESOURCES;
+ }
+ else
+ {
+ *p_handle = avdt_scb_to_hdl(p_scb);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_RemoveStream
+**
+** Description Remove a stream endpoint. This function is called when
+** the application is no longer using a stream endpoint.
+** If this function is called when the endpoint is connected
+** the connection is closed and then the stream endpoint
+** is removed.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_RemoveStream(UINT8 handle)
+{
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB *p_scb;
+
+ /* look up scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ {
+ /* send remove event to scb */
+ avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_DiscoverReq
+**
+** Description This function initiates a connection to the AVDTP service
+** on the peer device, if not already present, and discovers
+** the stream endpoints on the peer device. (Please note
+** that AVDTP discovery is unrelated to SDP discovery).
+** This function can be called at any time regardless of whether
+** there is an AVDTP connection to the peer device.
+**
+** When discovery is complete, an AVDT_DISCOVER_CFM_EVT
+** is sent to the application via its callback function.
+** The application must not call AVDT_GetCapReq() or
+** AVDT_DiscoverReq() again to the same device until
+** discovery is complete.
+**
+** The memory addressed by sep_info is allocated by the
+** application. This memory is written to by AVDTP as part
+** of the discovery procedure. This memory must remain
+** accessible until the application receives the
+** AVDT_DISCOVER_CFM_EVT.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_DiscoverReq(BD_ADDR bd_addr, tAVDT_SEP_INFO *p_sep_info,
+ UINT8 max_seps, tAVDT_CTRL_CBACK *p_cback)
+{
+ tAVDT_CCB *p_ccb;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_CCB_EVT evt;
+
+ /* find channel control block for this bd addr; if none, allocate one */
+ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL)
+ {
+ if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL)
+ {
+ /* could not allocate channel control block */
+ result = AVDT_NO_RESOURCES;
+ }
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ /* make sure no discovery or get capabilities req already in progress */
+ if (p_ccb->proc_busy)
+ {
+ result = AVDT_BUSY;
+ }
+ /* send event to ccb */
+ else
+ {
+ evt.discover.p_sep_info = p_sep_info;
+ evt.discover.num_seps = max_seps;
+ evt.discover.p_cback = p_cback;
+ avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt);
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function avdt_get_cap_req
+**
+** Description internal function to serve both AVDT_GetCapReq and
+** AVDT_GetAllCapReq
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+static UINT16 avdt_get_cap_req(BD_ADDR bd_addr, tAVDT_CCB_API_GETCAP *p_evt)
+{
+ tAVDT_CCB *p_ccb = NULL;
+ UINT16 result = AVDT_SUCCESS;
+
+ /* verify SEID */
+ if ((p_evt->single.seid < AVDT_SEID_MIN) || (p_evt->single.seid > AVDT_SEID_MAX))
+ {
+ AVDT_TRACE_ERROR("seid: %d\n", p_evt->single.seid);
+ result = AVDT_BAD_PARAMS;
+ }
+ /* find channel control block for this bd addr; if none, allocate one */
+ else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL)
+ {
+ if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL)
+ {
+ /* could not allocate channel control block */
+ result = AVDT_NO_RESOURCES;
+ }
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ /* make sure no discovery or get capabilities req already in progress */
+ if (p_ccb->proc_busy)
+ {
+ result = AVDT_BUSY;
+ }
+ /* send event to ccb */
+ else
+ {
+ avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT *)p_evt);
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_GetCapReq
+**
+** Description This function initiates a connection to the AVDTP service
+** on the peer device, if not already present, and gets the
+** capabilities of a stream endpoint on the peer device.
+** This function can be called at any time regardless of
+** whether there is an AVDTP connection to the peer device.
+**
+** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
+** sent to the application via its callback function. The
+** application must not call AVDT_GetCapReq() or
+** AVDT_DiscoverReq() again until the procedure is complete.
+**
+** The memory pointed to by p_cfg is allocated by the
+** application. This memory is written to by AVDTP as part
+** of the get capabilities procedure. This memory must
+** remain accessible until the application receives
+** the AVDT_GETCAP_CFM_EVT.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_GetCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback)
+{
+ tAVDT_CCB_API_GETCAP getcap;
+
+ getcap.single.seid = seid;
+ getcap.single.sig_id = AVDT_SIG_GETCAP;
+ getcap.p_cfg = p_cfg;
+ getcap.p_cback = p_cback;
+ return avdt_get_cap_req (bd_addr, &getcap);
+}
+
+/*******************************************************************************
+**
+** Function AVDT_GetAllCapReq
+**
+** Description This function initiates a connection to the AVDTP service
+** on the peer device, if not already present, and gets the
+** capabilities of a stream endpoint on the peer device.
+** This function can be called at any time regardless of
+** whether there is an AVDTP connection to the peer device.
+**
+** When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
+** sent to the application via its callback function. The
+** application must not call AVDT_GetCapReq() or
+** AVDT_DiscoverReq() again until the procedure is complete.
+**
+** The memory pointed to by p_cfg is allocated by the
+** application. This memory is written to by AVDTP as part
+** of the get capabilities procedure. This memory must
+** remain accessible until the application receives
+** the AVDT_GETCAP_CFM_EVT.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_GetAllCapReq(BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg, tAVDT_CTRL_CBACK *p_cback)
+{
+ tAVDT_CCB_API_GETCAP getcap;
+
+ getcap.single.seid = seid;
+ getcap.single.sig_id = AVDT_SIG_GET_ALLCAP;
+ getcap.p_cfg = p_cfg;
+ getcap.p_cback = p_cback;
+ return avdt_get_cap_req (bd_addr, &getcap);
+}
+
+/*******************************************************************************
+**
+** Function AVDT_DelayReport
+**
+** Description This functions sends a Delay Report to the peer device
+** that is associated with a particular SEID.
+** This function is called by SNK device.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_DelayReport(UINT8 handle, UINT8 seid, UINT16 delay)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB_EVT evt;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ /* send event to scb */
+ {
+ evt.apidelay.hdr.seid = seid;
+ evt.apidelay.delay = delay;
+ avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt);
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_OpenReq
+**
+** Description This function initiates a connection to the AVDTP service
+** on the peer device, if not already present, and connects
+** to a stream endpoint on a peer device. When the connection
+** is completed, an AVDT_OPEN_CFM_EVT is sent to the
+** application via the control callback function for this handle.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_OpenReq(UINT8 handle, BD_ADDR bd_addr, UINT8 seid, tAVDT_CFG *p_cfg)
+{
+ tAVDT_CCB *p_ccb = NULL;
+ tAVDT_SCB *p_scb = NULL;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB_EVT evt;
+
+ /* verify SEID */
+ if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX))
+ {
+ result = AVDT_BAD_PARAMS;
+ }
+ /* map handle to scb */
+ else if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* find channel control block for this bd addr; if none, allocate one */
+ else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL)
+ {
+ if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL)
+ {
+ /* could not allocate channel control block */
+ result = AVDT_NO_RESOURCES;
+ }
+ }
+
+ /* send event to scb */
+ if (result == AVDT_SUCCESS)
+ {
+ evt.msg.config_cmd.hdr.seid = seid;
+ evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb);
+ evt.msg.config_cmd.int_seid = handle;
+ evt.msg.config_cmd.p_cfg = p_cfg;
+ avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_ConfigRsp
+**
+** Description Respond to a configure request from the peer device. This
+** function must be called if the application receives an
+** AVDT_CONFIG_IND_EVT through its control callback.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_ConfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category)
+{
+ tAVDT_SCB *p_scb;
+ tAVDT_SCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+ UINT8 event_code;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* handle special case when this function is called but peer has not send
+ ** a configuration cmd; ignore and return error result
+ */
+ else if (!p_scb->in_use)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* send event to scb */
+ else
+ {
+ evt.msg.hdr.err_code = error_code;
+ evt.msg.hdr.err_param = category;
+ evt.msg.hdr.label = label;
+ if (error_code == 0)
+ {
+ event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT;
+ }
+ else
+ {
+ event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT;
+ }
+ avdt_scb_event(p_scb, event_code, &evt);
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_StartReq
+**
+** Description Start one or more stream endpoints. This initiates the
+** transfer of media packets for the streams. All stream
+** endpoints must previously be opened. When the streams
+** are started, an AVDT_START_CFM_EVT is sent to the
+** application via the control callback function for each stream.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_StartReq(UINT8 *p_handles, UINT8 num_handles)
+{
+ tAVDT_SCB *p_scb = NULL;
+ tAVDT_CCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+ int i;
+
+ if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS))
+ {
+ result = AVDT_BAD_PARAMS;
+ }
+ else
+ {
+ /* verify handles */
+ for (i = 0; i < num_handles; i++)
+ {
+ if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ break;
+ }
+ }
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ if (p_scb->p_ccb == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ {
+ /* send event to ccb */
+ memcpy(evt.msg.multi.seid_list, p_handles, num_handles);
+ evt.msg.multi.num_seps = num_handles;
+ avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_START_REQ_EVT, &evt);
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SuspendReq
+**
+** Description Suspend one or more stream endpoints. This suspends the
+** transfer of media packets for the streams. All stream
+** endpoints must previously be open and started. When the
+** streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to
+** the application via the control callback function for
+** each stream.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_SuspendReq(UINT8 *p_handles, UINT8 num_handles)
+{
+ tAVDT_SCB *p_scb = NULL;
+ tAVDT_CCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+ int i;
+
+ if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS))
+ {
+ result = AVDT_BAD_PARAMS;
+ }
+ else
+ {
+ /* verify handles */
+ for (i = 0; i < num_handles; i++)
+ {
+ if ((p_scb = avdt_scb_by_hdl(p_handles[i])) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ break;
+ }
+ }
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ if (p_scb->p_ccb == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ {
+ /* send event to ccb */
+ memcpy(evt.msg.multi.seid_list, p_handles, num_handles);
+ evt.msg.multi.num_seps = num_handles;
+ avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt);
+ }
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_CloseReq
+**
+** Description Close a stream endpoint. This stops the transfer of media
+** packets and closes the transport channel associated with
+** this stream endpoint. When the stream is closed, an
+** AVDT_CLOSE_CFM_EVT is sent to the application via the
+** control callback function for this handle.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_CloseReq(UINT8 handle)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ /* send event to scb */
+ {
+ avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL);
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_ReconfigReq
+**
+** Description Reconfigure a stream endpoint. This allows the application
+** to change the codec or content protection capabilities of
+** a stream endpoint after it has been opened. This function
+** can only be called if the stream is opened but not started
+** or if the stream has been suspended. When the procedure
+** is completed, an AVDT_RECONFIG_CFM_EVT is sent to the
+** application via the control callback function for this handle.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_ReconfigReq(UINT8 handle, tAVDT_CFG *p_cfg)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB_EVT evt;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* send event to scb */
+ else
+ {
+ /* force psc_mask to zero */
+ p_cfg->psc_mask = 0;
+
+ evt.msg.reconfig_cmd.p_cfg = p_cfg;
+ avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_ReconfigRsp
+**
+** Description Respond to a reconfigure request from the peer device.
+** This function must be called if the application receives
+** an AVDT_RECONFIG_IND_EVT through its control callback.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_ReconfigRsp(UINT8 handle, UINT8 label, UINT8 error_code, UINT8 category)
+{
+ tAVDT_SCB *p_scb;
+ tAVDT_SCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* send event to scb */
+ else
+ {
+ evt.msg.hdr.err_code = error_code;
+ evt.msg.hdr.err_param = category;
+ evt.msg.hdr.label = label;
+ avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt);
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SecurityReq
+**
+** Description Send a security request to the peer device. When the
+** security procedure is completed, an AVDT_SECURITY_CFM_EVT
+** is sent to the application via the control callback function
+** for this handle. (Please note that AVDTP security procedures
+** are unrelated to Bluetooth link level security.)
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_SecurityReq(UINT8 handle, UINT8 *p_data, UINT16 len)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB_EVT evt;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* send event to scb */
+ else
+ {
+ evt.msg.security_rsp.p_data = p_data;
+ evt.msg.security_rsp.len = len;
+ avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_SecurityRsp
+**
+** Description Respond to a security request from the peer device.
+** This function must be called if the application receives
+** an AVDT_SECURITY_IND_EVT through its control callback.
+** (Please note that AVDTP security procedures are unrelated
+** to Bluetooth link level security.)
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_SecurityRsp(UINT8 handle, UINT8 label, UINT8 error_code,
+ UINT8 *p_data, UINT16 len)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_SCB_EVT evt;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ /* send event to scb */
+ else
+ {
+ evt.msg.security_rsp.hdr.err_code = error_code;
+ evt.msg.security_rsp.hdr.label = label;
+ evt.msg.security_rsp.p_data = p_data;
+ evt.msg.security_rsp.len = len;
+ avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_WriteReqOpt
+**
+** Description Send a media packet to the peer device. The stream must
+** be started before this function is called. Also, this
+** function can only be called if the stream is a SRC.
+**
+** When AVDTP has sent the media packet and is ready for the
+** next packet, an AVDT_WRITE_CFM_EVT is sent to the
+** application via the control callback. The application must
+** wait for the AVDT_WRITE_CFM_EVT before it makes the next
+** call to AVDT_WriteReq(). If the applications calls
+** AVDT_WriteReq() before it receives the event the packet
+** will not be sent. The application may make its first call
+** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT
+** or AVDT_START_IND_EVT.
+**
+** The application passes the packet using the BT_HDR structure.
+** This structure is described in section 2.1. The offset
+** field must be equal to or greater than AVDT_MEDIA_OFFSET
+** (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used).
+** This allows enough space in the buffer for the L2CAP and
+** AVDTP headers.
+**
+** The memory pointed to by p_pkt must be a GKI buffer
+** allocated by the application. This buffer will be freed
+** by the protocol stack; the application must not free
+** this buffer.
+**
+** The opt parameter allows passing specific options like:
+** - NO_RTP : do not add the RTP header to buffer
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_WriteReqOpt(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt, tAVDT_DATA_OPT_MASK opt)
+{
+ tAVDT_SCB *p_scb;
+ tAVDT_SCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ {
+ evt.apiwrite.p_buf = p_pkt;
+ evt.apiwrite.time_stamp = time_stamp;
+ evt.apiwrite.m_pt = m_pt;
+ evt.apiwrite.opt = opt;
+#if AVDT_MULTIPLEXING == TRUE
+ GKI_init_q (&evt.apiwrite.frag_q);
+#endif
+ avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt);
+ }
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_WriteReq
+**
+** Description Send a media packet to the peer device. The stream must
+** be started before this function is called. Also, this
+** function can only be called if the stream is a SRC.
+**
+** When AVDTP has sent the media packet and is ready for the
+** next packet, an AVDT_WRITE_CFM_EVT is sent to the
+** application via the control callback. The application must
+** wait for the AVDT_WRITE_CFM_EVT before it makes the next
+** call to AVDT_WriteReq(). If the applications calls
+** AVDT_WriteReq() before it receives the event the packet
+** will not be sent. The application may make its first call
+** to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT
+** or AVDT_START_IND_EVT.
+**
+** The application passes the packet using the BT_HDR structure.
+** This structure is described in section 2.1. The offset
+** field must be equal to or greater than AVDT_MEDIA_OFFSET.
+** This allows enough space in the buffer for the L2CAP and
+** AVDTP headers.
+**
+** The memory pointed to by p_pkt must be a GKI buffer
+** allocated by the application. This buffer will be freed
+** by the protocol stack; the application must not free
+** this buffer.
+**
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_WriteReq(UINT8 handle, BT_HDR *p_pkt, UINT32 time_stamp, UINT8 m_pt)
+{
+ return AVDT_WriteReqOpt(handle, p_pkt, time_stamp, m_pt, AVDT_DATA_OPT_NONE);
+}
+
+/*******************************************************************************
+**
+** Function AVDT_ConnectReq
+**
+** Description This function initiates an AVDTP signaling connection
+** to the peer device. When the connection is completed, an
+** AVDT_CONNECT_IND_EVT is sent to the application via its
+** control callback function. If the connection attempt fails
+** an AVDT_DISCONNECT_IND_EVT is sent. The security mask
+** parameter overrides the outgoing security mask set in
+** AVDT_Register().
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_ConnectReq(BD_ADDR bd_addr, UINT8 sec_mask, tAVDT_CTRL_CBACK *p_cback)
+{
+ tAVDT_CCB *p_ccb = NULL;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_CCB_EVT evt;
+
+ /* find channel control block for this bd addr; if none, allocate one */
+ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL)
+ {
+ if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL)
+ {
+ /* could not allocate channel control block */
+ result = AVDT_NO_RESOURCES;
+ }
+ }
+ else if (p_ccb->ll_opened == FALSE)
+ {
+ AVDT_TRACE_WARNING("AVDT_ConnectReq: CCB LL is in the middle of opening");
+
+ /* ccb was already allocated for the incoming signalling. */
+ result = AVDT_BUSY;
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ /* send event to ccb */
+ evt.connect.p_cback = p_cback;
+ evt.connect.sec_mask = sec_mask;
+ avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_DisconnectReq
+**
+** Description This function disconnect an AVDTP signaling connection
+** to the peer device. When disconnected an
+** AVDT_DISCONNECT_IND_EVT is sent to the application via its
+** control callback function.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+UINT16 AVDT_DisconnectReq(BD_ADDR bd_addr, tAVDT_CTRL_CBACK *p_cback)
+{
+ tAVDT_CCB *p_ccb = NULL;
+ UINT16 result = AVDT_SUCCESS;
+ tAVDT_CCB_EVT evt;
+
+ /* find channel control block for this bd addr; if none, error */
+ if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL)
+ {
+ result = AVDT_BAD_PARAMS;
+ }
+
+ if (result == AVDT_SUCCESS)
+ {
+ /* send event to ccb */
+ evt.disconnect.p_cback = p_cback;
+ avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt);
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function AVDT_GetL2CapChannel
+**
+** Description Get the L2CAP CID used by the handle.
+**
+** Returns CID if successful, otherwise 0.
+**
+*******************************************************************************/
+UINT16 AVDT_GetL2CapChannel(UINT8 handle)
+{
+ tAVDT_SCB *p_scb;
+ tAVDT_CCB *p_ccb;
+ UINT8 tcid;
+ UINT16 lcid = 0;
+
+ /* map handle to scb */
+ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL)
+ && ((p_ccb = p_scb->p_ccb) != NULL))
+ {
+ /* get tcid from type, scb */
+ tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
+
+ lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+ }
+
+ return (lcid);
+}
+
+/*******************************************************************************
+**
+** Function AVDT_GetSignalChannel
+**
+** Description Get the L2CAP CID used by the signal channel of the given handle.
+**
+** Returns CID if successful, otherwise 0.
+**
+*******************************************************************************/
+UINT16 AVDT_GetSignalChannel(UINT8 handle, BD_ADDR bd_addr)
+{
+ tAVDT_SCB *p_scb;
+ tAVDT_CCB *p_ccb;
+ UINT8 tcid = 0; /* tcid is always 0 for signal channel */
+ UINT16 lcid = 0;
+
+ /* map handle to scb */
+ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL)
+ && ((p_ccb = p_scb->p_ccb) != NULL))
+ {
+ lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+ }
+ else if ((p_ccb = avdt_ccb_by_bd(bd_addr)) != NULL)
+ {
+ lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+ }
+
+ return (lcid);
+}
+
+#if AVDT_MULTIPLEXING == TRUE
+/*******************************************************************************
+**
+** Function AVDT_WriteDataReq
+**
+** Description Send a media packet to the peer device. The stream must
+** be started before this function is called. Also, this
+** function can only be called if the stream is a SRC.
+**
+** When AVDTP has sent the media packet and is ready for the
+** next packet, an AVDT_WRITE_CFM_EVT is sent to the
+** application via the control callback. The application must
+** wait for the AVDT_WRITE_CFM_EVT before it makes the next
+** call to AVDT_WriteDataReq(). If the applications calls
+** AVDT_WriteDataReq() before it receives the event the packet
+** will not be sent. The application may make its first call
+** to AVDT_WriteDataReq() after it receives an
+** AVDT_START_CFM_EVT or AVDT_START_IND_EVT.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+extern UINT16 AVDT_WriteDataReq(UINT8 handle, UINT8 *p_data, UINT32 data_len,
+ UINT32 time_stamp, UINT8 m_pt, UINT8 marker)
+{
+
+ tAVDT_SCB *p_scb;
+ tAVDT_SCB_EVT evt;
+ UINT16 result = AVDT_SUCCESS;
+
+ do
+ {
+ /* check length of media frame */
+ if(data_len > AVDT_MAX_MEDIA_SIZE)
+ {
+ result = AVDT_BAD_PARAMS;
+ break;
+ }
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ break;
+ }
+ AVDT_TRACE_WARNING("mux_tsid_media:%d\n", p_scb->curr_cfg.mux_tsid_media);
+
+ if (p_scb->p_pkt != NULL
+ || p_scb->p_ccb == NULL
+ || !GKI_queue_is_empty(&p_scb->frag_q)
+ || p_scb->frag_off != 0
+ || p_scb->curr_cfg.mux_tsid_media == 0)
+ {
+ result = AVDT_ERR_BAD_STATE;
++ AVDT_TRACE_WARNING("p_scb->p_pkt=%p, p_scb->p_ccb=%p, IsQueueEmpty=%x, p_scb->frag_off=%x\n",
+ p_scb->p_pkt, p_scb->p_ccb, GKI_queue_is_empty(&p_scb->frag_q), p_scb->frag_off);
+ break;
+ }
+ evt.apiwrite.p_buf = 0; /* it will indicate using of fragments queue frag_q */
+ /* create queue of media fragments */
+ GKI_init_q (&evt.apiwrite.frag_q);
+
+ /* compose fragments from media payload and put fragments into gueue */
+ avdt_scb_queue_frags(p_scb, &p_data, &data_len, &evt.apiwrite.frag_q);
+
+ if(GKI_queue_is_empty(&evt.apiwrite.frag_q))
+ {
+ AVDT_TRACE_WARNING("AVDT_WriteDataReq out of GKI buffers");
+ result = AVDT_ERR_RESOURCE;
+ break;
+ }
+ evt.apiwrite.data_len = data_len;
+ evt.apiwrite.p_data = p_data;
+
+ /* process the fragments queue */
+ evt.apiwrite.time_stamp = time_stamp;
+ evt.apiwrite.m_pt = m_pt | (marker<<7);
+ avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt);
+ } while (0);
+
+#if (BT_USE_TRACES == TRUE)
+ if(result != AVDT_SUCCESS)
+ {
+ AVDT_TRACE_WARNING("*** AVDT_WriteDataReq failed result=%d\n",result);
+ }
+#endif
+ return result;
+}
+#endif
+
+#if AVDT_MULTIPLEXING == TRUE
+/*******************************************************************************
+**
+** Function AVDT_SetMediaBuf
+**
+** Description Assigns buffer for media packets or forbids using of assigned
+** buffer if argument p_buf is NULL. This function can only
+** be called if the stream is a SNK.
+**
+** AVDTP uses this buffer to reassemble fragmented media packets.
+** When AVDTP receives a complete media packet, it calls the
+** p_media_cback assigned by AVDT_CreateStream().
+** This function can be called during callback to assign a
+** different buffer for next media packet or can leave the current
+** buffer for next packet.
+**
+** Returns AVDT_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+extern UINT16 AVDT_SetMediaBuf(UINT8 handle, UINT8 *p_buf, UINT32 buf_len)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_SUCCESS;
+
+ /* map handle to scb */
+ if ((p_scb = avdt_scb_by_hdl(handle)) == NULL)
+ {
+ result = AVDT_BAD_HANDLE;
+ }
+ else
+ {
+ if(p_buf && p_scb->cs.p_media_cback == NULL)
+ result = AVDT_NO_RESOURCES;
+ else
+ {
+ p_scb->p_media_buf = p_buf;
+ p_scb->media_buf_len = buf_len;
+ }
+ }
+
+ return result;
+}
+#endif
+
+#if AVDT_REPORTING == TRUE
+/*******************************************************************************
+**
+** Function AVDT_SendReport
+**
+** Description
+**
+**
+**
+** Returns
+**
+*******************************************************************************/
+UINT16 AVDT_SendReport(UINT8 handle, AVDT_REPORT_TYPE type,
+ tAVDT_REPORT_DATA *p_data)
+{
+ tAVDT_SCB *p_scb;
+ UINT16 result = AVDT_BAD_PARAMS;
+ BT_HDR *p_pkt;
+ tAVDT_TC_TBL *p_tbl;
+ UINT8 *p, *plen, *pm1, *p_end;
+#if AVDT_MULTIPLEXING == TRUE
+ UINT8 *p_al=NULL, u;
+#endif
+ UINT32 ssrc;
+ UINT16 len;
+
+ /* map handle to scb && verify parameters */
+ if (((p_scb = avdt_scb_by_hdl(handle)) != NULL)
+ && (p_scb->p_ccb != NULL)
+ && (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) ||
+ ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) ||
+ (type == AVDT_RTCP_PT_SDES)) )
+ {
+ result = AVDT_NO_RESOURCES;
+
+ /* build SR - assume fit in one packet */
+ p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb);
+ if((p_tbl->state == AVDT_AD_ST_OPEN) &&
+ (p_pkt = (BT_HDR *)GKI_getbuf(p_tbl->peer_mtu)) != NULL)
+ {
+ p_pkt->offset = L2CAP_MIN_OFFSET;
+ p = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+#if AVDT_MULTIPLEXING == TRUE
+ if(p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX)
+ {
+ /* Adaptation Layer header later */
+ p_al = p;
+ p += 2;
+ }
+#endif
+ pm1 = p;
+ *p++ = AVDT_MEDIA_OCTET1 | 1;
+ *p++ = type;
+ /* save the location for length */
+ plen = p;
+ p+= 2;
+ ssrc = avdt_scb_gen_ssrc(p_scb);
+ UINT32_TO_BE_STREAM(p, ssrc);
+
+ switch(type)
+ {
+ case AVDT_RTCP_PT_SR: /* Sender Report */
+ *pm1 = AVDT_MEDIA_OCTET1;
+ UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec);
+ UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac);
+ UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time);
+ UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count);
+ UINT32_TO_BE_STREAM(p, p_data->sr.octet_count);
+ break;
+
+ case AVDT_RTCP_PT_RR: /* Receiver Report */
+ *p++ = p_data->rr.frag_lost;
+ AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost);
+ p_data->rr.packet_lost &= 0xFFFFFF;
+ AVDT_TRACE_API("packet_lost: %d\n", p_data->rr.packet_lost);
+ UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost);
+ UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd);
+ UINT32_TO_BE_STREAM(p, p_data->rr.jitter);
+ UINT32_TO_BE_STREAM(p, p_data->rr.lsr);
+ UINT32_TO_BE_STREAM(p, p_data->rr.dlsr);
+ break;
+
+ case AVDT_RTCP_PT_SDES: /* Source Description */
+ *p++ = AVDT_RTCP_SDES_CNAME;
+ len = strlen((char *)p_data->cname);
+ if(len > AVDT_MAX_CNAME_SIZE)
+ len = AVDT_MAX_CNAME_SIZE;
+ *p++ = (UINT8)len;
+ BCM_STRNCPY_S((char *)p, len+1, (char *)p_data->cname, len+1);
+ p += len;
+ break;
+ }
+ p_end = p;
+ len = p - pm1 - 1;
+ UINT16_TO_BE_STREAM(plen, len);
+
+#if AVDT_MULTIPLEXING == TRUE
+ if(p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX)
+ {
+ /* Adaptation Layer header */
+ p = p_al;
+ len++;
+ UINT16_TO_BE_STREAM(p_al, len );
+ /* TSID, no-fragment bit and coding of length(9-bit length field) */
+ u = *p;
+ *p = (p_scb->curr_cfg.mux_tsid_report<<3) | AVDT_ALH_LCODE_9BITM0;
+ if(u)
+ *p |= AVDT_ALH_LCODE_9BITM1;
+ }
+#endif
+
+ /* set the actual payload length */
+ p_pkt->len = p_end - p;
+ /* send the packet */
+ if(L2CAP_DW_FAILED != avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt))
+ result = AVDT_SUCCESS;
+ }
+ }
+
+ return result;
+}
+#endif
+
+/******************************************************************************
+**
+** Function AVDT_SetTraceLevel
+**
+** Description Sets the trace level for AVDT. If 0xff is passed, the
+** current trace level is returned.
+**
+** Input Parameters:
+** new_level: The level to set the AVDT tracing to:
+** 0xff-returns the current setting.
+** 0-turns off tracing.
+** >= 1-Errors.
+** >= 2-Warnings.
+** >= 3-APIs.
+** >= 4-Events.
+** >= 5-Debug.
+**
+** Returns The new trace level or current trace level if
+** the input parameter is 0xff.
+**
+******************************************************************************/
+UINT8 AVDT_SetTraceLevel (UINT8 new_level)
+{
+ if (new_level != 0xFF)
+ avdt_cb.trace_level = new_level;
+
+ return (avdt_cb.trace_level);
+}
--- /dev/null
- AVDT_TRACE_WARNING("p_end: 0x%x - p:0x%x < 4\n", p_end, p);
+/******************************************************************************
+ *
+ * Copyright (C) 2002-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This module contains the action functions associated with the stream
+ * control block state machine.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bt_types.h"
+#include "bt_target.h"
+#include "bt_utils.h"
+#include "avdt_api.h"
+#include "avdtc_api.h"
+#include "avdt_int.h"
+#include "gki.h"
+#include "btu.h"
+
+/* This table is used to lookup the callback event that matches a particular
+** state machine API request event. Note that state machine API request
+** events are at the beginning of the event list starting at zero, thus
+** allowing for this table.
+*/
+const UINT8 avdt_scb_cback_evt[] = {
+ 0, /* API_REMOVE_EVT (no event) */
+ AVDT_WRITE_CFM_EVT, /* API_WRITE_REQ_EVT */
+ 0, /* API_GETCONFIG_REQ_EVT (no event) */
+ 0, /* API_DELAY_RPT_REQ_EVT (no event) */
+ AVDT_OPEN_CFM_EVT, /* API_SETCONFIG_REQ_EVT */
+ AVDT_OPEN_CFM_EVT, /* API_OPEN_REQ_EVT */
+ AVDT_CLOSE_CFM_EVT, /* API_CLOSE_REQ_EVT */
+ AVDT_RECONFIG_CFM_EVT, /* API_RECONFIG_REQ_EVT */
+ AVDT_SECURITY_CFM_EVT, /* API_SECURITY_REQ_EVT */
+ 0 /* API_ABORT_REQ_EVT (no event) */
+};
+
+/* This table is used to look up the callback event based on the signaling
+** role when the stream is closed.
+*/
+const UINT8 avdt_scb_role_evt[] = {
+ AVDT_CLOSE_IND_EVT, /* AVDT_CLOSE_ACP */
+ AVDT_CLOSE_CFM_EVT, /* AVDT_CLOSE_INT */
+ AVDT_CLOSE_IND_EVT, /* AVDT_OPEN_ACP */
+ AVDT_OPEN_CFM_EVT /* AVDT_OPEN_INT */
+};
+
+/*******************************************************************************
+**
+** Function avdt_scb_gen_ssrc
+**
+** Description This function generates a SSRC number unique to the stream.
+**
+** Returns SSRC value.
+**
+*******************************************************************************/
+UINT32 avdt_scb_gen_ssrc(tAVDT_SCB *p_scb)
+{
+ /* combine the value of the media type and codec type of the SCB */
+ return ((UINT32)(p_scb->cs.cfg.codec_info[1] | p_scb->cs.cfg.codec_info[2]));
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_abort_cmd
+**
+** Description This function sends the SCB an AVDT_SCB_API_ABORT_RSP_EVT
+** to initiate sending of an abort response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_abort_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ p_scb->role = AVDT_CLOSE_ACP;
+ avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_RSP_EVT, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_abort_rsp
+**
+** Description This function is an empty function; it serves as a
+** placeholder for a conformance API action function.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+ UNUSED(p_data);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_close_cmd
+**
+** Description This function sends the SCB an AVDT_SCB_API_CLOSE_RSP_EVT
+** to initiate sending of a close response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_close_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ p_scb->role = AVDT_CLOSE_ACP;
+ avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_RSP_EVT, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_close_rsp
+**
+** Description This function sets the close_code variable to the error
+** code returned in the close response.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ p_scb->close_code = p_data->msg.hdr.err_code;
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_getconfig_cmd
+**
+** Description This function retrieves the configuration parameters of
+** the SCB and sends the SCB an AVDT_SCB_API_GETCONFIG_RSP_EVT
+** to initiate sending of a get configuration response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_getconfig_cmd(tAVDT_SCB *p_scb,tAVDT_SCB_EVT *p_data)
+{
+ p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;
+
+ avdt_scb_event(p_scb, AVDT_SCB_API_GETCONFIG_RSP_EVT, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_getconfig_rsp
+**
+** Description This function is an empty function; it serves as a
+** placeholder for a conformance API action function.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+ UNUSED(p_data);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_open_cmd
+**
+** Description This function sends the SCB an AVDT_SCB_API_OPEN_RSP_EVT
+** to initiate sending of an open response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_open_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_RSP_EVT, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_open_rej
+**
+** Description This function calls the application callback function
+** indicating the open request has failed. It initializes
+** certain SCB variables and sends a AVDT_CCB_UL_CLOSE_EVT
+** to the CCB.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_open_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* do exactly same as setconfig reject */
+ avdt_scb_hdl_setconfig_rej(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_open_rsp
+**
+** Description This function calls avdt_ad_open_req() to initiate
+** connection of the transport channel for this stream.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ /* initiate opening of trans channels for this SEID */
+ p_scb->role = AVDT_OPEN_INT;
+ avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_INT);
+
+ /* start tc connect timer */
+ btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_pkt_no_frag
+**
+** Description
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_pkt_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UINT8 *p, *p_start;
+ UINT8 o_v, o_p, o_x, o_cc;
+ UINT8 m_pt;
+ UINT8 marker;
+ UINT16 seq;
+ UINT32 time_stamp;
+ UINT16 offset;
+ UINT16 ex_len;
+ UINT8 pad_len = 0;
+
+ p = p_start = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
+
+ /* parse media packet header */
+ AVDT_MSG_PRS_OCTET1(p, o_v, o_p, o_x, o_cc);
+ AVDT_MSG_PRS_M_PT(p, m_pt, marker);
+ BE_STREAM_TO_UINT16(seq, p);
+ BE_STREAM_TO_UINT32(time_stamp, p);
+ p += 4;
+
+ UNUSED(o_v);
+
+ /* skip over any csrc's in packet */
+ p += o_cc * 4;
+
+ /* check for and skip over extension header */
+ if (o_x)
+ {
+ p += 2;
+ BE_STREAM_TO_UINT16(ex_len, p);
+ p += ex_len * 4;
+ }
+
+ /* save our new offset */
+ offset = (UINT16) (p - p_start);
+
+ /* adjust length for any padding at end of packet */
+ if (o_p)
+ {
+ /* padding length in last byte of packet */
+ pad_len = *(p_start + p_data->p_pkt->len);
+ }
+
+ /* do sanity check */
+ if ((offset > p_data->p_pkt->len) || ((pad_len + offset) > p_data->p_pkt->len))
+ {
+ AVDT_TRACE_WARNING("Got bad media packet");
+ GKI_freebuf(p_data->p_pkt);
+ }
+ /* adjust offset and length and send it up */
+ else
+ {
+ p_data->p_pkt->len -= (offset + pad_len);
+ p_data->p_pkt->offset += offset;
+
+ if (p_scb->cs.p_data_cback != NULL)
+ {
+ /* report sequence number */
+ p_data->p_pkt->layer_specific = seq;
+ (*p_scb->cs.p_data_cback)(avdt_scb_to_hdl(p_scb), p_data->p_pkt,
+ time_stamp, (UINT8)(m_pt | (marker<<7)));
+ }
+ else
+ {
+#if AVDT_MULTIPLEXING == TRUE
+ if ((p_scb->cs.p_media_cback != NULL)
+ && (p_scb->p_media_buf != NULL)
+ && (p_scb->media_buf_len > p_data->p_pkt->len))
+ {
+ /* media buffer enough length is assigned by application. Lets use it*/
+ memcpy(p_scb->p_media_buf,(UINT8*)(p_data->p_pkt + 1) + p_data->p_pkt->offset,
+ p_data->p_pkt->len);
+ (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb),p_scb->p_media_buf,
+ p_scb->media_buf_len,time_stamp,seq,m_pt,marker);
+ }
+#endif
+ GKI_freebuf(p_data->p_pkt);
+ }
+ }
+}
+
+#if AVDT_REPORTING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_report
+**
+** Description
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+UINT8 * avdt_scb_hdl_report(tAVDT_SCB *p_scb, UINT8 *p, UINT16 len)
+{
+ UINT16 result = AVDT_SUCCESS;
+ UINT8 *p_start = p;
+ UINT32 ssrc;
+ UINT8 o_v, o_p, o_cc;
+ AVDT_REPORT_TYPE pt;
+ tAVDT_REPORT_DATA report, *p_rpt;
+
+ AVDT_TRACE_DEBUG( "avdt_scb_hdl_report");
+ if(p_scb->cs.p_report_cback)
+ {
+ p_rpt = &report;
+ /* parse report packet header */
+ AVDT_MSG_PRS_RPT_OCTET1(p, o_v, o_p, o_cc);
+ pt = *p++;
+ p += 2;
+ BE_STREAM_TO_UINT32(ssrc, p);
+
+ UNUSED(o_p);
+ UNUSED(o_v);
+
+ switch(pt)
+ {
+ case AVDT_RTCP_PT_SR: /* the packet type - SR (Sender Report) */
+ BE_STREAM_TO_UINT32(report.sr.ntp_sec, p);
+ BE_STREAM_TO_UINT32(report.sr.ntp_frac, p);
+ BE_STREAM_TO_UINT32(report.sr.rtp_time, p);
+ BE_STREAM_TO_UINT32(report.sr.pkt_count, p);
+ BE_STREAM_TO_UINT32(report.sr.octet_count, p);
+ break;
+
+ case AVDT_RTCP_PT_RR: /* the packet type - RR (Receiver Report) */
+ report.rr.frag_lost = *p;
+ BE_STREAM_TO_UINT32(report.rr.packet_lost, p);
+ report.rr.packet_lost &= 0xFFFFFF;
+ BE_STREAM_TO_UINT32(report.rr.seq_num_rcvd, p);
+ BE_STREAM_TO_UINT32(report.rr.jitter, p);
+ BE_STREAM_TO_UINT32(report.rr.lsr, p);
+ BE_STREAM_TO_UINT32(report.rr.dlsr, p);
+ break;
+
+ case AVDT_RTCP_PT_SDES: /* the packet type - SDES (Source Description) */
+ if(*p == AVDT_RTCP_SDES_CNAME)
+ {
+ p_rpt = (tAVDT_REPORT_DATA *)(p+2);
+ }
+ else
+ {
+ AVDT_TRACE_WARNING( " - SDES SSRC=0x%08x sc=%d %d len=%d %s\n",
+ ssrc, o_cc, *p, *(p+1), p+2);
+ result = AVDT_BUSY;
+ }
+ break;
+
+ default:
+ AVDT_TRACE_ERROR( "Bad Report pkt - packet type: %d\n", pt);
+ result = AVDT_BAD_PARAMS;
+ }
+
+ if(result == AVDT_SUCCESS)
+ (*p_scb->cs.p_report_cback)(avdt_scb_to_hdl(p_scb), pt, p_rpt);
+
+ }
+ p_start += len;
+ return p_start;
+}
+#endif
+
+#if AVDT_MULTIPLEXING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_pkt_frag
+**
+** Description
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_pkt_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* Fields of Adaptation Layer Header */
+ UINT8 al_tsid,al_frag,al_lcode;
+ UINT16 al_len;
+ /* media header fields */
+ UINT8 o_v, o_p, o_x, o_cc;
+ UINT8 m_pt;
+ UINT8 marker;
+ UINT16 seq;
+ UINT32 time_stamp;
+ UINT32 ssrc;
+ UINT16 ex_len;
+ UINT8 pad_len;
+ /* other variables */
+ UINT8 *p; /* current pointer */
+ UINT8 *p_end; /* end of all packet */
+ UINT8 *p_payload; /* pointer to media fragment payload in the buffer */
+ UINT32 payload_len; /* payload length */
+ UINT16 frag_len; /* fragment length */
+
+ p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
+ p_end = p + p_data->p_pkt->len;
+ /* parse all fragments */
+ while(p < p_end)
+ {
+ if (p_end - p < 4) /* length check. maximum length of AL header = 4 */
+ {
- AVDT_TRACE_WARNING("length check frag_off:%d p_media_buf:%d p_payload:%d\n",
++ AVDT_TRACE_WARNING("p_end: %p - p:%p < 4\n", p_end, p);
+ break;
+ }
+
+ /* parse first byte */
+ al_tsid = (*p)>>3;
+ al_frag = ( (*p) >> 2 ) & 0x01;
+ al_lcode = (*p++) & AVDT_ALH_LCODE_MASK;
+
+ /* in case of TSID=00000, a second AL header byte, before the length field,
+ ** is expected and contains the actual TSID, aligned with MSB */
+ if(al_tsid == 0)
+ al_tsid = *p++;
+
+ /* get remaining media length on base of lcode */
+ switch(al_lcode)
+ {
+ case AVDT_ALH_LCODE_NONE: /* No length field present. Take length from l2cap */
+ al_len = (UINT16)(p_end - p);
+ break;
+ case AVDT_ALH_LCODE_16BIT: /* 16 bit length field */
+ BE_STREAM_TO_UINT16(al_len, p);
+ break;
+ case AVDT_ALH_LCODE_9BITM0: /* 9 bit length field, MSB = 0, 8 LSBs in 1 octet following */
+ al_len = *p++;
+ break;
+ default: /* 9 bit length field, MSB = 1, 8 LSBs in 1 octet following */
+ al_len =(UINT16)*p++ + 0x100;
+ }
+
+ /* max fragment length */
+ frag_len = (UINT16)(p_end - p);
+ /* if it isn't last fragment */
+ if(frag_len >= al_len)
+ frag_len = al_len;
+
+ /* check TSID corresponds to config */
+ if (al_tsid != p_scb->curr_cfg.mux_tsid_media)
+ {
+#if AVDT_REPORTING == TRUE
+ if((p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT) &&
+ (al_tsid == p_scb->curr_cfg.mux_tsid_report))
+ {
+ /* parse reporting packet */
+ p = avdt_scb_hdl_report(p_scb, p, frag_len);
+ continue;
+ }
+ else
+#endif
+ {
+ AVDT_TRACE_WARNING("bad tsid: %d, mux_tsid_media:%d\n", al_tsid, p_scb->curr_cfg.mux_tsid_media);
+ break;
+ }
+ }
+ /* check are buffer for assembling and related callback set */
+ else if ((p_scb->p_media_buf == NULL) || (p_scb->cs.p_media_cback == NULL))
+ {
+ AVDT_TRACE_WARNING("NULL p_media_buf or p_media_cback");
+ break;
+ }
+
+
+ /* it is media fragment beginning */
+ if(!al_frag) /* is it first fragment of original media packet */
+ {
+ AVDT_TRACE_DEBUG("al:%d media:%d\n",
+ al_len, p_scb->media_buf_len);
+
+ p_scb->frag_off = 0;
+ p_scb->frag_org_len = al_len; /* total length of original media packet */
+ /* length check: minimum length of media header is 12 */
+ if (p_scb->frag_org_len < 12)
+ {
+ AVDT_TRACE_WARNING("bad al_len: %d(<12)\n", al_len);
+ break;
+ }
+ /* check that data fit into buffer */
+ if (al_len > p_scb->media_buf_len)
+ {
+ AVDT_TRACE_WARNING("bad al_len: %d(>%d)\n", al_len, p_scb->media_buf_len);
+ break;
+ }
+ /* make sure it is the last fragment in l2cap packet */
+ if (p + al_len < p_end)
+ {
+ AVDT_TRACE_WARNING("bad al_len: %d(>%d)\n", al_len, p_scb->media_buf_len);
+ break;
+ }
+ }
+ else
+ {
+ AVDT_TRACE_DEBUG("al:%d media:%d frag_org_len:%d frag_off:%d\n",
+ al_len, p_scb->media_buf_len, p_scb->frag_org_len, p_scb->frag_off);
+
+ /* check that remaining length from AL header equals to original len - length of already received fragments */
+ if(al_len != p_scb->frag_org_len - p_scb->frag_off)
+ {
+ AVDT_TRACE_WARNING("al_len:%d != (frag_org_len:%d - frag_off:%d) %d\n",
+ al_len, p_scb->frag_org_len, p_scb->frag_off,
+ (p_scb->frag_org_len- p_scb->frag_off));
+ break;
+ }
+
+ /* do sanity check */
+ if (p_scb->frag_off == 0)
+ {
+ AVDT_TRACE_WARNING("frag_off=0");
+ break;
+ }
+ }
+ /* do common sanity check */
+ if((p_scb->frag_org_len <= p_scb->frag_off) || (p_scb->frag_org_len >= p_scb->media_buf_len))
+ {
+ AVDT_TRACE_WARNING("common sanity frag_off:%d frag_org_len:%d media_buf_len:%d\n",
+ p_scb->frag_off, p_scb->frag_org_len, p_scb->media_buf_len);
+ break;
+ }
+
+ AVDT_TRACE_DEBUG("Received fragment org_len=%d off=%d al_len=%d frag_len=%d\n",
+ p_scb->frag_org_len, p_scb->frag_off, al_len, frag_len);
+
+ /* copy fragment into buffer */
+ memcpy(p_scb->p_media_buf + p_scb->frag_off, p, frag_len);
+ p_scb->frag_off += frag_len;
+ /* move to the next fragment */
+ p += frag_len;
+ /* if it is last fragment in original media packet then process total media pocket */
+ if(p_scb->frag_off == p_scb->frag_org_len)
+ {
+ p_payload = p_scb->p_media_buf;
+
+ /* media header */
+ AVDT_MSG_PRS_OCTET1(p_payload, o_v, o_p, o_x, o_cc);
+ AVDT_MSG_PRS_M_PT(p_payload, m_pt, marker);
+ BE_STREAM_TO_UINT16(seq, p_payload);
+ BE_STREAM_TO_UINT32(time_stamp, p_payload);
+ BE_STREAM_TO_UINT32(ssrc, p_payload);
+
+ UNUSED(o_v);
+ UNUSED(ssrc);
+
+ /* skip over any csrc's in packet */
+ p_payload += o_cc * 4;
+
+ /* check for and skip over extension header */
+ if (o_x)
+ {
+ if(p_scb->p_media_buf + p_scb->frag_off - p_payload < 4)
+ {
- AVDT_TRACE_WARNING("length check2 frag_off:%d p_media_buf:%d p_payload:%d\n",
++ AVDT_TRACE_WARNING("length check frag_off:%d p_media_buf:%p p_payload:%p\n",
+ p_scb->frag_off, p_scb->p_media_buf, p_payload);
+ break;/* length check */
+ }
+ p_payload += 2;
+ BE_STREAM_TO_UINT16(ex_len, p_payload);
+ p_payload += ex_len * 4;
+ }
+
+ if(p_payload >= p_scb->p_media_buf + p_scb->frag_off)
+ {
++ AVDT_TRACE_WARNING("length check2 frag_off:%d p_media_buf:%p p_payload:%p\n",
+ p_scb->frag_off, p_scb->p_media_buf, p_payload);
+ break;/* length check */
+ }
+
+ /* adjust length for any padding at end of packet */
+ if (o_p)
+ {
+ /* padding length in last byte of packet */
+ pad_len = *(p_scb->p_media_buf + p_scb->frag_off - 1);
+ }
+ else
+ pad_len = 0;
+ /* payload length */
+ payload_len = (UINT32)(p_scb->p_media_buf + p_scb->frag_off - pad_len - p_payload);
+
+ AVDT_TRACE_DEBUG("Received last fragment header=%d len=%d\n",
+ p_payload - p_scb->p_media_buf,payload_len);
+
+ /* send total media packet up */
+ if (p_scb->cs.p_media_cback != NULL)
+ {
+ (*p_scb->cs.p_media_cback)(avdt_scb_to_hdl(p_scb), p_payload,
+ payload_len, time_stamp, seq, m_pt, marker);
+ }
+ }
+ } /* while(p < p_end) */
+
+ if(p < p_end)
+ {
+ AVDT_TRACE_WARNING("*** Got bad media packet");
+ }
+ GKI_freebuf(p_data->p_pkt);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_pkt
+**
+** Description
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+#if AVDT_REPORTING == TRUE
+ UINT8 *p;
+#endif
+
+#if AVDT_MULTIPLEXING == TRUE
+ /* select right function in dependance of is fragmentation supported or not */
+ if( 0 != (p_scb->curr_cfg.psc_mask & AVDT_PSC_MUX))
+ {
+ avdt_scb_hdl_pkt_frag(p_scb, p_data);
+ }
+ else
+#endif
+#if AVDT_REPORTING == TRUE
+ if(p_data->p_pkt->layer_specific == AVDT_CHAN_REPORT)
+ {
+ p = (UINT8 *)(p_data->p_pkt + 1) + p_data->p_pkt->offset;
+ avdt_scb_hdl_report(p_scb, p, p_data->p_pkt->len);
+ GKI_freebuf(p_data->p_pkt);
+ }
+ else
+#endif
+ avdt_scb_hdl_pkt_no_frag(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_drop_pkt
+**
+** Description Drop an incoming media packet. This function is called if
+** a media packet is received in any state besides streaming.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_drop_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+
+ GKI_freebuf(p_data->p_pkt);
+ AVDT_TRACE_ERROR(" avdt_scb_drop_pkt Dropped incoming media packet");
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_reconfig_cmd
+**
+** Description This function calls the application callback function
+** with a reconfiguration indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_reconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* if command not supported */
+ if (p_scb->cs.nsc_mask & AVDT_NSC_RECONFIG)
+ {
+ /* send reject */
+ p_data->msg.hdr.err_code = AVDT_ERR_NSC;
+ p_data->msg.hdr.err_param = 0;
+ avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, p_data);
+ }
+ else
+ {
+ /* store requested configuration */
+ memcpy(&p_scb->req_cfg, p_data->msg.reconfig_cmd.p_cfg, sizeof(tAVDT_CFG));
+
+ /* call application callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ AVDT_RECONFIG_IND_EVT,
+ (tAVDT_CTRL *) &p_data->msg.reconfig_cmd);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_reconfig_rsp
+**
+** Description This function calls the application callback function
+** with a reconfiguration confirm.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ if (p_data->msg.hdr.err_code == 0)
+ {
+ /* store new configuration */
+ if (p_scb->req_cfg.num_codec > 0)
+ {
+ p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec;
+ memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE);
+ }
+ if (p_scb->req_cfg.num_protect > 0)
+ {
+ p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect;
+ memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE);
+ }
+ }
+
+ p_data->msg.svccap.p_cfg = &p_scb->curr_cfg;
+
+ /* call application callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ AVDT_RECONFIG_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.svccap);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_security_cmd
+**
+** Description This function calls the application callback with a
+** security indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_security_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* if command not supported */
+ if (p_scb->cs.nsc_mask & AVDT_NSC_SECURITY)
+ {
+ /* send reject */
+ p_data->msg.hdr.err_code = AVDT_ERR_NSC;
+ avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, p_data);
+ }
+ else
+ {
+ /* call application callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ AVDT_SECURITY_IND_EVT,
+ (tAVDT_CTRL *) &p_data->msg.security_cmd);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_security_rsp
+**
+** Description This function calls the application callback with a
+** security confirm.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* call application callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ AVDT_SECURITY_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.security_cmd);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_setconfig_cmd
+**
+** Description This function marks the SCB as in use and copies the
+** configuration and peer SEID to the SCB. It then calls
+** the application callback with a configuration indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_setconfig_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CFG *p_cfg;
+
+ if (!p_scb->in_use)
+ {
+ p_cfg = p_data->msg.config_cmd.p_cfg;
+ if(p_scb->cs.cfg.codec_info[AVDT_CODEC_TYPE_INDEX] == p_cfg->codec_info[AVDT_CODEC_TYPE_INDEX])
+ {
+ /* set sep as in use */
+ p_scb->in_use = TRUE;
+
+ /* copy info to scb */
+ p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
+ p_scb->peer_seid = p_data->msg.config_cmd.int_seid;
+ memcpy(&p_scb->req_cfg, p_cfg, sizeof(tAVDT_CFG));
+ /* call app callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), /* handle of scb- which is same as sep handle of bta_av_cb.p_scb*/
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_CONFIG_IND_EVT,
+ (tAVDT_CTRL *) &p_data->msg.config_cmd);
+ }
+ else
+ {
+ p_data->msg.hdr.err_code = AVDT_ERR_UNSUP_CFG;
+ p_data->msg.hdr.err_param = 0;
+ avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
+ p_data->msg.hdr.sig_id, &p_data->msg);
+ }
+ }
+ else
+ {
+ avdt_scb_rej_in_use(p_scb, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_setconfig_rej
+**
+** Description This function marks the SCB as not in use and calls the
+** application callback with an open confirm indicating failure.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* clear scb variables */
+ avdt_scb_clr_vars(p_scb, p_data);
+
+ /* tell ccb we're done with signaling channel */
+ avdt_ccb_event(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_CCB_UL_CLOSE_EVT, NULL);
+
+ /* call application callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ AVDT_OPEN_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_setconfig_rsp
+**
+** Description This function sends the SCB an AVDT_SCB_API_OPEN_REQ_EVT
+** to initiate sending of an open command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_EVT_HDR single;
+ UNUSED(p_data);
+
+ if (p_scb->p_ccb != NULL)
+ {
+ /* save configuration */
+ memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));
+
+ /* initiate open */
+ single.seid = p_scb->peer_seid;
+ avdt_scb_event(p_scb, AVDT_SCB_API_OPEN_REQ_EVT, (tAVDT_SCB_EVT *) &single);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_start_cmd
+**
+** Description This function calls the application callback with a
+** start indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_start_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_START_IND_EVT,
+ NULL);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_start_rsp
+**
+** Description This function calls the application callback with a
+** start confirm.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_start_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_START_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_suspend_cmd
+**
+** Description This function calls the application callback with a suspend
+** indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_suspend_cmd(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_SUSPEND_IND_EVT,
+ NULL);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_suspend_rsp
+**
+** Description This function calls the application callback with a suspend
+** confirm.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_suspend_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_SUSPEND_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_tc_close
+**
+** Description This function is called when the transport channel is
+** closed. It marks the SCB as not in use and
+** initializes certain SCB parameters. It then sends
+** an AVDT_CCB_UL_CLOSE_EVT to the CCB if the SCB
+** initiated the close. It then checks to see if the SCB
+** is to be removed. If it is it deallocates the SCB. Finally,
+** it calls the application callback with a close indication.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UINT8 hdl = avdt_scb_to_hdl(p_scb);
+ tAVDT_CTRL_CBACK *p_ctrl_cback = p_scb->cs.p_ctrl_cback;
+ tAVDT_CTRL avdt_ctrl;
+ UINT8 event;
+ tAVDT_CCB *p_ccb = p_scb->p_ccb;
+ BD_ADDR remote_addr;
+
+
+ memcpy (remote_addr, p_ccb->peer_addr, BD_ADDR_LEN);
+
+ /* set up hdr */
+ avdt_ctrl.hdr.err_code = p_scb->close_code;
+
+ /* clear sep variables */
+ avdt_scb_clr_vars(p_scb, p_data);
+ p_scb->media_seq = 0;
+ p_scb->cong = FALSE;
+
+ /* free pkt we're holding, if any */
+ if (p_scb->p_pkt != NULL)
+ {
+ GKI_freebuf(p_scb->p_pkt);
+ p_scb->p_pkt = NULL;
+ }
+
+ /* stop transport channel timer */
+ btu_stop_timer(&p_scb->timer_entry);
+
+ if ((p_scb->role == AVDT_CLOSE_INT) || (p_scb->role == AVDT_OPEN_INT))
+ {
+ /* tell ccb we're done with signaling channel */
+ avdt_ccb_event(p_ccb, AVDT_CCB_UL_CLOSE_EVT, NULL);
+ }
+ event = (p_scb->role == AVDT_CLOSE_INT) ? AVDT_CLOSE_CFM_EVT : AVDT_CLOSE_IND_EVT;
+ p_scb->role = AVDT_CLOSE_ACP;
+
+ if (p_scb->remove)
+ {
+ avdt_scb_dealloc(p_scb, NULL);
+ }
+
+ /* call app callback */
+ (*p_ctrl_cback)(hdl, remote_addr, event, &avdt_ctrl);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_delay_rpt_req
+**
+** Description This function calls the application callback with a delay
+** report.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_delay_rpt_req (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_DELAY_RPT, (tAVDT_MSG *) &p_data->apidelay);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_delay_rpt_cmd
+**
+** Description This function calls the application callback with a delay
+** report.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_delay_rpt_cmd (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_DELAY_REPORT_EVT,
+ (tAVDT_CTRL *) &p_data->msg.hdr);
+
+ if (p_scb->p_ccb)
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_DELAY_RPT, &p_data->msg);
+ else
+ avdt_scb_rej_not_in_use(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_delay_rpt_rsp
+**
+** Description This function calls the application callback with a delay
+** report.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_delay_rpt_rsp (tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_DELAY_REPORT_CFM_EVT,
+ (tAVDT_CTRL *) &p_data->msg.hdr);
+}
+
+#if AVDT_REPORTING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_tc_close_sto
+**
+** Description This function is called when a channel is closed in OPEN
+** state. Check the channel type and process accordingly.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_tc_close_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+ /* AVDT_CHAN_SIG does not visit this action */
+ if(p_data && p_data->close.type != AVDT_CHAN_MEDIA)
+ {
+ /* it's reporting or recovery channel,
+ * the channel close in open state means the peer does not support it */
+ if(p_data->close.old_tc_state == AVDT_AD_ST_OPEN)
+ {
+ avdt_ctrl.hdr.err_code = 0;
+ avdt_ctrl.hdr.err_param = 0;
+ /* call app callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_REPORT_DISCONN_EVT, &avdt_ctrl);
+ }
+ }
+ else
+ {
+ /* must be in OPEN state. need to go back to idle */
+ avdt_scb_event(p_scb, AVDT_SCB_MSG_ABORT_RSP_EVT, NULL);
+ avdt_scb_hdl_tc_close(p_scb, p_data);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_tc_open
+**
+** Description This function is called when the transport channel is
+** opened while in the opening state. It calls the
+** application callback with an open indication or open
+** confirm depending on who initiated the open procedure.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_tc_open(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UINT8 event;
+#if AVDT_REPORTING == TRUE
+ UINT8 role;
+#endif
+
+ /* stop transport channel connect timer */
+ btu_stop_timer(&p_scb->timer_entry);
+
+ event = (p_scb->role == AVDT_OPEN_INT) ? AVDT_OPEN_CFM_EVT : AVDT_OPEN_IND_EVT;
+ p_data->open.hdr.err_code = 0;
+
+ AVDT_TRACE_DEBUG("psc_mask: cfg: 0x%x, req:0x%x, cur: 0x%x\n",
+ p_scb->cs.cfg.psc_mask, p_scb->req_cfg.psc_mask, p_scb->curr_cfg.psc_mask);
+#if AVDT_REPORTING == TRUE
+ if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT)
+ {
+ /* open the reporting channel, if both devices support it */
+ role = (p_scb->role == AVDT_OPEN_INT) ? AVDT_INT : AVDT_ACP;
+ avdt_ad_open_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, role);
+ }
+#endif
+
+ /* call app callback */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ event,
+ (tAVDT_CTRL *) &p_data->open);
+}
+
+#if AVDT_REPORTING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_tc_open_sto
+**
+** Description This function is called when the transport channel is
+** opened while in the opening state. It calls the
+** application callback with an open indication or open
+** confirm depending on who initiated the open procedure.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_tc_open_sto(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+ /* open reporting channel here, when it is implemented */
+
+ /* call app callback */
+ if(p_data->open.hdr.err_code == AVDT_CHAN_REPORT)
+ {
+ avdt_ctrl.hdr.err_code = 0;
+ avdt_ctrl.hdr.err_param = 1;
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ p_scb->p_ccb ? p_scb->p_ccb->peer_addr : NULL,
+ AVDT_REPORT_CONN_EVT, &avdt_ctrl);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_write_req_no_frag
+**
+** Description This function frees the media packet currently stored in
+** the SCB, if any. Then it builds a new media packet from
+** with the passed in buffer and stores it in the SCB.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_write_req_no_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UINT8 *p;
+ UINT32 ssrc;
+
+ /* free packet we're holding, if any; to be replaced with new */
+ if (p_scb->p_pkt != NULL)
+ {
+ GKI_freebuf(p_scb->p_pkt);
+
+ /* this shouldn't be happening */
+ AVDT_TRACE_WARNING("Dropped media packet; congested");
+ }
+
+ /* build a media packet */
+ /* Add RTP header if required */
+ if ( !(p_data->apiwrite.opt & AVDT_DATA_OPT_NO_RTP) )
+ {
+ ssrc = avdt_scb_gen_ssrc(p_scb);
+
+ p_data->apiwrite.p_buf->len += AVDT_MEDIA_HDR_SIZE;
+ p_data->apiwrite.p_buf->offset -= AVDT_MEDIA_HDR_SIZE;
+ p_scb->media_seq++;
+ p = (UINT8 *)(p_data->apiwrite.p_buf + 1) + p_data->apiwrite.p_buf->offset;
+
+ UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1);
+ UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt);
+ UINT16_TO_BE_STREAM(p, p_scb->media_seq);
+ UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp);
+ UINT32_TO_BE_STREAM(p, ssrc);
+ }
+
+ /* store it */
+ p_scb->p_pkt = p_data->apiwrite.p_buf;
+}
+
+#if AVDT_MULTIPLEXING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_write_req_frag
+**
+** Description This function builds a new fragments of media packet from
+** the passed in buffers and stores them in the SCB.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_write_req_frag(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UINT8 *p;
+ UINT32 ssrc;
+ BT_HDR *p_frag;
+
+ /* free fragments we're holding, if any; it shouldn't happen */
+ if (!GKI_queue_is_empty(&p_scb->frag_q))
+ {
+ while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
+ GKI_freebuf(p_frag);
+
+ /* this shouldn't be happening */
+ AVDT_TRACE_WARNING("*** Dropped media packet; congested");
+ }
+
+ /* build a media fragments */
+ p_scb->frag_off = p_data->apiwrite.data_len;
+ p_scb->p_next_frag = p_data->apiwrite.p_data;
+
+ ssrc = avdt_scb_gen_ssrc(p_scb);
+
+ /* get first packet */
+ p_frag = (BT_HDR*)GKI_getfirst (&p_data->apiwrite.frag_q);
+ /* posit on Adaptation Layer header */
+ p_frag->len += AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE;
+ p_frag->offset -= AVDT_AL_HDR_SIZE + AVDT_MEDIA_HDR_SIZE;
+ p = (UINT8 *)(p_frag + 1) + p_frag->offset;
+
+ /* Adaptation Layer header */
+ /* TSID, no-fragment bit and coding of length(in 2 length octets following) */
+ *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | AVDT_ALH_LCODE_16BIT;
+
+ /* length of all remaining transport packet */
+ UINT16_TO_BE_STREAM(p, p_frag->layer_specific+AVDT_MEDIA_HDR_SIZE );
+ /* media header */
+ UINT8_TO_BE_STREAM(p, AVDT_MEDIA_OCTET1);
+ UINT8_TO_BE_STREAM(p, p_data->apiwrite.m_pt);
+ UINT16_TO_BE_STREAM(p, p_scb->media_seq);
+ UINT32_TO_BE_STREAM(p, p_data->apiwrite.time_stamp);
+ UINT32_TO_BE_STREAM(p, ssrc);
+ p_scb->media_seq++;
+
+ while((p_frag = (BT_HDR*)GKI_getnext (p_frag)) != NULL)
+ {
+ /* posit on Adaptation Layer header */
+ p_frag->len += AVDT_AL_HDR_SIZE;
+ p_frag->offset -= AVDT_AL_HDR_SIZE;
+ p = (UINT8 *)(p_frag + 1) + p_frag->offset;
+ /* Adaptation Layer header */
+ /* TSID, fragment bit and coding of length(in 2 length octets following) */
+ *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT);
+
+ /* length of all remaining transport packet */
+ UINT16_TO_BE_STREAM(p, p_frag->layer_specific );
+ }
+
+ /* store it */
+ p_scb->frag_q = p_data->apiwrite.frag_q;
+}
+#endif
+
+
+/*******************************************************************************
+**
+** Function avdt_scb_hdl_write_req
+**
+** Description This function calls one of the two versions of building functions
+** for case with and without fragmentation
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_hdl_write_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+#if AVDT_MULTIPLEXING == TRUE
+ if (GKI_queue_is_empty(&p_data->apiwrite.frag_q))
+#endif
+ avdt_scb_hdl_write_req_no_frag(p_scb, p_data);
+#if AVDT_MULTIPLEXING == TRUE
+ else
+ avdt_scb_hdl_write_req_frag(p_scb, p_data);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_abort_req
+**
+** Description This function sends an abort command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_abort_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_EVT_HDR hdr;
+ UNUSED(p_data);
+
+ if (p_scb->p_ccb != NULL)
+ {
+ p_scb->role = AVDT_CLOSE_INT;
+
+ hdr.seid = p_scb->peer_seid;
+
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_ABORT, (tAVDT_MSG *) &hdr);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_abort_rsp
+**
+** Description This function sends an abort response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_abort_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+
+ avdt_msg_send_rsp(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx), AVDT_SIG_ABORT,
+ &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_close_req
+**
+** Description This function sends a close command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_close_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_EVT_HDR hdr;
+ UNUSED(p_data);
+
+ p_scb->role = AVDT_CLOSE_INT;
+
+ hdr.seid = p_scb->peer_seid;
+
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_CLOSE, (tAVDT_MSG *) &hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_stream_close
+**
+** Description This function sends a close command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_stream_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+#if AVDT_MULTIPLEXING == TRUE
+ BT_HDR *p_frag;
+
+ AVDT_TRACE_WARNING("avdt_scb_snd_stream_close c:%d, off:%d\n",
+ GKI_queue_length(&p_scb->frag_q), p_scb->frag_off);
+ /* clean fragments queue */
+ while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
+ GKI_freebuf(p_frag);
+ p_scb->frag_off = 0;
+#endif
+ if (p_scb->p_pkt)
+ {
+ GKI_freebuf(p_scb->p_pkt);
+ p_scb->p_pkt = NULL;
+ }
+
+#if 0
+ if(p_scb->cong)
+ p_scb->cong = FALSE;
+
+ /* p_scb->curr_cfg.mux_tsid_media == 0 */
+#endif
+ avdt_scb_snd_close_req(p_scb, p_data);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_close_rsp
+**
+** Description This function sends a close response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_close_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_CLOSE, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_getconfig_req
+**
+** Description This function sends a get configuration command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_getconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_EVT_HDR hdr;
+ UNUSED(p_data);
+
+ hdr.seid = p_scb->peer_seid;
+
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_GETCONFIG, (tAVDT_MSG *) &hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_getconfig_rsp
+**
+** Description This function sends a get configuration response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_getconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_GETCONFIG, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_open_req
+**
+** Description This function sends an open command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_open_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_EVT_HDR hdr;
+ UNUSED(p_data);
+
+ hdr.seid = p_scb->peer_seid;
+
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_OPEN, (tAVDT_MSG *) &hdr);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_open_rsp
+**
+** Description This function sends an open response message. It also
+** calls avdt_ad_open_req() to accept a transport channel
+** connection.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_open_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ /* notify adaption that we're waiting for transport channel open */
+ p_scb->role = AVDT_OPEN_ACP;
+ avdt_ad_open_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, AVDT_ACP);
+
+ /* send response */
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_OPEN, &p_data->msg);
+
+ /* start tc connect timer */
+ btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_CONN_TOUT);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_reconfig_req
+**
+** Description This function stores the configuration parameters in the
+** SCB and sends a reconfiguration command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_reconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));
+ p_data->msg.hdr.seid = p_scb->peer_seid;
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_RECONFIG, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_reconfig_rsp
+**
+** Description This function stores the configuration parameters in the
+** SCB and sends a reconfiguration response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_reconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ if (p_data->msg.hdr.err_code == 0)
+ {
+ /* store new configuration */
+ if (p_scb->req_cfg.num_codec > 0)
+ {
+ p_scb->curr_cfg.num_codec = p_scb->req_cfg.num_codec;
+ memcpy(p_scb->curr_cfg.codec_info, p_scb->req_cfg.codec_info, AVDT_CODEC_SIZE);
+ }
+ if (p_scb->req_cfg.num_protect > 0)
+ {
+ p_scb->curr_cfg.num_protect = p_scb->req_cfg.num_protect;
+ memcpy(p_scb->curr_cfg.protect_info, p_scb->req_cfg.protect_info, AVDT_PROTECT_SIZE);
+ }
+
+ /* send response */
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg);
+ }
+ else
+ {
+ /* send reject */
+ avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_RECONFIG, &p_data->msg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_security_req
+**
+** Description This function sends a security command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_security_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ p_data->msg.hdr.seid = p_scb->peer_seid;
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SECURITY, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_security_rsp
+**
+** Description This function sends a security response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_security_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ if (p_data->msg.hdr.err_code == 0)
+ {
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg);
+ }
+ else
+ {
+ avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SECURITY, &p_data->msg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_setconfig_rej
+**
+** Description This function marks the SCB as not in use and sends a
+** set configuration reject message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_setconfig_rej(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ if (p_scb->p_ccb != NULL)
+ {
+ avdt_msg_send_rej(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);
+
+ /* clear scb variables */
+ avdt_scb_clr_vars(p_scb, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_setconfig_req
+**
+** Description This function marks the SCB as in use and copies the
+** configuration parameters to the SCB. Then the function
+** sends a set configuration command message and initiates
+** opening of the signaling channel.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_setconfig_req(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CFG *p_req, *p_cfg;
+
+ /* copy API parameters to scb, set scb as in use */
+ p_scb->in_use = TRUE;
+ p_scb->p_ccb = avdt_ccb_by_idx(p_data->msg.config_cmd.hdr.ccb_idx);
+ p_scb->peer_seid = p_data->msg.config_cmd.hdr.seid;
+ p_req = p_data->msg.config_cmd.p_cfg;
+ p_cfg = &p_scb->cs.cfg;
+#if AVDT_MULTIPLEXING == TRUE
+ p_req->mux_tsid_media = p_cfg->mux_tsid_media;
+ p_req->mux_tcid_media = p_cfg->mux_tcid_media;
+ if(p_req->psc_mask & AVDT_PSC_REPORT)
+ {
+ p_req->mux_tsid_report = p_cfg->mux_tsid_report;
+ p_req->mux_tcid_report = p_cfg->mux_tcid_report;
+ }
+#endif
+ memcpy(&p_scb->req_cfg, p_data->msg.config_cmd.p_cfg, sizeof(tAVDT_CFG));
+
+ avdt_msg_send_cmd(p_scb->p_ccb, p_scb, AVDT_SIG_SETCONFIG, &p_data->msg);
+
+ /* tell ccb to open channel */
+ avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_UL_OPEN_EVT, NULL);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_setconfig_rsp
+**
+** Description This function copies the requested configuration into the
+** current configuration and sends a set configuration
+** response message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_setconfig_rsp(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ if (p_scb->p_ccb != NULL)
+ {
+ memcpy(&p_scb->curr_cfg, &p_scb->req_cfg, sizeof(tAVDT_CFG));
+
+ avdt_msg_send_rsp(p_scb->p_ccb, AVDT_SIG_SETCONFIG, &p_data->msg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_snd_tc_close
+**
+** Description This function calls avdt_ad_close_req() to close the
+** transport channel for this SCB.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_snd_tc_close(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+#if AVDT_REPORTING == TRUE
+ if(p_scb->curr_cfg.psc_mask & AVDT_PSC_REPORT)
+ avdt_ad_close_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb);
+#endif
+ avdt_ad_close_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_cb_err
+**
+** Description This function calls the application callback function
+** indicating an error.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_cb_err(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+ UNUSED(p_data);
+
+ /* set error code and parameter */
+ avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
+ avdt_ctrl.hdr.err_param = 0;
+
+ /* call callback, using lookup table to get callback event */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb),
+ NULL,
+ avdt_scb_cback_evt[p_scb->curr_evt],
+ &avdt_ctrl);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_cong_state
+**
+** Description This function sets the congestion state of the SCB media
+** transport channel.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_cong_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ p_scb->cong = p_data->llcong;
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_rej_state
+**
+** Description This function sends a reject message to the peer indicating
+** incorrect state for the received command message.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_rej_state(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+
+ p_data->msg.hdr.err_code = AVDT_ERR_BAD_STATE;
+ p_data->msg.hdr.err_param = 0;
+ avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
+ p_data->msg.hdr.sig_id, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_rej_in_use
+**
+** Description This function sends a reject message to the peer indicating
+** the stream is in use.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_rej_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+
+ p_data->msg.hdr.err_code = AVDT_ERR_IN_USE;
+ p_data->msg.hdr.err_param = 0;
+ avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
+ p_data->msg.hdr.sig_id, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_rej_not_in_use
+**
+** Description This function sends a reject message to the peer indicating
+** the stream is in use.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_rej_not_in_use(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_scb);
+
+ p_data->msg.hdr.err_code = AVDT_ERR_NOT_IN_USE;
+ p_data->msg.hdr.err_param = 0;
+ avdt_msg_send_rej(avdt_ccb_by_idx(p_data->msg.hdr.ccb_idx),
+ p_data->msg.hdr.sig_id, &p_data->msg);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_set_remove
+**
+** Description This function marks an SCB to be removed.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_set_remove(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ p_scb->remove = TRUE;
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_free_pkt
+**
+** Description This function frees the media packet passed in.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_free_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+#if AVDT_MULTIPLEXING == TRUE
+ BT_HDR *p_frag;
+#endif
+
+ /* set error code and parameter */
+ avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
+ avdt_ctrl.hdr.err_param = 0;
+
+ /* p_buf can be NULL in case using of fragments queue frag_q */
+ if(p_data->apiwrite.p_buf)
+ GKI_freebuf(p_data->apiwrite.p_buf);
+
+#if AVDT_MULTIPLEXING == TRUE
+ /* clean fragments queue */
+ while((p_frag = (BT_HDR*)GKI_dequeue (&p_data->apiwrite.frag_q)) != NULL)
+ GKI_freebuf(p_frag);
+#endif
+
+ AVDT_TRACE_WARNING("Dropped media packet");
+
+ /* we need to call callback to keep data flow going */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
+ &avdt_ctrl);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_clr_pkt
+**
+** Description This function frees the media packet stored in the SCB.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_clr_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+ tAVDT_CCB *p_ccb;
+ UINT8 tcid;
+ UINT16 lcid;
+#if AVDT_MULTIPLEXING == TRUE
+ BT_HDR *p_frag;
+#endif
+ UNUSED(p_data);
+
+ /* set error code and parameter */
+ avdt_ctrl.hdr.err_code = AVDT_ERR_BAD_STATE;
+ avdt_ctrl.hdr.err_param = 0;
+ /* flush the media data queued at L2CAP */
+ if((p_ccb = p_scb->p_ccb) != NULL)
+ {
+ /* get tcid from type, scb */
+ tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
+
+ lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
+ L2CA_FlushChannel (lcid, L2CAP_FLUSH_CHANS_ALL);
+ }
+
+ if (p_scb->p_pkt != NULL)
+ {
+ GKI_freebuf(p_scb->p_pkt);
+ p_scb->p_pkt = NULL;
+
+ AVDT_TRACE_DEBUG("Dropped stored media packet");
+
+ /* we need to call callback to keep data flow going */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
+ &avdt_ctrl);
+ }
+#if AVDT_MULTIPLEXING == TRUE
+ else if(!GKI_queue_is_empty (&p_scb->frag_q))
+ {
+ AVDT_TRACE_DEBUG("Dropped fragments queue");
+ /* clean fragments queue */
+ while((p_frag = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
+ GKI_freebuf(p_frag);
+
+ p_scb->frag_off = 0;
+
+ /* we need to call callback to keep data flow going */
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT,
+ &avdt_ctrl);
+ }
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function avdt_scb_chk_snd_pkt
+**
+** Description This function checks if the SCB is congested, and if not
+** congested it sends a stored media packet, if any. After it
+** sends the packet it calls the application callback function
+** with a write confirm.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_chk_snd_pkt(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ tAVDT_CTRL avdt_ctrl;
+ BT_HDR *p_pkt;
+#if AVDT_MULTIPLEXING == TRUE
+ BOOLEAN sent = FALSE;
+ UINT8 res = AVDT_AD_SUCCESS;
+ tAVDT_SCB_EVT data;
+#endif
+ UNUSED(p_data);
+
+ avdt_ctrl.hdr.err_code = 0;
+
+ if (!p_scb->cong)
+ {
+ if (p_scb->p_pkt != NULL)
+ {
+ p_pkt = p_scb->p_pkt;
+ p_scb->p_pkt = NULL;
+ avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt);
+
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl);
+ }
+#if AVDT_MULTIPLEXING == TRUE
+ else
+ {
+#if 0
+ AVDT_TRACE_DEBUG("num_q=%d\n",
+ L2CA_FlushChannel(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb)].lcid),
+ L2CAP_FLUSH_CHANS_GET);
+#endif
+ while((p_pkt = (BT_HDR*)GKI_dequeue (&p_scb->frag_q)) != NULL)
+ {
+ sent = TRUE;
+ AVDT_TRACE_DEBUG("Send fragment len=%d\n",p_pkt->len);
+ /* fragments queue contains fragment to send */
+ res = avdt_ad_write_req(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb, p_pkt);
+ if(AVDT_AD_CONGESTED == res)
+ {
+ p_scb->cong = TRUE;
+ AVDT_TRACE_DEBUG("avdt/l2c congested!!");
+ break;/* exit loop if channel became congested */
+ }
+ }
+ AVDT_TRACE_DEBUG("res=%d left=%d\n",res, p_scb->frag_off);
+
+ if(p_scb->frag_off)
+ {
+ if(AVDT_AD_SUCCESS == res || GKI_queue_is_empty (&p_scb->frag_q))
+ {
+ /* all buffers were sent to L2CAP, compose more to queue */
+ avdt_scb_queue_frags(p_scb, &p_scb->p_next_frag, &p_scb->frag_off, &p_scb->frag_q);
+ if(!GKI_queue_is_empty (&p_scb->frag_q))
+ {
+ data.llcong = p_scb->cong;
+ avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &data);
+ }
+ }
+ }
+
+ /* Send event AVDT_WRITE_CFM_EVT if it was last fragment */
+ else if (sent && GKI_queue_is_empty (&p_scb->frag_q))
+ {
+ (*p_scb->cs.p_ctrl_cback)(avdt_scb_to_hdl(p_scb), NULL, AVDT_WRITE_CFM_EVT, &avdt_ctrl);
+ }
+ }
+#endif
+ }
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_tc_timer
+**
+** Description This function is called to start a timer when the peer
+** initiates closing of the stream. The timer verifies that
+** the peer disconnects the transport channel.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_tc_timer(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ btu_start_timer(&p_scb->timer_entry, BTU_TTYPE_AVDT_SCB_TC, AVDT_SCB_TC_DISC_TOUT);
+}
+
+/*******************************************************************************
+**
+** Function avdt_scb_clr_vars
+**
+** Description This function initializes certain SCB variables.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_clr_vars(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data)
+{
+ UNUSED(p_data);
+
+ if ((p_scb->cs.tsep == AVDT_TSEP_SNK) && (!p_scb->sink_activated))
+ {
+ p_scb->in_use = TRUE;
+ }
+ else
+ {
+ p_scb->in_use = FALSE;
+ }
+ p_scb->p_ccb = NULL;
+ p_scb->peer_seid = 0;
+}
+
+#if AVDT_MULTIPLEXING == TRUE
+/*******************************************************************************
+**
+** Function avdt_scb_queue_frags
+**
+** Description This function breaks media payload into fragments
+** and put the fragments in the given queue.
+**
+** Returns Nothing.
+**
+*******************************************************************************/
+void avdt_scb_queue_frags(tAVDT_SCB *p_scb, UINT8 **pp_data, UINT32 *p_data_len, BUFFER_Q *pq)
+{
+ UINT16 lcid;
+ UINT16 num_frag;
+ UINT16 mtu_used;
+ UINT8 *p;
+ BOOLEAN al_hdr = FALSE;
+ UINT8 tcid;
+ tAVDT_TC_TBL *p_tbl;
+ UINT16 buf_size;
+ UINT16 offset = AVDT_MEDIA_OFFSET + AVDT_AL_HDR_SIZE;
+ UINT16 cont_offset = offset - AVDT_MEDIA_HDR_SIZE;
+ BT_HDR *p_frag;
+
+ tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
+ lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_scb->p_ccb)][tcid].lcid;
+
+ if( p_scb->frag_off != 0)
+ {
+ /* continuing process is usually triggered by un-congest event.
+ * the number of buffers at L2CAP is very small (if not 0).
+ * we do not need to L2CA_FlushChannel() */
+ offset = cont_offset;
+ al_hdr = TRUE;
+ num_frag = AVDT_MAX_FRAG_COUNT;
+ }
+ else
+ {
+ num_frag = L2CA_FlushChannel(lcid, L2CAP_FLUSH_CHANS_GET);
+ AVDT_TRACE_DEBUG("num_q=%d lcid=%d\n", num_frag, lcid);
+ if(num_frag >= AVDT_MAX_FRAG_COUNT)
+ {
+ num_frag = 0;
+ }
+ else
+ {
+ num_frag = AVDT_MAX_FRAG_COUNT - num_frag;
+ }
+ }
+
+ /* look up transport channel table entry to get peer mtu */
+ p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_MEDIA, p_scb->p_ccb, p_scb);
+ buf_size = p_tbl->peer_mtu + BT_HDR_SIZE;
+ AVDT_TRACE_DEBUG("peer_mtu: %d, buf_size: %d num_frag=%d\n",
+ p_tbl->peer_mtu, buf_size, num_frag);
+
+ if(buf_size > AVDT_DATA_POOL_SIZE)
+ buf_size = AVDT_DATA_POOL_SIZE;
+
+ mtu_used = buf_size - BT_HDR_SIZE;
+
+ while(*p_data_len && num_frag)
+ {
+ /* allocate buffer for fragment */
+ if(NULL == (p_frag = (BT_HDR*)GKI_getbuf(buf_size)))
+ {
+ AVDT_TRACE_WARNING("avdt_scb_queue_frags len=%d(out of GKI buffers)\n",*p_data_len);
+ break;
+ }
+ /* fill fragment by chunk of media payload */
+ p_frag->layer_specific = *p_data_len;/* length of all remaining transport packet */
+ p_frag->offset = offset;
+ /* adjust packet offset for continuing packets */
+ offset = cont_offset;
+
+ p_frag->len = mtu_used - p_frag->offset;
+ if(p_frag->len > *p_data_len)
+ p_frag->len = *p_data_len;
+ memcpy((UINT8*)(p_frag+1) + p_frag->offset, *pp_data, p_frag->len);
+ *pp_data += p_frag->len;
+ *p_data_len -= p_frag->len;
+ AVDT_TRACE_DEBUG("Prepared fragment len=%d\n", p_frag->len);
+
+ if(al_hdr)
+ {
+ /* Adaptation Layer header */
+ p_frag->len += AVDT_AL_HDR_SIZE;
+ p_frag->offset -= AVDT_AL_HDR_SIZE;
+ p = (UINT8 *)(p_frag + 1) + p_frag->offset;
+ /* TSID, fragment bit and coding of length(in 2 length octets following) */
+ *p++ = (p_scb->curr_cfg.mux_tsid_media<<3) | (AVDT_ALH_FRAG_MASK|AVDT_ALH_LCODE_16BIT);
+
+ /* length of all remaining transport packet */
+ UINT16_TO_BE_STREAM(p, p_frag->layer_specific );
+ }
+ /* put fragment into gueue */
+ GKI_enqueue(pq, p_frag);
+ num_frag--;
+ }
+}
+#endif
+
--- /dev/null
- #include "EspAudio.h"
- #include "EspAudioCom.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "esp_system.h"
- EspAudioPlayerStreamWrite(data, len, 10);
++// #include "EspAudio.h"
++// #include "EspAudioCom.h"
+
+#include "bt_app_common.h"
+#include "esp_bt_stack_manager.h"
+#include "esp_gap_bt_api.h"
+#include "bta_api.h"
+#include "esp_a2dp_api.h"
+
+typedef enum {
+ BT_APP_EVT_STACK_ON = 0xa0,
+ BT_APP_EVT_MAX
+} bt_app_evt_t;
+
+typedef union {
+ esp_a2d_cb_param_t a2d;
+} bt_app_evt_arg;
+
+static void bt_app_handle_evt(UINT16 event, void *p_param);
+
+static void bt_app_a2d_cb(uint32_t event, void *param)
+{
+ switch (event) {
+ case ESP_A2D_CONNECTION_STATE_EVT:
+ case ESP_A2D_AUDIO_STATE_EVT:
+ case ESP_A2D_AUDIO_CFG_EVT:
+ {
+ bt_app_transfer_context(bt_app_handle_evt, event, param, sizeof(bt_app_evt_arg), NULL);
+ break;
+ }
+ default:
+ BT_APP_TRACE_ERROR("===a2dp invalid cb event: %d\n", event);
+ break;
+ }
+}
+
+static void bt_app_a2d_data_cb(uint8_t *data, uint32_t len)
+{
- EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT);
- EspAudio_SetupStream("stream.pcm", InputSrcType_Stream);
- EspAudio_SetVolume(99);
++ // EspAudioPlayerStreamWrite(data, len, 10);
++ return;
+}
+
+static void bt_app_handle_evt(UINT16 event, void *p_param)
+{
+ BT_APP_TRACE_EVENT("bt_app_handle_evt 0x%x\n", event);
+ esp_a2d_cb_param_t *a2d = NULL;
+ switch (event) {
+ case BT_APP_EVT_STACK_ON: {
+ char *dev_name = "ESP_SPEAKER";
+ // BTM_SetTraceLevel(BT_TRACE_LEVEL_WARNING);
+ esp_bt_gap_set_device_name(dev_name);
+
+ esp_a2d_register_callback(bt_app_a2d_cb);
+ esp_a2d_register_data_callback(bt_app_a2d_data_cb);
+
+ esp_a2d_sink_init();
+ esp_bt_gap_set_scan_mode(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ break;
+ }
+ case ESP_A2D_CONNECTION_STATE_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_ERROR("===a2dp conn_state_cb %d ===\n", a2d->conn_stat.state);
+ break;
+ }
+ case ESP_A2D_AUDIO_STATE_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_ERROR("===a2dp audio_state_cb %d ===\n", a2d->audio_stat.state);
+ break;
+ }
+ case ESP_A2D_AUDIO_CFG_EVT: {
+ a2d = (esp_a2d_cb_param_t *)(p_param);
+ BT_APP_TRACE_ERROR("===a2dp audio_cfg_cb type %d ===\n", a2d->audio_cfg.mcc.type);
+ if (a2d->audio_cfg.mcc.type == ESP_A2D_MCT_SBC) {
+ // temporarily hardcoded the PCM configuaration
+ BT_APP_TRACE_ERROR("configure audio player\n");
++ // EspAudioPlayerStreamCfg(StreamSampleRate_44k, 2, StreamBitLen_16BIT);
++ // EspAudio_SetupStream("stream.pcm", InputSrcType_Stream);
++ // EspAudio_SetVolume(99);
+ }
+ break;
+ }
+ default:
+ BT_APP_TRACE_ERROR("===application invalid event: %d\n", event);
+ break;
+ }
+
+}
+
+void app_main_entry(void)
+{
+ bt_status_t init, enable;
+ init = esp_bt_init_stack();
+ if (init != ESP_OK) {
+ return;
+ }
+
+ enable = esp_bt_enable_stack();
+ if (enable != ESP_OK) {
+ return;
+ }
+
+ bt_app_transfer_context(bt_app_handle_evt, BT_APP_EVT_STACK_ON, NULL, 0, NULL);
+}
--- /dev/null
- #include "EspAudio.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "bt.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+#include "nvs_flash.h"
+#include "esp_system.h"
- system_init();
- #include "psramApi.h"
- psram_cache_enable();
-
- printf("Free memory: %d bytes\n", system_get_free_heap_size());
- EspAudio_Init();
++// #include "EspAudio.h"
+
+extern void bte_main_boot_entry(void *);
+extern void bt_app_task_start_up(void);
+extern void bt_app_core_start(void);
+
+void app_main()
+{
+ nvs_flash_init();
- // bte_main_boot_entry(bt_app_core_start);
++ // system_init();
++ // printf("Free memory: %d bytes\n", system_get_free_heap_size());
++ // EspAudio_Init();
+ bt_controller_init();
+ bt_app_task_start_up();
+}