| /****************************************************************************** |
| * |
| * 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; |
| tMCA_CCB_MSG* p_evt_data = |
| (tMCA_CCB_MSG*)osi_malloc(sizeof(tMCA_CCB_MSG)); |
| 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 { |
| osi_free(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_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); |
| tMCA_CCB_MSG evt_data; |
| tMCA_DCB* p_dcb; |
| |
| 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)) { |
| 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_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_MSG* p_evt_data = |
| (tMCA_CCB_MSG*)osi_malloc(sizeof(tMCA_CCB_MSG)); |
| |
| 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, (tMCA_CCB_EVT*)p_evt_data); |
| 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_CCB_MSG evt_data; |
| 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; |
| 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; |
| mca_ccb_event(p_ccb, MCA_CCB_API_RSP_EVT, (tMCA_CCB_EVT*)&evt_data); |
| } 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_MSG* p_evt_data = (tMCA_CCB_MSG*)osi_malloc(sizeof(tMCA_CCB_MSG)); |
| 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_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_MSG* p_evt_data = (tMCA_CCB_MSG*)osi_malloc(sizeof(tMCA_CCB_MSG)); |
| 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); |
| } |
| 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; |
| } |