summaryrefslogtreecommitdiffstats
path: root/stack/mcap/mca_cact.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/mcap/mca_cact.c')
-rw-r--r--stack/mcap/mca_cact.c580
1 files changed, 580 insertions, 0 deletions
diff --git a/stack/mcap/mca_cact.c b/stack/mcap/mca_cact.c
new file mode 100644
index 0000000..85f19f4
--- /dev/null
+++ b/stack/mcap/mca_cact.c
@@ -0,0 +1,580 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This is the implementation file for the MCAP Control Channel Action
+ * Functions.
+ *
+ ******************************************************************************/
+#include <string.h>
+#include "bt_target.h"
+#include "gki.h"
+#include "btm_api.h"
+#include "mca_api.h"
+#include "mca_defs.h"
+#include "mca_int.h"
+
+
+#include "btu.h"
+/*****************************************************************************
+** constants
+*****************************************************************************/
+/*******************************************************************************
+**
+** Function mca_ccb_rsp_tout
+**
+** Description This function processes the response timeout.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_rsp_tout(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+ mca_ccb_report_event(p_ccb, MCA_RSP_TOUT_IND_EVT, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_report_event
+**
+** Description This function reports the given event.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_report_event(tMCA_CCB *p_ccb, UINT8 event, tMCA_CTRL *p_data)
+{
+ if (p_ccb && p_ccb->p_rcb && p_ccb->p_rcb->p_cback)
+ (*p_ccb->p_rcb->p_cback)(mca_rcb_to_handle(p_ccb->p_rcb), mca_ccb_to_hdl(p_ccb), event, p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_free_msg
+**
+** Description This function frees the received message.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_free_msg(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ GKI_freebuf (p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_snd_req
+**
+** Description This function builds a request and sends it to the peer.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_snd_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data;
+ BT_HDR *p_pkt;
+ UINT8 *p, *p_start;
+ BOOLEAN is_abort = FALSE;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_DEBUG2 ("mca_ccb_snd_req cong=%d req=%d", p_ccb->cong, p_msg->op_code);
+ /* check for abort request */
+ if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (p_msg->op_code == MCA_OP_MDL_ABORT_REQ))
+ {
+ p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
+ /* the Abort API does not have the associated mdl_id.
+ * Get the mdl_id in dcb to compose the request */
+ p_msg->mdl_id = p_dcb->mdl_id;
+ mca_dcb_event(p_dcb, MCA_DCB_API_CLOSE_EVT, NULL);
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ p_ccb->status = MCA_CCB_STAT_NORM;
+ is_abort = TRUE;
+ }
+
+ /* no pending outgoing messages or it's an abort request for a pending data channel */
+ if ((!p_ccb->p_tx_req) || is_abort)
+ {
+ p_ccb->p_tx_req = p_msg;
+ if (!p_ccb->cong)
+ {
+ p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_pkt)
+ {
+ p_pkt->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
+ *p++ = p_msg->op_code;
+ UINT16_TO_BE_STREAM (p, p_msg->mdl_id);
+ if (p_msg->op_code == MCA_OP_MDL_CREATE_REQ)
+ {
+ *p++ = p_msg->mdep_id;
+ *p++ = p_msg->param;
+ }
+ p_msg->hdr.layer_specific = TRUE; /* mark this message as sent */
+ p_pkt->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_pkt);
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE) p_ccb;
+ btu_start_timer(&p_ccb->timer_entry, BTU_TTYPE_MCA_CCB_RSP, p_ccb->p_rcb->reg.rsp_tout);
+ }
+ }
+ /* else the L2CAP channel is congested. keep the message to be sent later */
+ }
+ else
+ {
+ MCA_TRACE_WARNING0 ("dropping api req");
+ GKI_freebuf (p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_snd_rsp
+**
+** Description This function builds a response and sends it to
+** the peer.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_snd_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CCB_MSG *p_msg = (tMCA_CCB_MSG *)p_data;
+ BT_HDR *p_pkt;
+ UINT8 *p, *p_start;
+ BOOLEAN chk_mdl = FALSE;
+ tMCA_DCB *p_dcb;
+
+ MCA_TRACE_DEBUG2 ("mca_ccb_snd_rsp cong=%d req=%d", p_ccb->cong, p_msg->op_code);
+ /* assume that API functions verified the parameters */
+ p_pkt = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_pkt)
+ {
+ p_pkt->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_pkt + 1) + L2CAP_MIN_OFFSET;
+ *p++ = p_msg->op_code;
+ *p++ = p_msg->rsp_code;
+ UINT16_TO_BE_STREAM (p, p_msg->mdl_id);
+ if (p_msg->op_code == MCA_OP_MDL_CREATE_RSP)
+ {
+ *p++ = p_msg->param;
+ chk_mdl = TRUE;
+ }
+ else if (p_msg->op_code == MCA_OP_MDL_RECONNECT_RSP)
+ chk_mdl = TRUE;
+
+ if (chk_mdl && p_msg->rsp_code == MCA_RSP_SUCCESS)
+ {
+ p_dcb = mca_dcb_by_hdl(p_msg->dcb_idx);
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask,
+ p_ccb->p_rcb->reg.data_psm, BTM_SEC_PROTO_MCA, p_msg->dcb_idx);
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ /* set p_tx_req to block API_REQ/API_RSP before DL is up */
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ p_ccb->p_tx_req = p_ccb->p_rx_msg;
+ p_ccb->p_rx_msg = NULL;
+ p_ccb->p_tx_req->dcb_idx = p_msg->dcb_idx;
+ }
+ mca_free_buf ((void **)&p_ccb->p_rx_msg);
+ p_pkt->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_pkt);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_do_disconn
+**
+** Description This function closes a control channel.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_do_disconn (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ mca_dcb_close_by_mdl_id (p_ccb, MCA_ALL_MDL_ID);
+ L2CA_DisconnectReq(p_ccb->lcid);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_cong
+**
+** Description This function sets the congestion state for the CCB.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_cong(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ MCA_TRACE_DEBUG2 ("mca_ccb_cong cong=%d/%d", p_ccb->cong, p_data->llcong);
+ p_ccb->cong = p_data->llcong;
+ if (!p_ccb->cong)
+ {
+ /* if there's a held packet, send it now */
+ if (p_ccb->p_tx_req && !p_ccb->p_tx_req->hdr.layer_specific)
+ {
+ p_data = (tMCA_CCB_EVT *)p_ccb->p_tx_req;
+ p_ccb->p_tx_req = NULL;
+ mca_ccb_snd_req (p_ccb, p_data);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_hdl_req
+**
+** Description This function is called when a MCAP request is received from
+** the peer. It calls the application callback function to
+** report the event.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_hdl_req(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ BT_HDR *p_pkt = &p_data->hdr;
+ BT_HDR *p_buf;
+ UINT8 *p, *p_start;
+ tMCA_DCB *p_dcb;
+ tMCA_CTRL evt_data;
+ tMCA_CCB_MSG *p_rx_msg = NULL;
+ UINT8 reject_code = MCA_RSP_NO_RESOURCE;
+ BOOLEAN send_rsp = FALSE;
+ BOOLEAN check_req = FALSE;
+ UINT8 reject_opcode;
+
+ MCA_TRACE_DEBUG1 ("mca_ccb_hdl_req status:%d", p_ccb->status);
+ p_rx_msg = (tMCA_CCB_MSG *)p_pkt;
+ p = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ evt_data.hdr.op_code = *p++;
+ BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p);
+ reject_opcode = evt_data.hdr.op_code+1;
+
+ MCA_TRACE_DEBUG1 ("received mdl id: %d ", evt_data.hdr.mdl_id);
+ if (p_ccb->status == MCA_CCB_STAT_PENDING)
+ {
+ MCA_TRACE_DEBUG0 ("received req inpending state");
+ /* allow abort in pending state */
+ if ((p_ccb->status == MCA_CCB_STAT_PENDING) && (evt_data.hdr.op_code == MCA_OP_MDL_ABORT_REQ))
+ {
+ reject_code = MCA_RSP_SUCCESS;
+ send_rsp = TRUE;
+ /* clear the pending status */
+ p_ccb->status = MCA_CCB_STAT_NORM;
+ if (p_ccb->p_tx_req && ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx))!= NULL))
+ {
+ mca_dcb_dealloc (p_dcb, NULL);
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ }
+ }
+ else
+ reject_code = MCA_RSP_BAD_OP;
+ }
+ else if (p_ccb->p_rx_msg)
+ {
+ MCA_TRACE_DEBUG0 ("still handling prev req");
+ /* still holding previous message, reject this new one ?? */
+
+ }
+ else if (p_ccb->p_tx_req)
+ {
+ MCA_TRACE_DEBUG1 ("still waiting for a response ctrl_vpsm:0x%x", p_ccb->ctrl_vpsm);
+ /* sent a request; waiting for response */
+ if (p_ccb->ctrl_vpsm == 0)
+ {
+ MCA_TRACE_DEBUG0 ("local is ACP. accept the cmd from INT");
+ /* local is acceptor, need to handle the request */
+ check_req = TRUE;
+ reject_code = MCA_RSP_SUCCESS;
+ /* drop the previous request */
+ if ((p_ccb->p_tx_req->op_code == MCA_OP_MDL_CREATE_REQ) &&
+ ((p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL))
+ {
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_stop_timer(p_ccb);
+ }
+ else
+ {
+ /* local is initiator, ignore the req */
+ GKI_freebuf (p_pkt);
+ return;
+ }
+ }
+ else if (p_pkt->layer_specific != MCA_RSP_SUCCESS)
+ {
+
+ reject_code = (UINT8)p_pkt->layer_specific;
+ if (((evt_data.hdr.op_code >= MCA_NUM_STANDARD_OPCODE) &&
+ (evt_data.hdr.op_code < MCA_FIRST_SYNC_OP)) ||
+ (evt_data.hdr.op_code > MCA_LAST_SYNC_OP))
+ {
+ /* invalid op code */
+ reject_opcode = MCA_OP_ERROR_RSP;
+ evt_data.hdr.mdl_id = 0;
+ }
+ }
+ else
+ {
+ check_req = TRUE;
+ reject_code = MCA_RSP_SUCCESS;
+ }
+
+ if (check_req)
+ {
+ if (reject_code == MCA_RSP_SUCCESS)
+ {
+ reject_code = MCA_RSP_BAD_MDL;
+ if (MCA_IS_VALID_MDL_ID(evt_data.hdr.mdl_id) ||
+ ((evt_data.hdr.mdl_id == MCA_ALL_MDL_ID) && (evt_data.hdr.op_code == MCA_OP_MDL_DELETE_REQ)))
+ {
+ reject_code = MCA_RSP_SUCCESS;
+ /* mdl_id is valid according to the spec */
+ switch (evt_data.hdr.op_code)
+ {
+ case MCA_OP_MDL_CREATE_REQ:
+ evt_data.create_ind.dep_id = *p++;
+ evt_data.create_ind.cfg = *p++;
+ p_rx_msg->mdep_id = evt_data.create_ind.dep_id;
+ if (!mca_is_valid_dep_id(p_ccb->p_rcb, p_rx_msg->mdep_id))
+ {
+ MCA_TRACE_ERROR0 ("not a valid local mdep id");
+ reject_code = MCA_RSP_BAD_MDEP;
+ }
+ else if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id))
+ {
+ MCA_TRACE_DEBUG0 ("the mdl_id is currently used in the CL(create)");
+ mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
+ }
+ else
+ {
+ /* check if this dep still have MDL available */
+ if (mca_dep_free_mdl(p_ccb, evt_data.create_ind.dep_id) == 0)
+ {
+ MCA_TRACE_ERROR0 ("the mdep is currently using max_mdl");
+ reject_code = MCA_RSP_MDEP_BUSY;
+ }
+ }
+ break;
+
+ case MCA_OP_MDL_RECONNECT_REQ:
+ if (mca_ccb_uses_mdl_id(p_ccb, evt_data.hdr.mdl_id))
+ {
+ MCA_TRACE_ERROR0 ("the mdl_id is currently used in the CL(reconn)");
+ reject_code = MCA_RSP_MDL_BUSY;
+ }
+ break;
+
+ case MCA_OP_MDL_ABORT_REQ:
+ reject_code = MCA_RSP_BAD_OP;
+ break;
+
+ case MCA_OP_MDL_DELETE_REQ:
+ /* delete the associated mdl */
+ mca_dcb_close_by_mdl_id(p_ccb, evt_data.hdr.mdl_id);
+ send_rsp = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ if (((reject_code != MCA_RSP_SUCCESS) && (evt_data.hdr.op_code != MCA_OP_SYNC_INFO_IND))
+ || send_rsp)
+ {
+ p_buf = (BT_HDR *)GKI_getbuf (MCA_CTRL_MTU);
+ if (p_buf)
+ {
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p = p_start = (UINT8*)(p_buf + 1) + L2CAP_MIN_OFFSET;
+ *p++ = reject_opcode;
+ *p++ = reject_code;
+ UINT16_TO_BE_STREAM (p, evt_data.hdr.mdl_id);
+ /*
+ if (((*p_start) == MCA_OP_MDL_CREATE_RSP) && (reject_code == MCA_RSP_SUCCESS))
+ {
+ *p++ = evt_data.create_ind.cfg;
+ }
+ */
+
+ p_buf->len = p - p_start;
+ L2CA_DataWrite (p_ccb->lcid, p_buf);
+ }
+ }
+
+ if (reject_code == MCA_RSP_SUCCESS)
+ {
+ /* use the received GKI buffer to store information to double check response API */
+ p_rx_msg->op_code = evt_data.hdr.op_code;
+ p_rx_msg->mdl_id = evt_data.hdr.mdl_id;
+ p_ccb->p_rx_msg = p_rx_msg;
+ if (send_rsp)
+ {
+ GKI_freebuf (p_pkt);
+ p_ccb->p_rx_msg = NULL;
+ }
+ mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
+ }
+ else
+ GKI_freebuf (p_pkt);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_hdl_rsp
+**
+** Description This function is called when a MCAP response is received from
+** the peer. It calls the application callback function with
+** the results.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_hdl_rsp(tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ BT_HDR *p_pkt = &p_data->hdr;
+ UINT8 *p;
+ tMCA_CTRL evt_data;
+ BOOLEAN chk_mdl = FALSE;
+ tMCA_DCB *p_dcb;
+ tMCA_RESULT result = MCA_BAD_HANDLE;
+ tMCA_TC_TBL *p_tbl;
+
+ if (p_ccb->p_tx_req)
+ {
+ /* verify that the received response matches the sent request */
+ p = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+ evt_data.hdr.op_code = *p++;
+ if ((evt_data.hdr.op_code == 0) ||
+ ((p_ccb->p_tx_req->op_code + 1) == evt_data.hdr.op_code))
+ {
+ evt_data.rsp.rsp_code = *p++;
+ mca_stop_timer(p_ccb);
+ BE_STREAM_TO_UINT16 (evt_data.hdr.mdl_id, p);
+ if (evt_data.hdr.op_code == MCA_OP_MDL_CREATE_RSP)
+ {
+ evt_data.create_cfm.cfg = *p++;
+ chk_mdl = TRUE;
+ }
+ else if (evt_data.hdr.op_code == MCA_OP_MDL_RECONNECT_RSP)
+ chk_mdl = TRUE;
+
+ if (chk_mdl)
+ {
+ p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx);
+ if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS)
+ {
+ if (evt_data.hdr.mdl_id != p_dcb->mdl_id)
+ {
+ MCA_TRACE_ERROR2 ("peer's mdl_id=%d != our mdl_id=%d", evt_data.hdr.mdl_id, p_dcb->mdl_id);
+ /* change the response code to be an error */
+ if (evt_data.rsp.rsp_code == MCA_RSP_SUCCESS)
+ {
+ evt_data.rsp.rsp_code = MCA_RSP_BAD_MDL;
+ /* send Abort */
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ MCA_Abort(mca_ccb_to_hdl(p_ccb));
+ }
+ }
+ else if (p_dcb->p_chnl_cfg)
+ {
+ /* the data channel configuration is known. Proceed with data channel initiation */
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_MCAP_DATA, p_ccb->sec_mask,
+ p_ccb->data_vpsm, BTM_SEC_PROTO_MCA, p_ccb->p_tx_req->dcb_idx);
+ p_dcb->lcid = mca_l2c_open_req(p_ccb->peer_addr, p_ccb->data_vpsm, p_dcb->p_chnl_cfg);
+ if (p_dcb->lcid)
+ {
+ p_tbl = mca_tc_tbl_dalloc(p_dcb);
+ if (p_tbl)
+ {
+ p_tbl->state = MCA_TC_ST_CONN;
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ result = MCA_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ /* mark this MCL as pending and wait for MCA_DataChnlCfg */
+ p_ccb->status = MCA_CCB_STAT_PENDING;
+ result = MCA_SUCCESS;
+ }
+ }
+
+ if (result != MCA_SUCCESS && p_dcb)
+ {
+ mca_dcb_dealloc(p_dcb, NULL);
+ }
+ } /* end of chk_mdl */
+
+ if (p_ccb->status != MCA_CCB_STAT_PENDING)
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_ccb_report_event(p_ccb, evt_data.hdr.op_code, &evt_data);
+ }
+ /* else a bad response is received */
+ }
+ else
+ {
+ /* not expecting any response. drop it */
+ MCA_TRACE_WARNING0 ("dropping received rsp (not expecting a response)");
+ }
+ GKI_freebuf (p_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_ll_open
+**
+** Description This function is called to report MCA_CONNECT_IND_EVT event.
+** It also clears the congestion flag (ccb.cong).
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_ll_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ tMCA_CTRL evt_data;
+ p_ccb->cong = FALSE;
+ evt_data.connect_ind.mtu = p_data->open.peer_mtu;
+ memcpy (evt_data.connect_ind.bd_addr, p_ccb->peer_addr, BD_ADDR_LEN);
+ mca_ccb_report_event (p_ccb, MCA_CONNECT_IND_EVT, &evt_data);
+}
+
+/*******************************************************************************
+**
+** Function mca_ccb_dl_open
+**
+** Description This function is called when data channel is open.
+** It clears p_tx_req to allow other message exchage on this CL.
+**
+** Returns void.
+**
+*******************************************************************************/
+void mca_ccb_dl_open (tMCA_CCB *p_ccb, tMCA_CCB_EVT *p_data)
+{
+ mca_free_buf ((void **)&p_ccb->p_tx_req);
+ mca_free_buf ((void **)&p_ccb->p_rx_msg);
+ p_ccb->status = MCA_CCB_STAT_NORM;
+}
+