diff options
Diffstat (limited to 'stack/avdt/avdt_l2c.c')
-rw-r--r-- | stack/avdt/avdt_l2c.c | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/stack/avdt/avdt_l2c.c b/stack/avdt/avdt_l2c.c new file mode 100644 index 0000000..ecab37d --- /dev/null +++ b/stack/avdt/avdt_l2c.c @@ -0,0 +1,508 @@ +/***************************************************************************** +** +** Name: avdt_l2c.c +** +** Description: This AVDTP adaption layer module interfaces to L2CAP +** +** Copyright (c) 2002-2011, Broadcom Corp., All Rights Reserved. +** Broadcom 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 "btm_api.h" +#include "btm_int.h" + + +/* callback function declarations */ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avdt_l2c_appl = { + avdt_l2c_connect_ind_cback, + avdt_l2c_connect_cfm_cback, + NULL, + avdt_l2c_config_ind_cback, + avdt_l2c_config_cfm_cback, + avdt_l2c_disconnect_ind_cback, + avdt_l2c_disconnect_cfm_cback, + NULL, + avdt_l2c_data_ind_cback, + avdt_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avdt_sec_check_complete_term +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG1("avdt_sec_check_complete_term res: %d", res); + if (!bd_addr) + { + AVDT_TRACE_WARNING0("avdt_sec_check_complete_term: NULL BD_ADDR"); + return; + + } + p_ccb = avdt_ccb_by_bd(bd_addr); + + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_ACP); + if (p_tbl == NULL) + return; + + if (res == BTM_SUCCESS) + { + /* Send response to the L2CAP layer. */ + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK); + + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[p_tbl->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 = p_tbl->lcid; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} + +/******************************************************************************* +** +** Function avdt_sec_check_complete_orig +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void avdt_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tAVDT_CCB *p_ccb = NULL; + tL2CAP_CFG_INFO cfg; + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG1("avdt_sec_check_complete_orig res: %d", res); + if (bd_addr) + p_ccb = avdt_ccb_by_bd(bd_addr); + p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_SEC_INT); + if(p_tbl == NULL) + return; + + if( res == BTM_SUCCESS ) + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(p_tbl->lcid, &cfg); + } + else + { + L2CA_DisconnectReq (p_tbl->lcid); + avdt_ad_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK); + } +} +/******************************************************************************* +** +** Function avdt_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVDT_CCB *p_ccb; + tAVDT_TC_TBL *p_tbl = NULL; + UINT16 result; + tL2CAP_CFG_INFO cfg; + tBTM_STATUS rc; + + /* do we already have a control channel for this peer? */ + if ((p_ccb = avdt_ccb_by_bd(bd_addr)) == NULL) + { + /* no, allocate ccb */ + if ((p_ccb = avdt_ccb_alloc(bd_addr)) == NULL) + { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* allocate and set up entry; first channel is always signaling */ + p_tbl = avdt_ad_tc_tbl_alloc(p_ccb); + p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu; + p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO; + p_tbl->tcid = AVDT_CHAN_SIG; + p_tbl->lcid = lcid; + p_tbl->id = id; + p_tbl->state = AVDT_AD_ST_SEC_ACP; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_ACP; + + /* Check the security */ + rc = btm_sec_mx_access_request (bd_addr, AVDT_PSM, + FALSE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_term, NULL); + if(rc == BTM_CMD_STARTED) + { + L2CA_ConnectRsp (p_ccb->peer_addr, p_tbl->id, lcid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + } + /* deal with simultaneous control channel connect case */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_SIG, p_ccb, AVDT_AD_ST_CONN)) != NULL) + { + /* reject their connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + /* this must be a traffic channel; are we accepting a traffic channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_MEDIA, p_ccb, AVDT_AD_ST_ACP)) != NULL) + { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#if AVDT_REPORTING == TRUE + /* this must be a reporting channel; are we accepting a reporting channel + ** for this ccb? + */ + else if ((p_tbl = avdt_ad_tc_tbl_by_st(AVDT_CHAN_REPORT, p_ccb, AVDT_AD_ST_ACP)) != NULL) + { + /* yes; proceed with connection */ + result = L2CAP_CONN_OK; + } +#endif + /* else we're not listening for traffic channel; reject */ + else + { + result = L2CAP_CONN_NO_PSM; + } + + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store idx in LCID table, store LCID in routing table */ + avdt_cb.ad.lcid_tbl[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; + + /* transition to configuration state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + tL2CAP_CFG_INFO cfg; + tAVDT_CCB *p_ccb; + + AVDT_TRACE_DEBUG2("avdt_l2c_connect_cfm_cback lcid: %d, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + if(p_tbl->tcid != AVDT_CHAN_SIG) + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = p_tbl->my_mtu; + cfg.flush_to_present = TRUE; + cfg.flush_to = p_tbl->my_flush_to; + L2CA_ConfigReq(lcid, &cfg); + } + else + { + p_ccb = avdt_ccb_by_idx(p_tbl->ccb_idx); + if(p_ccb == NULL) + { + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* set channel state */ + p_tbl->state = AVDT_AD_ST_SEC_INT; + p_tbl->lcid = lcid; + p_tbl->cfg_flags = AVDT_L2C_CFG_CONN_INT; + + /* Check the security */ + btm_sec_mx_access_request (p_ccb->peer_addr, AVDT_PSM, + TRUE, BTM_SEC_PROTO_AVDT, + AVDT_CHAN_SIG, + &avdt_sec_check_complete_orig, NULL); + } + } + } + + /* failure; notify adaption that channel closed */ + if (result != L2CAP_CONN_OK) + { + avdt_ad_tc_close_ind(p_tbl, result); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_tbl->state == AVDT_AD_ST_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CONN_OK) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) + { + avdt_ad_tc_open_ind(p_tbl); + } + } + /* else failure */ + else + { + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_tbl->peer_mtu = p_cfg->mtu; + } + else + { + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + } + AVDT_TRACE_DEBUG2("peer_mtu: %d, lcid: x%x",p_tbl->peer_mtu, lcid); + + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + + /* if first config ind */ + if ((p_tbl->cfg_flags & AVDT_L2C_CFG_IND_DONE) == 0) + { + /* update cfg_flags */ + p_tbl->cfg_flags |= AVDT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_tbl->cfg_flags & AVDT_L2C_CFG_CFM_DONE) + { + avdt_ad_tc_open_ind(p_tbl); + } + } + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG2("avdt_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d", + lcid, ack_needed); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avdt_ad_tc_close_ind(p_tbl, 0); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVDT_TC_TBL *p_tbl; + + AVDT_TRACE_DEBUG2("avdt_l2c_disconnect_cfm_cback lcid: %d, result: %d", + lcid, result); + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_close_ind(p_tbl, result); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_cong_ind(p_tbl, is_congested); + } +} + +/******************************************************************************* +** +** Function avdt_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avdt_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVDT_TC_TBL *p_tbl; + + /* look up info for this channel */ + if ((p_tbl = avdt_ad_tc_tbl_by_lcid(lcid)) != NULL) + { + avdt_ad_tc_data_ind(p_tbl, p_buf); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + |