summaryrefslogtreecommitdiffstats
path: root/stack/mcap
diff options
context:
space:
mode:
Diffstat (limited to 'stack/mcap')
-rw-r--r--stack/mcap/mca_api.c925
-rw-r--r--stack/mcap/mca_cact.c580
-rw-r--r--stack/mcap/mca_csm.c385
-rw-r--r--stack/mcap/mca_dact.c162
-rw-r--r--stack/mcap/mca_dsm.c346
-rw-r--r--stack/mcap/mca_int.h356
-rw-r--r--stack/mcap/mca_l2c.c589
-rw-r--r--stack/mcap/mca_main.c643
8 files changed, 3986 insertions, 0 deletions
diff --git a/stack/mcap/mca_api.c b/stack/mcap/mca_api.c
new file mode 100644
index 0000000..1792f8c
--- /dev/null
+++ b/stack/mcap/mca_api.c
@@ -0,0 +1,925 @@
+/******************************************************************************
+ *
+ * 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 API implementation file for the Multi-Channel Adaptation
+ * Protocol (MCAP).
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "bt_target.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+
+#include "wcassert.h"
+#include "btu.h"
+
+
+/*******************************************************************************
+**
+** Function mca_process_timeout
+**
+** Description This function is called by BTU when an MCA timer
+** expires.
+**
+** This function is for use internal to the stack only.
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_process_timeout(TIMER_LIST_ENT *p_tle)
+{
+ if(p_tle->event == BTU_TTYPE_MCA_CCB_RSP)
+ {
+ p_tle->event = 0;
+ mca_ccb_event ((tMCA_CCB *) p_tle->param, MCA_CCB_RSP_TOUT_EVT, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function MCA_Init
+**
+** Description Initialize MCAP main control block.
+** This function is called at stack start up.
+**
+** Returns void
+**
+*******************************************************************************/
+void MCA_Init(void)
+{
+ memset(&mca_cb, 0, sizeof(tMCA_CB));
+
+#if defined(MCA_INITIAL_TRACE_LEVEL)
+ mca_cb.trace_level = MCA_INITIAL_TRACE_LEVEL;
+#else
+ mca_cb.trace_level = BT_TRACE_LEVEL_NONE;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function MCA_SetTraceLevel
+**
+** Description This function sets the debug trace level for MCA.
+** If 0xff is passed, the current trace level is returned.
+**
+** Input Parameters:
+** level: The level to set the MCA tracing to:
+** 0xff-returns the current setting.
+** 0-turns off tracing.
+** >= 1-Errors.
+** >= 2-Warnings.
+** >= 3-APIs.
+** >= 4-Events.
+** >= 5-Debug.
+**
+** Returns The new trace level or current trace level if
+** the input parameter is 0xff.
+**
+*******************************************************************************/
+UINT8 MCA_SetTraceLevel (UINT8 level)
+{
+ if (level != 0xFF)
+ mca_cb.trace_level = level;
+
+ return (mca_cb.trace_level);
+}
+
+/*******************************************************************************
+**
+** Function MCA_Register
+**
+** Description This function registers an MCAP implementation.
+** It is assumed that the control channel PSM and data channel
+** PSM are not used by any other instances of the stack.
+** If the given p_reg->ctrl_psm is 0, this handle is INT only.
+**
+** Returns 0, if failed. Otherwise, the MCA handle.
+**
+*******************************************************************************/
+tMCA_HANDLE MCA_Register(tMCA_REG *p_reg, tMCA_CTRL_CBACK *p_cback)
+{
+ tMCA_RCB *p_rcb;
+ tMCA_HANDLE handle = 0;
+ tL2CAP_APPL_INFO l2c_cacp_appl;
+ tL2CAP_APPL_INFO l2c_dacp_appl;
+
+ WC_ASSERT(p_reg != NULL );
+ WC_ASSERT(p_cback != NULL );
+
+ MCA_TRACE_API2 ("MCA_Register: ctrl_psm:0x%x, data_psm:0x%x", p_reg->ctrl_psm, p_reg->data_psm);
+
+ if ( (p_rcb = mca_rcb_alloc (p_reg)) != NULL)
+ {
+ if (p_reg->ctrl_psm)
+ {
+ if (L2C_INVALID_PSM(p_reg->ctrl_psm) || L2C_INVALID_PSM(p_reg->data_psm))
+ {
+ MCA_TRACE_ERROR0 ("INVALID_PSM");
+ return 0;
+ }
+
+ l2c_cacp_appl = *(tL2CAP_APPL_INFO *)&mca_l2c_int_appl;
+ l2c_cacp_appl.pL2CA_ConnectCfm_Cb = NULL;
+ l2c_dacp_appl = *(tL2CAP_APPL_INFO *)&l2c_cacp_appl;
+ l2c_cacp_appl.pL2CA_ConnectInd_Cb = mca_l2c_cconn_ind_cback;
+ l2c_dacp_appl.pL2CA_ConnectInd_Cb = mca_l2c_dconn_ind_cback;
+ if (L2CA_Register(p_reg->ctrl_psm, (tL2CAP_APPL_INFO *) &l2c_cacp_appl) &&
+ L2CA_Register(p_reg->data_psm, (tL2CAP_APPL_INFO *) &l2c_dacp_appl))
+ {
+ /* set security level */
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_CTRL, p_reg->sec_mask,
+ p_reg->ctrl_psm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID);
+
+ /* in theory, we do not need this one for data_psm
+ * If we don't, L2CAP rejects with security block (3),
+ * which is different reject code from what MCAP spec suggests.
+ * we set this one, so mca_l2c_dconn_ind_cback can reject /w no resources (4) */
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_DATA, p_reg->sec_mask,
+ p_reg->data_psm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID);
+ }
+ else
+ {
+ MCA_TRACE_ERROR0 ("Failed to register to L2CAP");
+ return 0;
+ }
+ }
+ else
+ p_rcb->reg.data_psm = 0;
+ handle = mca_rcb_to_handle (p_rcb);
+ p_rcb->p_cback = p_cback;
+ p_rcb->reg.rsp_tout = p_reg->rsp_tout;
+ }
+ return handle;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_Deregister
+**
+** Description This function is called to deregister an MCAP implementation.
+** Before this function can be called, all control and data
+** channels must be removed with MCA_DisconnectReq and MCA_CloseReq.
+**
+** Returns void
+**
+*******************************************************************************/
+void MCA_Deregister(tMCA_HANDLE handle)
+{
+ tMCA_RCB *p_rcb = mca_rcb_by_handle(handle);
+
+ MCA_TRACE_API1 ("MCA_Deregister: %d", handle);
+ if (p_rcb && p_rcb->reg.ctrl_psm)
+ {
+ L2CA_Deregister(p_rcb->reg.ctrl_psm);
+ L2CA_Deregister(p_rcb->reg.data_psm);
+ btm_sec_clr_service_by_psm (p_rcb->reg.ctrl_psm);
+ btm_sec_clr_service_by_psm (p_rcb->reg.data_psm);
+ }
+ mca_rcb_dealloc(handle);
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_CreateDep
+**
+** Description Create a data endpoint. If the MDEP is created successfully,
+** the MDEP ID is returned in *p_dep. After a data endpoint is
+** created, an application can initiate a connection between this
+** endpoint and an endpoint on a peer device.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_CreateDep(tMCA_HANDLE handle, tMCA_DEP *p_dep, tMCA_CS *p_cs)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ int i;
+ tMCA_RCB *p_rcb = mca_rcb_by_handle(handle);
+ tMCA_CS *p_depcs;
+
+ WC_ASSERT(p_dep != NULL );
+ WC_ASSERT(p_cs != NULL );
+ WC_ASSERT(p_cs->p_data_cback != NULL );
+
+ MCA_TRACE_API1 ("MCA_CreateDep: %d", handle);
+ if (p_rcb)
+ {
+ if (p_cs->max_mdl > MCA_NUM_MDLS)
+ {
+ MCA_TRACE_ERROR1 ("max_mdl: %d is too big", p_cs->max_mdl );
+ result = MCA_BAD_PARAMS;
+ }
+ else
+ {
+ p_depcs = p_rcb->dep;
+ if (p_cs->type == MCA_TDEP_ECHO)
+ {
+ if (p_depcs->p_data_cback)
+ {
+ MCA_TRACE_ERROR0 ("Already has ECHO MDEP");
+ return MCA_NO_RESOURCES;
+ }
+ memcpy (p_depcs, p_cs, sizeof (tMCA_CS));
+ *p_dep = 0;
+ result = MCA_SUCCESS;
+ }
+ else
+ {
+ result = MCA_NO_RESOURCES;
+ /* non-echo MDEP starts from 1 */
+ p_depcs++;
+ for (i=1; i<MCA_NUM_DEPS; i++, p_depcs++)
+ {
+ if (p_depcs->p_data_cback == NULL)
+ {
+ memcpy (p_depcs, p_cs, sizeof (tMCA_CS));
+ /* internally use type as the mdep id */
+ p_depcs->type = i;
+ *p_dep = i;
+ result = MCA_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_DeleteDep
+**
+** Description Delete a data endpoint. This function is called when
+** the implementation is no longer using a data endpoint.
+** If this function is called when the endpoint is connected
+** the connection is closed and the data endpoint
+** is removed.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_DeleteDep(tMCA_HANDLE handle, tMCA_DEP dep)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_RCB *p_rcb = mca_rcb_by_handle(handle);
+ tMCA_DCB *p_dcb;
+ int i, max;
+ tMCA_CS *p_depcs;
+
+ MCA_TRACE_API2 ("MCA_DeleteDep: %d dep:%d", handle, dep);
+ if (p_rcb)
+ {
+ if (dep < MCA_NUM_DEPS && p_rcb->dep[dep].p_data_cback)
+ {
+ result = MCA_SUCCESS;
+ p_rcb->dep[dep].p_data_cback = NULL;
+ p_depcs = &(p_rcb->dep[dep]);
+ i = handle - 1;
+ max = MCA_NUM_MDLS*MCA_NUM_LINKS;
+ p_dcb = &mca_cb.dcb[i*max];
+ /* make sure no MDL exists for this MDEP */
+ for (i=0; i<max; i++, p_dcb++)
+ {
+ if (p_dcb->state && p_dcb->p_cs == p_depcs)
+ {
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ }
+ }
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function MCA_ConnectReq
+**
+** Description This function initiates an MCAP control channel connection
+** to the peer device. When the connection is completed, an
+** MCA_CONNECT_IND_EVT is reported to the application via its
+** control callback function.
+** This control channel is identified by the tMCA_CL.
+** If the connection attempt fails, an MCA_DISCONNECT_IND_EVT is
+** reported. The security mask parameter overrides the outgoing
+** security mask set in MCA_Register().
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_ConnectReq(tMCA_HANDLE handle, BD_ADDR bd_addr,
+ UINT16 ctrl_psm, UINT16 sec_mask)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb;
+ tMCA_TC_TBL *p_tbl;
+
+ MCA_TRACE_API2 ("MCA_ConnectReq: %d psm:0x%x", handle, ctrl_psm);
+ if ((p_ccb = mca_ccb_by_bd(handle, bd_addr)) == NULL)
+ p_ccb = mca_ccb_alloc(handle, bd_addr);
+ else
+ {
+ MCA_TRACE_ERROR0 ("control channel already exists");
+ return MCA_BUSY;
+ }
+
+ if (p_ccb)
+ {
+ p_ccb->ctrl_vpsm = L2CA_Register (ctrl_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl);
+ result = MCA_NO_RESOURCES;
+ if (p_ccb->ctrl_vpsm)
+ {
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_CTRL, sec_mask,
+ p_ccb->ctrl_vpsm, BTM_SEC_PROTO_MCA, MCA_CTRL_TCID);
+ p_ccb->lcid = mca_l2c_open_req(bd_addr, p_ccb->ctrl_vpsm, NULL);
+ if (p_ccb->lcid)
+ {
+ p_tbl = mca_tc_tbl_calloc(p_ccb);
+ if (p_tbl)
+ {
+ p_tbl->state = MCA_TC_ST_CONN;
+ p_ccb->sec_mask = sec_mask;
+ result = MCA_SUCCESS;
+ }
+ }
+ }
+ if (result != MCA_SUCCESS)
+ mca_ccb_dealloc (p_ccb, NULL);
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_DisconnectReq
+**
+** Description This function disconnect an MCAP control channel
+** to the peer device.
+** If associated data channel exists, they are disconnected.
+** When the MCL is disconnected an MCA_DISCONNECT_IND_EVT is
+** reported to the application via its control callback function.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_DisconnectReq(tMCA_CL mcl)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+
+ MCA_TRACE_API1 ("MCA_DisconnectReq: %d ", mcl);
+ if (p_ccb)
+ {
+ result = MCA_SUCCESS;
+ mca_ccb_event (p_ccb, MCA_CCB_API_DISCONNECT_EVT, NULL);
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_CreateMdl
+**
+** Description This function sends a CREATE_MDL request to the peer device.
+** When the response is received, a MCA_CREATE_CFM_EVT is reported
+** with the given MDL ID.
+** If the response is successful, a data channel is open
+** with the given p_chnl_cfg
+** If p_chnl_cfg is NULL, the data channel is not initiated until
+** MCA_DataChnlCfg is called to provide the p_chnl_cfg.
+** When the data channel is open successfully, a MCA_OPEN_CFM_EVT
+** is reported. This data channel is identified as tMCA_DL.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_CreateMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm,
+ UINT16 mdl_id, UINT8 peer_dep_id,
+ UINT8 cfg, const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG *p_evt_data;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_API4 ("MCA_CreateMdl: %d dep=%d mdl_id=%d peer_dep_id=%d", mcl, dep, mdl_id, peer_dep_id);
+ if (p_ccb)
+ {
+ if (p_ccb->p_tx_req || p_ccb->p_rx_msg || p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("pending req");
+ return MCA_BUSY;
+ }
+
+ if ((peer_dep_id > MCA_MAX_MDEP_ID) || (!MCA_IS_VALID_MDL_ID(mdl_id)))
+ {
+ MCA_TRACE_ERROR2 ("bad peer dep id:%d or bad mdl id: %d ", peer_dep_id, mdl_id);
+ return MCA_BAD_PARAMS;
+ }
+
+ if (mca_ccb_uses_mdl_id(p_ccb, mdl_id))
+ {
+ MCA_TRACE_ERROR1 ("mdl id: %d is used in the control link", mdl_id);
+ return MCA_BAD_MDL_ID;
+ }
+
+ p_dcb = mca_dcb_alloc(p_ccb, dep);
+ result = MCA_NO_RESOURCES;
+ if (p_dcb)
+ {
+ /* save the info required by dcb connection */
+ p_dcb->p_chnl_cfg = p_chnl_cfg;
+ p_dcb->mdl_id = mdl_id;
+ p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG));
+ if (p_evt_data)
+ {
+ if (!p_ccb->data_vpsm)
+ p_ccb->data_vpsm = L2CA_Register (data_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl);
+ if (p_ccb->data_vpsm)
+ {
+ p_evt_data->dcb_idx = mca_dcb_to_hdl (p_dcb);
+ p_evt_data->mdep_id = peer_dep_id;
+ p_evt_data->mdl_id = mdl_id;
+ p_evt_data->param = cfg;
+ p_evt_data->op_code = MCA_OP_MDL_CREATE_REQ;
+ p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT;
+ p_evt_data->hdr.layer_specific = FALSE;
+ mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data);
+ return MCA_SUCCESS;
+ }
+ else
+ GKI_freebuf (p_evt_data);
+ }
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_CreateMdlRsp
+**
+** Description This function sends a CREATE_MDL response to the peer device
+** in response to a received MCA_CREATE_IND_EVT.
+** If the rsp_code is successful, a data channel is open
+** with the given p_chnl_cfg
+** When the data channel is open successfully, a MCA_OPEN_IND_EVT
+** is reported. This data channel is identified as tMCA_DL.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_CreateMdlRsp(tMCA_CL mcl, tMCA_DEP dep,
+ UINT16 mdl_id, UINT8 cfg, UINT8 rsp_code,
+ const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG evt_data;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_API5 ("MCA_CreateMdlRsp: %d dep=%d mdl_id=%d cfg=%d rsp_code=%d", mcl, dep, mdl_id, cfg, rsp_code);
+ WC_ASSERT(p_chnl_cfg != NULL );
+ if (p_ccb)
+ {
+ if (p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("congested");
+ return MCA_BUSY;
+ }
+ if (p_ccb->p_rx_msg && (p_ccb->p_rx_msg->mdep_id == dep )
+ && (p_ccb->p_rx_msg->mdl_id == mdl_id) && (p_ccb->p_rx_msg->op_code == MCA_OP_MDL_CREATE_REQ))
+ {
+ result = MCA_SUCCESS;
+ evt_data.dcb_idx = 0;
+ if (rsp_code == MCA_RSP_SUCCESS)
+ {
+ p_dcb = mca_dcb_alloc(p_ccb, dep);
+ if (p_dcb)
+ {
+ evt_data.dcb_idx = mca_dcb_to_hdl(p_dcb);
+ p_dcb->p_chnl_cfg = p_chnl_cfg;
+ p_dcb->mdl_id = mdl_id;
+ }
+ else
+ {
+ rsp_code = MCA_RSP_MDEP_BUSY;
+ result = MCA_NO_RESOURCES;
+ }
+ }
+
+ if (result == MCA_SUCCESS)
+ {
+ evt_data.mdl_id = mdl_id;
+ evt_data.param = cfg;
+ evt_data.rsp_code = rsp_code;
+ evt_data.op_code = MCA_OP_MDL_CREATE_RSP;
+ mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, (tMCA_CCB_EVT *)&evt_data);
+ }
+ }
+ else
+ {
+ MCA_TRACE_ERROR0 ("The given MCL is not expecting a MCA_CreateMdlRsp with the given parameters" );
+ result = MCA_BAD_PARAMS;
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function MCA_CloseReq
+**
+** Description Close a data channel. When the channel is closed, an
+** MCA_CLOSE_CFM_EVT is sent to the application via the
+** control callback function for this handle.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_CloseReq(tMCA_DL mdl)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl);
+
+ MCA_TRACE_API1 ("MCA_CloseReq: %d ", mdl);
+ if (p_dcb)
+ {
+ result = MCA_SUCCESS;
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_ReconnectMdl
+**
+** Description This function sends a RECONNECT_MDL request to the peer device.
+** When the response is received, a MCA_RECONNECT_CFM_EVT is reported.
+** If p_chnl_cfg is NULL, the data channel is not initiated until
+** MCA_DataChnlCfg is called to provide the p_chnl_cfg.
+** If the response is successful, a data channel is open.
+** When the data channel is open successfully, a MCA_OPEN_CFM_EVT
+** is reported.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_ReconnectMdl(tMCA_CL mcl, tMCA_DEP dep, UINT16 data_psm,
+ UINT16 mdl_id, const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG *p_evt_data;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_API1 ("MCA_ReconnectMdl: %d ", mcl);
+ WC_ASSERT(p_chnl_cfg != NULL );
+ if (p_ccb)
+ {
+ if (p_ccb->p_tx_req || p_ccb->p_rx_msg || p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("pending req");
+ return MCA_BUSY;
+ }
+
+ if (!MCA_IS_VALID_MDL_ID(mdl_id))
+ {
+ MCA_TRACE_ERROR1 ("bad mdl id: %d ", mdl_id);
+ return MCA_BAD_PARAMS;
+ }
+
+ if (mca_ccb_uses_mdl_id(p_ccb, mdl_id))
+ {
+ MCA_TRACE_ERROR1 ("mdl id: %d is used in the control link", mdl_id);
+ return MCA_BAD_MDL_ID;
+ }
+
+ p_dcb = mca_dcb_alloc(p_ccb, dep);
+ result = MCA_NO_RESOURCES;
+ if (p_dcb)
+ {
+ p_dcb->p_chnl_cfg = p_chnl_cfg;
+ p_dcb->mdl_id = mdl_id;
+ p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG));
+ if (p_evt_data)
+ {
+ if (!p_ccb->data_vpsm)
+ p_ccb->data_vpsm = L2CA_Register (data_psm, (tL2CAP_APPL_INFO *)&mca_l2c_int_appl);
+ p_evt_data->dcb_idx = mca_dcb_to_hdl(p_dcb);
+ p_evt_data->mdl_id = mdl_id;
+ p_evt_data->op_code = MCA_OP_MDL_RECONNECT_REQ;
+ p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT;
+ mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data);
+ return MCA_SUCCESS;
+ }
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_ReconnectMdlRsp
+**
+** Description This function sends a RECONNECT_MDL response to the peer device
+** in response to a MCA_RECONNECT_IND_EVT event.
+** If the response is successful, a data channel is open.
+** When the data channel is open successfully, a MCA_OPEN_IND_EVT
+** is reported.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_ReconnectMdlRsp(tMCA_CL mcl, tMCA_DEP dep,
+ UINT16 mdl_id, UINT8 rsp_code,
+ const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG evt_data;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_API1 ("MCA_ReconnectMdlRsp: %d ", mcl);
+ WC_ASSERT(p_chnl_cfg != NULL );
+ if (p_ccb)
+ {
+ if (p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("congested");
+ return MCA_BUSY;
+ }
+ if (p_ccb->p_rx_msg && (p_ccb->p_rx_msg->mdl_id == mdl_id) &&
+ (p_ccb->p_rx_msg->op_code == MCA_OP_MDL_RECONNECT_REQ))
+ {
+ result = MCA_SUCCESS;
+ evt_data.dcb_idx = 0;
+ if (rsp_code == MCA_RSP_SUCCESS)
+ {
+ p_dcb = mca_dcb_alloc(p_ccb, dep);
+ if (p_dcb)
+ {
+ evt_data.dcb_idx = mca_dcb_to_hdl(p_dcb);
+ p_dcb->p_chnl_cfg = p_chnl_cfg;
+ p_dcb->mdl_id = mdl_id;
+ }
+ else
+ {
+ MCA_TRACE_ERROR0 ("Out of MDL for this MDEP");
+ rsp_code = MCA_RSP_MDEP_BUSY;
+ result = MCA_NO_RESOURCES;
+ }
+ }
+
+ evt_data.mdl_id = mdl_id;
+ evt_data.rsp_code = rsp_code;
+ evt_data.op_code = MCA_OP_MDL_RECONNECT_RSP;
+ mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, (tMCA_CCB_EVT *)&evt_data);
+ }
+ else
+ {
+ MCA_TRACE_ERROR0 ("The given MCL is not expecting a MCA_ReconnectMdlRsp with the given parameters" );
+ result = MCA_BAD_PARAMS;
+ }
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_DataChnlCfg
+**
+** Description This function initiates a data channel connection toward the
+** connected peer device.
+** When the data channel is open successfully, a MCA_OPEN_CFM_EVT
+** is reported. This data channel is identified as tMCA_DL.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_DataChnlCfg(tMCA_CL mcl, const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_DCB *p_dcb;
+ tMCA_TC_TBL *p_tbl;
+
+ MCA_TRACE_API1 ("MCA_DataChnlCfg: %d ", mcl);
+ WC_ASSERT(p_chnl_cfg != NULL );
+ if (p_ccb)
+ {
+ result = MCA_NO_RESOURCES;
+ if ((p_ccb->p_tx_req == NULL) || (p_ccb->status != MCA_CCB_STAT_PENDING) ||
+ ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) == NULL))
+ {
+ MCA_TRACE_ERROR1 ("The given MCL is not expecting this API:%d", p_ccb->status);
+ return result;
+ }
+
+ p_dcb->p_chnl_cfg = p_chnl_cfg;
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask,
+ p_ccb->data_vpsm, BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx);
+ p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, p_dcb->p_chnl_cfg);
+ if (p_dcb->lcid)
+ {
+ p_tbl = mca_tc_tbl_dalloc(p_dcb);
+ if (p_tbl)
+ {
+ p_tbl->state = MCA_TC_ST_CONN;
+ result = MCA_SUCCESS;
+ }
+ }
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_Abort
+**
+** Description This function sends a ABORT_MDL request to the peer device.
+** When the response is received, a MCA_ABORT_CFM_EVT is reported.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_Abort(tMCA_CL mcl)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG *p_evt_data;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_API1 ("MCA_Abort: %d", mcl);
+ if (p_ccb)
+ {
+ result = MCA_NO_RESOURCES;
+ /* verify that we are waiting for data channel to come up with the given mdl */
+ if ((p_ccb->p_tx_req == NULL) || (p_ccb->status != MCA_CCB_STAT_PENDING) ||
+ ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) == NULL))
+ {
+ MCA_TRACE_ERROR1 ("The given MCL is not expecting this API:%d", p_ccb->status);
+ return result;
+ }
+
+ if (p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("congested");
+ return MCA_BUSY;
+ }
+
+ result = MCA_NO_RESOURCES;
+ p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG));
+ if (p_evt_data)
+ {
+ result = MCA_SUCCESS;
+ p_evt_data->op_code = MCA_OP_MDL_ABORT_REQ;
+ p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT;
+ mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data);
+ }
+
+ }
+ return result;
+}
+
+
+/*******************************************************************************
+**
+** Function MCA_Delete
+**
+** Description This function sends a DELETE_MDL request to the peer device.
+** When the response is received, a MCA_DELETE_CFM_EVT is reported.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_Delete(tMCA_CL mcl, UINT16 mdl_id)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_CCB *p_ccb = mca_ccb_by_hdl(mcl);
+ tMCA_CCB_MSG *p_evt_data;
+
+ MCA_TRACE_API1 ("MCA_Delete: %d ", mcl);
+ if (p_ccb)
+ {
+ if (p_ccb->cong)
+ {
+ MCA_TRACE_ERROR0 ("congested");
+ return MCA_BUSY;
+ }
+ if (!MCA_IS_VALID_MDL_ID(mdl_id) && (mdl_id != MCA_ALL_MDL_ID))
+ {
+ MCA_TRACE_ERROR1 ("bad mdl id: %d ", mdl_id);
+ return MCA_BAD_PARAMS;
+ }
+ p_evt_data = (tMCA_CCB_MSG *)GKI_getbuf (sizeof(tMCA_CCB_MSG));
+ if (p_evt_data)
+ {
+ result = MCA_SUCCESS;
+ p_evt_data->mdl_id = mdl_id;
+ p_evt_data->op_code = MCA_OP_MDL_DELETE_REQ;
+ p_evt_data->hdr.event = MCA_CCB_API_REQ_EVT;
+ mca_ccb_event(p_ccb, MCA_CCB_API_REQ_EVT, (tMCA_CCB_EVT *)p_evt_data);
+ }
+ else
+ result = MCA_NO_RESOURCES;
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function MCA_WriteReq
+**
+** Description Send a data packet to the peer device.
+**
+** The application passes the packet using the BT_HDR structure.
+** The offset field must be equal to or greater than L2CAP_MIN_OFFSET.
+** This allows enough space in the buffer for the L2CAP header.
+**
+** The memory pointed to by p_pkt must be a GKI buffer
+** allocated by the application. This buffer will be freed
+** by the protocol stack; the application must not free
+** this buffer.
+**
+** Returns MCA_SUCCESS if successful, otherwise error.
+**
+*******************************************************************************/
+tMCA_RESULT MCA_WriteReq(tMCA_DL mdl, BT_HDR *p_pkt)
+{
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl);
+ tMCA_DCB_EVT evt_data;
+
+ MCA_TRACE_API1 ("MCA_WriteReq: %d ", mdl);
+ if (p_dcb)
+ {
+ if (p_dcb->cong)
+ {
+ result = MCA_BUSY;
+ }
+ else
+ {
+ evt_data.p_pkt = p_pkt;
+ result = MCA_SUCCESS;
+ mca_dcb_event(p_dcb, MCA_DCB_API_WRITE_EVT, &evt_data);
+ }
+ }
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function MCA_GetL2CapChannel
+**
+** Description Get the L2CAP CID used by the given data channel handle.
+**
+** Returns L2CAP channel ID if successful, otherwise 0.
+**
+*******************************************************************************/
+UINT16 MCA_GetL2CapChannel (tMCA_DL mdl)
+{
+ UINT16 lcid = 0;
+ tMCA_DCB *p_dcb = mca_dcb_by_hdl(mdl);
+
+ MCA_TRACE_API1 ("MCA_GetL2CapChannel: %d ", mdl);
+ if (p_dcb)
+ lcid = p_dcb->lcid;
+ return lcid;
+}
+
diff --git a/stack/mcap/mca_cact.c b/stack/mcap/mca_cact.c
new file mode 100644
index 0000000..85f19f4
--- /dev/null
+++ b/stack/mcap/mca_cact.c
@@ -0,0 +1,580 @@
+/******************************************************************************
+ *
+ * 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 Control Channel Action
+ * Functions.
+ *
+ ******************************************************************************/
+#include <string.h>
+#include "bt_target.h"
+#include "gki.h"
+#include "btm_api.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+
+
+#include "btu.h"
+/*****************************************************************************
+** constants
+*****************************************************************************/
+/*******************************************************************************
+**
+** Function mca_ccb_rsp_tout
+**
+** Description This function processes the response timeout.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_rsp_tout(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+ mca_ccb_report_event(p_ccb, MCA_RSP_TOUT_IND_EVT, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_report_event
+**
+** Description This function reports the given event.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_report_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CTRL *p_data)
+{
+ if (p_ccb && p_ccb->p_rcb && p_ccb->p_rcb->p_cback)
+ (*p_ccb->p_rcb->p_cback)(mca_rcb_to_handle(p_ccb->p_rcb), mca_ccb_to_hdl(p_ccb), event, p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_free_msg
+**
+** Description This function frees the received message.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_free_msg(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ GKI_freebuf (p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_snd_req
+**
+** Description This function builds a request and sends it to the peer.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_snd_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data;
+ BT_HDR *p_pkt;
+ UINT8 *p, *p_start;
+ BOOLEAN is_abort = FALSE;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_DEBUG2 ("mca_ccb_snd_req cong=%d req=%d", p_ccb->cong, p_msg->op_code);
+ /* check for abort request */
+ if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (p_msg->op_code == MCA_OP_MDL_ABORT_REQ))
+ {
+ p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
+ /* the Abort API does not have the associated mdl_id.
+ * Get the mdl_id in dcb to compose the request */
+ p_msg->mdl_id = p_dcb->mdl_id;
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ p_ccb->status = MCA_CCB_STAT_NORM;
+ is_abort = TRUE;
+ }
+
+ /* no pending outgoing messages or it's an abort request for a pending data channel */
+ if ((!p_ccb->p_tx_req) || is_abort)
+ {
+ p_ccb->p_tx_req = p_msg;
+ if (!p_ccb->cong)
+ {
+ p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_pkt)
+ {
+ p_pkt->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
+ *p++ = p_msg->op_code;
+ UINT16_TO_BE_STREAM (p, p_msg->mdl_id);
+ if (p_msg->op_code == MCA_OP_MDL_CREATE_REQ)
+ {
+ *p++ = p_msg->mdep_id;
+ *p++ = p_msg->param;
+ }
+ p_msg->hdr.layer_specific = TRUE; /* mark this message as sent */
+ p_pkt->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_pkt);
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE) p_ccb;
+ btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_MCA_CCB_RSP, p_ccb->p_rcb->reg.rsp_tout);
+ }
+ }
+ /* else the L2CAP channel is congested. keep the message to be sent later */
+ }
+ else
+ {
+ MCA_TRACE_WARNING0 ("dropping api req");
+ GKI_freebuf (p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_snd_rsp
+**
+** Description This function builds a response and sends it to
+** the peer.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_snd_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data;
+ BT_HDR *p_pkt;
+ UINT8 *p, *p_start;
+ BOOLEAN chk_mdl = FALSE;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_DEBUG2 ("mca_ccb_snd_rsp cong=%d req=%d", p_ccb->cong, p_msg->op_code);
+ /* assume that API functions verified the parameters */
+ p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_pkt)
+ {
+ p_pkt->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
+ *p++ = p_msg->op_code;
+ *p++ = p_msg->rsp_code;
+ UINT16_TO_BE_STREAM (p, p_msg->mdl_id);
+ if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP)
+ {
+ *p++ = p_msg->param;
+ chk_mdl = TRUE;
+ }
+ else if (p_msg->op_code == MCA_OP_MDL_RECONNECT_RSP)
+ chk_mdl = TRUE;
+
+ if (chk_mdl && p_msg->rsp_code == MCA_RSP_SUCCESS)
+ {
+ p_dcb = mca_dcb_by_hdl(p_msg->dcb_idx);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask,
+ p_ccb->p_rcb->reg.data_psm, BTM_SEC_PROTO_MCA, p_msg->dcb_idx);
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ /* set p_tx_req to block API_REQ/API_RSP before DL is up */
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ p_ccb->p_tx_req = p_ccb->p_rx_msg;
+ p_ccb->p_rx_msg = NULL;
+ p_ccb->p_tx_req->dcb_idx = p_msg->dcb_idx;
+ }
+ mca_free_buf ((void **)&p_ccb->p_rx_msg);
+ p_pkt->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_pkt);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_do_disconn
+**
+** Description This function closes a control channel.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_do_disconn (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ mca_dcb_close_by_mdl_id (p_ccb, MCA_ALL_MDL_ID);
+ L2CA_DisconnectReq(p_ccb->lcid);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_cong
+**
+** Description This function sets the congestion state for the CCB.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_cong(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ MCA_TRACE_DEBUG2 ("mca_ccb_cong cong=%d/%d", p_ccb->cong, p_data->llcong);
+ p_ccb->cong = p_data->llcong;
+ if (!p_ccb->cong)
+ {
+ /* if there's a held packet, send it now */
+ if (p_ccb->p_tx_req && !p_ccb->p_tx_req->hdr.layer_specific)
+ {
+ p_data = (tMCA_CCB_EVT *)p_ccb->p_tx_req;
+ p_ccb->p_tx_req = NULL;
+ mca_ccb_snd_req (p_ccb, p_data);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_hdl_req
+**
+** Description This function is called when a MCAP request is received from
+** the peer. It calls the application callback function to
+** report the event.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_hdl_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ BT_HDR *p_pkt = &p_data->hdr;
+ BT_HDR *p_buf;
+ UINT8 *p, *p_start;
+ tMCA_DCB *p_dcb;
+ tMCA_CTRL evt_data;
+ tMCA_CCB_MSG *p_rx_msg = NULL;
+ UINT8 reject_code = MCA_RSP_NO_RESOURCE;
+ BOOLEAN send_rsp = FALSE;
+ BOOLEAN check_req = FALSE;
+ UINT8 reject_opcode;
+
+ MCA_TRACE_DEBUG1 ("mca_ccb_hdl_req status:%d", p_ccb->status);
+ p_rx_msg = (tMCA_CCB_MSG *)p_pkt;
+ p = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ evt_data.hdr.op_code = *p++;
+ BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p);
+ reject_opcode = evt_data.hdr.op_code+1;
+
+ MCA_TRACE_DEBUG1 ("received mdl id: %d ", evt_data.hdr.mdl_id);
+ if (p_ccb->status == MCA_CCB_STAT_PENDING)
+ {
+ MCA_TRACE_DEBUG0 ("received req inpending state");
+ /* allow abort in pending state */
+ if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (evt_data.hdr.op_code == MCA_OP_MDL_ABORT_REQ))
+ {
+ reject_code = MCA_RSP_SUCCESS;
+ send_rsp = TRUE;
+ /* clear the pending status */
+ p_ccb->status = MCA_CCB_STAT_NORM;
+ if (p_ccb->p_tx_req && ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx))!= NULL))
+ {
+ mca_dcb_dealloc (p_dcb, NULL);
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ }
+ }
+ else
+ reject_code = MCA_RSP_BAD_OP;
+ }
+ else if (p_ccb->p_rx_msg)
+ {
+ MCA_TRACE_DEBUG0 ("still handling prev req");
+ /* still holding previous message, reject this new one ?? */
+
+ }
+ else if (p_ccb->p_tx_req)
+ {
+ MCA_TRACE_DEBUG1 ("still waiting for a response ctrl_vpsm:0x%x", p_ccb->ctrl_vpsm);
+ /* sent a request; waiting for response */
+ if (p_ccb->ctrl_vpsm == 0)
+ {
+ MCA_TRACE_DEBUG0 ("local is ACP. accept the cmd from INT");
+ /* local is acceptor, need to handle the request */
+ check_req = TRUE;
+ reject_code = MCA_RSP_SUCCESS;
+ /* drop the previous request */
+ if ((p_ccb->p_tx_req->op_code == MCA_OP_MDL_CREATE_REQ) &&
+ ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL))
+ {
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_stop_timer(p_ccb);
+ }
+ else
+ {
+ /* local is initiator, ignore the req */
+ GKI_freebuf (p_pkt);
+ return;
+ }
+ }
+ else if (p_pkt->layer_specific != MCA_RSP_SUCCESS)
+ {
+
+ reject_code = (UINT8)p_pkt->layer_specific;
+ if (((evt_data.hdr.op_code >= MCA_NUM_STANDARD_OPCODE) &&
+ (evt_data.hdr.op_code < MCA_FIRST_SYNC_OP)) ||
+ (evt_data.hdr.op_code > MCA_LAST_SYNC_OP))
+ {
+ /* invalid op code */
+ reject_opcode = MCA_OP_ERROR_RSP;
+ evt_data.hdr.mdl_id = 0;
+ }
+ }
+ else
+ {
+ check_req = TRUE;
+ reject_code = MCA_RSP_SUCCESS;
+ }
+
+ if (check_req)
+ {
+ if (reject_code == MCA_RSP_SUCCESS)
+ {
+ reject_code = MCA_RSP_BAD_MDL;
+ if (MCA_IS_VALID_MDL_ID(evt_data.hdr.mdl_id) ||
+ ((evt_data.hdr.mdl_id == MCA_ALL_MDL_ID) && (evt_data.hdr.op_code == MCA_OP_MDL_DELETE_REQ)))
+ {
+ reject_code = MCA_RSP_SUCCESS;
+ /* mdl_id is valid according to the spec */
+ switch (evt_data.hdr.op_code)
+ {
+ case MCA_OP_MDL_CREATE_REQ:
+ evt_data.create_ind.dep_id = *p++;
+ evt_data.create_ind.cfg = *p++;
+ p_rx_msg->mdep_id = evt_data.create_ind.dep_id;
+ if (!mca_is_valid_dep_id(p_ccb->p_rcb, p_rx_msg->mdep_id))
+ {
+ MCA_TRACE_ERROR0 ("not a valid local mdep id");
+ reject_code = MCA_RSP_BAD_MDEP;
+ }
+ else if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id))
+ {
+ MCA_TRACE_DEBUG0 ("the mdl_id is currently used in the CL(create)");
+ mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
+ }
+ else
+ {
+ /* check if this dep still have MDL available */
+ if (mca_dep_free_mdl(p_ccb, evt_data.create_ind.dep_id) == 0)
+ {
+ MCA_TRACE_ERROR0 ("the mdep is currently using max_mdl");
+ reject_code = MCA_RSP_MDEP_BUSY;
+ }
+ }
+ break;
+
+ case MCA_OP_MDL_RECONNECT_REQ:
+ if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id))
+ {
+ MCA_TRACE_ERROR0 ("the mdl_id is currently used in the CL(reconn)");
+ reject_code = MCA_RSP_MDL_BUSY;
+ }
+ break;
+
+ case MCA_OP_MDL_ABORT_REQ:
+ reject_code = MCA_RSP_BAD_OP;
+ break;
+
+ case MCA_OP_MDL_DELETE_REQ:
+ /* delete the associated mdl */
+ mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
+ send_rsp = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (((reject_code != MCA_RSP_SUCCESS) && (evt_data.hdr.op_code != MCA_OP_SYNC_INFO_IND))
+ || send_rsp)
+ {
+ p_buf = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_buf)
+ {
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_buf + 1) + L2CAP_MIN_OFFSET;
+ *p++ = reject_opcode;
+ *p++ = reject_code;
+ UINT16_TO_BE_STREAM (p, evt_data.hdr.mdl_id);
+ /*
+ if (((*p_start) == MCA_OP_MDL_CREATE_RSP) && (reject_code == MCA_RSP_SUCCESS))
+ {
+ *p++ = evt_data.create_ind.cfg;
+ }
+ */
+
+ p_buf->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_buf);
+ }
+ }
+
+ if (reject_code == MCA_RSP_SUCCESS)
+ {
+ /* use the received GKI buffer to store information to double check response API */
+ p_rx_msg->op_code = evt_data.hdr.op_code;
+ p_rx_msg->mdl_id = evt_data.hdr.mdl_id;
+ p_ccb->p_rx_msg = p_rx_msg;
+ if (send_rsp)
+ {
+ GKI_freebuf (p_pkt);
+ p_ccb->p_rx_msg = NULL;
+ }
+ mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
+ }
+ else
+ GKI_freebuf (p_pkt);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_hdl_rsp
+**
+** Description This function is called when a MCAP response is received from
+** the peer. It calls the application callback function with
+** the results.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_hdl_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ BT_HDR *p_pkt = &p_data->hdr;
+ UINT8 *p;
+ tMCA_CTRL evt_data;
+ BOOLEAN chk_mdl = FALSE;
+ tMCA_DCB *p_dcb;
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_TC_TBL *p_tbl;
+
+ if (p_ccb->p_tx_req)
+ {
+ /* verify that the received response matches the sent request */
+ p = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ evt_data.hdr.op_code = *p++;
+ if ((evt_data.hdr.op_code == 0) ||
+ ((p_ccb->p_tx_req->op_code + 1) == evt_data.hdr.op_code))
+ {
+ evt_data.rsp.rsp_code = *p++;
+ mca_stop_timer(p_ccb);
+ BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p);
+ if (evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP)
+ {
+ evt_data.create_cfm.cfg = *p++;
+ chk_mdl = TRUE;
+ }
+ else if (evt_data.hdr.op_code == MCA_OP_MDL_RECONNECT_RSP)
+ chk_mdl = TRUE;
+
+ if (chk_mdl)
+ {
+ p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
+ if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS)
+ {
+ if (evt_data.hdr.mdl_id != p_dcb->mdl_id)
+ {
+ MCA_TRACE_ERROR2 ("peer's mdl_id=%d != our mdl_id=%d", evt_data.hdr.mdl_id, p_dcb->mdl_id);
+ /* change the response code to be an error */
+ if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS)
+ {
+ evt_data.rsp.rsp_code = MCA_RSP_BAD_MDL;
+ /* send Abort */
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ MCA_Abort(mca_ccb_to_hdl(p_ccb));
+ }
+ }
+ else if (p_dcb->p_chnl_cfg)
+ {
+ /* the data channel configuration is known. Proceed with data channel initiation */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask,
+ p_ccb->data_vpsm, BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx);
+ p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, p_dcb->p_chnl_cfg);
+ if (p_dcb->lcid)
+ {
+ p_tbl = mca_tc_tbl_dalloc(p_dcb);
+ if (p_tbl)
+ {
+ p_tbl->state = MCA_TC_ST_CONN;
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ result = MCA_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ /* mark this MCL as pending and wait for MCA_DataChnlCfg */
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ result = MCA_SUCCESS;
+ }
+ }
+
+ if (result != MCA_SUCCESS && p_dcb)
+ {
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ } /* end of chk_mdl */
+
+ if (p_ccb->status != MCA_CCB_STAT_PENDING)
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
+ }
+ /* else a bad response is received */
+ }
+ else
+ {
+ /* not expecting any response. drop it */
+ MCA_TRACE_WARNING0 ("dropping received rsp (not expecting a response)");
+ }
+ GKI_freebuf (p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_ll_open
+**
+** Description This function is called to report MCA_CONNECT_IND_EVT event.
+** It also clears the congestion flag (ccb.cong).
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_ll_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+ p_ccb->cong = FALSE;
+ evt_data.connect_ind.mtu = p_data->open.peer_mtu;
+ memcpy (evt_data.connect_ind.bd_addr, p_ccb->peer_addr, BD_ADDR_LEN);
+ mca_ccb_report_event (p_ccb, MCA_CONNECT_IND_EVT, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_dl_open
+**
+** Description This function is called when data channel is open.
+** It clears p_tx_req to allow other message exchage on this CL.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_dl_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_free_buf ((void **)&p_ccb->p_rx_msg);
+ p_ccb->status = MCA_CCB_STAT_NORM;
+}
+
diff --git a/stack/mcap/mca_csm.c b/stack/mcap/mca_csm.c
new file mode 100644
index 0000000..af43bef
--- /dev/null
+++ b/stack/mcap/mca_csm.c
@@ -0,0 +1,385 @@
+/******************************************************************************
+ *
+ * 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 Control channel state
+ * machine.
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "bt_target.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+#include "btu.h"
+
+/*****************************************************************************
+** data channel state machine constants and types
+*****************************************************************************/
+enum
+{
+ MCA_CCB_FREE_MSG,
+ MCA_CCB_SND_REQ,
+ MCA_CCB_SND_RSP,
+ MCA_CCB_DO_DISCONN,
+ MCA_CCB_CONG,
+ MCA_CCB_HDL_REQ,
+ MCA_CCB_HDL_RSP,
+ MCA_CCB_LL_OPEN,
+ MCA_CCB_DL_OPEN,
+ MCA_CCB_DEALLOC,
+ MCA_CCB_RSP_TOUT,
+ MCA_CCB_NUM_ACTIONS
+};
+#define MCA_CCB_IGNORE MCA_CCB_NUM_ACTIONS
+
+/* action function list */
+const tMCA_CCB_ACTION mca_ccb_action[] = {
+ mca_ccb_free_msg,
+ mca_ccb_snd_req,
+ mca_ccb_snd_rsp,
+ mca_ccb_do_disconn,
+ mca_ccb_cong,
+ mca_ccb_hdl_req,
+ mca_ccb_hdl_rsp,
+ mca_ccb_ll_open,
+ mca_ccb_dl_open,
+ mca_ccb_dealloc,
+ mca_ccb_rsp_tout,
+};
+
+/* state table information */
+#define MCA_CCB_ACTIONS 1 /* number of actions */
+#define MCA_CCB_ACT_COL 0 /* position of action function */
+#define MCA_CCB_NEXT_STATE 1 /* position of next state */
+#define MCA_CCB_NUM_COLS 2 /* number of columns in state tables */
+
+/* state table for opening state */
+const UINT8 mca_ccb_st_opening[][MCA_CCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST},
+/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_DO_DISCONN, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST},
+/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST},
+/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST},
+/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_OPENING_ST},
+/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST},
+/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_LL_OPEN, MCA_CCB_OPEN_ST},
+/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST},
+/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_CONG, MCA_CCB_OPENING_ST},
+/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPENING_ST}
+};
+
+/* state table for open state */
+const UINT8 mca_ccb_st_open[][MCA_CCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPEN_ST},
+/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_DO_DISCONN, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_SND_REQ, MCA_CCB_OPEN_ST},
+/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_SND_RSP, MCA_CCB_OPEN_ST},
+/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_HDL_REQ, MCA_CCB_OPEN_ST},
+/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_HDL_RSP, MCA_CCB_OPEN_ST},
+/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_DL_OPEN, MCA_CCB_OPEN_ST},
+/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_OPEN_ST},
+/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST},
+/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_CONG, MCA_CCB_OPEN_ST},
+/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_RSP_TOUT, MCA_CCB_OPEN_ST}
+};
+
+/* state table for closing state */
+const UINT8 mca_ccb_st_closing[][MCA_CCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_CCB_API_CONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_API_DISCONNECT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_API_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_API_RSP_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_MSG_REQ_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_MSG_RSP_EVT */ {MCA_CCB_FREE_MSG, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_DL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_LL_OPEN_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_LL_CLOSE_EVT */ {MCA_CCB_DEALLOC, MCA_CCB_NULL_ST},
+/* MCA_CCB_LL_CONG_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST},
+/* MCA_CCB_RSP_TOUT_EVT */ {MCA_CCB_IGNORE, MCA_CCB_CLOSING_ST}
+};
+
+/* type for state table */
+typedef const UINT8 (*tMCA_CCB_ST_TBL)[MCA_CCB_NUM_COLS];
+
+/* state table */
+const tMCA_CCB_ST_TBL mca_ccb_st_tbl[] = {
+ mca_ccb_st_opening,
+ mca_ccb_st_open,
+ mca_ccb_st_closing
+};
+
+#if (BT_TRACE_VERBOSE == TRUE)
+/* verbose event strings for trace */
+static const char * const mca_ccb_evt_str[] = {
+ "API_CONNECT_EVT",
+ "API_DISCONNECT_EVT",
+ "API_REQ_EVT",
+ "API_RSP_EVT",
+ "MSG_REQ_EVT",
+ "MSG_RSP_EVT",
+ "DL_OPEN_EVT",
+ "LL_OPEN_EVT",
+ "LL_CLOSE_EVT",
+ "LL_CONG_EVT",
+ "RSP_TOUT_EVT"
+};
+/* verbose state strings for trace */
+static const char * const mca_ccb_st_str[] = {
+ "NULL_ST",
+ "OPENING_ST",
+ "OPEN_ST",
+ "CLOSING_ST"
+};
+#endif
+
+/*******************************************************************************
+**
+** Function mca_stop_timer
+**
+** Description This function is stop a MCAP timer
+**
+** This function is for use internal to MCAP only.
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_stop_timer(tMCA_CCB *p_ccb)
+{
+ if (p_ccb->timer_entry.event == BTU_TTYPE_MCA_CCB_RSP)
+ {
+ btu_stop_timer(&p_ccb->timer_entry);
+ p_ccb->timer_entry.event = 0;
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_event
+**
+** Description This function is the CCB state machine main function.
+** It uses the state and action function tables to execute
+** action functions.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CCB_ST_TBL state_table;
+ UINT8 action;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ MCA_TRACE_EVENT3("CCB ccb=%d event=%s state=%s", mca_ccb_to_hdl(p_ccb), mca_ccb_evt_str[event], mca_ccb_st_str[p_ccb->state]);
+#else
+ MCA_TRACE_EVENT3("CCB ccb=%d event=%d state=%d", mca_ccb_to_hdl(p_ccb), event, p_ccb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = mca_ccb_st_tbl[p_ccb->state - 1];
+
+ /* set next state */
+ p_ccb->state = state_table[event][MCA_CCB_NEXT_STATE];
+
+ /* execute action functions */
+ if ((action = state_table[event][MCA_CCB_ACT_COL]) != MCA_CCB_IGNORE)
+ {
+ (*mca_ccb_action[action])(p_ccb, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_by_bd
+**
+** Description This function looks up the CCB based on the BD address.
+** It returns a pointer to the CCB.
+** If no CCB is found it returns NULL.
+**
+** Returns void.
+**
+*******************************************************************************/
+tMCA_CCB *mca_ccb_by_bd(tMCA_HANDLE handle, BD_ADDR bd_addr)
+{
+ tMCA_CCB *p_ccb = NULL;
+ tMCA_RCB *p_rcb = mca_rcb_by_handle(handle);
+ tMCA_CCB *p_ccb_tmp;
+ int i;
+
+ if (p_rcb)
+ {
+ i = handle-1;
+ p_ccb_tmp = &mca_cb.ccb[i*MCA_NUM_LINKS];
+ for (i=0; i<MCA_NUM_LINKS; i++, p_ccb_tmp++)
+ {
+ if (p_ccb_tmp->state != MCA_CCB_NULL_ST && memcmp(p_ccb_tmp->peer_addr, bd_addr, BD_ADDR_LEN) == 0)
+ {
+ p_ccb = p_ccb_tmp;
+ break;
+ }
+ }
+ }
+ return p_ccb;
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_alloc
+**
+** Description This function allocates a CCB and copies the BD address to
+** the CCB. It returns a pointer to the CCB. If no CCB can
+** be allocated it returns NULL.
+**
+** Returns void.
+**
+*******************************************************************************/
+tMCA_CCB *mca_ccb_alloc(tMCA_HANDLE handle, BD_ADDR bd_addr)
+{
+ tMCA_CCB *p_ccb = NULL;
+ tMCA_RCB *p_rcb = mca_rcb_by_handle(handle);
+ tMCA_CCB *p_ccb_tmp;
+ int i;
+
+ MCA_TRACE_DEBUG1("mca_ccb_alloc handle:0x%x", handle);
+ if (p_rcb)
+ {
+ i = handle-1;
+ p_ccb_tmp = &mca_cb.ccb[i*MCA_NUM_LINKS];
+ for (i=0; i<MCA_NUM_LINKS; i++, p_ccb_tmp++)
+ {
+ if (p_ccb_tmp->state == MCA_CCB_NULL_ST)
+ {
+ p_ccb_tmp->p_rcb = p_rcb;
+ p_ccb_tmp->state = MCA_CCB_OPENING_ST;
+ p_ccb_tmp->cong = TRUE;
+ memcpy(p_ccb_tmp->peer_addr, bd_addr, BD_ADDR_LEN);
+ p_ccb = p_ccb_tmp;
+ break;
+ }
+ }
+ }
+ return p_ccb;
+}
+
+
+/*******************************************************************************
+**
+** Function mca_ccb_dealloc
+**
+** Description This function deallocates a CCB.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_dealloc(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+
+ MCA_TRACE_DEBUG1("mca_ccb_dealloc ctrl_vpsm:0x%x", p_ccb->ctrl_vpsm);
+ mca_dcb_close_by_mdl_id (p_ccb, MCA_ALL_MDL_ID);
+ if (p_ccb->ctrl_vpsm)
+ {
+ L2CA_Deregister (p_ccb->ctrl_vpsm);
+ }
+ if (p_ccb->data_vpsm)
+ {
+ L2CA_Deregister (p_ccb->data_vpsm);
+ }
+ mca_free_buf ((void **)&p_ccb->p_rx_msg);
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_stop_timer(p_ccb);
+
+ if (p_data)
+ {
+ /* non-NULL -> an action function -> report disconnect event */
+ memcpy (evt_data.disconnect_ind.bd_addr, p_ccb->peer_addr, BD_ADDR_LEN);
+ evt_data.disconnect_ind.reason = p_data->close.reason;
+ mca_ccb_report_event(p_ccb, MCA_DISCONNECT_IND_EVT, &evt_data);
+ }
+ mca_free_tc_tbl_by_lcid (p_ccb->lcid);
+ memset (p_ccb, 0, sizeof (tMCA_CCB));
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_to_hdl
+**
+** Description This function converts a pointer to a CCB to a tMCA_CL
+** and returns the value.
+**
+** Returns void.
+**
+*******************************************************************************/
+tMCA_CL mca_ccb_to_hdl(tMCA_CCB *p_ccb)
+{
+ return (UINT8) (p_ccb - mca_cb.ccb + 1);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_by_hdl
+**
+** Description This function converts an index value to a CCB. It returns
+** a pointer to the CCB. If no valid CCB matches the index it
+** returns NULL.
+**
+** Returns void.
+**
+*******************************************************************************/
+tMCA_CCB *mca_ccb_by_hdl(tMCA_CL mcl)
+{
+ tMCA_CCB * p_ccb = NULL;
+ if (mcl && mcl <= MCA_NUM_CCBS && mca_cb.ccb[mcl-1].state)
+ p_ccb = &mca_cb.ccb[mcl-1];
+ return p_ccb;
+}
+
+
+/*******************************************************************************
+**
+** Function mca_ccb_uses_mdl_id
+**
+** Description This function checkes if a given mdl_id is in use.
+**
+** Returns TRUE, if the given mdl_id is currently used in the MCL.
+**
+*******************************************************************************/
+BOOLEAN mca_ccb_uses_mdl_id(tMCA_CCB *p_ccb, UINT16 mdl_id)
+{
+ BOOLEAN uses = FALSE;
+ tMCA_DCB *p_dcb;
+ int i;
+
+ i = mca_ccb_to_hdl(p_ccb)-1;
+ p_dcb = &mca_cb.dcb[i*MCA_NUM_MDLS];
+ for (i=0; i<MCA_NUM_MDLS; i++, p_dcb++)
+ {
+ if (p_dcb->state != MCA_DCB_NULL_ST && p_dcb->mdl_id == mdl_id)
+ {
+ uses = TRUE;
+ break;
+ }
+ }
+
+ return uses;
+}
diff --git a/stack/mcap/mca_dact.c b/stack/mcap/mca_dact.c
new file mode 100644
index 0000000..353153e
--- /dev/null
+++ b/stack/mcap/mca_dact.c
@@ -0,0 +1,162 @@
+/******************************************************************************
+ *
+ * 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 Data Channel Action
+ * Functions.
+ *
+ ******************************************************************************/
+#include "bt_target.h"
+#include "gki.h"
+#include "mca_api.h"
+#include "mca_int.h"
+
+/*******************************************************************************
+**
+** Function mca_dcb_report_cong
+**
+** Description This function is called to report the congestion flag.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_report_cong (tMCA_DCB *p_dcb)
+{
+ tMCA_CTRL evt_data;
+
+ evt_data.cong_chg.cong = p_dcb->cong;
+ evt_data.cong_chg.mdl = mca_dcb_to_hdl(p_dcb);
+ evt_data.cong_chg.mdl_id = p_dcb->mdl_id;
+ mca_ccb_report_event (p_dcb->p_ccb, MCA_CONG_CHG_EVT, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_tc_open
+**
+** Description This function is called to report MCA_OPEN_IND_EVT or
+** MCA_OPEN_CFM_EVT event.
+** It also clears the congestion flag (dcb.cong).
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_tc_open (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+ tMCA_CCB *p_ccb = p_dcb->p_ccb;
+ UINT8 event = MCA_OPEN_IND_EVT;
+
+ if (p_data->open.param == MCA_INT)
+ event = MCA_OPEN_CFM_EVT;
+ p_dcb->cong = FALSE;
+ evt_data.open_cfm.mtu = p_data->open.peer_mtu;
+ evt_data.open_cfm.mdl_id = p_dcb->mdl_id;
+ evt_data.open_cfm.mdl = mca_dcb_to_hdl(p_dcb);
+ mca_ccb_event (p_ccb, MCA_CCB_DL_OPEN_EVT, NULL);
+ mca_ccb_report_event (p_ccb, event, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_cong
+**
+** Description This function sets the congestion state for the DCB.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_cong (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ p_dcb->cong = p_data->llcong;
+ mca_dcb_report_cong(p_dcb);
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_free_data
+**
+** Description This function frees the received message.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_free_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ GKI_freebuf (p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_do_disconn
+**
+** Description This function closes a data channel.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_do_disconn (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ tMCA_CLOSE close;
+ if ((p_dcb->lcid == 0) || (L2CA_DisconnectReq(p_dcb->lcid) == FALSE))
+ {
+ close.param = MCA_INT;
+ close.reason = L2CAP_DISC_OK;
+ close.lcid = 0;
+ mca_dcb_event(p_dcb, MCA_DCB_TC_CLOSE_EVT, (tMCA_DCB_EVT *) &close);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_snd_data
+**
+** Description This function sends the data from application to the peer device.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_snd_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ UINT8 status;
+
+ /* do not need to check cong, because API already checked the status */
+ status = L2CA_DataWrite (p_dcb->lcid, p_data->p_pkt);
+ if (status == L2CAP_DW_CONGESTED)
+ {
+ p_dcb->cong = TRUE;
+ mca_dcb_report_cong(p_dcb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_hdl_data
+**
+** Description This function reports the received data through the data
+** callback function.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_hdl_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ (*p_dcb->p_cs->p_data_cback) (mca_dcb_to_hdl(p_dcb), (BT_HDR *)p_data);
+}
+
diff --git a/stack/mcap/mca_dsm.c b/stack/mcap/mca_dsm.c
new file mode 100644
index 0000000..255b5d4
--- /dev/null
+++ b/stack/mcap/mca_dsm.c
@@ -0,0 +1,346 @@
+/******************************************************************************
+ *
+ * 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 Data chahnel state machine.
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "bt_target.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+
+/*****************************************************************************
+** data channel state machine constants and types
+*****************************************************************************/
+enum
+{
+ MCA_DCB_TC_OPEN,
+ MCA_DCB_CONG,
+ MCA_DCB_FREE_DATA,
+ MCA_DCB_DEALLOC,
+ MCA_DCB_DO_DISCONN,
+ MCA_DCB_SND_DATA,
+ MCA_DCB_HDL_DATA,
+ MCA_DCB_NUM_ACTIONS
+};
+#define MCA_DCB_IGNORE MCA_DCB_NUM_ACTIONS
+
+/* action function list */
+const tMCA_DCB_ACTION mca_dcb_action[] = {
+ mca_dcb_tc_open,
+ mca_dcb_cong,
+ mca_dcb_free_data,
+ mca_dcb_dealloc,
+ mca_dcb_do_disconn,
+ mca_dcb_snd_data,
+ mca_dcb_hdl_data
+};
+
+/* state table information */
+#define MCA_DCB_ACTIONS 1 /* number of actions */
+#define MCA_DCB_ACT_COL 0 /* position of action function */
+#define MCA_DCB_NEXT_STATE 1 /* position of next state */
+#define MCA_DCB_NUM_COLS 2 /* number of columns in state tables */
+
+/* state table for opening state */
+const UINT8 mca_dcb_st_opening[][MCA_DCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_DO_DISCONN, MCA_DCB_CLOSING_ST},
+/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_OPENING_ST},
+/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_TC_OPEN, MCA_DCB_OPEN_ST},
+/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST},
+/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_CONG, MCA_DCB_OPENING_ST},
+/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_FREE_DATA, MCA_DCB_OPENING_ST}
+};
+
+/* state table for open state */
+const UINT8 mca_dcb_st_open[][MCA_DCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_DO_DISCONN, MCA_DCB_CLOSING_ST},
+/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_SND_DATA, MCA_DCB_OPEN_ST},
+/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_IGNORE, MCA_DCB_OPEN_ST},
+/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST},
+/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_CONG, MCA_DCB_OPEN_ST},
+/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_HDL_DATA, MCA_DCB_OPEN_ST}
+};
+
+/* state table for closing state */
+const UINT8 mca_dcb_st_closing[][MCA_DCB_NUM_COLS] = {
+/* Event Action Next State */
+/* MCA_DCB_API_CLOSE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST},
+/* MCA_DCB_API_WRITE_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST},
+/* MCA_DCB_TC_OPEN_EVT */ {MCA_DCB_TC_OPEN, MCA_DCB_OPEN_ST},
+/* MCA_DCB_TC_CLOSE_EVT */ {MCA_DCB_DEALLOC, MCA_DCB_NULL_ST},
+/* MCA_DCB_TC_CONG_EVT */ {MCA_DCB_IGNORE, MCA_DCB_CLOSING_ST},
+/* MCA_DCB_TC_DATA_EVT */ {MCA_DCB_FREE_DATA, MCA_DCB_CLOSING_ST}
+};
+
+/* type for state table */
+typedef const UINT8 (*tMCA_DCB_ST_TBL)[MCA_DCB_NUM_COLS];
+
+/* state table */
+const tMCA_DCB_ST_TBL mca_dcb_st_tbl[] = {
+ mca_dcb_st_opening,
+ mca_dcb_st_open,
+ mca_dcb_st_closing
+};
+
+#if (BT_TRACE_VERBOSE == TRUE)
+/* verbose event strings for trace */
+const char * const mca_dcb_evt_str[] = {
+ "API_CLOSE_EVT",
+ "API_WRITE_EVT",
+ "TC_OPEN_EVT",
+ "TC_CLOSE_EVT",
+ "TC_CONG_EVT",
+ "TC_DATA_EVT"
+};
+/* verbose state strings for trace */
+const char * const mca_dcb_st_str[] = {
+ "NULL_ST",
+ "OPENING_ST",
+ "OPEN_ST",
+ "CLOSING_ST"
+};
+#endif
+
+/*******************************************************************************
+**
+** Function mca_dcb_event
+**
+** Description This function is the DCB state machine main function.
+** It uses the state and action function tables to execute
+** action functions.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_event(tMCA_DCB *p_dcb, UINT8 event, tMCA_DCB_EVT *p_data)
+{
+ tMCA_DCB_ST_TBL state_table;
+ UINT8 action;
+
+ if (p_dcb == NULL)
+ return;
+#if (BT_TRACE_VERBOSE == TRUE)
+ MCA_TRACE_EVENT3("DCB dcb=%d event=%s state=%s", mca_dcb_to_hdl(p_dcb), mca_dcb_evt_str[event], mca_dcb_st_str[p_dcb->state]);
+#else
+ MCA_TRACE_EVENT3("DCB dcb=%d event=%d state=%d", mca_dcb_to_hdl(p_dcb), event, p_dcb->state);
+#endif
+
+ /* look up the state table for the current state */
+ state_table = mca_dcb_st_tbl[p_dcb->state - 1];
+
+ /* set next state */
+ p_dcb->state = state_table[event][MCA_DCB_NEXT_STATE];
+
+ /* execute action functions */
+ if ((action = state_table[event][MCA_DCB_ACT_COL]) != MCA_DCB_IGNORE)
+ {
+ (*mca_dcb_action[action])(p_dcb, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_alloc
+**
+** Description This function is called to allocate an DCB.
+** It initializes the DCB with the data passed to the function.
+**
+** Returns tMCA_DCB *
+**
+*******************************************************************************/
+tMCA_DCB *mca_dcb_alloc(tMCA_CCB*p_ccb, tMCA_DEP dep)
+{
+ tMCA_DCB *p_dcb = NULL, *p_dcb_tmp;
+ tMCA_RCB *p_rcb = p_ccb->p_rcb;
+ tMCA_CS *p_cs;
+ int i, max;
+
+ if (dep < MCA_NUM_DEPS)
+ {
+ p_cs = &p_rcb->dep[dep];
+ i = mca_ccb_to_hdl(p_ccb)-1;
+ p_dcb_tmp = &mca_cb.dcb[i*MCA_NUM_MDLS];
+ /* make sure p_cs->max_mdl is smaller than MCA_NUM_MDLS at MCA_CreateDep */
+ max = p_cs->max_mdl;
+ for (i=0; i<max; i++, p_dcb_tmp++)
+ {
+ if (p_dcb_tmp->state == MCA_DCB_NULL_ST)
+ {
+ p_dcb_tmp->p_ccb = p_ccb;
+ p_dcb_tmp->state = MCA_DCB_OPENING_ST;
+ p_dcb_tmp->cong = TRUE;
+ p_dcb_tmp->p_cs = p_cs;
+ p_dcb = p_dcb_tmp;
+ break;
+ }
+ }
+ }
+ return p_dcb;
+}
+
+/*******************************************************************************
+**
+** Function mca_dep_free_mdl
+**
+** Description This function is called to check the number of free mdl for
+** the given dep.
+**
+** Returns the number of free mdl for the given dep
+**
+*******************************************************************************/
+UINT8 mca_dep_free_mdl(tMCA_CCB *p_ccb, tMCA_DEP dep)
+{
+ tMCA_DCB *p_dcb;
+ tMCA_RCB *p_rcb = p_ccb->p_rcb;
+ tMCA_CS *p_cs;
+ int i, max;
+ UINT8 count = 0;
+ UINT8 left;
+
+ if (dep < MCA_NUM_DEPS)
+ {
+ p_cs = &p_rcb->dep[dep];
+ i = mca_ccb_to_hdl(p_ccb)-1;
+ p_dcb = &mca_cb.dcb[i * MCA_NUM_MDLS];
+ /* make sure p_cs->max_mdl is smaller than MCA_NUM_MDLS at MCA_CreateDep */
+ max = p_cs->max_mdl;
+ for (i=0; i<max; i++, p_dcb++)
+ {
+ if ((p_dcb->state != MCA_DCB_NULL_ST) && (p_dcb->p_cs == p_cs))
+ {
+ count++;
+ break;
+ }
+ }
+ }
+ else
+ {
+ max = 0;
+ MCA_TRACE_WARNING0("Invalid Dep ID");
+ }
+ left = max - count;
+ return left;
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_dealloc
+**
+** Description This function deallocates an DCB.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_dcb_dealloc(tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data)
+{
+ tMCA_CCB *p_ccb = p_dcb->p_ccb;
+ UINT8 event = MCA_CLOSE_IND_EVT;
+ tMCA_CTRL evt_data;
+
+ MCA_TRACE_DEBUG0("mca_dcb_dealloc");
+ mca_free_buf ((void **)&p_dcb->p_data);
+ if (p_data)
+ {
+ /* non-NULL -> an action function -> report disconnect event */
+ evt_data.close_cfm.mdl = mca_dcb_to_hdl(p_dcb);
+ evt_data.close_cfm.reason = p_data->close.reason;
+ evt_data.close_cfm.mdl_id = p_dcb->mdl_id;
+ if (p_data->close.param == MCA_INT)
+ event = MCA_CLOSE_CFM_EVT;
+ if (p_data->close.lcid)
+ mca_ccb_report_event(p_ccb, event, &evt_data);
+ }
+ mca_free_tc_tbl_by_lcid (p_dcb->lcid);
+ memset (p_dcb, 0, sizeof (tMCA_DCB));
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_to_hdl
+**
+** Description This function converts a pointer to an DCB to a handle (tMCA_DL).
+** It returns the handle.
+**
+** Returns tMCA_DL.
+**
+*******************************************************************************/
+tMCA_DL mca_dcb_to_hdl(tMCA_DCB *p_dcb)
+{
+ return (UINT8) (p_dcb - mca_cb.dcb + 1);
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_by_hdl
+**
+** Description This function finds the DCB for a handle (tMCA_DL).
+** It returns a pointer to the DCB.
+** If no DCB matches the handle it returns NULL.
+**
+** Returns tMCA_DCB *
+**
+*******************************************************************************/
+tMCA_DCB *mca_dcb_by_hdl(tMCA_DL hdl)
+{
+ tMCA_DCB * p_dcb = NULL;
+ if (hdl && hdl <= MCA_NUM_DCBS && mca_cb.dcb[hdl-1].state)
+ p_dcb = &mca_cb.dcb[hdl-1];
+ return p_dcb;
+}
+
+/*******************************************************************************
+**
+** Function mca_dcb_close_by_mdl_id
+**
+** Description This function finds the DCB for a mdl_id and
+** disconnect the mdl
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_dcb_close_by_mdl_id(tMCA_CCB*p_ccb, UINT16 mdl_id)
+{
+ tMCA_DCB *p_dcb;
+ int i;
+
+ MCA_TRACE_DEBUG1("mca_dcb_close_by_mdl_id mdl_id=%d", mdl_id);
+ i = mca_ccb_to_hdl(p_ccb)-1;
+ p_dcb = &mca_cb.dcb[i*MCA_NUM_MDLS];
+ for (i=0; i<MCA_NUM_MDLS; i++, p_dcb++)
+ {
+ if (p_dcb->state)
+ {
+ if (p_dcb->mdl_id == mdl_id)
+ {
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ break;
+ }
+ else if (mdl_id == MCA_ALL_MDL_ID)
+ {
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ }
+ }
+ }
+}
diff --git a/stack/mcap/mca_int.h b/stack/mcap/mca_int.h
new file mode 100644
index 0000000..2563f7f
--- /dev/null
+++ b/stack/mcap/mca_int.h
@@ -0,0 +1,356 @@
+/******************************************************************************
+ *
+ * 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 file contains interfaces which are internal to MCAP.
+ *
+ ******************************************************************************/
+#ifndef MCA_INT_H
+#define MCA_INT_H
+#include "gki.h"
+#include "mca_api.h"
+
+/*****************************************************************************
+** constants
+*****************************************************************************/
+
+/* INT initiates the L2CAP channel */
+#define MCA_ACP 0
+#define MCA_INT 1
+
+/*****************************************************************************
+** Type Definitions
+*****************************************************************************/
+
+/* Header structure for api/received request/response. */
+typedef struct {
+ BT_HDR hdr; /* layer specific information */
+ UINT8 op_code; /* the request/response opcode */
+ UINT8 rsp_code; /* valid only if op_code is a response */
+ UINT16 mdl_id; /* the MDL ID associated with this request/response */
+ UINT8 param; /* other parameter */
+ UINT8 mdep_id; /* the MDEP ID associated with this request/response */
+ /* tMCA_HANDLE rcb_idx; For internal use only */
+ /* tMCA_CL ccb_idx; For internal use only */
+ tMCA_DL dcb_idx; /* For internal use only */
+} tMCA_CCB_MSG;
+
+/* This data structure is associated with the AVDT_OPEN_IND_EVT and AVDT_OPEN_CFM_EVT. */
+typedef struct {
+ BT_HDR hdr; /* Event header */
+ UINT16 peer_mtu; /* Transport channel L2CAP MTU of the peer */
+ UINT16 lcid; /* L2CAP LCID */
+ UINT8 param;
+} tMCA_OPEN;
+
+typedef struct {
+ UINT16 reason; /* disconnect reason from L2CAP */
+ UINT8 param; /* MCA_INT or MCA_ACP */
+ UINT16 lcid; /* L2CAP LCID */
+} tMCA_CLOSE;
+
+/* Header structure for state machine event parameters. */
+typedef union {
+ BT_HDR hdr; /* layer specific information */
+ tMCA_CCB_MSG api;
+ BOOLEAN llcong;
+ UINT8 param;
+ tMCA_OPEN open;
+ tMCA_CLOSE close;
+} tMCA_CCB_EVT;
+
+/* control channel states */
+enum
+{
+ MCA_CCB_NULL_ST, /* not allocated */
+ MCA_CCB_OPENING_ST,
+ MCA_CCB_OPEN_ST, /* open */
+ MCA_CCB_CLOSING_ST, /* disconnecting */
+ MCA_CCB_MAX_ST
+};
+typedef UINT8 tMCA_CCB_STATE;
+
+/* control channel events */
+enum
+{
+ MCA_CCB_API_CONNECT_EVT, /* application initiates a connect request. */
+ MCA_CCB_API_DISCONNECT_EVT, /* application initiates a disconnect request. */
+ MCA_CCB_API_REQ_EVT, /* application initiates a request. The request may be create_mdl, delete_mdl, reconnect_mdl or abort_mdl. */
+ MCA_CCB_API_RSP_EVT, /* application initiates a create_mdl or reconnect_mdl response. */
+ MCA_CCB_MSG_REQ_EVT, /* a create_mdl, delete_mdl, reconnect_mdl or abort_mdl request message is received from the peer. */
+ MCA_CCB_MSG_RSP_EVT, /* Response received event. This event is sent whenever a response message is received for an outstanding request message. */
+ MCA_CCB_DL_OPEN_EVT, /* data channel open. */
+ MCA_CCB_LL_OPEN_EVT, /* Lower layer open. This event is sent when the lower layer channel is open. */
+ MCA_CCB_LL_CLOSE_EVT, /* Lower layer close. This event is sent when the lower layer channel is closed. */
+ MCA_CCB_LL_CONG_EVT, /* Lower layer congestion. This event is sent when the lower layer is congested. */
+ MCA_CCB_RSP_TOUT_EVT /* time out for waiting the message response on the control channel */
+};
+
+/* Header structure for callback event parameters. */
+typedef union {
+ tMCA_OPEN open;
+ tMCA_CLOSE close;
+ BT_HDR hdr; /* layer specific information */
+ BT_HDR *p_pkt;
+ BOOLEAN llcong;
+ UINT16 mdl_id; /* the MDL ID associated with this request/response */
+ /* tMCA_HANDLE rcb_idx; For internal use only */
+ /* tMCA_CL ccb_idx; For internal use only */
+ /* tMCA_DL dcb_idx; For internal use only */
+} tMCA_DCB_EVT;
+
+/* data channel states */
+enum
+{
+ MCA_DCB_NULL_ST, /* not allocated */
+ MCA_DCB_OPENING_ST, /* create/reconnect sequence is successful, waiting for data channel connection */
+ MCA_DCB_OPEN_ST, /* open */
+ MCA_DCB_CLOSING_ST, /* disconnecting */
+ MCA_DCB_MAX_ST
+};
+typedef UINT8 tMCA_DCB_STATE;
+
+/* data channel events */
+enum
+{
+ MCA_DCB_API_CLOSE_EVT, /* This event is sent when the application wants to disconnect the data channel.*/
+ MCA_DCB_API_WRITE_EVT, /* This event is sent when the application wants to send a data packet to the peer.*/
+ MCA_DCB_TC_OPEN_EVT, /* Transport Channel open. This event is sent when the channel is open.*/
+ MCA_DCB_TC_CLOSE_EVT, /* Transport Channel close.*/
+ MCA_DCB_TC_CONG_EVT, /* Transport Channel congestion status.*/
+ MCA_DCB_TC_DATA_EVT /* This event is sent when a data packet is received from the peer.*/
+};
+
+
+
+
+/* "states" used in transport channel table */
+#define MCA_TC_ST_UNUSED 0 /* Unused - unallocated */
+#define MCA_TC_ST_IDLE 1 /* No connection */
+#define MCA_TC_ST_ACP 2 /* Waiting to accept a connection */
+#define MCA_TC_ST_INT 3 /* Initiating a connection */
+#define MCA_TC_ST_CONN 4 /* Waiting for connection confirm */
+#define MCA_TC_ST_CFG 5 /* Waiting for configuration complete */
+#define MCA_TC_ST_OPEN 6 /* Channel opened */
+#define MCA_TC_ST_SEC_INT 7 /* Security process as INT */
+#define MCA_TC_ST_SEC_ACP 8 /* Security process as ACP */
+
+/* Configuration flags. tMCA_TC_TBL.cfg_flags */
+#define MCA_L2C_CFG_IND_DONE (1<<0)
+#define MCA_L2C_CFG_CFM_DONE (1<<1)
+#define MCA_L2C_CFG_CONN_INT (1<<2)
+#define MCA_L2C_CFG_CONN_ACP (1<<3)
+#define MCA_L2C_CFG_DISCN_INT (1<<4)
+#define MCA_L2C_CFG_DISCN_ACP (1<<5)
+
+
+#define MCA_CTRL_TCID 0 /* to identify control channel by tMCA_TC_TBL.tcid */
+
+/* transport channel table */
+typedef struct {
+ UINT16 peer_mtu; /* L2CAP mtu of the peer device */
+ UINT16 my_mtu; /* Our MTU for this channel */
+ UINT16 lcid; /* L2CAP LCID */
+ UINT8 tcid; /* transport channel id (0, for control channel. (MDEP ID + 1) for data channel) */
+ tMCA_DL cb_idx; /* 1-based index to ccb or dcb */
+ UINT8 state; /* transport channel state */
+ UINT8 cfg_flags; /* L2CAP configuration flags */
+ UINT8 id; /* L2CAP id sent by peer device (need this to handle security pending) */
+} tMCA_TC_TBL;
+
+/* transport control block */
+typedef struct {
+ tMCA_TC_TBL tc_tbl[MCA_NUM_TC_TBL];
+ UINT8 lcid_tbl[MAX_L2CAP_CHANNELS]; /* map LCID to tc_tbl index */
+} tMCA_TC;
+
+/* registration control block */
+typedef struct {
+ tMCA_REG reg; /* the parameter at register */
+ tMCA_CS dep[MCA_NUM_DEPS]; /* the registration info for each MDEP */
+ tMCA_CTRL_CBACK *p_cback; /* control callback function */
+} tMCA_RCB;
+
+enum
+{
+ MCA_CCB_STAT_NORM, /* normal operation (based on ccb state) */
+ MCA_CCB_STAT_PENDING, /* waiting for data channel */
+ MCA_CCB_STAT_RECONN, /* reinitiate connection after transitioning from CLOSING to IDLE state */
+ MCA_CCB_STAT_DISC /* MCA_DisconnectReq or MCA_Deregister is called. waiting for all associated CL and DL to detach */
+};
+typedef UINT8 tMCA_CCB_STAT;
+
+/* control channel control block */
+/* the ccbs association with the rcbs
+ * ccb[0] ...ccb[MCA_NUM_LINKS*1-1] -> rcb[0]
+ * ccb[MCA_NUM_LINKS*1]...ccb[MCA_NUM_LINKS*2-1] -> rcb[1]
+ * ccb[MCA_NUM_LINKS*2]...ccb[MCA_NUM_LINKS*3-1] -> rcb[2]
+ */
+typedef struct {
+ tMCA_RCB *p_rcb; /* the associated registration control block */
+ TIMER_LIST_ENT timer_entry; /* CCB timer list entry */
+ tMCA_CCB_MSG *p_tx_req; /* Current request being sent/awaiting response */
+ tMCA_CCB_MSG *p_rx_msg; /* Current message received/being processed */
+ BD_ADDR peer_addr; /* BD address of peer */
+ UINT16 sec_mask; /* Security mask for connections as initiator */
+ UINT16 ctrl_vpsm; /* The virtual PSM that peer is listening for control channel */
+ UINT16 data_vpsm; /* The virtual PSM that peer is listening for data channel. */
+ UINT16 lcid; /* L2CAP lcid for this control channel */
+ UINT8 state; /* The CCB state machine state */
+ BOOLEAN cong; /* Whether control channel is congested */
+ tMCA_CCB_STAT status; /* see tMCA_CCB_STAT */
+} tMCA_CCB;
+typedef void (*tMCA_CCB_ACTION)(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+
+enum
+{
+ MCA_DCB_STAT_NORM, /* normal operation (based on dcb state) */
+ MCA_DCB_STAT_DEL, /* MCA_Delete is called. waiting for the DL to detach */
+ MCA_DCB_STAT_DISC /* MCA_CloseReq is called. waiting for the DL to detach */
+};
+typedef UINT8 tMCA_DCB_STAT;
+
+/* data channel control block */
+/* the dcbs association with the ccbs
+ * dcb[0] ...dcb[MCA_NUM_MDLS*1-1] -> ccb[0]
+ * dcb[MCA_NUM_MDLS*1]...dcb[MCA_NUM_MDLS*2-1] -> ccb[1]
+ * dcb[MCA_NUM_MDLS*2]...dcb[MCA_NUM_MDLS*3-1] -> ccb[2]
+ *
+ * the dcbs association with the rcbs
+ * dcb[0] ...dcb[MCA_NUM_MDLS*1*MCA_NUM_LINKS*1-1] -> rcb[0]
+ * dcb[MCA_NUM_MDLS*1*MCA_NUM_LINKS*1]...dcb[MCA_NUM_MDLS*2*MCA_NUM_LINKS*2-1] -> rcb[1]
+ * dcb[MCA_NUM_MDLS*2*MCA_NUM_LINKS*2]...dcb[MCA_NUM_MDLS*3*MCA_NUM_LINKS*3-1] -> rcb[2]
+ */
+typedef struct {
+ tMCA_CCB *p_ccb; /* the associated control control block */
+ BT_HDR *p_data; /* data packet held due to L2CAP channel congestion */
+ tMCA_CS *p_cs; /* the associated MDEP info. p_cs->type is the mdep id(internal use) */
+ const tMCA_CHNL_CFG *p_chnl_cfg; /* cfg params for L2CAP channel */
+ UINT16 mdl_id; /* the MDL ID for this data channel */
+ UINT16 lcid; /* L2CAP lcid */
+ UINT8 state; /* The DCB state machine state */
+ BOOLEAN cong; /* Whether data channel is congested */
+ tMCA_DCB_STAT status; /* see tMCA_DCB_STAT */
+} tMCA_DCB;
+
+typedef void (*tMCA_DCB_ACTION)(tMCA_DCB *p_ccb, tMCA_DCB_EVT *p_data);
+
+/* Control block for MCA */
+typedef struct {
+ tMCA_RCB rcb[MCA_NUM_REGS]; /* registration control block */
+ tMCA_CCB ccb[MCA_NUM_CCBS]; /* control channel control blocks */
+ tMCA_DCB dcb[MCA_NUM_DCBS]; /* data channel control blocks */
+ tMCA_TC tc; /* transport control block */
+ UINT8 trace_level; /* trace level */
+} tMCA_CB;
+
+/* csm functions */
+extern void mca_ccb_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CCB_EVT *p_data);
+extern tMCA_CCB *mca_ccb_by_bd(tMCA_HANDLE handle, BD_ADDR bd_addr);
+extern tMCA_CCB *mca_ccb_alloc(tMCA_HANDLE handle, BD_ADDR bd_addr);
+extern void mca_ccb_rsp_tout(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_dealloc(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern tMCA_CL mca_ccb_to_hdl(tMCA_CCB *p_ccb);
+extern tMCA_CCB *mca_ccb_by_hdl(tMCA_CL mcl);
+extern BOOLEAN mca_ccb_uses_mdl_id(tMCA_CCB *p_ccb, UINT16 mdl_id);
+
+/* cact functions */
+extern void mca_ccb_report_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CTRL *p_data);
+extern void mca_ccb_free_msg(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_snd_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_snd_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_do_disconn (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_cong(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_hdl_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_hdl_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_ll_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+extern void mca_ccb_dl_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data);
+
+/* dsm functions */
+extern void mca_dcb_event(tMCA_DCB *p_dcb, UINT8 event, tMCA_DCB_EVT *p_data);
+extern tMCA_DCB *mca_dcb_alloc(tMCA_CCB*p_ccb, tMCA_DEP dep);
+extern UINT8 mca_dep_free_mdl(tMCA_CCB*p_ccb, tMCA_DEP dep);
+extern void mca_dcb_dealloc(tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern tMCA_DL mca_dcb_to_hdl(tMCA_DCB *p_dcb);
+extern tMCA_DCB *mca_dcb_by_hdl(tMCA_DL hdl);
+extern void mca_dcb_close_by_mdl_id(tMCA_CCB*p_ccb, UINT16 mdl_id);
+
+/* dact functions */
+extern void mca_dcb_tc_open (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern void mca_dcb_cong (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern void mca_dcb_free_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern void mca_dcb_do_disconn (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern void mca_dcb_snd_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+extern void mca_dcb_hdl_data (tMCA_DCB *p_dcb, tMCA_DCB_EVT *p_data);
+
+
+/* main/utils functions */
+extern tMCA_HANDLE mca_handle_by_cpsm(UINT16 psm);
+extern tMCA_HANDLE mca_handle_by_dpsm(UINT16 psm);
+extern tMCA_TC_TBL * mca_tc_tbl_calloc(tMCA_CCB *p_ccb);
+extern tMCA_TC_TBL * mca_tc_tbl_dalloc(tMCA_DCB *p_dcb);
+extern tMCA_TC_TBL * mca_tc_tbl_by_lcid(UINT16 lcid);
+extern void mca_free_tc_tbl_by_lcid(UINT16 lcid);
+extern void mca_set_cfg_by_tbl(tL2CAP_CFG_INFO *p_cfg, tMCA_TC_TBL *p_tbl);
+extern void mca_tc_close_ind(tMCA_TC_TBL *p_tbl, UINT16 reason);
+extern void mca_tc_open_ind(tMCA_TC_TBL *p_tbl);
+extern void mca_tc_cong_ind(tMCA_TC_TBL *p_tbl, BOOLEAN is_congested);
+extern void mca_tc_data_ind(tMCA_TC_TBL *p_tbl, BT_HDR *p_buf);
+extern tMCA_RCB * mca_rcb_alloc(tMCA_REG *p_reg);
+extern void mca_rcb_dealloc(tMCA_HANDLE handle);
+extern tMCA_HANDLE mca_rcb_to_handle(tMCA_RCB *p_rcb);
+extern tMCA_RCB *mca_rcb_by_handle(tMCA_HANDLE handle);
+extern BOOLEAN mca_is_valid_dep_id(tMCA_RCB *p_rcb, tMCA_DEP dep);
+extern void mca_free_buf(void **p_buf);
+extern void mca_process_timeout(TIMER_LIST_ENT *p_tle);
+extern void mca_stop_timer(tMCA_CCB *p_ccb);
+
+/* l2c functions */
+extern void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
+extern void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
+extern UINT16 mca_l2c_open_req(BD_ADDR bd_addr, UINT16 PSM, const tMCA_CHNL_CFG *p_chnl_cfg);
+
+/*****************************************************************************
+** global data
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/******************************************************************************
+** Main Control Block
+*******************************************************************************/
+#if MCA_DYNAMIC_MEMORY == FALSE
+MCA_API extern tMCA_CB mca_cb;
+#else
+MCA_API extern tMCA_CB *mca_cb_ptr;
+#define mca_cb (*mca_cb_ptr)
+#endif
+
+/* L2CAP callback registration structure */
+extern const tL2CAP_APPL_INFO mca_l2c_int_appl;
+extern const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def;
+extern const UINT8 mca_std_msg_len[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MCA_INT_H */
diff --git a/stack/mcap/mca_l2c.c b/stack/mcap/mca_l2c.c
new file mode 100644
index 0000000..f8a48bf
--- /dev/null
+++ b/stack/mcap/mca_l2c.c
@@ -0,0 +1,589 @@
+/******************************************************************************
+ *
+ * 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 at L2CAP Interface.
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "bt_target.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+
+/* callback function declarations */
+void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
+void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
+void mca_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result);
+void mca_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
+void mca_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
+void mca_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed);
+void mca_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result);
+void mca_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested);
+void mca_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf);
+
+/* L2CAP callback function structure */
+const tL2CAP_APPL_INFO mca_l2c_int_appl = {
+ NULL,
+ mca_l2c_connect_cfm_cback,
+ NULL,
+ mca_l2c_config_ind_cback,
+ mca_l2c_config_cfm_cback,
+ mca_l2c_disconnect_ind_cback,
+ mca_l2c_disconnect_cfm_cback,
+ NULL,
+ mca_l2c_data_ind_cback,
+ mca_l2c_congestion_ind_cback
+};
+
+/* Control channel eL2CAP default options */
+const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def = {
+ L2CAP_FCR_ERTM_MODE, /* Mandatory for MCAP */
+ MCA_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
+ MCA_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */
+ MCA_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
+ MCA_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
+ MCA_FCR_OPT_MPS_SIZE /* MPS segment size */
+};
+
+
+/*******************************************************************************
+**
+** Function mca_sec_check_complete_term
+**
+** Description The function called when Security Manager finishes
+** verification of the service side connection
+**
+** Returns void
+**
+*******************************************************************************/
+static void mca_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res)
+{
+ tMCA_TC_TBL *p_tbl = (tMCA_TC_TBL *)p_ref_data;
+ tL2CAP_CFG_INFO cfg;
+ tL2CAP_ERTM_INFO ertm_info;
+
+ MCA_TRACE_DEBUG1("mca_sec_check_complete_term res: %d", res);
+
+ if ( res == BTM_SUCCESS )
+ {
+ MCA_TRACE_DEBUG2 ("lcid:x%x id:x%x", p_tbl->lcid, p_tbl->id);
+ /* Set the FCR options: control channel mandates ERTM */
+ ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
+ ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+ ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID;
+ ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID;
+ ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID;
+ ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID;
+ /* Send response to the L2CAP layer. */
+ L2CA_ErtmConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK, L2CAP_CONN_OK, &ertm_info);
+
+ /* transition to configuration state */
+ p_tbl->state = MCA_TC_ST_CFG;
+
+ /* Send L2CAP config req */
+ mca_set_cfg_by_tbl (&cfg, p_tbl);
+ L2CA_ConfigReq(p_tbl->lcid, &cfg);
+ }
+ else
+ {
+ L2CA_ConnectRsp (bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK);
+ mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_sec_check_complete_orig
+**
+** Description The function called when Security Manager finishes
+** verification of the service side connection
+**
+** Returns void
+**
+*******************************************************************************/
+static void mca_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res)
+{
+ tMCA_TC_TBL *p_tbl = (tMCA_TC_TBL *)p_ref_data;
+ tL2CAP_CFG_INFO cfg;
+
+ MCA_TRACE_DEBUG1("mca_sec_check_complete_orig res: %d", res);
+
+ if ( res == BTM_SUCCESS )
+ {
+ /* set channel state */
+ p_tbl->state = MCA_TC_ST_CFG;
+
+ /* Send L2CAP config req */
+ mca_set_cfg_by_tbl (&cfg, p_tbl);
+ L2CA_ConfigReq(p_tbl->lcid, &cfg);
+ }
+ else
+ {
+ L2CA_DisconnectReq (p_tbl->lcid);
+ mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
+ }
+}
+/*******************************************************************************
+**
+** Function mca_l2c_cconn_ind_cback
+**
+** Description This is the L2CAP connect indication callback function.
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_cconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
+{
+ tMCA_HANDLE handle = mca_handle_by_cpsm(psm);
+ tMCA_CCB *p_ccb;
+ tMCA_TC_TBL *p_tbl = NULL;
+ UINT16 result = L2CAP_CONN_NO_RESOURCES;
+ tBTM_STATUS rc;
+ tL2CAP_ERTM_INFO ertm_info, *p_ertm_info = NULL;
+ tL2CAP_CFG_INFO cfg;
+
+ MCA_TRACE_EVENT3 ("mca_l2c_cconn_ind_cback: lcid:x%x psm:x%x id:x%x", lcid, psm, id);
+
+ /* do we already have a control channel for this peer? */
+ if ((p_ccb = mca_ccb_by_bd(handle, bd_addr)) == NULL)
+ {
+ /* no, allocate ccb */
+ if ((p_ccb = mca_ccb_alloc(handle, bd_addr)) != NULL)
+ {
+ /* allocate and set up entry */
+ p_ccb->lcid = lcid;
+ p_tbl = mca_tc_tbl_calloc(p_ccb);
+ p_tbl->id = id;
+ p_tbl->cfg_flags= MCA_L2C_CFG_CONN_ACP;
+ /* proceed with connection */
+ /* Check the security */
+ rc = btm_sec_mx_access_request (bd_addr, psm, FALSE, BTM_SEC_PROTO_MCA, 0,
+ &mca_sec_check_complete_term, p_tbl);
+ if (rc == BTM_CMD_STARTED)
+ {
+ /* Set the FCR options: control channel mandates ERTM */
+ ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
+ ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+ ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID;
+ ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID;
+ ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID;
+ ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID;
+ p_ertm_info = &ertm_info;
+ result = L2CAP_CONN_PENDING;
+ }
+ else
+ result = L2CAP_CONN_OK;
+ }
+
+ /* deal with simultaneous control channel connect case */
+ }
+ /* else reject their connection */
+
+ if (!p_tbl || (p_tbl->state != MCA_TC_ST_CFG))
+ {
+ /* Send L2CAP connect rsp */
+ L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info);
+
+ /* if result ok, proceed with connection and send L2CAP
+ config req */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* set channel state */
+ p_tbl->state = MCA_TC_ST_CFG;
+
+ /* Send L2CAP config req */
+ mca_set_cfg_by_tbl (&cfg, p_tbl);
+ L2CA_ConfigReq(p_tbl->lcid, &cfg);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_dconn_ind_cback
+**
+** Description This is the L2CAP connect indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_dconn_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
+{
+ tMCA_HANDLE handle = mca_handle_by_dpsm(psm);
+ tMCA_CCB *p_ccb;
+ tMCA_DCB *p_dcb;
+ tMCA_TC_TBL *p_tbl = NULL;
+ UINT16 result;
+ tL2CAP_CFG_INFO cfg;
+ tL2CAP_ERTM_INFO *p_ertm_info = NULL, ertm_info;
+ const tMCA_CHNL_CFG *p_chnl_cfg;
+
+ MCA_TRACE_EVENT2 ("mca_l2c_dconn_ind_cback: lcid:x%x psm:x%x ", lcid, psm);
+
+ if (((p_ccb = mca_ccb_by_bd(handle, bd_addr)) != NULL) && /* find the CCB */
+ (p_ccb->status == MCA_CCB_STAT_PENDING) && /* this CCB is expecting a MDL */
+ (p_ccb->p_tx_req && (p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL))
+ {
+ /* found the associated dcb in listening mode */
+ /* proceed with connection */
+ p_dcb->lcid = lcid;
+ p_tbl = mca_tc_tbl_dalloc(p_dcb);
+ p_tbl->id = id;
+ p_tbl->cfg_flags= MCA_L2C_CFG_CONN_ACP;
+ p_chnl_cfg = p_dcb->p_chnl_cfg;
+ /* assume that control channel has verified the security requirement */
+ /* Set the FCR options: control channel mandates ERTM */
+ ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
+ ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
+ ertm_info.user_rx_pool_id = p_chnl_cfg->user_rx_pool_id;
+ ertm_info.user_tx_pool_id = p_chnl_cfg->user_tx_pool_id;
+ ertm_info.fcr_rx_pool_id = p_chnl_cfg->fcr_rx_pool_id;
+ ertm_info.fcr_tx_pool_id = p_chnl_cfg->fcr_tx_pool_id;
+ p_ertm_info = &ertm_info;
+ result = L2CAP_CONN_OK;
+ }
+ else
+ {
+ /* else we're not listening for traffic channel; reject
+ * (this error code is specified by MCAP spec) */
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+
+ /* Send L2CAP connect rsp */
+ L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, result, p_ertm_info);
+
+ /* if result ok, proceed with connection */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* transition to configuration state */
+ p_tbl->state = MCA_TC_ST_CFG;
+
+ /* Send L2CAP config req */
+ mca_set_cfg_by_tbl (&cfg, p_tbl);
+ L2CA_ConfigReq(lcid, &cfg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_connect_cfm_cback
+**
+** Description This is the L2CAP connect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result)
+{
+ tMCA_TC_TBL *p_tbl;
+ tL2CAP_CFG_INFO cfg;
+ tMCA_CCB *p_ccb;
+
+ MCA_TRACE_DEBUG2("mca_l2c_connect_cfm_cback lcid: x%x, result: %d",
+ lcid, result);
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ MCA_TRACE_DEBUG2("p_tbl state: %d, tcid: %d", p_tbl->state, p_tbl->tcid);
+ /* if in correct state */
+ if (p_tbl->state == MCA_TC_ST_CONN)
+ {
+ /* if result successful */
+ if (result == L2CAP_CONN_OK)
+ {
+ if (p_tbl->tcid != 0)
+ {
+ /* set channel state */
+ p_tbl->state = MCA_TC_ST_CFG;
+
+ /* Send L2CAP config req */
+ mca_set_cfg_by_tbl (&cfg, p_tbl);
+ L2CA_ConfigReq(lcid, &cfg);
+ }
+ else
+ {
+ p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
+ if (p_ccb == NULL)
+ {
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+ else
+ {
+ /* set channel state */
+ p_tbl->state = MCA_TC_ST_SEC_INT;
+ p_tbl->lcid = lcid;
+ p_tbl->cfg_flags= MCA_L2C_CFG_CONN_INT;
+
+ /* Check the security */
+ btm_sec_mx_access_request (p_ccb->peer_addr, p_ccb->ctrl_vpsm,
+ TRUE, BTM_SEC_PROTO_MCA,
+ p_tbl->tcid,
+ &mca_sec_check_complete_orig, p_tbl);
+ }
+ }
+ }
+
+ /* failure; notify adaption that channel closed */
+ if (result != L2CAP_CONN_OK)
+ {
+ p_tbl->cfg_flags |= MCA_L2C_CFG_DISCN_INT;
+ mca_tc_close_ind(p_tbl, result);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_config_cfm_cback
+**
+** Description This is the L2CAP config confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tMCA_TC_TBL *p_tbl;
+
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ /* if in correct state */
+ if (p_tbl->state == MCA_TC_ST_CFG)
+ {
+ /* if result successful */
+ if (p_cfg->result == L2CAP_CONN_OK)
+ {
+ /* update cfg_flags */
+ p_tbl->cfg_flags |= MCA_L2C_CFG_CFM_DONE;
+
+ /* if configuration complete */
+ if (p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE)
+ {
+ mca_tc_open_ind(p_tbl);
+ }
+ }
+ /* else failure */
+ else
+ {
+ /* Send L2CAP disconnect req */
+ L2CA_DisconnectReq(lcid);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_config_ind_cback
+**
+** Description This is the L2CAP config indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tMCA_TC_TBL *p_tbl;
+ UINT16 result = L2CAP_CFG_OK;
+
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ /* store the mtu in tbl */
+ if (p_cfg->mtu_present)
+ {
+ p_tbl->peer_mtu = p_cfg->mtu;
+ if (p_tbl->peer_mtu < MCA_MIN_MTU)
+ {
+ result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ }
+ }
+ else
+ {
+ p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
+ }
+ MCA_TRACE_DEBUG3("peer_mtu: %d, lcid: x%x mtu_present:%d",p_tbl->peer_mtu, lcid, p_cfg->mtu_present);
+
+ /* send L2CAP configure response */
+ memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ p_cfg->result = result;
+ L2CA_ConfigRsp(lcid, p_cfg);
+
+ /* if first config ind */
+ if ((p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) == 0)
+ {
+ /* update cfg_flags */
+ p_tbl->cfg_flags |= MCA_L2C_CFG_IND_DONE;
+
+ /* if configuration complete */
+ if (p_tbl->cfg_flags & MCA_L2C_CFG_CFM_DONE)
+ {
+ mca_tc_open_ind(p_tbl);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_disconnect_ind_cback
+**
+** Description This is the L2CAP disconnect indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed)
+{
+ tMCA_TC_TBL *p_tbl;
+ UINT16 reason = L2CAP_DISC_TIMEOUT;
+
+ MCA_TRACE_DEBUG2("mca_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d",
+ lcid, ack_needed);
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ if (ack_needed)
+ {
+ /* send L2CAP disconnect response */
+ L2CA_DisconnectRsp(lcid);
+ }
+
+ p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_ACP;
+ if (ack_needed)
+ reason = L2CAP_DISC_OK;
+ mca_tc_close_ind(p_tbl, reason);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_disconnect_cfm_cback
+**
+** Description This is the L2CAP disconnect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result)
+{
+ tMCA_TC_TBL *p_tbl;
+
+ MCA_TRACE_DEBUG2("mca_l2c_disconnect_cfm_cback lcid: x%x, result: %d",
+ lcid, result);
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_INT;
+ mca_tc_close_ind(p_tbl, result);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function mca_l2c_congestion_ind_cback
+**
+** Description This is the L2CAP congestion indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested)
+{
+ tMCA_TC_TBL *p_tbl;
+
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ mca_tc_cong_ind(p_tbl, is_congested);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_l2c_data_ind_cback
+**
+** Description This is the L2CAP data indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void mca_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf)
+{
+ tMCA_TC_TBL *p_tbl;
+
+ /* look up info for this channel */
+ if ((p_tbl = mca_tc_tbl_by_lcid(lcid)) != NULL)
+ {
+ mca_tc_data_ind(p_tbl, p_buf);
+ }
+ else /* prevent buffer leak */
+ GKI_freebuf(p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function mca_l2c_open_req
+**
+** Description This function calls L2CA_ConnectReq() to initiate a L2CAP channel.
+**
+** Returns void.
+**
+*******************************************************************************/
+UINT16 mca_l2c_open_req(BD_ADDR bd_addr, UINT16 psm, const tMCA_CHNL_CFG *p_chnl_cfg)
+{
+ tL2CAP_ERTM_INFO ertm_info;
+
+ if (p_chnl_cfg)
+ {
+ ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
+ ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
+ ertm_info.user_rx_pool_id = p_chnl_cfg->user_rx_pool_id;
+ ertm_info.user_tx_pool_id = p_chnl_cfg->user_tx_pool_id;
+ ertm_info.fcr_rx_pool_id = p_chnl_cfg->fcr_rx_pool_id;
+ ertm_info.fcr_tx_pool_id = p_chnl_cfg->fcr_tx_pool_id;
+ }
+ else
+ {
+ ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
+ ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+ ertm_info.user_rx_pool_id = MCA_USER_RX_POOL_ID;
+ ertm_info.user_tx_pool_id = MCA_USER_TX_POOL_ID;
+ ertm_info.fcr_rx_pool_id = MCA_FCR_RX_POOL_ID;
+ ertm_info.fcr_tx_pool_id = MCA_FCR_TX_POOL_ID;
+ }
+ return L2CA_ErtmConnectReq (psm, bd_addr, &ertm_info);
+}
+
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;
+ }
+}