You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1089 lines
38 KiB
1089 lines
38 KiB
/******************************************************************************
|
|
*
|
|
* Copyright 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 "avdt_api.h"
|
|
#include <string.h>
|
|
#include "avdt_int.h"
|
|
#include "avdtc_api.h"
|
|
#include "bt_target.h"
|
|
#include "bt_types.h"
|
|
#include "bta/include/bta_api.h"
|
|
#include "btm_api.h"
|
|
#include "btu.h"
|
|
#include "l2c_api.h"
|
|
#include "main/shim/dumpsys.h"
|
|
#include "osi/include/log.h"
|
|
#include "stack/btm/btm_sec.h"
|
|
#include "stack/include/a2dp_codec_api.h"
|
|
|
|
/* Control block for AVDTP */
|
|
AvdtpCb avdtp_cb;
|
|
|
|
void avdt_ccb_idle_ccb_timer_timeout(void* data) {
|
|
AvdtpCcb* p_ccb = (AvdtpCcb*)data;
|
|
uint8_t avdt_event = AVDT_CCB_IDLE_TOUT_EVT;
|
|
uint8_t err_code = AVDT_ERR_TIMEOUT;
|
|
|
|
tAVDT_CCB_EVT avdt_ccb_evt;
|
|
avdt_ccb_evt.err_code = err_code;
|
|
avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
|
|
}
|
|
|
|
void avdt_ccb_ret_ccb_timer_timeout(void* data) {
|
|
AvdtpCcb* p_ccb = (AvdtpCcb*)data;
|
|
uint8_t avdt_event = AVDT_CCB_RET_TOUT_EVT;
|
|
uint8_t err_code = AVDT_ERR_TIMEOUT;
|
|
|
|
tAVDT_CCB_EVT avdt_ccb_evt;
|
|
avdt_ccb_evt.err_code = err_code;
|
|
avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
|
|
}
|
|
|
|
void avdt_ccb_rsp_ccb_timer_timeout(void* data) {
|
|
AvdtpCcb* p_ccb = (AvdtpCcb*)data;
|
|
uint8_t avdt_event = AVDT_CCB_RSP_TOUT_EVT;
|
|
uint8_t err_code = AVDT_ERR_TIMEOUT;
|
|
|
|
tAVDT_CCB_EVT avdt_ccb_evt;
|
|
avdt_ccb_evt.err_code = err_code;
|
|
avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
|
|
}
|
|
|
|
void avdt_scb_transport_channel_timer_timeout(void* data) {
|
|
AvdtpScb* p_scb = (AvdtpScb*)data;
|
|
uint8_t avdt_event = AVDT_SCB_TC_TOUT_EVT;
|
|
|
|
avdt_scb_event(p_scb, avdt_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(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) {
|
|
/* register PSM with L2CAP */
|
|
L2CA_Register2(AVDT_PSM, avdt_l2c_appl, true /* enable_snoop */, nullptr,
|
|
kAvdtpMtu, 0, BTA_SEC_AUTHENTICATE);
|
|
|
|
/* initialize AVDTP data structures */
|
|
avdt_scb_init();
|
|
avdt_ccb_init();
|
|
avdt_ad_init();
|
|
|
|
/* copy registration struct */
|
|
avdtp_cb.rcb = *p_reg;
|
|
avdtp_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);
|
|
}
|
|
|
|
void AVDT_AbortReq(uint8_t handle) {
|
|
AVDT_TRACE_WARNING("%s: avdt_handle=%d", __func__, handle);
|
|
|
|
AvdtpScb* 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 avdp_handle=%d, can not abort the stream",
|
|
__func__, handle);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* 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_t AVDT_CreateStream(uint8_t peer_id, uint8_t* p_handle,
|
|
const AvdtpStreamConfig& avdtp_stream_config) {
|
|
tAVDT_RESULT result = AVDT_SUCCESS;
|
|
AvdtpScb* p_scb;
|
|
|
|
/* Verify parameters; if invalid, return failure */
|
|
if (((avdtp_stream_config.cfg.psc_mask & (~AVDT_PSC)) != 0) ||
|
|
(avdtp_stream_config.p_avdt_ctrl_cback == NULL)) {
|
|
result = AVDT_BAD_PARAMS;
|
|
LOG_ERROR("Invalid AVDT stream endpoint parameters peer_id=%d scb_index=%d",
|
|
peer_id, avdtp_stream_config.scb_index);
|
|
}
|
|
|
|
/* Allocate scb; if no scbs, return failure */
|
|
else {
|
|
p_scb = avdt_scb_alloc(peer_id, avdtp_stream_config);
|
|
if (p_scb == NULL) {
|
|
LOG_ERROR("Unable to create AVDT stream endpoint peer_id=%d scb_index=%d",
|
|
peer_id, avdtp_stream_config.scb_index);
|
|
result = AVDT_NO_RESOURCES;
|
|
} else {
|
|
*p_handle = avdt_scb_to_hdl(p_scb);
|
|
LOG_DEBUG("Created stream endpoint peer_id=%d handle=%hhu", peer_id,
|
|
*p_handle);
|
|
}
|
|
}
|
|
return static_cast<uint16_t>(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_t AVDT_RemoveStream(uint8_t handle) {
|
|
uint16_t result = AVDT_SUCCESS;
|
|
AvdtpScb* p_scb;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d", __func__, handle);
|
|
|
|
/* look up scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == NULL) {
|
|
result = AVDT_BAD_HANDLE;
|
|
} else {
|
|
/* send remove event to scb */
|
|
avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
|
|
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_t AVDT_DiscoverReq(const RawAddress& bd_addr, uint8_t channel_index,
|
|
tAVDT_SEP_INFO* p_sep_info, uint8_t max_seps,
|
|
tAVDT_CTRL_CBACK* p_cback) {
|
|
AvdtpCcb* p_ccb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_CCB_EVT evt;
|
|
|
|
AVDT_TRACE_DEBUG("%s", __func__);
|
|
|
|
/* find channel control block for this bd addr; if none, allocate one */
|
|
p_ccb = avdt_ccb_by_bd(bd_addr);
|
|
if (p_ccb == NULL) {
|
|
p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
|
|
if (p_ccb == 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);
|
|
}
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
|
|
bd_addr.ToString().c_str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Function avdt_get_cap_req
|
|
*
|
|
* Description internal function to serve AVDT_GetCapReq
|
|
*
|
|
* Returns AVDT_SUCCESS if successful, otherwise error.
|
|
*
|
|
******************************************************************************/
|
|
static uint16_t avdt_get_cap_req(const RawAddress& bd_addr,
|
|
uint8_t channel_index,
|
|
tAVDT_CCB_API_GETCAP* p_evt) {
|
|
AvdtpCcb* p_ccb = NULL;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
|
|
AVDT_TRACE_DEBUG("%s", __func__);
|
|
|
|
/* verify SEID */
|
|
if ((p_evt->single.seid < AVDT_SEID_MIN) ||
|
|
(p_evt->single.seid > AVDT_SEID_MAX)) {
|
|
AVDT_TRACE_ERROR("seid: %d", p_evt->single.seid);
|
|
result = AVDT_BAD_PARAMS;
|
|
}
|
|
/* find channel control block for this bd addr; if none, allocate one */
|
|
else {
|
|
p_ccb = avdt_ccb_by_bd(bd_addr);
|
|
if (p_ccb == NULL) {
|
|
p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
|
|
if (p_ccb == 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);
|
|
}
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
|
|
bd_addr.ToString().c_str());
|
|
}
|
|
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_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t channel_index,
|
|
uint8_t seid, AvdtpSepConfig* p_cfg,
|
|
tAVDT_CTRL_CBACK* p_cback, bool get_all_cap) {
|
|
tAVDT_CCB_API_GETCAP getcap;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
|
|
AVDT_TRACE_DEBUG("%s", __func__);
|
|
|
|
getcap.single.seid = seid;
|
|
if (get_all_cap) {
|
|
getcap.single.sig_id = AVDT_SIG_GET_ALLCAP;
|
|
} else {
|
|
getcap.single.sig_id = AVDT_SIG_GETCAP;
|
|
}
|
|
getcap.p_cfg = p_cfg;
|
|
getcap.p_cback = p_cback;
|
|
result = avdt_get_cap_req(bd_addr, channel_index, &getcap);
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d address=%s", __func__, result,
|
|
bd_addr.ToString().c_str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* 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_t AVDT_DelayReport(uint8_t handle, uint8_t seid, uint16_t delay) {
|
|
AvdtpScb* p_scb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_SCB_EVT evt;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d seid=%d delay=%d", __func__, handle,
|
|
seid, delay);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d seid=%d", __func__, result,
|
|
handle, seid);
|
|
}
|
|
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_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr,
|
|
uint8_t channel_index, uint8_t seid,
|
|
AvdtpSepConfig* p_cfg) {
|
|
AvdtpCcb* p_ccb = NULL;
|
|
AvdtpScb* p_scb = NULL;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_SCB_EVT evt;
|
|
|
|
AVDT_TRACE_API("%s: address=%s avdt_handle=%d seid=%d", __func__,
|
|
bd_addr.ToString().c_str(), handle, seid);
|
|
|
|
/* verify SEID */
|
|
if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) {
|
|
result = AVDT_BAD_PARAMS;
|
|
}
|
|
/* map handle to scb */
|
|
else {
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == NULL) {
|
|
result = AVDT_BAD_HANDLE;
|
|
}
|
|
/* find channel control block for this bd addr; if none, allocate one */
|
|
else {
|
|
p_ccb = avdt_ccb_by_bd(bd_addr);
|
|
if (p_ccb == NULL) {
|
|
p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
|
|
if (p_ccb == NULL) {
|
|
/* could not allocate channel control block */
|
|
result = AVDT_NO_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* send event to scb */
|
|
if (result == AVDT_SUCCESS) {
|
|
AVDT_TRACE_DEBUG("%s: codec: %s", __func__,
|
|
A2DP_CodecInfoString(p_cfg->codec_info).c_str());
|
|
|
|
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);
|
|
} else {
|
|
AVDT_TRACE_ERROR("%s: result=%d address=%s avdt_handle=%d", __func__,
|
|
result, bd_addr.ToString().c_str(), handle);
|
|
}
|
|
|
|
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_t AVDT_ConfigRsp(uint8_t handle, uint8_t label, uint8_t error_code,
|
|
uint8_t category) {
|
|
AvdtpScb* p_scb;
|
|
tAVDT_SCB_EVT evt;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
uint8_t event_code;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d label=%d error_code=0x%x category=%d",
|
|
__func__, handle, label, error_code, category);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
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_t AVDT_StartReq(uint8_t* p_handles, uint8_t num_handles) {
|
|
AvdtpScb* p_scb = NULL;
|
|
tAVDT_CCB_EVT evt;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
int i;
|
|
|
|
AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles);
|
|
|
|
if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
|
|
result = AVDT_BAD_PARAMS;
|
|
} else {
|
|
/* verify handles */
|
|
for (i = 0; i < num_handles; i++) {
|
|
p_scb = avdt_scb_by_hdl(p_handles[i]);
|
|
if (p_scb == 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);
|
|
}
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
|
|
AVDT_TRACE_ERROR("%s: result=%d num_handles=%d invalid", __func__, result,
|
|
num_handles);
|
|
} else {
|
|
AVDT_TRACE_ERROR(
|
|
"%s: result=%d avdt_handle=%d", __func__, result,
|
|
(i < num_handles ? p_handles[i] : p_handles[num_handles - 1]));
|
|
}
|
|
}
|
|
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_t AVDT_SuspendReq(uint8_t* p_handles, uint8_t num_handles) {
|
|
AvdtpScb* p_scb = NULL;
|
|
tAVDT_CCB_EVT evt;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
int i;
|
|
|
|
AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles);
|
|
|
|
if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
|
|
result = AVDT_BAD_PARAMS;
|
|
} else {
|
|
/* verify handles */
|
|
for (i = 0; i < num_handles; i++) {
|
|
p_scb = avdt_scb_by_hdl(p_handles[i]);
|
|
if (p_scb == 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);
|
|
}
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
|
|
AVDT_TRACE_ERROR("%s: result=%d num_handles=%d invalid", __func__, result,
|
|
num_handles);
|
|
} else {
|
|
AVDT_TRACE_ERROR(
|
|
"%s: result=%d avdt_handle=%d", __func__, result,
|
|
(i < num_handles ? p_handles[i] : p_handles[num_handles - 1]));
|
|
}
|
|
}
|
|
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_t AVDT_CloseReq(uint8_t handle) {
|
|
AvdtpScb* p_scb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
|
|
AVDT_TRACE_API("%s: avdt_handle=%d", __func__, handle);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == NULL) {
|
|
result = AVDT_BAD_HANDLE;
|
|
} else
|
|
/* send event to scb */
|
|
{
|
|
avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
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_t AVDT_ReconfigReq(uint8_t handle, AvdtpSepConfig* p_cfg) {
|
|
AvdtpScb* p_scb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_SCB_EVT evt;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d", __func__, handle);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
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_t AVDT_SecurityReq(uint8_t handle, uint8_t* p_data, uint16_t len) {
|
|
AvdtpScb* p_scb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_SCB_EVT evt;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d len=%d", __func__, handle, len);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
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_t AVDT_SecurityRsp(uint8_t handle, uint8_t label, uint8_t error_code,
|
|
uint8_t* p_data, uint16_t len) {
|
|
AvdtpScb* p_scb;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_SCB_EVT evt;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d label=%d error_code=0x%x len=%d",
|
|
__func__, handle, label, error_code, len);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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);
|
|
}
|
|
|
|
if (result != AVDT_SUCCESS) {
|
|
AVDT_TRACE_ERROR("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
}
|
|
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_t AVDT_WriteReqOpt(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp,
|
|
uint8_t m_pt, tAVDT_DATA_OPT_MASK opt) {
|
|
AvdtpScb* p_scb;
|
|
tAVDT_SCB_EVT evt;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
|
|
AVDT_TRACE_DEBUG("%s: avdt_handle=%d timestamp=%d m_pt=0x%x opt=0x%x",
|
|
__func__, handle, time_stamp, m_pt, opt);
|
|
|
|
/* map handle to scb */
|
|
p_scb = avdt_scb_by_hdl(handle);
|
|
if (p_scb == 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;
|
|
avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt);
|
|
}
|
|
|
|
AVDT_TRACE_DEBUG("%s: result=%d avdt_handle=%d", __func__, result, handle);
|
|
return result;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* 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_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t channel_index,
|
|
tAVDT_CTRL_CBACK* p_cback) {
|
|
AvdtpCcb* p_ccb = NULL;
|
|
uint16_t result = AVDT_SUCCESS;
|
|
tAVDT_CCB_EVT evt;
|
|
|
|
AVDT_TRACE_WARNING("%s: address=%s channel_index=%d", __func__,
|
|
bd_addr.ToString().c_str(), channel_index);
|
|
|
|
/* find channel control block for this bd addr; if none, allocate one */
|
|
p_ccb = avdt_ccb_by_bd(bd_addr);
|
|
if (p_ccb == NULL) {
|
|
p_ccb = avdt_ccb_alloc_by_channel_index(bd_addr, channel_index);
|
|
if (p_ccb == NULL) {
|
|
/* could not allocate channel control block */
|
|
result = AVDT_NO_RESOURCES;
|
|
}
|
|
} else if (!p_ccb->ll_opened) {
|
|
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;
|
|
avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt);
|
|
}
|
|
|
|
AVDT_TRACE_WARNING("%s: address=%s result=%d", __func__,
|
|
bd_addr.ToString().c_str(), result);
|
|
|
|
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_t AVDT_DisconnectReq(const RawAddress& bd_addr,
|
|
tAVDT_CTRL_CBACK* p_cback) {
|
|
AvdtpCcb* p_ccb = NULL;
|
|
tAVDT_RESULT result = AVDT_SUCCESS;
|
|
tAVDT_CCB_EVT evt;
|
|
|
|
/* find channel control block for this bd addr; if none, error */
|
|
p_ccb = avdt_ccb_by_bd(bd_addr);
|
|
if (p_ccb == NULL) {
|
|
LOG_ERROR("Unable to find AVDT stream endpoint peer:%s",
|
|
PRIVATE_ADDRESS(bd_addr));
|
|
result = AVDT_BAD_PARAMS;
|
|
} else {
|
|
LOG_DEBUG("Sending disconnect request to ccb peer:%s",
|
|
PRIVATE_ADDRESS(bd_addr));
|
|
evt.disconnect.p_cback = p_cback;
|
|
avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt);
|
|
}
|
|
return static_cast<uint16_t>(result);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Function AVDT_GetL2CapChannel
|
|
*
|
|
* Description Get the L2CAP CID used by the handle.
|
|
*
|
|
* Returns CID if successful, otherwise 0.
|
|
*
|
|
******************************************************************************/
|
|
uint16_t AVDT_GetL2CapChannel(uint8_t handle) {
|
|
AvdtpScb* p_scb;
|
|
AvdtpCcb* p_ccb;
|
|
uint8_t tcid;
|
|
uint16_t 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 = avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
|
|
}
|
|
|
|
return (lcid);
|
|
}
|
|
|
|
/******************************************************************************
|
|
*
|
|
* 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_t AVDT_SetTraceLevel(uint8_t new_level) {
|
|
if (new_level != 0xFF) avdtp_cb.SetTraceLevel(new_level);
|
|
|
|
return avdtp_cb.TraceLevel();
|
|
}
|
|
|
|
void stack_debug_avdtp_api_dump(int fd) {
|
|
if (appl_trace_level < BT_TRACE_LEVEL_DEBUG) return;
|
|
|
|
dprintf(fd, "\nAVDTP Stack State:\n");
|
|
dprintf(fd, " AVDTP signalling L2CAP channel MTU: %d\n",
|
|
avdtp_cb.rcb.ctrl_mtu);
|
|
|
|
for (size_t i = 0; i < AVDT_NUM_LINKS; i++) {
|
|
const AvdtpCcb& ccb = avdtp_cb.ccb[i];
|
|
if (ccb.peer_addr.IsEmpty()) {
|
|
continue;
|
|
}
|
|
dprintf(fd, "\n Channel control block: %zu peer: %s\n", i,
|
|
ccb.peer_addr.ToString().c_str());
|
|
dprintf(fd, " Allocated: %s\n", ccb.allocated ? "true" : "false");
|
|
dprintf(fd, " State: %d\n", ccb.state);
|
|
dprintf(fd, " Link-layer opened: %s\n",
|
|
ccb.ll_opened ? "true" : "false");
|
|
dprintf(fd, " Discover in progress: %s\n",
|
|
ccb.proc_busy ? "true" : "false");
|
|
dprintf(fd, " Congested: %s\n", ccb.cong ? "true" : "false");
|
|
dprintf(fd, " Reinitiate connection on idle: %s\n",
|
|
ccb.reconn ? "true" : "false");
|
|
dprintf(fd, " Command retransmission count: %d\n", ccb.ret_count);
|
|
dprintf(fd, " BTA AV SCB index: %d\n", ccb.BtaAvScbIndex());
|
|
|
|
for (size_t i = 0; i < AVDT_NUM_SEPS; i++) {
|
|
const AvdtpScb& scb = ccb.scb[i];
|
|
if (!scb.in_use) {
|
|
continue;
|
|
}
|
|
dprintf(fd, "\n Stream control block: %zu\n", i);
|
|
dprintf(fd, " SEP codec: %s\n",
|
|
A2DP_CodecName(scb.stream_config.cfg.codec_info));
|
|
dprintf(fd, " SEP protocol service capabilities: 0x%x\n",
|
|
scb.stream_config.cfg.psc_mask);
|
|
dprintf(fd, " SEP type: 0x%x\n", scb.stream_config.tsep);
|
|
dprintf(fd, " Media type: 0x%x\n", scb.stream_config.media_type);
|
|
dprintf(fd, " MTU: %d\n", scb.stream_config.mtu);
|
|
dprintf(fd, " AVDT SCB handle: %d\n", scb.ScbHandle());
|
|
dprintf(fd, " SCB index: %d\n", scb.stream_config.scb_index);
|
|
dprintf(fd, " Configured codec: %s\n",
|
|
A2DP_CodecName(scb.curr_cfg.codec_info));
|
|
dprintf(fd, " Requested codec: %s\n",
|
|
A2DP_CodecName(scb.req_cfg.codec_info));
|
|
dprintf(fd, " Transport channel connect timer: %s\n",
|
|
alarm_is_scheduled(scb.transport_channel_timer)
|
|
? "Scheduled"
|
|
: "Not scheduled");
|
|
dprintf(fd, " Channel control block peer: %s\n",
|
|
(scb.p_ccb != nullptr) ? scb.p_ccb->peer_addr.ToString().c_str()
|
|
: "null");
|
|
dprintf(fd, " Allocated: %s\n", scb.allocated ? "true" : "false");
|
|
dprintf(fd, " In use: %s\n", scb.in_use ? "true" : "false");
|
|
dprintf(fd, " Role: 0x%x\n", scb.role);
|
|
dprintf(fd, " Remove: %s\n", scb.remove ? "true" : "false");
|
|
dprintf(fd, " State: %d\n", scb.state);
|
|
dprintf(fd, " Peer SEID: %d\n", scb.peer_seid);
|
|
dprintf(fd, " Current event: %d\n", scb.curr_evt);
|
|
dprintf(fd, " Congested: %s\n", scb.cong ? "true" : "false");
|
|
dprintf(fd, " Close response code: %d\n", scb.close_code);
|
|
}
|
|
}
|
|
}
|