/******************************************************************************* ** ** Function gatt_notify_enc_cmpl ** ** Description link encryption complete notification for all encryption process ** initiated outside GATT. ** ** Returns ** *******************************************************************************/ void gatt_notify_enc_cmpl(BD_ADDR bd_addr) { tGATT_TCB *p_tcb; tGATT_PENDING_ENC_CLCB *p_buf; UINT16 count; UINT8 i = 0; if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE)) != NULL) { for (i = 0; i < GATT_MAX_APPS; i++) { if (gatt_cb.cl_rcb[i].in_use && gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb) { (*gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb)(gatt_cb.cl_rcb[i].gatt_if, bd_addr); } } if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) { gatt_set_sec_act(p_tcb, GATT_SEC_NONE); count = GKI_queue_length(&p_tcb->pending_enc_clcb); for (; count > 0; count --) { if ((p_buf = (tGATT_PENDING_ENC_CLCB *)GKI_dequeue (&p_tcb->pending_enc_clcb)) != NULL) { gatt_security_check_start(p_buf->p_clcb); GKI_freebuf(p_buf); } else { break; } } } } else { GATT_TRACE_DEBUG("notify GATT for encryption completion of unknown device"); } return; }
/******************************************************************************* ** ** Function port_flow_control_user ** ** Description Check the current user flow control and if necessary return ** events to be send to the user based on the user's specified ** flow control type. ** ** Returns event mask to be returned to the application ** *******************************************************************************/ UINT32 port_flow_control_user (tPORT *p_port) { UINT32 event = 0; /* Flow control to the user can be caused by flow controlling by the peer */ /* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */ /* tx_queue is full */ BOOLEAN fc = p_port->tx.peer_fc || !p_port->rfc.p_mcb || !p_port->rfc.p_mcb->peer_ready || (p_port->tx.queue_size > PORT_TX_HIGH_WM) || (GKI_queue_length(&p_port->tx.queue) > PORT_TX_BUF_HIGH_WM); if (p_port->tx.user_fc == fc) return (0); p_port->tx.user_fc = fc; if (fc) event = PORT_EV_FC; else event = PORT_EV_FC | PORT_EV_FCS; return (event); }
/******************************************************************************* ** ** Function gatt_enc_cmpl_cback ** ** Description link encryption complete callback. ** ** Returns ** *******************************************************************************/ void gatt_enc_cmpl_cback(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, tBTM_STATUS result) { tGATT_TCB *p_tcb; UINT8 sec_flag; BOOLEAN status = FALSE; tGATT_PENDING_ENC_CLCB *p_buf; UINT16 count; UNUSED(p_ref_data); GATT_TRACE_DEBUG("gatt_enc_cmpl_cback"); if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, transport)) != NULL) { if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENC_PENDING) { return; } if ((p_buf = (tGATT_PENDING_ENC_CLCB *)GKI_dequeue (&p_tcb->pending_enc_clcb)) != NULL) { if (result == BTM_SUCCESS) { if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM ) { BTM_GetSecurityFlagsByTransport(bd_addr, &sec_flag, transport); if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { status = TRUE; } } else { status = TRUE; } } gatt_sec_check_complete(status , p_buf->p_clcb, p_tcb->sec_act); GKI_freebuf(p_buf); /* start all other pending operation in queue */ count = GKI_queue_length(&p_tcb->pending_enc_clcb); for (; count > 0; count --) { if ((p_buf = (tGATT_PENDING_ENC_CLCB *)GKI_dequeue (&p_tcb->pending_enc_clcb)) != NULL) { gatt_security_check_start(p_buf->p_clcb); GKI_freebuf(p_buf); } else { break; } } } else { GATT_TRACE_ERROR("Unknown operation encryption completed"); } } else { GATT_TRACE_ERROR("enc callback for unknown bd_addr"); } }
UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags) { tL2C_CCB *p_ccb; /* Find the channel control block. We don't know the link it is on. */ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) { L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid); GKI_freebuf (p_data); return (L2CAP_DW_FAILED); } #ifndef TESTER /* Tester may send any amount of data. otherwise sending message bigger than mtu size of peer is a violation of protocol */ if (p_data->len > p_ccb->peer_cfg.mtu) { L2CAP_TRACE_WARNING ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid); GKI_freebuf (p_data); return (L2CAP_DW_FAILED); } #endif /* channel based, packet based flushable or non-flushable */ p_data->layer_specific = flags; /* If already congested, do not accept any more packets */ if (p_ccb->cong_sent) { L2CAP_TRACE_ERROR ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u", p_ccb->local_cid, GKI_queue_length(&p_ccb->xmit_hold_q), p_ccb->buff_quota); GKI_freebuf (p_data); return (L2CAP_DW_FAILED); } //counter_add("l2cap.dyn.tx.bytes", p_data->len); //counter_add("l2cap.dyn.tx.pkts", 1); l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data); if (p_ccb->cong_sent) { return (L2CAP_DW_CONGESTED); } return (L2CAP_DW_SUCCESS); }
/******************************************************************************* ** ** Function port_flow_control_peer ** ** Description Send flow control messages to the peer for both enabling ** and disabling flow control, for both credit-based and ** TS 07.10 flow control mechanisms. ** ** Returns nothing ** *******************************************************************************/ void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count) { if (!p_port->rfc.p_mcb) return; /* If using credit based flow control */ if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) { /* if want to enable flow from peer */ if (enable) { /* update rx credits */ if (count > p_port->credit_rx) { p_port->credit_rx = 0; } else { p_port->credit_rx -= count; } /* If credit count is less than low credit watermark, and user */ /* did not force flow control, send a credit update */ /* There might be a special case when we just adjusted rx_max */ if ((p_port->credit_rx <= p_port->credit_rx_low) && !p_port->rx.user_fc && (p_port->credit_rx_max > p_port->credit_rx)) { rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci, (UINT8) (p_port->credit_rx_max - p_port->credit_rx)); p_port->credit_rx = p_port->credit_rx_max; p_port->rx.peer_fc = FALSE; } } /* else want to disable flow from peer */ else { /* if client registered data callback, just do what they want */ if (p_port->p_data_callback || p_port->p_data_co_callback) { p_port->rx.peer_fc = TRUE; } /* if queue count reached credit rx max, set peer fc */ else if (GKI_queue_length(&p_port->rx.queue) >= p_port->credit_rx_max) { p_port->rx.peer_fc = TRUE; } } } /* else using TS 07.10 flow control */ else { /* if want to enable flow from peer */ if (enable) { /* If rfcomm suspended traffic from the peer based on the rx_queue_size */ /* check if it can be resumed now */ if (p_port->rx.peer_fc && (p_port->rx.queue_size < PORT_RX_LOW_WM) && (GKI_queue_length(&p_port->rx.queue) < PORT_RX_BUF_LOW_WM)) { p_port->rx.peer_fc = FALSE; /* If user did not force flow control allow traffic now */ if (!p_port->rx.user_fc) RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE); } } /* else want to disable flow from peer */ else { /* if client registered data callback, just do what they want */ if (p_port->p_data_callback || p_port->p_data_co_callback) { p_port->rx.peer_fc = TRUE; RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); } /* Check the size of the rx queue. If it exceeds certain */ /* level and flow control has not been sent to the peer do it now */ else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM) || (GKI_queue_length(&p_port->rx.queue) > PORT_RX_BUF_HIGH_WM)) && !p_port->rx.peer_fc) { RFCOMM_TRACE_EVENT ("PORT_DataInd Data reached HW. Sending FC set."); p_port->rx.peer_fc = TRUE; RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE); } } } }
/******************************************************************************* ** ** Function PORT_DataInd ** ** Description This function is called from the RFCOMM layer when data ** buffer is received from the peer. ** *******************************************************************************/ void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf) { tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); UINT8 rx_char1; UINT32 events = 0; UINT8 *p; int i; RFCOMM_TRACE_EVENT("PORT_DataInd with data length %d, p_mcb:%p,p_port:%p,dlci:%d", p_buf->len, p_mcb, p_port, dlci); if (!p_port) { GKI_freebuf (p_buf); return; } /* If client registered callout callback with flow control we can just deliver receive data */ if (p_port->p_data_co_callback) { /* Another packet is delivered to user. Send credits to peer if required */ if(p_port->p_data_co_callback(p_port->inx, (UINT8*)p_buf, -1, DATA_CO_CALLBACK_TYPE_INCOMING)) port_flow_control_peer(p_port, TRUE, 1); else port_flow_control_peer(p_port, FALSE, 0); //GKI_freebuf (p_buf); return; } else RFCOMM_TRACE_ERROR("PORT_DataInd, p_port:%p, p_data_co_callback is null", p_port); /* If client registered callback we can just deliver receive data */ if (p_port->p_data_callback) { /* Another packet is delivered to user. Send credits to peer if required */ port_flow_control_peer(p_port, TRUE, 1); p_port->p_data_callback (p_port->inx, (UINT8 *)(p_buf + 1) + p_buf->offset, p_buf->len); GKI_freebuf (p_buf); return; } /* Check if rx queue exceeds the limit */ if ((p_port->rx.queue_size + p_buf->len > PORT_RX_CRITICAL_WM) || (GKI_queue_length(&p_port->rx.queue) + 1 > p_port->rx_buf_critical)) { RFCOMM_TRACE_EVENT ("PORT_DataInd. Buffer over run. Dropping the buffer"); GKI_freebuf (p_buf); RFCOMM_LineStatusReq (p_mcb, dlci, LINE_STATUS_OVERRUN); return; } /* If user registered to receive notification when a particular byte is */ /* received we mast check all received bytes */ if (((rx_char1 = p_port->user_port_pars.rx_char1) != 0) && (p_port->ev_mask & PORT_EV_RXFLAG)) { for (i = 0, p = (UINT8 *)(p_buf + 1) + p_buf->offset; i < p_buf->len; i++) { if (*p++ == rx_char1) { events |= PORT_EV_RXFLAG; break; } } } PORT_SCHEDULE_LOCK; GKI_enqueue (&p_port->rx.queue, p_buf); p_port->rx.queue_size += p_buf->len; PORT_SCHEDULE_UNLOCK; /* perform flow control procedures if necessary */ port_flow_control_peer(p_port, FALSE, 0); /* If user indicated flow control can not deliver any notifications to him */ if (p_port->rx.user_fc) { if (events & PORT_EV_RXFLAG) p_port->rx_flag_ev_pending = TRUE; return; } events |= PORT_EV_RXCHAR; /* Mask out all events that are not of interest to user */ events &= p_port->ev_mask; if (p_port->p_callback && events) p_port->p_callback (events, p_port->inx); }