diff options
Diffstat (limited to 'stack/avdt/avdt_ad.c')
-rw-r--r-- | stack/avdt/avdt_ad.c | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/stack/avdt/avdt_ad.c b/stack/avdt/avdt_ad.c new file mode 100644 index 0000000..cebbb93 --- /dev/null +++ b/stack/avdt/avdt_ad.c @@ -0,0 +1,615 @@ +/***************************************************************************** +** +** Name: avdt_ad.c +** +** Description: This module contains the AVDTP adaption layer. +** +** Copyright (c) 2002-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#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); + } +} + |