summaryrefslogtreecommitdiffstats
path: root/stack/avdt/avdt_ad.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/avdt/avdt_ad.c')
-rw-r--r--stack/avdt/avdt_ad.c628
1 files changed, 628 insertions, 0 deletions
diff --git a/stack/avdt/avdt_ad.c b/stack/avdt/avdt_ad.c
new file mode 100644
index 0000000..92f429a
--- /dev/null
+++ b/stack/avdt/avdt_ad.c
@@ -0,0 +1,628 @@
+/******************************************************************************
+ *
+ * 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 <string.h>
+#include "data_types.h"
+#include "bt_target.h"
+#include "avdt_api.h"
+#include "avdtc_api.h"
+#include "avdt_int.h"
+#include "l2c_api.h"
+#include "l2cdefs.h"
+#include "wcassert.h"
+
+
+/*******************************************************************************
+**
+** Function avdt_ad_type_to_tcid
+**
+** Description Derives the TCID from the channel type and SCB.
+**
+**
+** Returns TCID value.
+**
+*******************************************************************************/
+UINT8 avdt_ad_type_to_tcid(UINT8 type, tAVDT_SCB *p_scb)
+{
+ UINT8 scb_idx;
+
+ if (type == AVDT_CHAN_SIG)
+ {
+ return 0;
+ }
+ else
+ {
+ scb_idx = avdt_scb_to_hdl(p_scb) - 1;
+ /*
+ AVDT_TRACE_DEBUG2("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 avdt_ad_tcid_to_type(UINT8 tcid)
+{
+ UINT8 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_DEBUG2("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 type, tAVDT_CCB *p_ccb, UINT8 state)
+{
+ int i;
+ tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl;
+ UINT8 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 lcid)
+{
+ UINT8 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 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb)
+{
+ UINT8 tcid;
+ int i;
+ tAVDT_TC_TBL *p_tbl = avdt_cb.ad.tc_tbl;
+ UINT8 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;
+ }
+ }
+
+ WC_ASSERT(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 */
+ WC_ASSERT(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 avdt_ad_tc_tbl_to_idx(tAVDT_TC_TBL *p_tbl)
+{
+ AVDT_TRACE_DEBUG1("avdt_ad_tc_tbl_to_idx: %d", (p_tbl - avdt_cb.ad.tc_tbl));
+ /* use array arithmetic to determine index */
+ return (UINT8) (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, UINT16 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_DEBUG2("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);
+ avdt_scb_event(p_scb, AVDT_SCB_TC_CLOSE_EVT, (tAVDT_SCB_EVT *)&close);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** 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;
+ }
+ avdt_ccb_event(p_ccb, AVDT_CCB_LL_OPEN_EVT, (tAVDT_CCB_EVT *)&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);
+ avdt_scb_event(p_scb, AVDT_SCB_TC_OPEN_EVT, (tAVDT_SCB_EVT *) &open);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** 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, BOOLEAN 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);
+ avdt_ccb_event(p_ccb, AVDT_CCB_LL_CONG_EVT, (tAVDT_CCB_EVT *) &is_congested);
+ }
+ /* 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)
+ {
+ avdt_scb_event(p_scb, AVDT_SCB_TC_CONG_EVT, (tAVDT_SCB_EVT *) &is_congested);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** 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
+ GKI_freebuf(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, else FALSE
+** AVDT_AD_CONGESTED, if data accepted and the channel is congested
+** AVDT_AD_FAILED, if error
+**
+*******************************************************************************/
+UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf)
+{
+ UINT8 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 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role)
+{
+ tAVDT_TC_TBL *p_tbl;
+ UINT16 lcid;
+
+ p_tbl = avdt_ad_tc_tbl_alloc(p_ccb);
+
+ p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
+ AVDT_TRACE_DEBUG3("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_DEBUG3("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 */
+ if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 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_DEBUG2("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_DEBUG3("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 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb)
+{
+ UINT8 tcid;
+ tAVDT_TC_TBL *p_tbl;
+
+ p_tbl = avdt_ad_tc_tbl_by_type(type, p_ccb, p_scb);
+ AVDT_TRACE_DEBUG1("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);
+ }
+}
+