summaryrefslogtreecommitdiffstats
path: root/stack/mcap/mca_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/mcap/mca_main.c')
-rw-r--r--stack/mcap/mca_main.c643
1 files changed, 643 insertions, 0 deletions
diff --git a/stack/mcap/mca_main.c b/stack/mcap/mca_main.c
new file mode 100644
index 0000000..2e05d5e
--- /dev/null
+++ b/stack/mcap/mca_main.c
@@ -0,0 +1,643 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This is the implementation file for the MCAP Main Control Block and
+ * Utility functions.
+ *
+ ******************************************************************************/
+#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;
+ }
+}