| /****************************************************************************** |
| * |
| * 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" |
| |
| /******************************************************************************* |
| * |
| * 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); |
| } |
| } |