/******************************************************************************* ** ** 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_DEBUG ("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 */ if (p_dcb) { 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_WARNING ("dropping api req"); GKI_freebuf (p_data); } }
/******************************************************************************* ** ** Function avdt_ad_write_req ** ** Description This function is called by a CCB or SCB to send data to a ** transport channel. It looks up the LCID of the channel ** based on the type, CCB, and SCB (if present). Then it ** passes the data to L2CA_DataWrite(). ** ** ** Returns AVDT_AD_SUCCESS, if data accepted, else FALSE ** AVDT_AD_CONGESTED, if data accepted and the channel is congested ** AVDT_AD_FAILED, if error ** *******************************************************************************/ UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf) { UINT8 tcid; /* get tcid from type, scb */ tcid = avdt_ad_type_to_tcid(type, p_scb); return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf); }
/******************************************************************************* ** ** 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_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_DEBUG ("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); } }
static UINT8 L2cap_DataWrite (UINT16 cid, char *p_data, UINT32 len) { BTIF_TRACE_DEBUG("L2cap_DataWrite:: Invoked"); BT_HDR *p_msg = NULL;(BT_HDR *) GKI_getpoolbuf (GKI_POOL_ID_3); UINT8 *ptr, *p_start; p_msg = (BT_HDR *) GKI_getpoolbuf (GKI_POOL_ID_3); BTIF_TRACE_DEBUG("GKI_getpoolbuf"); if (!p_msg) { BTIF_TRACE_DEBUG("No resource to allocate"); return BT_STATUS_FAIL; } p_msg->offset = L2CAP_MIN_OFFSET; ptr = p_start = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; p_msg->len = len; //Sends len bytes, irrespective of what you copy to the buffer memcpy(ptr, p_data, len); return L2CA_DataWrite(cid, p_msg); }
/******************************************************************************* ** ** Function attp_send_msg_to_l2cap ** ** Description Send message to L2CAP. ** *******************************************************************************/ tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) { UINT16 l2cap_ret; if (p_tcb->att_lcid == L2CAP_ATT_CID) l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); else l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); if (l2cap_ret == L2CAP_DW_FAILED) { GATT_TRACE_ERROR("ATT failed to pass msg:0x%0x to L2CAP", *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); return GATT_INTERNAL_ERROR; } else if (l2cap_ret == L2CAP_DW_CONGESTED) { GATT_TRACE_DEBUG("ATT congested, message accepted"); return GATT_CONGESTED; } return GATT_SUCCESS; }
/******************************************************************************* ** ** Function rfc_check_send_cmd ** ** Description This function is called to send an RFCOMM command message ** or to handle the RFCOMM command message queue. ** ** Returns void ** *******************************************************************************/ void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf) { BT_HDR *p; /* if passed a buffer queue it */ if (p_buf != NULL) { if (p_mcb->cmd_q == NULL) { RFCOMM_TRACE_ERROR("%s: empty queue: p_mcb = %p p_mcb->lcid = %u cached p_mcb = %p", __func__, p_mcb, p_mcb->lcid, rfc_find_lcid_mcb(p_mcb->lcid)); } fixed_queue_enqueue(p_mcb->cmd_q, p_buf); } /* handle queue if L2CAP not congested */ while (p_mcb->l2cap_congested == FALSE) { if ((p = (BT_HDR *)fixed_queue_try_dequeue(p_mcb->cmd_q)) == NULL) { break; } L2CA_DataWrite (p_mcb->lcid, p); } }
/******************************************************************************* ** ** 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_DEBUG ("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_DEBUG ("received mdl id: %d ", evt_data.hdr.mdl_id); if (p_ccb->status == MCA_CCB_STAT_PENDING) { MCA_TRACE_DEBUG ("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_DEBUG ("still handling prev req"); /* still holding previous message, reject this new one ?? */ } else if (p_ccb->p_tx_req) { MCA_TRACE_DEBUG ("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_DEBUG ("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_ERROR ("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_DEBUG ("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_ERROR ("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_ERROR ("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 hidh_conn_snd_data ** ** Description This function is sends out data. ** ** Returns tHID_STATUS ** *******************************************************************************/ tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *buf) { tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn; BT_HDR *p_buf; UINT8 *p_out; UINT16 bytes_copied; BOOLEAN seg_req = FALSE; UINT16 data_size; UINT16 cid; UINT8 pool_id; UINT8 use_data = 0 ; BOOLEAN blank_datc = FALSE; if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr, BT_TRANSPORT_BR_EDR)) { if (buf) GKI_freebuf ((void *)buf); return( HID_ERR_NO_CONNECTION ); } if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { if (buf) GKI_freebuf ((void *)buf); return( HID_ERR_CONGESTED ); } switch( trans_type ) { case HID_TRANS_CONTROL: case HID_TRANS_GET_REPORT: case HID_TRANS_SET_REPORT: case HID_TRANS_GET_PROTOCOL: case HID_TRANS_SET_PROTOCOL: case HID_TRANS_GET_IDLE: case HID_TRANS_SET_IDLE: cid = p_hcon->ctrl_cid; pool_id = HID_CONTROL_POOL_ID; break; case HID_TRANS_DATA: cid = p_hcon->intr_cid; pool_id = HID_INTERRUPT_POOL_ID; break; default: return (HID_ERR_INVALID_PARAM) ; } if( trans_type == HID_TRANS_SET_IDLE ) use_data = 1; else if( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) ) use_data = 2; do { if ( buf == NULL || blank_datc ) { if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) return (HID_ERR_NO_RESOURCES); p_buf->offset = L2CAP_MIN_OFFSET; seg_req = FALSE; data_size = 0; bytes_copied = 0; blank_datc = FALSE; } else if ( (buf->len > (p_hcon->rem_mtu_size - 1))) { if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL) return (HID_ERR_NO_RESOURCES); p_buf->offset = L2CAP_MIN_OFFSET; seg_req = TRUE; data_size = buf->len; bytes_copied = p_hcon->rem_mtu_size - 1; } else { p_buf = buf ; p_buf->offset -= 1; seg_req = FALSE; data_size = buf->len; bytes_copied = buf->len; } p_out = (UINT8 *)(p_buf + 1) + p_buf->offset; *p_out++ = HID_BUILD_HDR(trans_type, param); /* If report ID required for this device */ if( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) ) { *p_out = report_id; data_size = bytes_copied = 1; } if (seg_req) { memcpy (p_out, (((UINT8 *)(buf+1)) + buf->offset), bytes_copied); buf->offset += bytes_copied; buf->len -= bytes_copied; } else if( use_data == 1) { *(p_out+bytes_copied) = data & 0xff; } else if( use_data == 2 ) { *(p_out+bytes_copied) = data & 0xff; *(p_out+bytes_copied+1) = (data >> 8) & 0xff ; } p_buf->len = bytes_copied + 1 + use_data; data_size -= bytes_copied; /* Send the buffer through L2CAP */ if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (!L2CA_DataWrite (cid, p_buf))) return (HID_ERR_CONGESTED); if (data_size) trans_type = HID_TRANS_DATAC; else if( bytes_copied == (p_hcon->rem_mtu_size - 1) ) { trans_type = HID_TRANS_DATAC; blank_datc = TRUE; } } while ((data_size != 0) || blank_datc ) ;