diff options
Diffstat (limited to 'stack/mcap')
-rw-r--r-- | stack/mcap/mca_api.c | 912 | ||||
-rw-r--r-- | stack/mcap/mca_cact.c | 567 | ||||
-rw-r--r-- | stack/mcap/mca_csm.c | 372 | ||||
-rw-r--r-- | stack/mcap/mca_dact.c | 149 | ||||
-rw-r--r-- | stack/mcap/mca_dsm.c | 334 | ||||
-rw-r--r-- | stack/mcap/mca_int.h | 343 | ||||
-rw-r--r-- | stack/mcap/mca_l2c.c | 577 | ||||
-rw-r--r-- | stack/mcap/mca_main.c | 630 |
8 files changed, 3884 insertions, 0 deletions
diff --git a/stack/mcap/mca_api.c b/stack/mcap/mca_api.c new file mode 100644 index 0000000..c4cf7a3 --- /dev/null +++ b/stack/mcap/mca_api.c @@ -0,0 +1,912 @@ +/***************************************************************************** +** +** Name: mca_api.c +** +** Description: This is the API implementation file for the +** Multi-Channel Adaptation Protocol (MCAP). +** +** Copyright (c) 2009-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..d074172 --- /dev/null +++ b/stack/mcap/mca_cact.c @@ -0,0 +1,567 @@ +/***************************************************************************** +** +** Name: mca_cact.c +** +** Description: This is the implementation file for the MCAP +** Control Channel Action Functions. +** +** Copyright (c) 2009-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..411ee6a --- /dev/null +++ b/stack/mcap/mca_csm.c @@ -0,0 +1,372 @@ +/***************************************************************************** +** +** Name: mca_csm.c +** +** Description: This is the implementation file for the MCAP +** Control channel state machine. +** +** Copyright (c) 2009-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..5ace8fa --- /dev/null +++ b/stack/mcap/mca_dact.c @@ -0,0 +1,149 @@ +/***************************************************************************** +** +** Name: mca_dact.c +** +** Description: This is the implementation file for the MCAP +** Data Channel Action Functions. +** +** Copyright (c) 2009-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..e6139a3 --- /dev/null +++ b/stack/mcap/mca_dsm.c @@ -0,0 +1,334 @@ +/***************************************************************************** +** +** Name: mca_dsm.c +** +** Description: This is the implementation file for the MCAP +** Data chahnel state machine. +** +** Copyright (c) 2009-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..f248dbe --- /dev/null +++ b/stack/mcap/mca_int.h @@ -0,0 +1,343 @@ +/***************************************************************************** +** +** Name: mca_int.h +** +** Description: This file contains interfaces which are internal to MCAP. +** +** Copyright (c) 2009-2010, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..a776568 --- /dev/null +++ b/stack/mcap/mca_l2c.c @@ -0,0 +1,577 @@ +/***************************************************************************** +** +** Name: mca_l2c.c +** +** Description: This is the implementation file for the MCAP +** at L2CAP Interface. +** +** Copyright (c) 2009-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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..bc6118c --- /dev/null +++ b/stack/mcap/mca_main.c @@ -0,0 +1,630 @@ +/***************************************************************************** +** +** Name: mca_main.c +** +** Description: This is the implementation file for the MCAP +** Main Control Block and Utility functions. +** +** Copyright (c) 2009-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#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; + } +} |