860 lines
30 KiB
C++
860 lines
30 KiB
C++
/******************************************************************************
|
|
*
|
|
* 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 <base/logging.h>
|
|
#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 "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_ccb_timer_timeout(void* data) {
|
|
tMCA_CCB* p_ccb = (tMCA_CCB*)data;
|
|
|
|
mca_ccb_event(p_ccb, 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_t MCA_SetTraceLevel(uint8_t 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;
|
|
|
|
CHECK(p_reg != NULL);
|
|
CHECK(p_cback != NULL);
|
|
|
|
MCA_TRACE_API("MCA_Register: ctrl_psm:0x%x, data_psm:0x%x", p_reg->ctrl_psm,
|
|
p_reg->data_psm);
|
|
|
|
p_rcb = mca_rcb_alloc(p_reg);
|
|
if (p_rcb != NULL) {
|
|
if (p_reg->ctrl_psm) {
|
|
if (L2C_INVALID_PSM(p_reg->ctrl_psm) ||
|
|
L2C_INVALID_PSM(p_reg->data_psm)) {
|
|
MCA_TRACE_ERROR("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_ERROR("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 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_API("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;
|
|
|
|
CHECK(p_dep != NULL);
|
|
CHECK(p_cs != NULL);
|
|
CHECK(p_cs->p_data_cback != NULL);
|
|
|
|
MCA_TRACE_API("MCA_CreateDep: %d", handle);
|
|
if (p_rcb) {
|
|
if (p_cs->max_mdl > MCA_NUM_MDLS) {
|
|
MCA_TRACE_ERROR("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_ERROR("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_API("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, const RawAddress& bd_addr,
|
|
uint16_t ctrl_psm, uint16_t sec_mask) {
|
|
tMCA_RESULT result = MCA_BAD_HANDLE;
|
|
tMCA_CCB* p_ccb;
|
|
tMCA_TC_TBL* p_tbl;
|
|
|
|
MCA_TRACE_API("MCA_ConnectReq: %d psm:0x%x", handle, ctrl_psm);
|
|
p_ccb = mca_ccb_by_bd(handle, bd_addr);
|
|
if (p_ccb == NULL)
|
|
p_ccb = mca_ccb_alloc(handle, bd_addr);
|
|
else {
|
|
MCA_TRACE_ERROR("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_API("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_t data_psm,
|
|
uint16_t mdl_id, uint8_t peer_dep_id, uint8_t cfg,
|
|
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;
|
|
|
|
MCA_TRACE_API("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_ERROR("pending req");
|
|
return MCA_BUSY;
|
|
}
|
|
|
|
if ((peer_dep_id > MCA_MAX_MDEP_ID) || (!MCA_IS_VALID_MDL_ID(mdl_id))) {
|
|
MCA_TRACE_ERROR("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_ERROR("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;
|
|
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) {
|
|
tMCA_CCB_EVT* mca_ccb_evt =
|
|
(tMCA_CCB_EVT*)osi_malloc(sizeof(tMCA_CCB_EVT));
|
|
tMCA_CCB_MSG* p_evt_data = &mca_ccb_evt->api;
|
|
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, mca_ccb_evt);
|
|
return MCA_SUCCESS;
|
|
}
|
|
|
|
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_t mdl_id,
|
|
uint8_t cfg, uint8_t rsp_code,
|
|
const tMCA_CHNL_CFG* p_chnl_cfg) {
|
|
tMCA_RESULT result = MCA_BAD_HANDLE;
|
|
tMCA_CCB* p_ccb = mca_ccb_by_hdl(mcl);
|
|
|
|
MCA_TRACE_API("MCA_CreateMdlRsp: %d dep=%d mdl_id=%d cfg=%d rsp_code=%d", mcl,
|
|
dep, mdl_id, cfg, rsp_code);
|
|
CHECK(p_chnl_cfg != NULL);
|
|
if (p_ccb) {
|
|
if (p_ccb->cong) {
|
|
MCA_TRACE_ERROR("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)) {
|
|
tMCA_CCB_MSG evt_data;
|
|
result = MCA_SUCCESS;
|
|
evt_data.dcb_idx = 0;
|
|
if (rsp_code == MCA_RSP_SUCCESS) {
|
|
tMCA_DCB* 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;
|
|
tMCA_CCB_EVT mca_ccb_evt;
|
|
mca_ccb_evt.api = evt_data;
|
|
mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, &mca_ccb_evt);
|
|
}
|
|
} else {
|
|
MCA_TRACE_ERROR(
|
|
"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_API("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_t data_psm,
|
|
uint16_t 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_DCB* p_dcb;
|
|
|
|
MCA_TRACE_API("MCA_ReconnectMdl: %d ", mcl);
|
|
CHECK(p_chnl_cfg != NULL);
|
|
if (p_ccb) {
|
|
if (p_ccb->p_tx_req || p_ccb->p_rx_msg || p_ccb->cong) {
|
|
MCA_TRACE_ERROR("pending req");
|
|
return MCA_BUSY;
|
|
}
|
|
|
|
if (!MCA_IS_VALID_MDL_ID(mdl_id)) {
|
|
MCA_TRACE_ERROR("bad mdl id: %d ", mdl_id);
|
|
return MCA_BAD_PARAMS;
|
|
}
|
|
|
|
if (mca_ccb_uses_mdl_id(p_ccb, mdl_id)) {
|
|
MCA_TRACE_ERROR("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) {
|
|
tMCA_CCB_EVT* mca_ccb_evt =
|
|
(tMCA_CCB_EVT*)osi_malloc(sizeof(tMCA_CCB_EVT));
|
|
tMCA_CCB_MSG* p_evt_data = &mca_ccb_evt->api;
|
|
|
|
p_dcb->p_chnl_cfg = p_chnl_cfg;
|
|
p_dcb->mdl_id = mdl_id;
|
|
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, mca_ccb_evt);
|
|
return MCA_SUCCESS;
|
|
}
|
|
}
|
|
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_t mdl_id,
|
|
uint8_t 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_DCB* p_dcb;
|
|
|
|
MCA_TRACE_API("MCA_ReconnectMdlRsp: %d ", mcl);
|
|
CHECK(p_chnl_cfg != NULL);
|
|
if (p_ccb) {
|
|
if (p_ccb->cong) {
|
|
MCA_TRACE_ERROR("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;
|
|
tMCA_CCB_MSG evt_data;
|
|
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_ERROR("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;
|
|
tMCA_CCB_EVT mca_ccb_evt;
|
|
mca_ccb_evt.api = evt_data;
|
|
mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, &mca_ccb_evt);
|
|
} else {
|
|
MCA_TRACE_ERROR(
|
|
"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_API("MCA_DataChnlCfg: %d ", mcl);
|
|
CHECK(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_ERROR("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_DCB* p_dcb;
|
|
|
|
MCA_TRACE_API("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_ERROR("The given MCL is not expecting this API:%d",
|
|
p_ccb->status);
|
|
return result;
|
|
}
|
|
|
|
if (p_ccb->cong) {
|
|
MCA_TRACE_ERROR("congested");
|
|
return MCA_BUSY;
|
|
}
|
|
|
|
tMCA_CCB_EVT* mca_ccb_evt = (tMCA_CCB_EVT*)osi_malloc(sizeof(tMCA_CCB_EVT));
|
|
tMCA_CCB_MSG* p_evt_data = &mca_ccb_evt->api;
|
|
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, mca_ccb_evt);
|
|
}
|
|
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_t mdl_id) {
|
|
tMCA_RESULT result = MCA_BAD_HANDLE;
|
|
tMCA_CCB* p_ccb = mca_ccb_by_hdl(mcl);
|
|
|
|
MCA_TRACE_API("MCA_Delete: %d ", mcl);
|
|
if (p_ccb) {
|
|
if (p_ccb->cong) {
|
|
MCA_TRACE_ERROR("congested");
|
|
return MCA_BUSY;
|
|
}
|
|
if (!MCA_IS_VALID_MDL_ID(mdl_id) && (mdl_id != MCA_ALL_MDL_ID)) {
|
|
MCA_TRACE_ERROR("bad mdl id: %d ", mdl_id);
|
|
return MCA_BAD_PARAMS;
|
|
}
|
|
|
|
tMCA_CCB_EVT* mca_ccb_evt = (tMCA_CCB_EVT*)osi_malloc(sizeof(tMCA_CCB_EVT));
|
|
tMCA_CCB_MSG* p_evt_data = &mca_ccb_evt->api;
|
|
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, mca_ccb_evt);
|
|
}
|
|
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_API("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_t MCA_GetL2CapChannel(tMCA_DL mdl) {
|
|
uint16_t lcid = 0;
|
|
tMCA_DCB* p_dcb = mca_dcb_by_hdl(mdl);
|
|
|
|
MCA_TRACE_API("MCA_GetL2CapChannel: %d ", mdl);
|
|
if (p_dcb) lcid = p_dcb->lcid;
|
|
return lcid;
|
|
}
|
|
|
|
static const btmcap_test_interface_t mcap_test_interface = {
|
|
sizeof(btmcap_test_interface_t),
|
|
MCA_Init,
|
|
MCA_Register,
|
|
MCA_Deregister,
|
|
MCA_CreateDep,
|
|
MCA_DeleteDep,
|
|
MCA_ConnectReq,
|
|
MCA_DisconnectReq,
|
|
MCA_CreateMdl,
|
|
MCA_CreateMdlRsp,
|
|
MCA_CloseReq,
|
|
MCA_ReconnectMdl,
|
|
MCA_ReconnectMdlRsp,
|
|
MCA_DataChnlCfg,
|
|
MCA_Abort,
|
|
MCA_Delete,
|
|
MCA_WriteReq,
|
|
MCA_GetL2CapChannel,
|
|
};
|
|
|
|
const btmcap_test_interface_t* stack_mcap_get_interface(void) {
|
|
BTIF_TRACE_EVENT("%s", __func__);
|
|
return &mcap_test_interface;
|
|
}
|