diff options
Diffstat (limited to 'stack/mcap/mca_main.c')
-rw-r--r-- | stack/mcap/mca_main.c | 630 |
1 files changed, 630 insertions, 0 deletions
diff --git a/stack/mcap/mca_main.c b/stack/mcap/mca_main.c new file mode 100644 index 0000000..bc6118c --- /dev/null +++ b/stack/mcap/mca_main.c @@ -0,0 +1,630 @@ +/***************************************************************************** +** +** Name: mca_main.c +** +** Description: This is the implementation file for the MCAP +** Main Control Block and Utility functions. +** +** Copyright (c) 2009-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "gki.h" +#include "mca_api.h" +#include "mca_defs.h" +#include "mca_int.h" +#include "wcassert.h" +#include "l2c_api.h" + +/* Main Control block for MCA */ +#if MCA_DYNAMIC_MEMORY == FALSE +tMCA_CB mca_cb; +#endif + +/***************************************************************************** +** constants +*****************************************************************************/ + +/* table of standard opcode message size */ +const UINT8 mca_std_msg_len[MCA_NUM_STANDARD_OPCODE] = { + 4, /* MCA_OP_ERROR_RSP */ + 5, /* MCA_OP_MDL_CREATE_REQ */ + 5, /* MCA_OP_MDL_CREATE_RSP */ + 3, /* MCA_OP_MDL_RECONNECT_REQ */ + 4, /* MCA_OP_MDL_RECONNECT_RSP */ + 3, /* MCA_OP_MDL_ABORT_REQ */ + 4, /* MCA_OP_MDL_ABORT_RSP */ + 3, /* MCA_OP_MDL_DELETE_REQ */ + 4 /* MCA_OP_MDL_DELETE_RSP */ +}; + + +/******************************************************************************* +** +** Function mca_handle_by_cpsm +** +** Description This function returns the handle for the given control +** channel PSM. 0, if not found. +** +** Returns the MCA handle. +** +*******************************************************************************/ +tMCA_HANDLE mca_handle_by_cpsm(UINT16 psm) +{ + int i; + tMCA_HANDLE handle = 0; + tMCA_RCB *p_rcb = &mca_cb.rcb[0]; + + for (i=0; i<MCA_NUM_REGS; i++, p_rcb++) + { + if (p_rcb->p_cback && p_rcb->reg.ctrl_psm == psm) + { + handle = i+1; + break; + } + } + return handle; +} + +/******************************************************************************* +** +** Function mca_handle_by_dpsm +** +** Description This function returns the handle for the given data +** channel PSM. 0, if not found. +** +** Returns the MCA handle. +** +*******************************************************************************/ +tMCA_HANDLE mca_handle_by_dpsm(UINT16 psm) +{ + int i; + tMCA_HANDLE handle = 0; + tMCA_RCB *p_rcb = &mca_cb.rcb[0]; + + for (i=0; i<MCA_NUM_REGS; i++, p_rcb++) + { + if (p_rcb->p_cback && p_rcb->reg.data_psm == psm) + { + handle = i+1; + break; + } + } + return handle; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_calloc +** +** Description This function allocates a transport table for the given +** control channel. +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL * mca_tc_tbl_calloc(tMCA_CCB *p_ccb) +{ + tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl; + int i; + + /* find next free entry in tc table */ + for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++) + { + if (p_tbl->state == MCA_TC_ST_UNUSED) + { + break; + } + } + + /* sanity check */ + WC_ASSERT(i != MCA_NUM_TC_TBL); + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags= 0; + p_tbl->cb_idx = mca_ccb_to_hdl(p_ccb); + p_tbl->tcid = MCA_CTRL_TCID; + p_tbl->my_mtu = MCA_CTRL_MTU; + p_tbl->state = MCA_TC_ST_IDLE; + p_tbl->lcid = p_ccb->lcid; + mca_cb.tc.lcid_tbl[p_ccb->lcid - L2CAP_BASE_APPL_CID] = i; + MCA_TRACE_DEBUG1("mca_tc_tbl_calloc cb_idx: %d", p_tbl->cb_idx); + + return p_tbl; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_dalloc +** +** Description This function allocates a transport table for the given +** data channel. +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL * mca_tc_tbl_dalloc(tMCA_DCB *p_dcb) +{ + tMCA_TC_TBL *p_tbl = mca_cb.tc.tc_tbl; + int i; + + /* find next free entry in tc table */ + for (i = 0; i < MCA_NUM_TC_TBL; i++, p_tbl++) + { + if (p_tbl->state == MCA_TC_ST_UNUSED) + { + break; + } + } + + /* sanity check */ + WC_ASSERT(i != MCA_NUM_TC_TBL); + + /* initialize entry */ + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + p_tbl->cfg_flags= 0; + p_tbl->cb_idx = mca_dcb_to_hdl(p_dcb); + p_tbl->tcid = p_dcb->p_cs->type + 1; + p_tbl->my_mtu = p_dcb->p_chnl_cfg->data_mtu; + p_tbl->state = MCA_TC_ST_IDLE; + p_tbl->lcid = p_dcb->lcid; + mca_cb.tc.lcid_tbl[p_dcb->lcid - L2CAP_BASE_APPL_CID] = i; + MCA_TRACE_DEBUG2("mca_tc_tbl_dalloc tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + + return p_tbl; +} + +/******************************************************************************* +** +** Function mca_tc_tbl_by_lcid +** +** Description Find the transport channel table entry by LCID. +** +** +** Returns The tranport table. +** +*******************************************************************************/ +tMCA_TC_TBL *mca_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + if (lcid) + { + idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < MCA_NUM_TC_TBL) + { + return &mca_cb.tc.tc_tbl[idx]; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function mca_free_tc_tbl_by_lcid +** +** Description Find the transport table entry by LCID +** and free the tc_tbl +** +** Returns void. +** +*******************************************************************************/ +void mca_free_tc_tbl_by_lcid(UINT16 lcid) +{ + UINT8 idx; + + if (lcid) + { + idx = mca_cb.tc.lcid_tbl[lcid - L2CAP_BASE_APPL_CID]; + + if (idx < MCA_NUM_TC_TBL) + { + mca_cb.tc.tc_tbl[idx].state = MCA_TC_ST_UNUSED; + } + } +} + + +/******************************************************************************* +** +** Function mca_set_cfg_by_tbl +** +** Description Set the L2CAP configuration information +** +** Returns none. +** +*******************************************************************************/ +void mca_set_cfg_by_tbl(tL2CAP_CFG_INFO *p_cfg, tMCA_TC_TBL *p_tbl) +{ + tMCA_DCB *p_dcb; + const tL2CAP_FCR_OPTS *p_opt; + tMCA_FCS_OPT fcs = MCA_FCS_NONE; + + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_opt = &mca_l2c_fcr_opts_def; + } + else + { + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + p_opt = &p_dcb->p_chnl_cfg->fcr_opt; + fcs = p_dcb->p_chnl_cfg->fcs; + } + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->mtu_present = TRUE; + p_cfg->mtu = p_tbl->my_mtu; + p_cfg->fcr_present = TRUE; + memcpy(&p_cfg->fcr, p_opt, sizeof (tL2CAP_FCR_OPTS)); + if (fcs & MCA_FCS_PRESNT_MASK) + { + p_cfg->fcs_present = TRUE; + p_cfg->fcs = (fcs & MCA_FCS_USE_MASK); + } +} + +/******************************************************************************* +** +** Function mca_tc_close_ind +** +** Description This function is called by the L2CAP interface when the +** L2CAP channel is closed. It looks up the CCB or DCB for +** the channel and sends it a close event. The reason +** parameter is the same value passed by the L2CAP +** callback function. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + tMCA_CLOSE close; + + close.param = MCA_ACP; + close.reason = reason; + close.lcid = p_tbl->lcid; + + MCA_TRACE_DEBUG3("mca_tc_close_ind tcid: %d, cb_idx:%d, old: %d", + p_tbl->tcid, p_tbl->cb_idx, p_tbl->state); + + /* Check if the transport channel is in use */ + if (p_tbl->state == MCA_TC_ST_UNUSED) + return; + + /* clear mca_tc_tbl entry */ + if (p_tbl->cfg_flags&MCA_L2C_CFG_DISCN_INT) + close.param = MCA_INT; + p_tbl->cfg_flags = 0; + p_tbl->peer_mtu = L2CAP_DEFAULT_MTU; + + /* if control channel, notify ccb that channel close */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + mca_ccb_event(p_ccb, MCA_CCB_LL_CLOSE_EVT, (tMCA_CCB_EVT *)&close); + } + /* notify dcb that channel close */ + else + { + /* look up dcb */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close); + } + } + p_tbl->state = MCA_TC_ST_UNUSED; +} + +/******************************************************************************* +** +** Function mca_tc_open_ind +** +** Description This function is called by the L2CAP interface when +** the L2CAP channel is opened. It looks up the CCB or DCB +** for the channel and sends it an open event. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_open_ind(tMCA_TC_TBL *p_tbl) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + tMCA_OPEN open; + + MCA_TRACE_DEBUG2("mca_tc_open_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + p_tbl->state = MCA_TC_ST_OPEN; + + open.peer_mtu = p_tbl->peer_mtu; + open.lcid = p_tbl->lcid; + /* use param to indicate the role of connection. + * MCA_ACP, if ACP */ + open.param = MCA_INT; + if (p_tbl->cfg_flags & MCA_L2C_CFG_CONN_ACP) + { + open.param = MCA_ACP; + } + + /* if control channel, notify ccb that channel open */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + + mca_ccb_event(p_ccb, MCA_CCB_LL_OPEN_EVT, (tMCA_CCB_EVT *)&open); + } + /* must be data channel, notify dcb that channel open */ + else + { + /* look up dcb */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + + /* put lcid in event data */ + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_OPEN_EVT, (tMCA_DCB_EVT *) &open); + } + } +} + + +/******************************************************************************* +** +** Function mca_tc_cong_ind +** +** Description This function is called by the L2CAP interface layer when +** L2CAP calls the congestion callback. It looks up the CCB +** or DCB for the channel and sends it a congestion event. +** The is_congested parameter is the same value passed by +** the L2CAP callback function. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + + MCA_TRACE_DEBUG2("mca_tc_cong_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + /* if control channel, notify ccb of congestion */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + mca_ccb_event(p_ccb, MCA_CCB_LL_CONG_EVT, (tMCA_CCB_EVT *) &is_congested); + } + /* notify dcb that channel open */ + else + { + /* look up dcb by cb_idx */ + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_CONG_EVT, (tMCA_DCB_EVT *) &is_congested); + } + } +} + + +/******************************************************************************* +** +** Function mca_tc_data_ind +** +** Description This function is called by the L2CAP interface layer when +** incoming data is received from L2CAP. It looks up the CCB +** or DCB for the channel and routes the data accordingly. +** +** Returns Nothing. +** +*******************************************************************************/ +void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf) +{ + tMCA_CCB *p_ccb; + tMCA_DCB *p_dcb; + UINT8 event = MCA_CCB_MSG_RSP_EVT; + UINT8 *p; + UINT8 rej_rsp_code = MCA_RSP_SUCCESS; + + MCA_TRACE_DEBUG2("mca_tc_data_ind tcid: %d, cb_idx: %d", p_tbl->tcid, p_tbl->cb_idx); + + + /* if control channel, handle control message */ + if (p_tbl->tcid == MCA_CTRL_TCID) + { + p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx); + if (p_ccb) + { + p = (UINT8*)(p_buf+1) + p_buf->offset; + /* all the request opcode has bit 0 set. response code has bit 0 clear */ + if ((*p) & 0x01) + event = MCA_CCB_MSG_REQ_EVT; + + if (*p < MCA_NUM_STANDARD_OPCODE) + { + if (p_buf->len != mca_std_msg_len[*p]) + { + MCA_TRACE_ERROR3 ("opcode: %d required len:%d, got len:%d", *p, mca_std_msg_len[*p], p_buf->len); + rej_rsp_code = MCA_RSP_BAD_PARAM; + } + } + else if ((*p >= MCA_FIRST_SYNC_OP) && (*p <= MCA_LAST_SYNC_OP)) + { + MCA_TRACE_ERROR2 ("unsupported SYNC opcode: %d len:%d", *p, p_buf->len); + /* reject unsupported request */ + rej_rsp_code = MCA_RSP_NO_SUPPORT; + } + else + { + MCA_TRACE_ERROR2 ("bad opcode: %d len:%d", *p, p_buf->len); + /* reject unsupported request */ + rej_rsp_code = MCA_RSP_BAD_OPCODE; + } + + p_buf->layer_specific = rej_rsp_code; + /* forward the request/response to state machine */ + mca_ccb_event(p_ccb, event, (tMCA_CCB_EVT *) p_buf); + } /* got a valid ccb */ + else + GKI_freebuf(p_buf); + } + /* else send event to dcb */ + else + { + p_dcb = mca_dcb_by_hdl(p_tbl->cb_idx); + if (p_dcb != NULL) + { + mca_dcb_event(p_dcb, MCA_DCB_TC_DATA_EVT, (tMCA_DCB_EVT *) p_buf); + } + else + GKI_freebuf(p_buf); + } +} + +/******************************************************************************* +** +** Function mca_rcb_alloc +** +** Description This function allocates a registration control block. +** If no free RCB is available, it returns NULL. +** +** Returns tMCA_RCB * +** +*******************************************************************************/ +tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg) +{ + int i; + tMCA_RCB *p_rcb = NULL; + + for (i=0; i<MCA_NUM_REGS; i++) + { + if (mca_cb.rcb[i].p_cback == NULL) + { + p_rcb = &mca_cb.rcb[i]; + memcpy (&p_rcb->reg, p_reg, sizeof(tMCA_REG)); + break; + } + } + return p_rcb; +} + +/******************************************************************************* +** +** Function mca_rcb_dealloc +** +** Description This function deallocates the RCB with the given handle. +** +** Returns void. +** +*******************************************************************************/ +void mca_rcb_dealloc(tMCA_HANDLE handle) +{ + int i; + BOOLEAN done = TRUE; + tMCA_RCB *p_rcb; + tMCA_CCB *p_ccb; + + if (handle && (handle<=MCA_NUM_REGS)) + { + handle--; + p_rcb = &mca_cb.rcb[handle]; + if (p_rcb->p_cback) + { + p_ccb = &mca_cb.ccb[handle*MCA_NUM_LINKS]; + /* check if all associated CCB are disconnected */ + for (i=0; i<MCA_NUM_LINKS; i++, p_ccb++) + { + if (p_ccb->p_rcb) + { + done = FALSE; + mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL); + } + } + + if (done) + { + memset (p_rcb, 0, sizeof(tMCA_RCB)); + } + } + } +} + +/******************************************************************************* +** +** Function mca_rcb_to_handle +** +** Description This function converts a pointer to an RCB to +** a handle (tMCA_HANDLE). It returns the handle. +** +** Returns void. +** +*******************************************************************************/ +tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb) +{ + return(UINT8) (p_rcb - mca_cb.rcb + 1); +} + +/******************************************************************************* +** +** Function mca_rcb_by_handle +** +** Description This function finds the RCB for a handle (tMCA_HANDLE). +** It returns a pointer to the RCB. If no RCB matches the +** handle it returns NULL. +** +** Returns tMCA_RCB * +** +*******************************************************************************/ +tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle) +{ + tMCA_RCB *p_rcb = NULL; + + if (handle && (handle<=MCA_NUM_REGS) && mca_cb.rcb[handle-1].p_cback) + { + p_rcb = &mca_cb.rcb[handle-1]; + } + return p_rcb; +} + +/******************************************************************************* +** +** Function mca_is_valid_dep_id +** +** Description This function checks if the given dep_id is valid. +** +** Returns TRUE, if this is a valid local dep_id +** +*******************************************************************************/ +BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep) +{ + BOOLEAN valid = FALSE; + if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback) + { + valid = TRUE; + } + return valid; +} + +/******************************************************************************* +** +** Function mca_free_buf +** +** Description free memory for specified GKI packet +** +** Returns void +** +*******************************************************************************/ +void mca_free_buf (void **p_buf) +{ + if (p_buf && *p_buf) + { + GKI_freebuf(*p_buf); + *p_buf = NULL; + } +} |