diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/avct | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/avct')
-rw-r--r-- | stack/avct/avct_api.c | 453 | ||||
-rw-r--r-- | stack/avct/avct_bcb_act.c | 749 | ||||
-rw-r--r-- | stack/avct/avct_ccb.c | 137 | ||||
-rw-r--r-- | stack/avct/avct_defs.h | 51 | ||||
-rw-r--r-- | stack/avct/avct_int.h | 227 | ||||
-rw-r--r-- | stack/avct/avct_l2c.c | 421 | ||||
-rw-r--r-- | stack/avct/avct_l2c_br.c | 397 | ||||
-rw-r--r-- | stack/avct/avct_lcb.c | 459 | ||||
-rw-r--r-- | stack/avct/avct_lcb_act.c | 690 |
9 files changed, 3584 insertions, 0 deletions
diff --git a/stack/avct/avct_api.c b/stack/avct/avct_api.c new file mode 100644 index 0000000..d1d5cee --- /dev/null +++ b/stack/avct/avct_api.c @@ -0,0 +1,453 @@ +/***************************************************************************** +** +** Name: avct_api.c +** +** Description: This module contains API of the audio/video control +** transport protocol. +** +** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "gki.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "btm_api.h" +#include "avct_api.h" +#include "avct_int.h" + +/* Control block for AVCT */ +#if AVCT_DYNAMIC_MEMORY == FALSE +tAVCT_CB avct_cb; +#endif + +/******************************************************************************* +** +** Function AVCT_Register +** +** Description This is the system level registration function for the +** AVCTP protocol. This function initializes AVCTP and +** prepares the protocol stack for its use. This function +** must be called once by the system or platform using AVCTP +** before the other functions of the API an be used. +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Register(UINT16 mtu, UINT16 mtu_br, UINT8 sec_mask) +{ + AVCT_TRACE_API0("AVCT_Register"); + + /* register PSM with L2CAP */ + L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_appl); + + /* set security level */ + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0, 0); + + /* initialize AVCTP data structures */ + memset(&avct_cb, 0, sizeof(tAVCT_CB)); + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* Include the browsing channel which uses eFCR */ + L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO *) &avct_l2c_br_appl); + + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_AVCTP_BROWSE, sec_mask, AVCT_BR_PSM, 0, 0); + + if (mtu_br < AVCT_MIN_BROWSE_MTU) + mtu_br = AVCT_MIN_BROWSE_MTU; + avct_cb.mtu_br = mtu_br; +#endif + +#if defined(AVCT_INITIAL_TRACE_LEVEL) + avct_cb.trace_level = AVCT_INITIAL_TRACE_LEVEL; +#else + avct_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + + if (mtu < AVCT_MIN_CONTROL_MTU) + mtu = AVCT_MIN_CONTROL_MTU; + /* store mtu */ + avct_cb.mtu = mtu; +} + +/******************************************************************************* +** +** Function AVCT_Deregister +** +** Description This function is called to deregister use AVCTP protocol. +** It is called when AVCTP is no longer being used by any +** application in the system. Before this function can be +** called, all connections must be removed with +** AVCT_RemoveConn(). +** +** +** Returns void +** +*******************************************************************************/ +void AVCT_Deregister(void) +{ + AVCT_TRACE_API0("AVCT_Deregister"); + + /* deregister PSM with L2CAP */ + L2CA_Deregister(AVCT_PSM); +} + +/******************************************************************************* +** +** Function AVCT_CreateConn +** +** Description Create an AVCTP connection. There are two types of +** connections, initiator and acceptor, as determined by +** the p_cc->role parameter. When this function is called to +** create an initiator connection, an AVCTP connection to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateConn(UINT8 *p_handle, tAVCT_CC *p_cc, BD_ADDR peer_addr) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_LCB *p_lcb; + + AVCT_TRACE_API2("AVCT_CreateConn: %d, control:%d", p_cc->role, p_cc->control); + + /* Allocate ccb; if no ccbs, return failure */ + if ((p_ccb = avct_ccb_alloc(p_cc)) == NULL) + { + result = AVCT_NO_RESOURCES; + } + else + { + /* get handle */ + *p_handle = avct_ccb_to_idx(p_ccb); + + /* if initiator connection */ + if (p_cc->role == AVCT_INT) + { + /* find link; if none allocate a new one */ + if ((p_lcb = avct_lcb_by_bd(peer_addr)) == NULL) + { + if ((p_lcb = avct_lcb_alloc(peer_addr)) == NULL) + { + /* no link resources; free ccb as well */ + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_NO_RESOURCES; + } + } + /* check if PID already in use */ + else if (avct_lcb_has_pid(p_lcb, p_cc->pid)) + { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + result = AVCT_PID_IN_USE; + } + + if (result == AVCT_SUCCESS) + { + /* bind lcb to ccb */ + p_ccb->p_lcb = p_lcb; + AVCT_TRACE_DEBUG1("ch_state: %d", p_lcb->ch_state); + avct_lcb_event(p_lcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_RemoveConn +** +** Description Remove an AVCTP connection. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveConn(UINT8 handle) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API0("AVCT_RemoveConn"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + } + /* if connection not bound to lcb, dealloc */ + else if (p_ccb->p_lcb == NULL) + { + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + } + /* send unbind event to lcb */ + else + { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +} + +/******************************************************************************* +** +** Function AVCT_CreateBrowse +** +** Description Create an AVCTP Browse channel. There are two types of +** connections, initiator and acceptor, as determined by +** the role parameter. When this function is called to +** create an initiator connection, the Browse channel to +** the peer device is initiated if one does not already exist. +** If an acceptor connection is created, the connection waits +** passively for an incoming AVCTP connection from a peer device. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_CreateBrowse (UINT8 handle, UINT8 role) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_BCB *p_bcb; + int index; + + AVCT_TRACE_API1("AVCT_CreateBrowse: %d", role); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + return AVCT_BAD_HANDLE; + } + else + { + /* mark this CCB as supporting browsing channel */ + if ((p_ccb->allocated & AVCT_ALOC_BCB) == 0) + { + p_ccb->allocated |= AVCT_ALOC_BCB; + } + } + + /* if initiator connection */ + if (role == AVCT_INT) + { + /* the link control block must exist before this function is called as INT. */ + if (p_ccb->p_lcb == NULL) + { + result = AVCT_NOT_OPEN; + } + else + { + /* find link; if none allocate a new one */ + index = p_ccb->p_lcb->allocated; + if (index > AVCT_NUM_LINKS) + { + result = AVCT_BAD_HANDLE; + } + else + { + p_bcb = &avct_cb.bcb[index - 1]; + p_bcb->allocated = index; + } + } + + if (result == AVCT_SUCCESS) + { + /* bind bcb to ccb */ + p_ccb->p_bcb = p_bcb; + AVCT_TRACE_DEBUG1("ch_state: %d", p_bcb->ch_state); + avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + } + + return result; +#else + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_RemoveBrowse +** +** Description Remove an AVCTP Browse channel. This function is called when +** the application is no longer using a connection. If this +** is the last connection to a peer the L2CAP channel for AVCTP +** will be closed. +** +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_RemoveBrowse (UINT8 handle) +{ +#if (AVCT_BROWSE_INCLUDED == TRUE) + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + + AVCT_TRACE_API0("AVCT_RemoveBrowse"); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + } + else if (p_ccb->p_bcb != NULL) + /* send unbind event to bcb */ + { + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + } + return result; +#else + return AVCT_NO_RESOURCES; +#endif +} + +/******************************************************************************* +** +** Function AVCT_GetBrowseMtu +** +** Description Get the peer_mtu for the AVCTP Browse channel of the given +** connection. +** +** Returns the peer browsing channel MTU. +** +*******************************************************************************/ +UINT16 AVCT_GetBrowseMtu (UINT8 handle) +{ + UINT16 peer_mtu = AVCT_MIN_BROWSE_MTU; +#if (AVCT_BROWSE_INCLUDED == TRUE) + tAVCT_CCB *p_ccb; + + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL && p_ccb->p_bcb != NULL) + { + peer_mtu = p_ccb->p_bcb->peer_mtu; + } +#endif + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_GetPeerMtu +** +** Description Get the peer_mtu for the AVCTP channel of the given +** connection. +** +** Returns the peer MTU size. +** +*******************************************************************************/ +UINT16 AVCT_GetPeerMtu (UINT8 handle) +{ + UINT16 peer_mtu = L2CAP_DEFAULT_MTU; + tAVCT_CCB *p_ccb; + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) != NULL) + { + if (p_ccb->p_lcb) + { + peer_mtu = p_ccb->p_lcb->peer_mtu; + } + } + + return peer_mtu; +} + +/******************************************************************************* +** +** Function AVCT_MsgReq +** +** Description Send an AVCTP message to a peer device. In calling +** AVCT_MsgReq(), the application should keep track of the +** congestion state of AVCTP as communicated with events +** AVCT_CONG_IND_EVT and AVCT_UNCONG_IND_EVT. If the +** application calls AVCT_MsgReq() when AVCTP is congested +** the message may be discarded. The application may make its +** first call to AVCT_MsgReq() after it receives an +** AVCT_CONNECT_CFM_EVT or AVCT_CONNECT_IND_EVT on control channel or +** AVCT_BROWSE_CONN_CFM_EVT or AVCT_BROWSE_CONN_IND_EVT on browsing channel. +** +** p_msg->layer_specific must be set to +** AVCT_DATA_CTRL for control channel traffic; +** AVCT_DATA_BROWSE for for browse channel traffic. +** +** Returns AVCT_SUCCESS if successful, otherwise error. +** +*******************************************************************************/ +UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg) +{ + UINT16 result = AVCT_SUCCESS; + tAVCT_CCB *p_ccb; + tAVCT_UL_MSG ul_msg; + + AVCT_TRACE_API0("AVCT_MsgReq"); + + /* verify p_msg parameter */ + if (p_msg == NULL) + { + return AVCT_NO_RESOURCES; + } + AVCT_TRACE_API1("len: %d", p_msg->len); + + /* map handle to ccb */ + if ((p_ccb = avct_ccb_by_idx(handle)) == NULL) + { + result = AVCT_BAD_HANDLE; + GKI_freebuf(p_msg); + } + /* verify channel is bound to link */ + else if (p_ccb->p_lcb == NULL) + { + result = AVCT_NOT_OPEN; + GKI_freebuf(p_msg); + } + + if (result == AVCT_SUCCESS) + { + ul_msg.p_buf = p_msg; + ul_msg.p_ccb = p_ccb; + ul_msg.label = label; + ul_msg.cr = cr; + +#if (AVCT_BROWSE_INCLUDED == TRUE) + /* send msg event to bcb */ + if (p_msg->layer_specific == AVCT_DATA_BROWSE) + { + if (p_ccb->p_bcb == NULL && (p_ccb->allocated & AVCT_ALOC_BCB) == 0) + { + /* BCB channel is not open and not allocated */ + result = AVCT_BAD_HANDLE; + GKI_freebuf(p_msg); + } + else + { + p_ccb->p_bcb = avct_bcb_by_lcb(p_ccb->p_lcb); + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + /* send msg event to lcb */ + else +#endif + { + avct_lcb_event(p_ccb->p_lcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + } + return result; +} + diff --git a/stack/avct/avct_bcb_act.c b/stack/avct/avct_bcb_act.c new file mode 100644 index 0000000..ad6d651 --- /dev/null +++ b/stack/avct/avct_bcb_act.c @@ -0,0 +1,749 @@ +/***************************************************************************** +** +** Name: avct_bcb_act.c +** +** Description: This module contains action functions of the browsing control +** state machine. +** +** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "gki.h" + + +#include "btm_api.h" + +#if (AVCT_BROWSE_INCLUDED == TRUE) +/* action function list */ +const tAVCT_BCB_ACTION avct_bcb_action[] = { + avct_bcb_chnl_open, /* AVCT_LCB_CHNL_OPEN */ + avct_bcb_chnl_disc, /* AVCT_LCB_CHNL_DISC */ + avct_bcb_send_msg, /* AVCT_LCB_SEND_MSG */ + avct_bcb_open_ind, /* AVCT_LCB_OPEN_IND */ + avct_bcb_open_fail, /* AVCT_LCB_OPEN_FAIL */ + avct_bcb_close_ind, /* AVCT_LCB_CLOSE_IND */ + avct_bcb_close_cfm, /* AVCT_LCB_CLOSE_CFM */ + avct_bcb_msg_ind, /* AVCT_LCB_MSG_IND */ + avct_bcb_cong_ind, /* AVCT_LCB_CONG_IND */ + avct_bcb_bind_conn, /* AVCT_LCB_BIND_CONN */ + avct_bcb_bind_fail, /* AVCT_LCB_BIND_FAIL */ + avct_bcb_unbind_disc, /* AVCT_LCB_UNBIND_DISC */ + avct_bcb_chk_disc, /* AVCT_LCB_CHK_DISC */ + avct_bcb_discard_msg, /* AVCT_LCB_DISCARD_MSG */ + avct_bcb_dealloc, /* AVCT_LCB_DEALLOC */ + avct_bcb_free_msg_ind /* AVCT_LCB_FREE_MSG_IND */ +}; + + +/******************************************************************************* +** +** Function avct_bcb_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +static BT_HDR *avct_bcb_msg_asmbl(tAVCT_BCB *p_bcb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret = NULL; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_PRS_PKT_TYPE(p, pkt_type); + + /* must be single packet - can not fragment */ + if (pkt_type == AVCT_PKT_TYPE_SINGLE) + { + p_ret = p_buf; + } + else + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING1("Pkt type=%d - fragmentation not allowed. drop it", pkt_type); + } + return p_ret; +} + + +/******************************************************************************* +** +** Function avct_bcb_chnl_open +** +** Description Open L2CAP channel to peer +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 result = AVCT_RESULT_FAIL; + tAVCT_LCB *p_lcb = avct_lcb_by_bcb(p_bcb); + tL2CAP_ERTM_INFO ertm_info; + + BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP_BROWSE, 0); + + /* Set the FCR options: Browsing channel mandates ERTM */ + ertm_info.preferred_mode = avct_l2c_br_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = AVCT_BR_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = AVCT_BR_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = AVCT_BR_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = AVCT_BR_FCR_TX_POOL_ID; + + /* call l2cap connect req */ + p_bcb->ch_state = AVCT_CH_CONN; + if ((p_bcb->ch_lcid = L2CA_ErtmConnectReq(AVCT_BR_PSM, p_lcb->peer_addr, &ertm_info)) == 0) + { + /* if connect req failed, send ourselves close event */ + avct_bcb_event(p_bcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } +} + +/******************************************************************************* +** +** Function avct_bcb_unbind_disc +** +** Description call callback with disconnect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + p_data->p_ccb->p_bcb = NULL; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), AVCT_BROWSE_DISCONN_CFM_EVT, 0, NULL); +} + +/******************************************************************************* +** +** Function avct_bcb_open_ind +** +** Description Handle an LL_OPEN event. For each allocated ccb already +** bound to this lcb, send a connect event. For each +** unbound ccb with a new PID, bind that ccb to this lcb and +** send a connect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + tAVCT_CCB *p_ccb_bind = NULL; + int i; + BOOLEAN bind = FALSE; + tAVCT_UL_MSG ul_msg; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + /* if bound to this lcb send connect confirm event */ + if (p_ccb->p_bcb == p_bcb) + { + bind = TRUE; + p_ccb_bind = p_ccb; + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_CONN_CFM_EVT, + 0, p_ccb->p_lcb->peer_addr); + } + /* if unbound acceptor and lcb e */ + else if ((p_ccb->p_bcb == NULL) && (p_ccb->cc.role == AVCT_ACP) && + (p_ccb->p_lcb != NULL)) + { + /* bind ccb to lcb and send connect ind event */ + bind = TRUE; + p_ccb_bind = p_ccb; + p_ccb->p_bcb = p_bcb; + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_CONN_IND_EVT, + 0, p_ccb->p_lcb->peer_addr); + } + } + } + + /* if no ccbs bound to this lcb, disconnect */ + if (bind == FALSE) + { + avct_bcb_event(p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } + else if (p_bcb->p_tx_msg) + { + if (p_ccb_bind) + { + ul_msg.p_buf = p_bcb->p_tx_msg; + ul_msg.p_ccb = p_ccb_bind; + ul_msg.label = (UINT8)(p_bcb->p_tx_msg->layer_specific & 0xFF); + ul_msg.cr = (UINT8)((p_bcb->p_tx_msg->layer_specific & 0xFF00) >> 8); + p_bcb->p_tx_msg->layer_specific = AVCT_DATA_BROWSE; + p_bcb->p_tx_msg = NULL; + + /* send msg event to bcb */ + avct_bcb_event(p_bcb, AVCT_LCB_UL_MSG_EVT, (tAVCT_LCB_EVT *) &ul_msg); + } + else + { + GKI_freebuf (p_bcb->p_tx_msg); + p_bcb->p_tx_msg = NULL; + } + + } +} + +/******************************************************************************* +** +** Function avct_bcb_open_fail +** +** Description L2CAP channel open attempt failed. Mark the ccbs +** as NULL bcb. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + p_ccb->p_bcb = NULL; + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_close_ind +** +** Description L2CAP channel closed by peer. Deallocate any initiator +** ccbs on this lcb and send disconnect ind event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + tAVCT_LCB *p_lcb = avct_lcb_by_bcb(p_bcb); + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + if (p_ccb->cc.role == AVCT_INT) + { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_DISCONN_CFM_EVT, 0, p_lcb->peer_addr); + } + else + { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_DISCONN_IND_EVT, 0, NULL); + } + p_ccb->p_bcb = NULL; + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_close_cfm +** +** Description L2CAP channel closed by us. Deallocate any initiator +** ccbs on this lcb and send disconnect ind or cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event = 0; + BOOLEAN ch_close = p_bcb->ch_close; /* Whether BCB initiated channel close */ + tAVCT_CTRL_CBACK *p_cback; + + p_bcb->ch_close = FALSE; + p_bcb->allocated = 0; + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + /* if this ccb initiated close send disconnect cfm otherwise ind */ + if (ch_close) + { + event = AVCT_BROWSE_DISCONN_CFM_EVT; + } + else + { + event = AVCT_BROWSE_DISCONN_IND_EVT; + } + + p_cback = p_ccb->cc.p_ctrl_cback; + p_ccb->p_bcb = NULL; + if (p_ccb->p_lcb == NULL) + avct_ccb_dealloc(p_ccb, AVCT_NO_EVT, 0, NULL); + (*p_cback)(avct_ccb_to_idx(p_ccb), event, + p_data->result, NULL); + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_bind_conn +** +** Description Bind ccb to lcb and send connect cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB *p_lcb = avct_lcb_by_bcb(p_bcb); + p_data->p_ccb->p_bcb = p_bcb; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), + AVCT_BROWSE_CONN_CFM_EVT, 0, p_lcb->peer_addr); +} + +/******************************************************************************* +** +** Function avct_bcb_chk_disc +** +** Description A ccb wants to close; if it is the last ccb on this lcb, +** close channel. Otherwise just deallocate and call +** callback. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("avct_bcb_chk_disc"); + p_bcb->ch_close = avct_bcb_last_ccb(p_bcb, p_data->p_ccb); + if (p_bcb->ch_close) + { + AVCT_TRACE_WARNING0("b closing"); + avct_bcb_event(p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } + else + { + AVCT_TRACE_WARNING0("b unbind_disc"); + avct_bcb_unbind_disc(p_bcb, p_data); + } +} + +/******************************************************************************* +** +** Function avct_bcb_chnl_disc +** +** Description Disconnect L2CAP channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + L2CA_DisconnectReq(p_bcb->ch_lcid); +} + +/******************************************************************************* +** +** Function avct_bcb_bind_fail +** +** Description Deallocate ccb and call callback with connect event +** with failure result. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + p_data->p_ccb->p_bcb = NULL; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), AVCT_BROWSE_CONN_CFM_EVT, AVCT_RESULT_FAIL, NULL); +} + +/******************************************************************************* +** +** Function avct_bcb_cong_ind +** +** Description Handle congestion indication from L2CAP. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + tAVCT_LCB *p_lcb = avct_lcb_by_bcb(p_bcb); + + /* set event */ + event = (p_data->cong) ? AVCT_BROWSE_CONG_IND_EVT : AVCT_BROWSE_UNCONG_IND_EVT; + + /* send event to all ccbs on this lcb */ + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0, p_lcb->peer_addr); + } + } + +} + +/******************************************************************************* +** +** Function avct_bcb_discard_msg +** +** Description Discard a message sent in from the API. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("save msg in control block"); + + if (p_bcb->p_tx_msg) + { + GKI_freebuf(p_bcb->p_tx_msg); + p_bcb->p_tx_msg = NULL; + } + + /* if control channel is up, save the message and open the browsing channel */ + if (p_data->ul_msg.p_ccb->p_lcb) + { + p_bcb->p_tx_msg = p_data->ul_msg.p_buf; + + if (p_bcb->p_tx_msg) + { + p_bcb->p_tx_msg->layer_specific = (p_data->ul_msg.cr << 8) + p_data->ul_msg.label; + + /* the channel is closed, opening or closing - open it again */ + AVCT_TRACE_DEBUG2("ch_state: %d, allocated:%d", p_bcb->ch_state, p_bcb->allocated); + p_bcb->allocated = p_data->ul_msg.p_ccb->p_lcb->allocated; + AVCT_TRACE_DEBUG2("ch_state: %d, allocated:%d", p_bcb->ch_state, p_bcb->allocated); + avct_bcb_event(p_bcb, AVCT_LCB_UL_BIND_EVT, (tAVCT_LCB_EVT *) p_data->ul_msg.p_ccb); + } + } + else + GKI_freebuf(p_data->ul_msg.p_buf); +} + +/******************************************************************************* +** +** Function avct_bcb_send_msg +** +** Description Build and send an AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 curr_msg_len; + UINT8 pkt_type = AVCT_PKT_TYPE_SINGLE; + UINT8 hdr_len; + BT_HDR *p_buf; + UINT8 *p; + + /* store msg len */ + curr_msg_len = p_data->ul_msg.p_buf->len; + + /* initialize packet type and other stuff */ + if (curr_msg_len > (p_bcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) + { + AVCT_TRACE_ERROR3 ("avct_bcb_send_msg msg len (%d) exceeds peer mtu(%d-%d)!!", curr_msg_len, p_bcb->peer_mtu, AVCT_HDR_LEN_SINGLE); + GKI_freebuf(p_data->ul_msg.p_buf); + return; + } + + /* set header len */ + hdr_len = avct_lcb_pkt_type_len[pkt_type]; + p_buf = p_data->ul_msg.p_buf; + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr); + UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid); + + /* send message to L2CAP */ + L2CA_DataWrite(p_bcb->ch_lcid, p_buf); + return; +} + +/******************************************************************************* +** +** Function avct_bcb_free_msg_ind +** +** Description Discard an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + if (p_data) + GKI_freebuf(p_data->p_buf); + return; +} + +/******************************************************************************* +** +** Function avct_bcb_msg_ind +** +** Description Handle an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + UINT8 *p; + UINT8 label, type, cr_ipid; + UINT16 pid; + tAVCT_CCB *p_ccb; + BT_HDR *p_buf; + tAVCT_LCB *p_lcb = avct_lcb_by_bcb(p_bcb); + + /* this p_buf is to be reported through p_msg_cback. The layer_specific + * needs to be set properly to indicate that it is received through + * broasing channel */ + p_data->p_buf->layer_specific = AVCT_DATA_BROWSE; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_data->p_buf = avct_bcb_msg_asmbl(p_bcb, p_data->p_buf)) == NULL) + { + return; + } + + p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset; + + /* parse header byte */ + AVCT_PRS_HDR(p, label, type, cr_ipid); + + /* check for invalid cr_ipid */ + if (cr_ipid == AVCT_CR_IPID_INVALID) + { + AVCT_TRACE_WARNING1("Invalid cr_ipid", cr_ipid); + GKI_freebuf(p_data->p_buf); + return; + } + + /* parse and lookup PID */ + BE_STREAM_TO_UINT16(pid, p); + if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL) + { + /* PID found; send msg up, adjust bt hdr and call msg callback */ + p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE; + p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE; + (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf); + } + else + { + /* PID not found; drop message */ + AVCT_TRACE_WARNING1("No ccb for PID=%x", pid); + GKI_freebuf(p_data->p_buf); + + /* if command send reject */ + if (cr_ipid == AVCT_CMD) + { + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVCT_CMD_POOL_ID)) != NULL) + { + p_buf->len = AVCT_HDR_LEN_SINGLE; + p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ); + UINT16_TO_BE_STREAM(p, pid); + L2CA_DataWrite(p_bcb->ch_lcid, p_buf); + } + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_dealloc +** +** Description Deallocate a link control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + tAVCT_CCB *p_ccb_found = NULL; + BOOLEAN found = FALSE; + int i; + + AVCT_TRACE_DEBUG1("avct_bcb_dealloc %d", p_bcb->allocated); + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + if (p_ccb->p_bcb == p_bcb) + { + p_ccb_found = p_ccb; + p_ccb->p_bcb = NULL; + AVCT_TRACE_DEBUG1("avct_bcb_dealloc used by ccb: %d", i); + found = TRUE; + break; + } + } + } + + /* the browsing channel is down. Check if we have pending messages */ + if (p_bcb->p_tx_msg != NULL) + { + GKI_freebuf(p_bcb->p_tx_msg); + } + memset(p_bcb, 0, sizeof(tAVCT_BCB)); +} + +/******************************************************************************* +** +** Function avct_close_bcb +** +** Description this function is called right before LCB disconnects. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_BCB *p_bcb = avct_bcb_by_lcb(p_lcb); + if (p_bcb->allocated) + { + avct_bcb_event(p_bcb, AVCT_LCB_UL_UNBIND_EVT, p_data); + } +} + + +/******************************************************************************* +** +** Function avct_lcb_by_bcb +** +** Description This lookup function finds the lcb for a bcb. +** +** Returns pointer to the lcb. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb) +{ + return &avct_cb.lcb[p_bcb->allocated - 1]; +} + +/******************************************************************************* +** +** Function avct_bcb_by_lcb +** +** Description This lookup function finds the bcb for a lcb. +** +** Returns pointer to the lcb. +** +*******************************************************************************/ +tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb) +{ + return &avct_cb.bcb[p_lcb->allocated - 1]; +} + +/******************************************************************************* +** +** Function avct_bcb_last_ccb +** +** Description See if given ccb is only one on the bcb. +** +** +** Returns 0, if ccb is last, (ccb index + 1) otherwise. +** +*******************************************************************************/ +UINT8 avct_bcb_last_ccb(tAVCT_BCB *p_bcb, tAVCT_CCB *p_ccb_last) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 idx = 0; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + if (p_ccb != p_ccb_last) + return 0; + idx = (UINT8)(i + 1); + } + } + return idx; +} + +/******************************************************************************* +** +** Function avct_bcb_by_lcid +** +** Description Find the BCB associated with the L2CAP LCID +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid) +{ + tAVCT_BCB *p_bcb = &avct_cb.bcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_bcb++) + { + if (p_bcb->allocated && (p_bcb->ch_lcid == lcid)) + { + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + p_bcb = NULL; + /* out of lcbs */ + AVCT_TRACE_WARNING1("No bcb for lcid %x", lcid); + } + + return p_bcb; +} +#endif /* (AVCT_BROWSE_INCLUDED == TRUE) */ diff --git a/stack/avct/avct_ccb.c b/stack/avct/avct_ccb.c new file mode 100644 index 0000000..89465a2 --- /dev/null +++ b/stack/avct/avct_ccb.c @@ -0,0 +1,137 @@ +/***************************************************************************** +** +** Name: avct_ccb.c +** +** Description: This module contains functions which operate on the +** AVCTP connection control block. +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" + +/******************************************************************************* +** +** Function avct_ccb_alloc +** +** Description Allocate a connection control block; copy parameters to ccb. +** +** +** Returns pointer to the ccb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (!p_ccb->allocated) + { + p_ccb->allocated = AVCT_ALOC_LCB; + memcpy(&p_ccb->cc, p_cc, sizeof(tAVCT_CC)); + AVCT_TRACE_DEBUG1("avct_ccb_alloc %d", i); + break; + } + } + + if (i == AVCT_NUM_CONN) + { + /* out of ccbs */ + p_ccb = NULL; + AVCT_TRACE_WARNING0("Out of ccbs"); + } + return p_ccb; +} + +/******************************************************************************* +** +** Function avct_ccb_dealloc +** +** Description Deallocate a connection control block and call application +** callback. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr) +{ + tAVCT_CTRL_CBACK *p_cback = p_ccb->cc.p_ctrl_cback; + + AVCT_TRACE_DEBUG1("avct_ccb_dealloc %d", avct_ccb_to_idx(p_ccb)); +#if (AVCT_BROWSE_INCLUDED == TRUE) + if(p_ccb->p_bcb == NULL) + memset(p_ccb, 0, sizeof(tAVCT_CCB)); + else + { + /* control channel is down, but the browsing channel is still connected 0 disconnect it now */ + avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); + p_ccb->p_lcb = NULL; + } +#else + memset(p_ccb, 0, sizeof(tAVCT_CCB)); +#endif + + if (event != AVCT_NO_EVT) + { + (*p_cback)(avct_ccb_to_idx(p_ccb), event, result, bd_addr); + } +} + +/******************************************************************************* +** +** Function avct_ccb_to_idx +** +** Description Given a pointer to an ccb, return its index. +** +** +** Returns Index of ccb. +** +*******************************************************************************/ +UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb) +{ + /* use array arithmetic to determine index */ + return (UINT8) (p_ccb - avct_cb.ccb); +} + +/******************************************************************************* +** +** Function avct_ccb_by_idx +** +** Description Return ccb pointer based on ccb index (or handle). +** +** +** Returns pointer to the ccb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_CCB *avct_ccb_by_idx(UINT8 idx) +{ + tAVCT_CCB *p_ccb; + + /* verify index */ + if (idx < AVCT_NUM_CONN) + { + p_ccb = &avct_cb.ccb[idx]; + + /* verify ccb is allocated */ + if (!p_ccb->allocated) + { + p_ccb = NULL; + AVCT_TRACE_WARNING1("ccb %d not allocated", idx); + } + } + else + { + p_ccb = NULL; + AVCT_TRACE_WARNING1("No ccb for idx %d", idx); + } + return p_ccb; +} diff --git a/stack/avct/avct_defs.h b/stack/avct/avct_defs.h new file mode 100644 index 0000000..dd03c1e --- /dev/null +++ b/stack/avct/avct_defs.h @@ -0,0 +1,51 @@ +/***************************************************************************** +** +** Name: avct_defs.h +** +** Description: This contains constants definitions and other information +** from the AVCTP specification. This file is intended for +** use internal to AVCT only. +** +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#ifndef AVCT_DEFS_H +#define AVCT_DEFS_H + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* packet type */ +#define AVCT_PKT_TYPE_SINGLE 0 /* single packet */ +#define AVCT_PKT_TYPE_START 1 /* start packet */ +#define AVCT_PKT_TYPE_CONT 2 /* continue packet */ +#define AVCT_PKT_TYPE_END 3 /* end packet */ + +/* header lengths for different packet types */ +#define AVCT_HDR_LEN_SINGLE 3 +#define AVCT_HDR_LEN_START 4 +#define AVCT_HDR_LEN_CONT 1 +#define AVCT_HDR_LEN_END 1 + +/* invalid cr+ipid value */ +#define AVCT_CR_IPID_INVALID 1 + +/***************************************************************************** +** message parsing and building macros +*****************************************************************************/ + +#define AVCT_BLD_HDR(p, label, type, cr_ipid) \ + *(p)++ = ((label) << 4) | ((type) << 2) | (cr_ipid); + +#define AVCT_PRS_HDR(p, label, type, cr_ipid) \ + label = *(p) >> 4; \ + type = (*(p) >> 2) & 3; \ + cr_ipid = *(p)++ & 3; + +#define AVCT_PRS_PKT_TYPE(p, type) \ + type = (*(p) >> 2) & 3; + +#endif /* AVCT_DEFS_H */ diff --git a/stack/avct/avct_int.h b/stack/avct/avct_int.h new file mode 100644 index 0000000..d67c404 --- /dev/null +++ b/stack/avct/avct_int.h @@ -0,0 +1,227 @@ +/***************************************************************************** +** +** Name: avct_int.h +** +** Description: This file contains interfaces which are internal to AVCTP. +** +** +** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#ifndef AVCT_INT_H +#define AVCT_INT_H + +#include "gki.h" +#include "avct_api.h" +#include "avct_defs.h" +#include "l2c_api.h" + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* lcb state machine events */ +enum { + AVCT_LCB_UL_BIND_EVT, + AVCT_LCB_UL_UNBIND_EVT, + AVCT_LCB_UL_MSG_EVT, + AVCT_LCB_INT_CLOSE_EVT, + AVCT_LCB_LL_OPEN_EVT, + AVCT_LCB_LL_CLOSE_EVT, + AVCT_LCB_LL_MSG_EVT, + AVCT_LCB_LL_CONG_EVT +}; + + +/* "states" used for L2CAP channel */ +#define AVCT_CH_IDLE 0 /* No connection */ +#define AVCT_CH_CONN 1 /* Waiting for connection confirm */ +#define AVCT_CH_CFG 2 /* Waiting for configuration complete */ +#define AVCT_CH_OPEN 3 /* Channel opened */ + +/* "no event" indicator used by ccb dealloc */ +#define AVCT_NO_EVT 0xFF + +/***************************************************************************** +** data types +*****************************************************************************/ +/* sub control block type - common data members for tAVCT_LCB and tAVCT_BCB */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ +} tAVCT_SCB; + +/* link control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_rx_msg; /* Message being reassembled */ + UINT16 conflict_lcid; /* L2CAP channel LCID */ + BD_ADDR peer_addr; /* BD address of peer */ + BUFFER_Q tx_q; /* Transmit data buffer queue */ + BOOLEAN cong; /* TRUE, if congested */ +} tAVCT_LCB; + +/* browse control block type */ +typedef struct { + UINT16 peer_mtu; /* peer l2c mtu */ + UINT16 ch_result; /* L2CAP connection result value */ + UINT16 ch_lcid; /* L2CAP channel LCID */ + UINT8 allocated; /* 0, not allocated. index+1, otherwise. */ + UINT8 state; /* The state machine state */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BT_HDR *p_tx_msg; /* Message to be sent - in case the browsing channel is not open when MsgReg is called */ + UINT8 ch_close; /* CCB index+1, if CCB initiated channel close */ +} tAVCT_BCB; + +#define AVCT_ALOC_LCB 0x01 +#define AVCT_ALOC_BCB 0x02 +/* connection control block */ +typedef struct { + tAVCT_CC cc; /* parameters from connection creation */ + tAVCT_LCB *p_lcb; /* Associated LCB */ + tAVCT_BCB *p_bcb; /* associated BCB */ + BOOLEAN ch_close; /* Whether CCB initiated channel close */ + UINT8 allocated; /* Whether LCB/BCB is allocated */ +} tAVCT_CCB; + +/* data type associated with UL_MSG_EVT */ +typedef struct { + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT8 label; + UINT8 cr; +} tAVCT_UL_MSG; + +/* union associated with lcb state machine events */ +typedef union { + tAVCT_UL_MSG ul_msg; + BT_HDR *p_buf; + tAVCT_CCB *p_ccb; + UINT16 result; + BOOLEAN cong; + UINT8 err_code; +} tAVCT_LCB_EVT; + +/* Control block for AVCT */ +typedef struct { + tAVCT_LCB lcb[AVCT_NUM_LINKS]; /* link control blocks */ + tAVCT_BCB bcb[AVCT_NUM_LINKS]; /* browse control blocks */ + tAVCT_CCB ccb[AVCT_NUM_CONN]; /* connection control blocks */ + UINT16 mtu; /* our L2CAP MTU */ + UINT16 mtu_br; /* our L2CAP MTU for the Browsing channel */ + UINT8 trace_level; /* trace level */ +} tAVCT_CB; + +/***************************************************************************** +** function declarations +*****************************************************************************/ + +/* LCB function declarations */ +extern void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data); +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data); +extern void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb); +extern tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb); +extern BOOLEAN avct_bcb_last_ccb(tAVCT_BCB *p_bcb, tAVCT_CCB *p_ccb_last); +extern tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid); +#endif +extern tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr); +extern tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr); +extern void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid); +extern tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid); +extern BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last); + +/* LCB action functions */ +extern void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); +extern void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data); + +/* BCB action functions */ +#if (AVCT_BROWSE_INCLUDED == TRUE) +typedef void (*tAVCT_BCB_ACTION)(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); +extern void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data); + +extern const tAVCT_BCB_ACTION avct_bcb_action[]; +extern const UINT8 avct_lcb_pkt_type_len[]; +extern const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def; +#endif + +/* CCB function declarations */ +extern tAVCT_CCB *avct_ccb_alloc(tAVCT_CC *p_cc); +extern void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_addr); +extern UINT8 avct_ccb_to_idx(tAVCT_CCB *p_ccb); +extern tAVCT_CCB *avct_ccb_by_idx(UINT8 idx); + + +/***************************************************************************** +** global data +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Main control block */ +#if AVCT_DYNAMIC_MEMORY == FALSE +AVCT_API extern tAVCT_CB avct_cb; +#else +AVCT_API extern tAVCT_CB *avct_cb_ptr; +#define avct_cb (*avct_cb_ptr) +#endif + +/* L2CAP callback registration structure */ +extern const tL2CAP_APPL_INFO avct_l2c_appl; +#if (AVCT_BROWSE_INCLUDED == TRUE) +extern const tL2CAP_APPL_INFO avct_l2c_br_appl; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AVCT_INT_H */ + + diff --git a/stack/avct/avct_l2c.c b/stack/avct/avct_l2c.c new file mode 100644 index 0000000..cf1a652 --- /dev/null +++ b/stack/avct/avct_l2c.c @@ -0,0 +1,421 @@ +/***************************************************************************** +** +** Name: avct_l2c.c +** +** Description: This AVCTP module interfaces to L2CAP +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" + +/* Configuration flags. */ +#define AVCT_L2C_CFG_IND_DONE (1<<0) +#define AVCT_L2C_CFG_CFM_DONE (1<<1) + +/* callback function declarations */ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avct_l2c_appl = { + avct_l2c_connect_ind_cback, + avct_l2c_connect_cfm_cback, + NULL, + avct_l2c_config_ind_cback, + avct_l2c_config_cfm_cback, + avct_l2c_disconnect_ind_cback, + avct_l2c_disconnect_cfm_cback, + NULL, + avct_l2c_data_ind_cback, + avct_l2c_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/******************************************************************************* +** +** Function avct_l2c_is_passive +** +** Description check is the CCB associated with the given LCB was created +** as passive +** +** Returns TRUE, if the given LCB is created as AVCT_PASSIVE +** +*******************************************************************************/ +static BOOLEAN avct_l2c_is_passive (tAVCT_LCB *p_lcb) +{ + BOOLEAN is_passive = FALSE; + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + AVCT_TRACE_DEBUG1("avct_l2c_is_ct control:x%x", p_ccb->cc.control); + if (p_ccb->cc.control & AVCT_PASSIVE) + { + is_passive = TRUE; + break; + } + } + } + return is_passive; +} + +/******************************************************************************* +** +** Function avct_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVCT_LCB *p_lcb; + UINT16 result = L2CAP_CONN_OK; + tL2CAP_CFG_INFO cfg; + + /* do we already have a channel for this peer? */ + if ((p_lcb = avct_lcb_by_bd(bd_addr)) == NULL) + { + /* no, allocate lcb */ + if ((p_lcb = avct_lcb_alloc(bd_addr)) == NULL) + { + /* no ccb available, reject L2CAP connection */ + result = L2CAP_CONN_NO_RESOURCES; + } + } + /* else we already have a channel for this peer */ + else + { + if (!avct_l2c_is_passive (p_lcb) || (p_lcb->ch_state == AVCT_CH_OPEN)) + { + /* this LCB included CT role - reject */ + result = L2CAP_CONN_NO_RESOURCES; + } + else + { + /* TG role only - accept the connection from CT. move the channel ID to the conflict list */ + p_lcb->conflict_lcid = p_lcb->ch_lcid; + AVCT_TRACE_DEBUG1("avct_l2c_connect_ind_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + } + } + + if(p_lcb) + { + AVCT_TRACE_DEBUG3("avct_l2c_connect_ind_cback: 0x%x, res: %d, ch_state: %d", + lcid, result, p_lcb->ch_state); + } + /* Send L2CAP connect rsp */ + L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store LCID */ + p_lcb->ch_lcid = lcid; + + /* transition to configuration state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req"); + } + +#if (BT_USE_TRACES == TRUE) + if(p_lcb) + AVCT_TRACE_DEBUG1("ch_state cni: %d ", p_lcb->ch_state); +#endif +} + +/******************************************************************************* +** +** Function avct_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + tL2CAP_CFG_INFO cfg; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG4("avct_l2c_connect_cfm_cback lcid:0x%x result: %d ch_state: %d, conflict_lcid:0x%x", + lcid, result, p_lcb->ch_state, p_lcb->conflict_lcid); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu; + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG0("avct_l2c snd Cfg Req"); + } + /* else failure */ + else + { + AVCT_TRACE_DEBUG1("avct_l2c_connect_cfm_cback conflict_lcid:0x%x", p_lcb->conflict_lcid); + if (p_lcb->conflict_lcid == lcid) + p_lcb->conflict_lcid = 0; + else + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } + } + else if (p_lcb->conflict_lcid == lcid) + { + /* we must be in AVCT_CH_CFG state for the ch_lcid channel */ + AVCT_TRACE_DEBUG2("avct_l2c_connect_cfm_cback ch_state: %d, conflict_lcid:0x%x", p_lcb->ch_state, p_lcb->conflict_lcid); + if (result == L2CAP_CONN_OK) + { + /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + p_lcb->conflict_lcid = 0; + } + AVCT_TRACE_DEBUG1("ch_state cnc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG3("avct_l2c_config_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, p_cfg->result); + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + /* else failure */ + else + { + AVCT_TRACE_DEBUG1("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ", p_lcb->ch_state); + /* store result value */ + p_lcb->ch_result = p_cfg->result; + + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + AVCT_TRACE_DEBUG1("ch_state cfc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_LCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG2("avct_l2c_config_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_lcb->peer_mtu = p_cfg->mtu; + } + else + { + p_lcb->peer_mtu = L2CAP_DEFAULT_MTU; + } + + /* 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_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_CFM_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + AVCT_TRACE_DEBUG1("ch_state cfi: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVCT_LCB *p_lcb; + UINT16 result = AVCT_RESULT_FAIL; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG2("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d", lcid, p_lcb->ch_state); + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + AVCT_TRACE_DEBUG1("ch_state di: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_LCB *p_lcb; + UINT16 res; + + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG3("avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_lcb->ch_state, result); + /* result value may be previously stored */ + res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result; + p_lcb->ch_result = 0; + + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &res); + AVCT_TRACE_DEBUG1("ch_state dc: %d ", p_lcb->ch_state); + } +} + +/******************************************************************************* +** +** Function avct_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG1("avct_l2c_congestion_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + avct_lcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested); + } +} + +/******************************************************************************* +** +** Function avct_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVCT_LCB *p_lcb; + + AVCT_TRACE_DEBUG1("avct_l2c_data_ind_cback: 0x%x", lcid); + /* look up lcb for this channel */ + if ((p_lcb = avct_lcb_by_lcid(lcid)) != NULL) + { + avct_lcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf); + } + else /* prevent buffer leak */ + { + AVCT_TRACE_WARNING0("ERROR -> avct_l2c_data_ind_cback drop buffer"); + GKI_freebuf(p_buf); + } +} + diff --git a/stack/avct/avct_l2c_br.c b/stack/avct/avct_l2c_br.c new file mode 100644 index 0000000..e333edf --- /dev/null +++ b/stack/avct/avct_l2c_br.c @@ -0,0 +1,397 @@ +/***************************************************************************** +** +** Name: avct_l2c_br.c +** +** Description: This AVCTP module interfaces to L2CAP +** +** Copyright (c) 2008, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" + +/* Configuration flags. */ +#define AVCT_L2C_CFG_IND_DONE (1<<0) +#define AVCT_L2C_CFG_CFM_DONE (1<<1) + +#if (AVCT_BROWSE_INCLUDED == TRUE) +/* callback function declarations */ +void avct_l2c_br_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avct_l2c_br_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_br_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_br_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_br_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avct_l2c_br_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_br_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avct_l2c_br_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + +/* L2CAP callback function structure */ +const tL2CAP_APPL_INFO avct_l2c_br_appl = { + avct_l2c_br_connect_ind_cback, + avct_l2c_br_connect_cfm_cback, + NULL, + avct_l2c_br_config_ind_cback, + avct_l2c_br_config_cfm_cback, + avct_l2c_br_disconnect_ind_cback, + avct_l2c_br_disconnect_cfm_cback, + NULL, + avct_l2c_br_data_ind_cback, + avct_l2c_br_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; + +/* Browsing channel eL2CAP default options */ +const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def = { + L2CAP_FCR_ERTM_MODE, /* Mandatory for Browsing channel */ + AVCT_BR_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */ + AVCT_BR_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + AVCT_BR_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + AVCT_BR_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + L2CAP_DEFAULT_ERM_MPS /* MPS segment size */ +}; + +/******************************************************************************* +** +** Function avct_l2c_br_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVCT_LCB *p_lcb; + UINT16 result = L2CAP_CONN_NO_RESOURCES; + tL2CAP_CFG_INFO cfg; + tAVCT_BCB *p_bcb; + tL2CAP_ERTM_INFO ertm_info; + + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + + if ((p_lcb = avct_lcb_by_bd(bd_addr)) != NULL) + { + /* control channel exists */ + p_bcb = avct_bcb_by_lcb(p_lcb); + + if (!p_bcb->allocated) + { + /* browsing channel does not exist yet and the browsing channel is registered + * - accept connection */ + p_bcb->allocated = p_lcb->allocated; /* copy the index from lcb */ + + result = L2CAP_CONN_OK; + cfg.mtu = avct_cb.mtu_br; + + cfg.fcr_present = TRUE; + cfg.fcr = avct_l2c_br_fcr_opts_def; + } + } + /* else no control channel yet, reject */ + + /* Set the FCR options: Browsing channel mandates ERTM */ + ertm_info.preferred_mode = cfg.fcr.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = AVCT_BR_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = AVCT_BR_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = AVCT_BR_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = AVCT_BR_FCR_TX_POOL_ID; + + /* Send L2CAP connect rsp */ + L2CA_ErtmConnectRsp(bd_addr, id, lcid, result, 0, &ertm_info); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store LCID */ + p_bcb->ch_lcid = lcid; + + /* transition to configuration state */ + p_bcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + L2CA_ConfigReq(lcid, &cfg); + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_BCB *p_lcb; + tL2CAP_CFG_INFO cfg; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_lcb->ch_state = AVCT_CH_CFG; + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu_br; + + cfg.fcr_present = TRUE; + cfg.fcr = avct_l2c_br_fcr_opts_def; + + L2CA_ConfigReq(lcid, &cfg); + } + /* else failure */ + else + { + avct_bcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } + } + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_BCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + /* if in correct state */ + if (p_lcb->ch_state == AVCT_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_bcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + /* else failure */ + else + { + /* store result value */ + p_lcb->ch_result = p_cfg->result; + + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_BCB *p_lcb; + UINT16 max_mtu = GKI_MAX_BUF_SIZE - L2CAP_MIN_OFFSET - BT_HDR_SIZE; + + /* Don't include QoS nor flush timeout in the response since we + currently always accept these values. Note: fcr_present is left + untouched since l2cap negotiates this internally + */ + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_lcb->peer_mtu = p_cfg->mtu; + } + else + { + p_lcb->peer_mtu = L2CAP_DEFAULT_MTU; + } + + if (p_lcb->peer_mtu > max_mtu) + { + p_lcb->peer_mtu = p_cfg->mtu = max_mtu; + + /* Must tell the peer what the adjusted value is */ + p_cfg->mtu_present = TRUE; + } + else /* Don't include in the response */ + p_cfg->mtu_present = FALSE; + AVCT_TRACE_DEBUG2 ("avct_l2c_br_config_ind_cback peer_mtu:%d use:%d", p_lcb->peer_mtu, max_mtu); + + if (p_lcb->peer_mtu >= AVCT_MIN_BROWSE_MTU) + p_cfg->result = L2CAP_CFG_OK; + else + { + p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS; + p_cfg->mtu_present = TRUE; + p_cfg->mtu = AVCT_MIN_BROWSE_MTU; + } + + /* send L2CAP configure response */ + L2CA_ConfigRsp(lcid, p_cfg); + + if (p_cfg->result != L2CAP_CFG_OK) + { + return; + } + + /* if first config ind */ + if ((p_lcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_lcb->ch_flags |= AVCT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_lcb->ch_flags & AVCT_L2C_CFG_CFM_DONE) + { + p_lcb->ch_state = AVCT_CH_OPEN; + avct_bcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVCT_BCB *p_lcb; + UINT16 result = AVCT_RESULT_FAIL; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + + avct_bcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_BCB *p_lcb; + UINT16 res; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + /* result value may be previously stored */ + res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result; + p_lcb->ch_result = 0; + + avct_bcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &res); + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVCT_BCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + avct_bcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested); + } +} + +/******************************************************************************* +** +** Function avct_l2c_br_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void avct_l2c_br_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVCT_BCB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = avct_bcb_by_lcid(lcid)) != NULL) + { + avct_bcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} +#endif /* (AVCT_BROWSE_INCLUDED == TRUE) */ + + diff --git a/stack/avct/avct_lcb.c b/stack/avct/avct_lcb.c new file mode 100644 index 0000000..f7127a3 --- /dev/null +++ b/stack/avct/avct_lcb.c @@ -0,0 +1,459 @@ +/***************************************************************************** +** +** Name: avct_lcb.c +** +** Description: This module contains the link control state +** machine and functions which operate on the link +** control block. +** +** Copyright (c) 2003-2008, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "gki.h" + +/***************************************************************************** +** state machine constants and types +*****************************************************************************/ + +#if BT_TRACE_VERBOSE == TRUE + +/* verbose state strings for trace */ +const char * const avct_lcb_st_str[] = { + "LCB_IDLE_ST", + "LCB_OPENING_ST", + "LCB_OPEN_ST", + "LCB_CLOSING_ST" +}; + +/* verbose event strings for trace */ +const char * const avct_lcb_evt_str[] = { + "UL_BIND_EVT", + "UL_UNBIND_EVT", + "UL_MSG_EVT", + "INT_CLOSE_EVT", + "LL_OPEN_EVT", + "LL_CLOSE_EVT", + "LL_MSG_EVT", + "LL_CONG_EVT" +}; + +#endif + +/* lcb state machine states */ +enum { + AVCT_LCB_IDLE_ST, + AVCT_LCB_OPENING_ST, + AVCT_LCB_OPEN_ST, + AVCT_LCB_CLOSING_ST +}; + +/* state machine action enumeration list */ +enum { + AVCT_LCB_CHNL_OPEN, + AVCT_LCB_CHNL_DISC, + AVCT_LCB_SEND_MSG, + AVCT_LCB_OPEN_IND, + AVCT_LCB_OPEN_FAIL, + AVCT_LCB_CLOSE_IND, + AVCT_LCB_CLOSE_CFM, + AVCT_LCB_MSG_IND, + AVCT_LCB_CONG_IND, + AVCT_LCB_BIND_CONN, + AVCT_LCB_BIND_FAIL, + AVCT_LCB_UNBIND_DISC, + AVCT_LCB_CHK_DISC, + AVCT_LCB_DISCARD_MSG, + AVCT_LCB_DEALLOC, + AVCT_LCB_FREE_MSG_IND, + AVCT_LCB_NUM_ACTIONS +}; + +#define AVCT_LCB_IGNORE AVCT_LCB_NUM_ACTIONS + +/* type for action functions */ +typedef void (*tAVCT_LCB_ACTION)(tAVCT_LCB *p_ccb, tAVCT_LCB_EVT *p_data); + +/* action function list */ +const tAVCT_LCB_ACTION avct_lcb_action[] = { + avct_lcb_chnl_open, + avct_lcb_chnl_disc, + avct_lcb_send_msg, + avct_lcb_open_ind, + avct_lcb_open_fail, + avct_lcb_close_ind, + avct_lcb_close_cfm, + avct_lcb_msg_ind, + avct_lcb_cong_ind, + avct_lcb_bind_conn, + avct_lcb_bind_fail, + avct_lcb_unbind_disc, + avct_lcb_chk_disc, + avct_lcb_discard_msg, + avct_lcb_dealloc, + avct_lcb_free_msg_ind +}; + +/* state table information */ +#define AVCT_LCB_ACTIONS 2 /* number of actions */ +#define AVCT_LCB_NEXT_STATE 2 /* position of next state */ +#define AVCT_LCB_NUM_COLS 3 /* number of columns in state tables */ + +/* state table for idle state */ +const UINT8 avct_lcb_st_idle[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_CHNL_OPEN, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_IDLE_ST} +}; + +/* state table for opening state */ +const UINT8 avct_lcb_st_opening[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_UNBIND_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_OPEN_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_OPEN_FAIL, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPENING_ST} +}; + +/* state table for open state */ +const UINT8 avct_lcb_st_open[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_BIND_CONN, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_CHK_DISC, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_SEND_MSG, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_CHNL_DISC, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_IND, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_CONG_IND, AVCT_LCB_IGNORE, AVCT_LCB_OPEN_ST} +}; + +/* state table for closing state */ +const UINT8 avct_lcb_st_closing[][AVCT_LCB_NUM_COLS] = { +/* Event Action 1 Action 2 Next state */ +/* UL_BIND_EVT */ {AVCT_LCB_BIND_FAIL, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* UL_UNBIND_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* UL_MSG_EVT */ {AVCT_LCB_DISCARD_MSG, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* INT_CLOSE_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_OPEN_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_CLOSE_EVT */ {AVCT_LCB_CLOSE_CFM, AVCT_LCB_DEALLOC, AVCT_LCB_IDLE_ST}, +/* LL_MSG_EVT */ {AVCT_LCB_FREE_MSG_IND, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST}, +/* LL_CONG_EVT */ {AVCT_LCB_IGNORE, AVCT_LCB_IGNORE, AVCT_LCB_CLOSING_ST} +}; + +/* type for state table */ +typedef const UINT8 (*tAVCT_LCB_ST_TBL)[AVCT_LCB_NUM_COLS]; + +/* state table */ +const tAVCT_LCB_ST_TBL avct_lcb_st_tbl[] = { + avct_lcb_st_idle, + avct_lcb_st_opening, + avct_lcb_st_open, + avct_lcb_st_closing +}; + +/******************************************************************************* +** +** Function avct_lcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_event(tAVCT_LCB *p_lcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT3("LCB lcb=%d event=%s state=%s", p_lcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_lcb->state]); +#else + AVCT_TRACE_EVENT3("LCB lcb=%d event=%d state=%d", p_lcb->allocated, event, p_lcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_lcb->state]; + + /* set next state */ + p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) + { + (*avct_lcb_action[action])(p_lcb, p_data); + } + else + { + break; + } + } +} + +/******************************************************************************* +** +** Function avct_bcb_event +** +** Description State machine event handling function for lcb +** +** +** Returns Nothing. +** +*******************************************************************************/ +#if (AVCT_BROWSE_INCLUDED == TRUE) +void avct_bcb_event(tAVCT_BCB *p_bcb, UINT8 event, tAVCT_LCB_EVT *p_data) +{ + tAVCT_LCB_ST_TBL state_table; + UINT8 action; + int i; + +#if BT_TRACE_VERBOSE == TRUE + AVCT_TRACE_EVENT3("BCB lcb=%d event=%s state=%s", p_bcb->allocated, avct_lcb_evt_str[event], avct_lcb_st_str[p_bcb->state]); +#else + AVCT_TRACE_EVENT3("BCB lcb=%d event=%d state=%d", p_bcb->allocated, event, p_bcb->state); +#endif + + /* look up the state table for the current state */ + state_table = avct_lcb_st_tbl[p_bcb->state]; + + /* set next state */ + p_bcb->state = state_table[event][AVCT_LCB_NEXT_STATE]; + + /* execute action functions */ + for (i = 0; i < AVCT_LCB_ACTIONS; i++) + { + if ((action = state_table[event][i]) != AVCT_LCB_IGNORE) + { + (*avct_bcb_action[action])(p_bcb, p_data); + } + else + { + break; + } + } +} +#endif + +/******************************************************************************* +** +** Function avct_lcb_by_bd +** +** Description This lookup function finds the lcb for a BD address. +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + /* if allocated lcb has matching lcb */ + if (p_lcb->allocated && (!memcmp(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN))) + { + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* if no lcb found */ + p_lcb = NULL; + + AVCT_TRACE_DEBUG6("No lcb for addr %02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_alloc +** +** Description Allocate a link control block. +** +** +** Returns pointer to the lcb, or NULL if none could be allocated. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + if (!p_lcb->allocated) + { + p_lcb->allocated = (UINT8)(i + 1); + memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN); + AVCT_TRACE_DEBUG1("avct_lcb_alloc %d", p_lcb->allocated); + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING0("Out of lcbs"); + } + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_dealloc +** +** Description Deallocate a link control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + BOOLEAN found = FALSE; + int i; + + AVCT_TRACE_DEBUG1("avct_lcb_dealloc %d", p_lcb->allocated); + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + if (p_ccb->p_lcb == p_lcb) + { + AVCT_TRACE_DEBUG1("avct_lcb_dealloc used by ccb: %d", i); + found = TRUE; + break; + } + } + } + + if (!found) + { + AVCT_TRACE_DEBUG0("avct_lcb_dealloc now"); + + /* clear reassembled msg buffer if in use */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + } + memset(p_lcb, 0, sizeof(tAVCT_LCB)); + } +} + +/******************************************************************************* +** +** Function avct_lcb_by_lcid +** +** Description Find the LCB associated with the L2CAP LCID +** +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid) +{ + tAVCT_LCB *p_lcb = &avct_cb.lcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) + { + if (p_lcb->allocated && ((p_lcb->ch_lcid == lcid) || (p_lcb->conflict_lcid == lcid))) + { + break; + } + } + + if (i == AVCT_NUM_LINKS) + { + /* out of lcbs */ + p_lcb = NULL; + AVCT_TRACE_WARNING1("No lcb for lcid %x", lcid); + } + + return p_lcb; +} + +/******************************************************************************* +** +** Function avct_lcb_has_pid +** +** Description See if any ccbs on this lcb have a particular pid. +** +** +** Returns Pointer to CCB if PID found, NULL otherwise. +** +*******************************************************************************/ +tAVCT_CCB *avct_lcb_has_pid(tAVCT_LCB *p_lcb, UINT16 pid) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb->cc.pid == pid)) + { + return p_ccb; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function avct_lcb_last_ccb +** +** Description See if given ccb is only one on the lcb. +** +** +** Returns TRUE if ccb is last, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + AVCT_TRACE_WARNING0("avct_lcb_last_ccb"); + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + AVCT_TRACE_WARNING6("%x: aloc:%d, lcb:0x%x/0x%x, ccb:0x%x/0x%x", + i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last); + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) + { + return FALSE; + } + } + return TRUE; +} + + + diff --git a/stack/avct/avct_lcb_act.c b/stack/avct/avct_lcb_act.c new file mode 100644 index 0000000..8d48c10 --- /dev/null +++ b/stack/avct/avct_lcb_act.c @@ -0,0 +1,690 @@ +/***************************************************************************** +** +** Name: avct_lcb_act.c +** +** Description: This module contains action functions of the link control +** state machine. +** +** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "data_types.h" +#include "bt_target.h" +#include "avct_api.h" +#include "avct_int.h" +#include "gki.h" +#include "btm_api.h" + +/* packet header length lookup table */ +const UINT8 avct_lcb_pkt_type_len[] = { + AVCT_HDR_LEN_SINGLE, + AVCT_HDR_LEN_START, + AVCT_HDR_LEN_CONT, + AVCT_HDR_LEN_END +}; + +/******************************************************************************* +** +** Function avct_lcb_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +static BT_HDR *avct_lcb_msg_asmbl(tAVCT_LCB *p_lcb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_PRS_PKT_TYPE(p, pkt_type); + + /* quick sanity check on length */ + if (p_buf->len < avct_lcb_pkt_type_len[pkt_type]) + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING0("Bad length during reassembly"); + p_ret = NULL; + } + /* single packet */ + else if (pkt_type == AVCT_PKT_TYPE_SINGLE) + { + /* if reassembly in progress drop message and process new single */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + AVCT_TRACE_WARNING0("Got single during reassembly"); + } + p_ret = p_buf; + } + /* start packet */ + else if (pkt_type == AVCT_PKT_TYPE_START) + { + /* if reassembly in progress drop message and process new start */ + if (p_lcb->p_rx_msg != NULL) + { + GKI_freebuf(p_lcb->p_rx_msg); + AVCT_TRACE_WARNING0("Got start during reassembly"); + } + p_lcb->p_rx_msg = p_buf; + + /* copy first header byte over nosp */ + *(p + 1) = *p; + + /* set offset to point to where to copy next */ + p_lcb->p_rx_msg->offset += p_lcb->p_rx_msg->len; + + /* adjust length for packet header */ + p_lcb->p_rx_msg->len -= 1; + + p_ret = NULL; + } + /* continue or end */ + else + { + /* if no reassembly in progress drop message */ + if (p_lcb->p_rx_msg == NULL) + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING1("Pkt type=%d out of order", pkt_type); + p_ret = NULL; + } + else + { + /* get size of buffer holding assembled message */ + buf_len = GKI_get_buf_size(p_lcb->p_rx_msg) - sizeof(BT_HDR); + + /* adjust offset and len of fragment for header byte */ + p_buf->offset += AVCT_HDR_LEN_CONT; + p_buf->len -= AVCT_HDR_LEN_CONT; + + /* verify length */ + if ((p_lcb->p_rx_msg->offset + p_buf->len) > buf_len) + { + /* won't fit; free everything */ + GKI_freebuf(p_lcb->p_rx_msg); + p_lcb->p_rx_msg = NULL; + GKI_freebuf(p_buf); + p_ret = NULL; + AVCT_TRACE_WARNING0("Fragmented message to big!"); + } + else + { + /* copy contents of p_buf to p_rx_msg */ + memcpy((UINT8 *)(p_lcb->p_rx_msg + 1) + p_lcb->p_rx_msg->offset, + (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); + + if (pkt_type == AVCT_PKT_TYPE_END) + { + p_lcb->p_rx_msg->offset -= p_lcb->p_rx_msg->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = p_lcb->p_rx_msg; + p_lcb->p_rx_msg = NULL; + } + else + { + p_lcb->p_rx_msg->offset += p_buf->len; + p_lcb->p_rx_msg->len += p_buf->len; + p_ret = NULL; + } + GKI_freebuf(p_buf); + } + } + } + return p_ret; +} + + +/******************************************************************************* +** +** Function avct_lcb_chnl_open +** +** Description Open L2CAP channel to peer +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 result = AVCT_RESULT_FAIL; + + BTM_SetOutService(p_lcb->peer_addr, BTM_SEC_SERVICE_AVCTP, 0); + /* call l2cap connect req */ + p_lcb->ch_state = AVCT_CH_CONN; + if ((p_lcb->ch_lcid = L2CA_ConnectReq(AVCT_PSM, p_lcb->peer_addr)) == 0) + { + /* if connect req failed, send ourselves close event */ + avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } +} + +/******************************************************************************* +** +** Function avct_lcb_unbind_disc +** +** Description Deallocate ccb and call callback with disconnect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + avct_ccb_dealloc(p_data->p_ccb, AVCT_DISCONNECT_CFM_EVT, 0, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_open_ind +** +** Description Handle an LL_OPEN event. For each allocated ccb already +** bound to this lcb, send a connect event. For each +** unbound ccb with a new PID, bind that ccb to this lcb and +** send a connect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + BOOLEAN bind = FALSE; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + /* if bound to this lcb send connect confirm event */ + if (p_ccb->p_lcb == p_lcb) + { + bind = TRUE; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_CFM_EVT, + 0, p_lcb->peer_addr); + } + /* if unbound acceptor and lcb doesn't already have a ccb for this PID */ + else if ((p_ccb->p_lcb == NULL) && (p_ccb->cc.role == AVCT_ACP) && + (avct_lcb_has_pid(p_lcb, p_ccb->cc.pid) == NULL)) + { + /* bind ccb to lcb and send connect ind event */ + bind = TRUE; + p_ccb->p_lcb = p_lcb; + L2CA_SetTxPriority(p_lcb->ch_lcid, L2CAP_CHNL_PRIORITY_HIGH); + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_CONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } + + /* if no ccbs bound to this lcb, disconnect */ + if (bind == FALSE) + { + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_open_fail +** +** Description L2CAP channel open attempt failed. Deallocate any ccbs +** on this lcb and send connect confirm event with failure. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + avct_ccb_dealloc(p_ccb, AVCT_CONNECT_CFM_EVT, + p_data->result, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_ind +** +** Description L2CAP channel closed by peer. Deallocate any initiator +** ccbs on this lcb and send disconnect ind event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + if (p_ccb->cc.role == AVCT_INT) + { + avct_ccb_dealloc(p_ccb, AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + else + { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), AVCT_DISCONNECT_IND_EVT, + 0, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_close_cfm +** +** Description L2CAP channel closed by us. Deallocate any initiator +** ccbs on this lcb and send disconnect ind or cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + /* if this ccb initiated close send disconnect cfm otherwise ind */ + if (p_ccb->ch_close) + { + p_ccb->ch_close = FALSE; + event = AVCT_DISCONNECT_CFM_EVT; + } + else + { + event = AVCT_DISCONNECT_IND_EVT; + } + + if (p_ccb->cc.role == AVCT_INT) + { + avct_ccb_dealloc(p_ccb, event, p_data->result, p_lcb->peer_addr); + } + else + { + p_ccb->p_lcb = NULL; + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, + p_data->result, p_lcb->peer_addr); + } + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_bind_conn +** +** Description Bind ccb to lcb and send connect cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + p_data->p_ccb->p_lcb = p_lcb; + (*p_data->p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_data->p_ccb), + AVCT_CONNECT_CFM_EVT, 0, p_lcb->peer_addr); +} + +/******************************************************************************* +** +** Function avct_lcb_chk_disc +** +** Description A ccb wants to close; if it is the last ccb on this lcb, +** close channel. Otherwise just deallocate and call +** callback. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("avct_lcb_chk_disc"); +#if (AVCT_BROWSE_INCLUDED == TRUE) + avct_close_bcb(p_lcb, p_data); +#endif + if (avct_lcb_last_ccb(p_lcb, p_data->p_ccb)) + { + AVCT_TRACE_WARNING0("closing"); + p_data->p_ccb->ch_close = TRUE; + avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } + else + { + AVCT_TRACE_WARNING0("dealloc ccb"); + avct_lcb_unbind_disc(p_lcb, p_data); + } +} + +/******************************************************************************* +** +** Function avct_lcb_chnl_disc +** +** Description Disconnect L2CAP channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + L2CA_DisconnectReq(p_lcb->ch_lcid); +} + +/******************************************************************************* +** +** Function avct_lcb_bind_fail +** +** Description Deallocate ccb and call callback with connect event +** with failure result. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL); +} + +/******************************************************************************* +** +** Function avct_lcb_cong_ind +** +** Description Handle congestion indication from L2CAP. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + UINT8 event; + BT_HDR *p_buf; + + /* set event */ + event = (p_data->cong) ? AVCT_CONG_IND_EVT : AVCT_UNCONG_IND_EVT; + p_lcb->cong = p_data->cong; + if (p_lcb->cong == FALSE && GKI_getfirst(&p_lcb->tx_q)) + { + while ( !p_lcb->cong && (p_buf = (BT_HDR *)GKI_dequeue(&p_lcb->tx_q)) != NULL) + { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_lcb->cong = TRUE; + } + } + } + + /* send event to all ccbs on this lcb */ + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) + { + (*p_ccb->cc.p_ctrl_cback)(avct_ccb_to_idx(p_ccb), event, 0, p_lcb->peer_addr); + } + } +} + +/******************************************************************************* +** +** Function avct_lcb_discard_msg +** +** Description Discard a message sent in from the API. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_WARNING0("Dropping msg"); + + GKI_freebuf(p_data->ul_msg.p_buf); +} + +/******************************************************************************* +** +** Function avct_lcb_send_msg +** +** Description Build and send an AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 hdr_len; + BT_HDR *p_buf; + UINT8 *p; + UINT8 nosp = 0; /* number of subsequent packets */ + UINT16 temp; + UINT16 buf_size = p_lcb->peer_mtu + L2CAP_MIN_OFFSET + BT_HDR_SIZE; + + + /* store msg len */ + curr_msg_len = p_data->ul_msg.p_buf->len; + + /* initialize packet type and other stuff */ + if (curr_msg_len <= (p_lcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) + { + pkt_type = AVCT_PKT_TYPE_SINGLE; + } + else + { + pkt_type = AVCT_PKT_TYPE_START; + temp = (curr_msg_len + AVCT_HDR_LEN_START - p_lcb->peer_mtu); + nosp = temp / (p_lcb->peer_mtu - 1) + 1; + if ( (temp % (p_lcb->peer_mtu - 1)) != 0) + nosp++; + } + + /* while we haven't sent all packets */ + while (curr_msg_len != 0) + { + /* set header len */ + hdr_len = avct_lcb_pkt_type_len[pkt_type]; + + /* if remaining msg must be fragmented */ + if (p_data->ul_msg.p_buf->len > (p_lcb->peer_mtu - hdr_len)) + { + /* get a new buffer for fragment we are sending */ + if ((p_buf = (BT_HDR *) GKI_getbuf(buf_size)) == NULL) + { + /* whoops; free original msg buf and bail */ + AVCT_TRACE_ERROR0 ("avct_lcb_send_msg cannot alloc buffer!!"); + GKI_freebuf(p_data->ul_msg.p_buf); + break; + } + + /* copy portion of data from current message to new buffer */ + p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; + p_buf->len = p_lcb->peer_mtu - hdr_len; + + memcpy((UINT8 *)(p_buf + 1) + p_buf->offset, + (UINT8 *)(p_data->ul_msg.p_buf + 1) + p_data->ul_msg.p_buf->offset, p_buf->len); + + p_data->ul_msg.p_buf->offset += p_buf->len; + p_data->ul_msg.p_buf->len -= p_buf->len; + } + else + { + p_buf = p_data->ul_msg.p_buf; + } + + curr_msg_len -= p_buf->len; + + /* set up to build header */ + p_buf->len += hdr_len; + p_buf->offset -= hdr_len; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* build header */ + AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr); + if (pkt_type == AVCT_PKT_TYPE_START) + { + UINT8_TO_STREAM(p, nosp); + } + if ((pkt_type == AVCT_PKT_TYPE_START) || (pkt_type == AVCT_PKT_TYPE_SINGLE)) + { + UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid); + } + + if (p_lcb->cong == TRUE) + { + GKI_enqueue (&p_lcb->tx_q, p_buf); + } + + /* send message to L2CAP */ + else + { + if (L2CA_DataWrite(p_lcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_lcb->cong = TRUE; + } + } + + /* update pkt type for next packet */ + if (curr_msg_len > (p_lcb->peer_mtu - AVCT_HDR_LEN_END)) + { + pkt_type = AVCT_PKT_TYPE_CONT; + } + else + { + pkt_type = AVCT_PKT_TYPE_END; + } + } + AVCT_TRACE_DEBUG1 ("avct_lcb_send_msg tx_q_count:%d", p_lcb->tx_q.count); + return; +} + +/******************************************************************************* +** +** Function avct_lcb_free_msg_ind +** +** Description Discard an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + if (p_data) + GKI_freebuf(p_data->p_buf); + return; +} + +/******************************************************************************* +** +** Function avct_lcb_msg_ind +** +** Description Handle an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + UINT8 *p; + UINT8 label, type, cr_ipid; + UINT16 pid; + tAVCT_CCB *p_ccb; + BT_HDR *p_buf; + + /* this p_buf is to be reported through p_msg_cback. The layer_specific + * needs to be set properly to indicate that it is received through + * control channel */ + p_data->p_buf->layer_specific = AVCT_DATA_CTRL; + + /* reassemble message; if no message available (we received a fragment) return */ + if ((p_data->p_buf = avct_lcb_msg_asmbl(p_lcb, p_data->p_buf)) == NULL) + { + return; + } + + p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset; + + /* parse header byte */ + AVCT_PRS_HDR(p, label, type, cr_ipid); + + /* check for invalid cr_ipid */ + if (cr_ipid == AVCT_CR_IPID_INVALID) + { + AVCT_TRACE_WARNING1("Invalid cr_ipid", cr_ipid); + GKI_freebuf(p_data->p_buf); + return; + } + + /* parse and lookup PID */ + BE_STREAM_TO_UINT16(pid, p); + if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL) + { + /* PID found; send msg up, adjust bt hdr and call msg callback */ + p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE; + p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE; + (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf); + } + else + { + /* PID not found; drop message */ + AVCT_TRACE_WARNING1("No ccb for PID=%x", pid); + GKI_freebuf(p_data->p_buf); + + /* if command send reject */ + if (cr_ipid == AVCT_CMD) + { + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVCT_CMD_POOL_ID)) != NULL) + { + p_buf->len = AVCT_HDR_LEN_SINGLE; + p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ); + UINT16_TO_BE_STREAM(p, pid); + L2CA_DataWrite(p_lcb->ch_lcid, p_buf); + } + } + } +} |