summaryrefslogtreecommitdiffstats
path: root/stack/mcap/mca_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/mcap/mca_api.c')
-rw-r--r--stack/mcap/mca_api.c925
1 files changed, 925 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;
+}
+