/******************************************************************************
*
* 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 the AVDTP adaption layer.
*
******************************************************************************/
#include <base/logging.h>
#include <string.h>
#include "avdt_api.h"
#include "avdt_int.h"
#include "avdtc_api.h"
#include "bt_target.h"
#include "bt_types.h"
#include "bt_utils.h"
#include "l2c_api.h"
#include "l2cdefs.h"
#include "osi/include/osi.h"
AvdtpScb* AvdtpAdaptationLayer::LookupAvdtpScb(
const AvdtpTransportChannel& tc) {
if (tc.ccb_idx >= AVDT_NUM_LINKS) {
AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid ccb_idx:%d",
__func__, tc.ccb_idx);
return nullptr;
}
if (tc.tcid >= AVDT_NUM_RT_TBL) {
AVDT_TRACE_ERROR("%s: AvdtpScb entry not found: invalid tcid:%d", __func__,
tc.tcid);
return nullptr;
}
const AvdtpRoutingEntry& re = rt_tbl[tc.ccb_idx][tc.tcid];
AVDT_TRACE_DEBUG("%s: ccb_idx:%d tcid:%d scb_hdl:%d", __func__, tc.ccb_idx,
tc.tcid, re.scb_hdl);
return avdt_scb_by_hdl(re.scb_hdl);
}
/*******************************************************************************
*
* Function avdt_ad_type_to_tcid
*
* Description Derives the TCID from the channel type and SCB.
*
*
* Returns TCID value.
*
******************************************************************************/
uint8_t avdt_ad_type_to_tcid(uint8_t type, AvdtpScb* p_scb) {
if (type == AVDT_CHAN_SIG) {
return 0;
}
// The SCB Handle is unique in the [1, AVDT_NUM_LINKS * AVDT_NUM_SEPS]
// range. The scb_idx computed here is the SCB index for the corresponding
// SEP, and it is in the range [0, AVDT_NUM_SEPS) for a particular link.
uint8_t scb_idx = (avdt_scb_to_hdl(p_scb) - 1) % AVDT_NUM_LINKS;
// There are AVDT_CHAN_NUM_TYPES channel types per SEP. Here we compute
// the type index (TCID) from the SEP index and the type itself.
uint8_t tcid = (scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type;
AVDT_TRACE_DEBUG("%s: type:%d, tcid: %d", __func__, type, tcid);
return tcid;
}
/*******************************************************************************
*
* Function avdt_ad_tcid_to_type
*
* Description Derives the channel type from the TCID.
*
*
* Returns Channel type value.
*
******************************************************************************/
static uint8_t avdt_ad_tcid_to_type(uint8_t tcid) {
uint8_t type;
if (tcid == 0) {
type = AVDT_CHAN_SIG;
} else {
/* tcid translates to type based on number of channels, as follows:
** only media channel : tcid=1,2,3,4,5,6... type=1,1,1,1,1,1...
** media and report : tcid=1,2,3,4,5,6... type=1,2,1,2,1,2...
** media, report, recov : tcid=1,2,3,4,5,6... type=1,2,3,1,2,3...
*/
type = ((tcid + AVDT_CHAN_NUM_TYPES - 2) % (AVDT_CHAN_NUM_TYPES - 1)) + 1;
}
AVDT_TRACE_DEBUG("tcid: %d, type: %d", tcid, type);
return type;
}
/*******************************************************************************
*
* Function avdt_ad_init
*
* Description Initialize adaption layer.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_init(void) {
int i;
AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
avdtp_cb.ad.Reset();
/* make sure the peer_mtu is a valid value */
for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
}
}
/*******************************************************************************
*
* Function avdt_ad_tc_tbl_by_st
*
* Description Find adaption layer transport channel table entry matching
* the given state.
*
*
* Returns Pointer to matching entry. For control channel it returns
* the matching entry. For media or other it returns the
* first matching entry (there could be more than one).
*
******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_st(uint8_t type, AvdtpCcb* p_ccb,
uint8_t state) {
int i;
AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
uint8_t ccb_idx;
if (p_ccb == NULL) {
/* resending security req */
for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
/* must be AVDT_CHAN_SIG - tcid always zero */
if ((p_tbl->tcid == 0) && (p_tbl->state == state)) {
break;
}
}
} else {
ccb_idx = avdt_ccb_to_idx(p_ccb);
for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
if (type == AVDT_CHAN_SIG) {
/* if control channel, tcid always zero */
if ((p_tbl->tcid == 0) && (p_tbl->ccb_idx == ccb_idx) &&
(p_tbl->state == state)) {
break;
}
} else {
/* if other channel, tcid is always > zero */
if ((p_tbl->tcid > 0) && (p_tbl->ccb_idx == ccb_idx) &&
(p_tbl->state == state)) {
break;
}
}
}
}
/* if nothing found return null */
if (i == AVDT_NUM_TC_TBL) {
p_tbl = NULL;
}
return p_tbl;
}
/*******************************************************************************
*
* Function avdt_ad_tc_tbl_by_lcid
*
* Description Find adaption layer transport channel table entry by LCID.
*
*
* Returns Pointer to entry.
*
******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_lcid(uint16_t lcid) {
uint8_t idx;
idx = avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID];
if (idx < AVDT_NUM_TC_TBL) {
return &avdtp_cb.ad.tc_tbl[idx];
} else {
return NULL;
}
}
/*******************************************************************************
*
* Function avdt_ad_tc_tbl_by_type
*
* Description This function retrieves the transport channel table entry
* for a particular channel.
*
*
* Returns Pointer to transport channel table entry.
*
******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_by_type(uint8_t type, AvdtpCcb* p_ccb,
AvdtpScb* p_scb) {
uint8_t tcid;
int i;
AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
uint8_t ccb_idx = avdt_ccb_to_idx(p_ccb);
/* get tcid from type, scb */
tcid = avdt_ad_type_to_tcid(type, p_scb);
for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
if ((p_tbl->tcid == tcid) && (p_tbl->ccb_idx == ccb_idx)) {
break;
}
}
CHECK(i != AVDT_NUM_TC_TBL);
return p_tbl;
}
/*******************************************************************************
*
* Function avdt_ad_tc_tbl_alloc
*
* Description Allocate an entry in the traffic channel table.
*
*
* Returns Pointer to entry.
*
******************************************************************************/
AvdtpTransportChannel* avdt_ad_tc_tbl_alloc(AvdtpCcb* p_ccb) {
int i;
AvdtpTransportChannel* p_tbl = avdtp_cb.ad.tc_tbl;
/* find next free entry in tc table */
for (i = 0; i < AVDT_NUM_TC_TBL; i++, p_tbl++) {
if (p_tbl->state == AVDT_AD_ST_UNUSED) {
break;
}
}
/* sanity check */
CHECK(i != AVDT_NUM_TC_TBL);
/* initialize entry */
p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
p_tbl->cfg_flags = 0;
p_tbl->ccb_idx = avdt_ccb_to_idx(p_ccb);
p_tbl->state = AVDT_AD_ST_IDLE;
return p_tbl;
}
/*******************************************************************************
*
* Function avdt_ad_tc_tbl_to_idx
*
* Description Convert a transport channel table entry to an index.
*
*
* Returns Index value.
*
******************************************************************************/
uint8_t avdt_ad_tc_tbl_to_idx(AvdtpTransportChannel* p_tbl) {
AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdtp_cb.ad.tc_tbl));
/* use array arithmetic to determine index */
return (uint8_t)(p_tbl - avdtp_cb.ad.tc_tbl);
}
/*******************************************************************************
*
* Function avdt_ad_tc_close_ind
*
* Description This function is called by the L2CAP interface when the
* L2CAP channel is closed. It looks up the CCB or SCB for
* the channel and sends it a close event. The reason
* parameter is the same value passed by the L2CAP
* callback function.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_tc_close_ind(AvdtpTransportChannel* p_tbl,
UNUSED_ATTR uint16_t reason) {
AvdtpCcb* p_ccb;
AvdtpScb* p_scb;
tAVDT_SCB_TC_CLOSE close;
close.old_tc_state = p_tbl->state;
/* clear avdt_ad_tc_tbl entry */
p_tbl->state = AVDT_AD_ST_UNUSED;
p_tbl->cfg_flags = 0;
p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
AVDT_TRACE_DEBUG("%s: tcid: %d, old: %d", __func__, p_tbl->tcid,
close.old_tc_state);
/* if signaling channel, notify ccb that channel open */
if (p_tbl->tcid == 0) {
p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
avdt_ccb_event(p_ccb, AVDT_CCB_LL_CLOSE_EVT, NULL);
return;
}
/* if media or other channel, notify scb that channel close */
/* look up scb in stream routing table by ccb, tcid */
p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
if (p_scb == nullptr) {
AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
__func__, p_tbl->ccb_idx, p_tbl->tcid);
return;
}
close.tcid = p_tbl->tcid;
close.type = avdt_ad_tcid_to_type(p_tbl->tcid);
tAVDT_SCB_EVT avdt_scb_evt;
avdt_scb_evt.close = close;
avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, &avdt_scb_evt);
}
/*******************************************************************************
*
* Function avdt_ad_tc_open_ind
*
* Description This function is called by the L2CAP interface when
* the L2CAP channel is opened. It looks up the CCB or SCB
* for the channel and sends it an open event.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_tc_open_ind(AvdtpTransportChannel* p_tbl) {
AvdtpCcb* p_ccb;
AvdtpScb* p_scb;
tAVDT_OPEN open;
tAVDT_EVT_HDR evt;
AVDT_TRACE_DEBUG("%s: p_tbl:%p state:%d ccb_idx:%d tcid:%d scb_hdl:%d",
__func__, p_tbl, p_tbl->state, p_tbl->ccb_idx, p_tbl->tcid,
avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl);
p_tbl->state = AVDT_AD_ST_OPEN;
/* if signaling channel, notify ccb that channel open */
if (p_tbl->tcid == 0) {
/* set the signal channel to use high priority within the ACL link */
L2CA_SetTxPriority(avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][AVDT_CHAN_SIG].lcid,
L2CAP_CHNL_PRIORITY_HIGH);
p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
/* use err_param to indicate the role of connection.
* AVDT_ACP, if ACP */
evt.err_param = AVDT_INT;
if (p_tbl->cfg_flags & AVDT_L2C_CFG_CONN_ACP) {
evt.err_param = AVDT_ACP;
}
tAVDT_CCB_EVT avdt_ccb_evt;
avdt_ccb_evt.msg.hdr = evt;
avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, &avdt_ccb_evt);
return;
}
/* if media or other channel, notify scb that channel open */
/* look up scb in stream routing table by ccb, tcid */
p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
if (p_scb == nullptr) {
AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
__func__, p_tbl->ccb_idx, p_tbl->tcid);
return;
}
/* put lcid in event data */
open.peer_mtu = p_tbl->peer_mtu;
open.lcid = avdtp_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].lcid;
open.hdr.err_code = avdt_ad_tcid_to_type(p_tbl->tcid);
tAVDT_SCB_EVT avdt_scb_evt;
avdt_scb_evt.open = open;
avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, &avdt_scb_evt);
}
/*******************************************************************************
*
* Function avdt_ad_tc_cong_ind
*
* Description This function is called by the L2CAP interface layer when
* L2CAP calls the congestion callback. It looks up the CCB
* or SCB for the channel and sends it a congestion event.
* The is_congested parameter is the same value passed by
* the L2CAP callback function.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_tc_cong_ind(AvdtpTransportChannel* p_tbl, bool is_congested) {
AvdtpCcb* p_ccb;
AvdtpScb* p_scb;
/* if signaling channel, notify ccb of congestion */
if (p_tbl->tcid == 0) {
p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
tAVDT_CCB_EVT avdt_ccb_evt;
avdt_ccb_evt.llcong = is_congested;
avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, &avdt_ccb_evt);
return;
}
/* if media or other channel, notify scb that channel open */
/* look up scb in stream routing table by ccb, tcid */
p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
if (p_scb == nullptr) {
AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
__func__, p_tbl->ccb_idx, p_tbl->tcid);
return;
}
tAVDT_SCB_EVT avdt_scb_evt;
avdt_scb_evt.llcong = is_congested;
avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, &avdt_scb_evt);
}
/*******************************************************************************
*
* Function avdt_ad_tc_data_ind
*
* Description This function is called by the L2CAP interface layer when
* incoming data is received from L2CAP. It looks up the CCB
* or SCB for the channel and routes the data accordingly.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_tc_data_ind(AvdtpTransportChannel* p_tbl, BT_HDR* p_buf) {
AvdtpCcb* p_ccb;
AvdtpScb* p_scb;
/* store type (media, recovery, reporting) */
p_buf->layer_specific = avdt_ad_tcid_to_type(p_tbl->tcid);
/* if signaling channel, handle control message */
if (p_tbl->tcid == 0) {
p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx);
avdt_msg_ind(p_ccb, p_buf);
return;
}
/* if media or other channel, send event to scb */
p_scb = avdtp_cb.ad.LookupAvdtpScb(*p_tbl);
if (p_scb == nullptr) {
AVDT_TRACE_ERROR("%s: Cannot find AvdtScb entry: ccb_idx:%d tcid:%d",
__func__, p_tbl->ccb_idx, p_tbl->tcid);
osi_free(p_buf);
AVDT_TRACE_ERROR("%s: buffer freed", __func__);
return;
}
avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT*)&p_buf);
}
/*******************************************************************************
*
* Function avdt_ad_write_req
*
* Description This function is called by a CCB or SCB to send data to a
* transport channel. It looks up the LCID of the channel
* based on the type, CCB, and SCB (if present). Then it
* passes the data to L2CA_DataWrite().
*
*
* Returns AVDT_AD_SUCCESS, if data accepted
* AVDT_AD_CONGESTED, if data accepted and the channel is
* congested
* AVDT_AD_FAILED, if error
*
******************************************************************************/
uint8_t avdt_ad_write_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
BT_HDR* p_buf) {
uint8_t tcid;
/* get tcid from type, scb */
tcid = avdt_ad_type_to_tcid(type, p_scb);
return L2CA_DataWrite(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid,
p_buf);
}
/*******************************************************************************
*
* Function avdt_ad_open_req
*
* Description This function is called by a CCB or SCB to open a transport
* channel. This function allocates and initializes a
* transport channel table entry. The channel can be opened
* in two roles: as an initiator or acceptor. When opened
* as an initiator the function will start an L2CAP connection.
* When opened as an acceptor the function simply configures
* the table entry to listen for an incoming channel.
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_open_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb,
uint8_t role) {
AvdtpTransportChannel* p_tbl;
uint16_t lcid;
p_tbl = avdt_ad_tc_tbl_alloc(p_ccb);
if (p_tbl == NULL) {
AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl");
return;
}
p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d", type, role,
p_tbl->tcid);
if (type == AVDT_CHAN_SIG) {
/* if signaling, get mtu from registration control block */
p_tbl->my_mtu = avdtp_cb.rcb.ctrl_mtu;
p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
} else {
/* otherwise get mtu from scb */
p_tbl->my_mtu = p_scb->stream_config.mtu;
p_tbl->my_flush_to = p_scb->stream_config.flush_to;
/* also set scb_hdl in rt_tbl */
avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl =
avdt_scb_to_hdl(p_scb);
AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].scb_hdl = %d",
avdt_ccb_to_idx(p_ccb), p_tbl->tcid,
avdt_scb_to_hdl(p_scb));
}
/* if we're acceptor, we're done; just sit back and listen */
if (role == AVDT_ACP) {
p_tbl->state = AVDT_AD_ST_ACP;
}
/* else we're inititator, start the L2CAP connection */
else {
p_tbl->state = AVDT_AD_ST_CONN;
/* call l2cap connect req */
lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr);
if (lcid != 0) {
/* if connect req ok, store tcid in lcid table */
avdtp_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] =
avdt_ad_tc_tbl_to_idx(p_tbl);
AVDT_TRACE_DEBUG("avdtp_cb.ad.lcid_tbl[%d] = %d",
(lcid - L2CAP_BASE_APPL_CID),
avdt_ad_tc_tbl_to_idx(p_tbl));
avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
AVDT_TRACE_DEBUG("avdtp_cb.ad.rt_tbl[%d][%d].lcid = 0x%x",
avdt_ccb_to_idx(p_ccb), p_tbl->tcid, lcid);
} else {
/* if connect req failed, call avdt_ad_tc_close_ind() */
avdt_ad_tc_close_ind(p_tbl, 0);
}
}
}
/*******************************************************************************
*
* Function avdt_ad_close_req
*
* Description This function is called by a CCB or SCB to close a
* transport channel. The function looks up the LCID for the
* channel and calls L2CA_DisconnectReq().
*
*
* Returns Nothing.
*
******************************************************************************/
void avdt_ad_close_req(uint8_t type, AvdtpCcb* p_ccb, AvdtpScb* p_scb) {
uint8_t tcid;
AvdtpTransportChannel* p_tbl;
p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb);
AVDT_TRACE_DEBUG("avdt_ad_close_req state: %d", p_tbl->state);
switch (p_tbl->state) {
case AVDT_AD_ST_UNUSED:
/* probably for reporting */
break;
case AVDT_AD_ST_ACP:
/* if we're listening on this channel, send ourselves a close ind */
avdt_ad_tc_close_ind(p_tbl, 0);
break;
default:
/* get tcid from type, scb */
tcid = avdt_ad_type_to_tcid(type, p_scb);
/* call l2cap disconnect req */
L2CA_DisconnectReq(avdtp_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid);
}
}