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.
582 lines
19 KiB
582 lines
19 KiB
/****************************************************************************** |
|
* |
|
* 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 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" |
|
|
|
/******************************************************************************* |
|
* |
|
* 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, tAVDT_SCB* p_scb) { |
|
uint8_t scb_idx; |
|
|
|
if (type == AVDT_CHAN_SIG) { |
|
return 0; |
|
} else { |
|
scb_idx = avdt_scb_to_hdl(p_scb) - 1; |
|
/* |
|
AVDT_TRACE_DEBUG("type: %d, tcid: %d", type, ((scb_idx * |
|
(AVDT_CHAN_NUM_TYPES - 1)) + type)); |
|
*/ |
|
return ((scb_idx * (AVDT_CHAN_NUM_TYPES - 1)) + type); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* 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; |
|
tAVDT_TC_TBL* p_tbl = avdt_cb.ad.tc_tbl; |
|
memset(&avdt_cb.ad, 0, sizeof(tAVDT_AD)); |
|
|
|
/* 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). |
|
* |
|
******************************************************************************/ |
|
tAVDT_TC_TBL* avdt_ad_tc_tbl_by_st(uint8_t type, tAVDT_CCB* p_ccb, |
|
uint8_t state) { |
|
int i; |
|
tAVDT_TC_TBL* p_tbl = avdt_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. |
|
* |
|
******************************************************************************/ |
|
tAVDT_TC_TBL* avdt_ad_tc_tbl_by_lcid(uint16_t lcid) { |
|
uint8_t idx; |
|
|
|
idx = avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; |
|
|
|
if (idx < AVDT_NUM_TC_TBL) { |
|
return &avdt_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. |
|
* |
|
******************************************************************************/ |
|
tAVDT_TC_TBL* avdt_ad_tc_tbl_by_type(uint8_t type, tAVDT_CCB* p_ccb, |
|
tAVDT_SCB* p_scb) { |
|
uint8_t tcid; |
|
int i; |
|
tAVDT_TC_TBL* p_tbl = avdt_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. |
|
* |
|
******************************************************************************/ |
|
tAVDT_TC_TBL* avdt_ad_tc_tbl_alloc(tAVDT_CCB* p_ccb) { |
|
int i; |
|
tAVDT_TC_TBL* p_tbl = avdt_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(tAVDT_TC_TBL* p_tbl) { |
|
AVDT_TRACE_DEBUG("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdt_cb.ad.tc_tbl)); |
|
/* use array arithmetic to determine index */ |
|
return (uint8_t)(p_tbl - avdt_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(tAVDT_TC_TBL* p_tbl, UNUSED_ATTR uint16_t reason) { |
|
tAVDT_CCB* p_ccb; |
|
tAVDT_SCB* 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("avdt_ad_tc_close_ind tcid: %d, old: %d", 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); |
|
} |
|
/* if media or other channel, notify scb that channel close */ |
|
else { |
|
/* look up scb in stream routing table by ccb, tcid */ |
|
p_scb = |
|
avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); |
|
if (p_scb != NULL) { |
|
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(tAVDT_TC_TBL* p_tbl) { |
|
tAVDT_CCB* p_ccb; |
|
tAVDT_SCB* p_scb; |
|
tAVDT_OPEN open; |
|
tAVDT_EVT_HDR evt; |
|
|
|
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(avdt_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); |
|
} |
|
/* if media or other channel, notify scb that channel open */ |
|
else { |
|
/* look up scb in stream routing table by ccb, tcid */ |
|
p_scb = |
|
avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); |
|
|
|
/* put lcid in event data */ |
|
if (p_scb != NULL) { |
|
open.peer_mtu = p_tbl->peer_mtu; |
|
open.lcid = avdt_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(tAVDT_TC_TBL* p_tbl, bool is_congested) { |
|
tAVDT_CCB* p_ccb; |
|
tAVDT_SCB* 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); |
|
} |
|
/* if media or other channel, notify scb that channel open */ |
|
else { |
|
/* look up scb in stream routing table by ccb, tcid */ |
|
p_scb = |
|
avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); |
|
if (p_scb != NULL) { |
|
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(tAVDT_TC_TBL* p_tbl, BT_HDR* p_buf) { |
|
tAVDT_CCB* p_ccb; |
|
tAVDT_SCB* 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); |
|
} |
|
/* if media or other channel, send event to scb */ |
|
else { |
|
p_scb = |
|
avdt_scb_by_hdl(avdt_cb.ad.rt_tbl[p_tbl->ccb_idx][p_tbl->tcid].scb_hdl); |
|
if (p_scb != NULL) { |
|
avdt_scb_event(p_scb, AVDT_SCB_TC_DATA_EVT, (tAVDT_SCB_EVT*)&p_buf); |
|
} else { |
|
osi_free(p_buf); |
|
AVDT_TRACE_ERROR(" avdt_ad_tc_data_ind buffer freed"); |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* 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, tAVDT_CCB* p_ccb, tAVDT_SCB* 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(avdt_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, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb, |
|
uint8_t role) { |
|
tAVDT_TC_TBL* 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 = avdt_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->cs.mtu; |
|
p_tbl->my_flush_to = p_scb->cs.flush_to; |
|
|
|
/* also set scb_hdl in rt_tbl */ |
|
avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].scb_hdl = |
|
avdt_scb_to_hdl(p_scb); |
|
AVDT_TRACE_DEBUG("avdt_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 */ |
|
avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = |
|
avdt_ad_tc_tbl_to_idx(p_tbl); |
|
AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d", |
|
(lcid - L2CAP_BASE_APPL_CID), |
|
avdt_ad_tc_tbl_to_idx(p_tbl)); |
|
|
|
avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid; |
|
AVDT_TRACE_DEBUG("avdt_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, tAVDT_CCB* p_ccb, tAVDT_SCB* p_scb) { |
|
uint8_t tcid; |
|
tAVDT_TC_TBL* 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(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid); |
|
} |
|
}
|
|
|