/******************************************************************************* ** ** 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 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 = p_tcb->pending_enc_clcb.count; 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"); } }
/******************************************************************************* ** ** Function gatt_le_cong_cback ** ** Description This function is called when GATT fixed channel is congested ** or uncongested. ** ** Returns void ** *******************************************************************************/ static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congested) { tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(remote_bda, BT_TRANSPORT_LE); /* if uncongested, check to see if there is any more pending data */ if (p_tcb != NULL) { gatt_channel_congestion(p_tcb, congested); } }
/******************************************************************************* ** ** Function gatt_act_connect ** ** Description GATT connection initiation. ** ** Returns void. ** *******************************************************************************/ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr) { BOOLEAN ret = FALSE; tGATT_TCB *p_tcb; UINT8 st; GATT_TRACE_DEBUG0("gatt_act_connect"); if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL) { ret = TRUE; st = gatt_get_ch_state(p_tcb); /* before link down, another app try to open a GATT connection */ if(st == GATT_CH_OPEN && gatt_num_apps_hold_link(p_tcb) == 0 && /* only connection on fix channel when the l2cap channel is already open */ p_tcb->att_lcid == L2CAP_ATT_CID ) { if (!gatt_connect(bd_addr, p_tcb)) ret = FALSE; } else if(st == GATT_CH_CLOSING) { /* need to complete the closing first */ ret = FALSE; } } else { if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL) { if (!gatt_connect(bd_addr, p_tcb)) { GATT_TRACE_ERROR0("gatt_connect failed"); memset(p_tcb, 0, sizeof(tGATT_TCB)); } else ret = TRUE; } else { ret = 0; GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if); } } if (ret) { gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE); } return ret; }
/******************************************************************************* ** ** Function gatt_act_connect ** ** Description GATT connection initiation. ** ** Returns void. ** *******************************************************************************/ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, tBT_TRANSPORT transport) { BOOLEAN ret = FALSE; tGATT_TCB *p_tcb; UINT8 st; if ((p_tcb = gatt_find_tcb_by_addr(bd_addr, transport)) != NULL) { ret = TRUE; st = gatt_get_ch_state(p_tcb); /* before link down, another app try to open a GATT connection */ if(st == GATT_CH_OPEN && gatt_num_apps_hold_link(p_tcb) == 0 && transport == BT_TRANSPORT_LE ) { if (!gatt_connect(bd_addr, p_tcb, transport)) ret = FALSE; } else if(st == GATT_CH_CLOSING) { /* need to complete the closing first */ ret = FALSE; } } else { if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, transport)) != NULL) { if (!gatt_connect(bd_addr, p_tcb, transport)) { GATT_TRACE_ERROR("gatt_connect failed"); memset(p_tcb, 0, sizeof(tGATT_TCB)); } else ret = TRUE; } else { ret = 0; GATT_TRACE_ERROR("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if); } } if (ret) { gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE); } return ret; }
/******************************************************************************* ** ** Function gatt_l2cif_connect_ind ** ** Description This function handles an inbound connection indication ** from L2CAP. This is the case where we are acting as a ** server. ** ** Returns void ** *******************************************************************************/ static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) { /* do we already have a control channel for this peer? */ UINT8 result = L2CAP_CONN_OK; tL2CAP_CFG_INFO cfg; tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_BR_EDR); UNUSED(psm); GATT_TRACE_ERROR("Connection indication cid = %d", lcid); /* new connection ? */ if (p_tcb == NULL) { /* allocate tcb */ if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) { /* no tcb available, reject L2CAP connection */ result = L2CAP_CONN_NO_RESOURCES; } else p_tcb->att_lcid = lcid; } else /* existing connection , reject it */ { result = L2CAP_CONN_NO_RESOURCES; } /* Send L2CAP connect rsp */ L2CA_ConnectRsp(bd_addr, id, lcid, result, 0); /* if result ok, proceed with connection */ if (result == L2CAP_CONN_OK) { /* transition to configuration state */ gatt_set_ch_state(p_tcb, GATT_CH_CFG); /* Send L2CAP config req */ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); cfg.mtu_present = TRUE; cfg.mtu = GATT_MAX_MTU_SIZE; L2CA_ConfigReq(lcid, &cfg); } }
/******************************************************************************* ** ** Function gatt_act_connect ** ** Description GATT connection initiation. ** ** Returns void. ** *******************************************************************************/ BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr) { BOOLEAN ret = FALSE; tGATT_TCB *p_tcb; GATT_TRACE_DEBUG0("gatt_act_connect"); if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL) { ret = TRUE; if(gatt_get_ch_state(p_tcb) == GATT_CH_CLOSING ) { /* need to complete the closing first */ ret = FALSE; } } else { if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL) { if (!gatt_connect(bd_addr, p_tcb)) { GATT_TRACE_ERROR0("gatt_connect failed"); memset(p_tcb, 0, sizeof(tGATT_TCB)); } else ret = TRUE; } else { ret = 0; GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if); } } if (ret) { gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE); } return ret; }
/******************************************************************************* ** ** Function gatt_le_data_ind ** ** Description This function is called when data is received from L2CAP. ** if we are the originator of the connection, we are the ATT ** client, and the received message is queued up for the client. ** ** If we are the destination of the connection, we are the ATT ** server, so the message is passed to the server processing ** function. ** ** Returns void ** *******************************************************************************/ static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf) { tGATT_TCB *p_tcb; /* Find CCB based on bd addr */ if ((p_tcb = gatt_find_tcb_by_addr (bd_addr, BT_TRANSPORT_LE)) != NULL && gatt_get_ch_state(p_tcb) >= GATT_CH_OPEN) { gatt_data_process(p_tcb, p_buf); } else { GKI_freebuf (p_buf); if (p_tcb != NULL) { GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d", gatt_get_ch_state(p_tcb)); } } }
/******************************************************************************* ** ** Function gatt_disconnect ** ** Description This function is called to disconnect to an ATT device. ** ** Parameter rem_bda: remote device address to disconnect from. ** ** Returns TRUE: if connection found and to be disconnected; otherwise ** return FALSE. ** *******************************************************************************/ BOOLEAN gatt_disconnect (BD_ADDR rem_bda) { tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(rem_bda); BOOLEAN ret = FALSE; tGATT_CH_STATE ch_state; GATT_TRACE_DEBUG0 ("gatt_disconnect "); if (p_tcb != NULL) { ret = TRUE; if ( (ch_state = gatt_get_ch_state(p_tcb)) != GATT_CH_CLOSING ) { if (p_tcb->att_lcid == L2CAP_ATT_CID) { if (ch_state == GATT_CH_OPEN) { /* only LCB exist between remote device and local */ ret = L2CA_RemoveFixedChnl (L2CAP_ATT_CID, rem_bda); } else { gatt_set_ch_state(p_tcb, GATT_CH_CLOSING); ret = L2CA_CancelBleConnectReq (rem_bda); } } else { ret = L2CA_DisconnectReq(p_tcb->att_lcid); } } else { GATT_TRACE_DEBUG0 ("gatt_disconnect already in closing state"); } } return ret; }
/******************************************************************************* ** ** Function gatt_le_connect_cback ** ** Description This callback function is called by L2CAP to indicate that ** the ATT fixed channel for LE is ** connected (conn = TRUE)/disconnected (conn = FALSE). ** *******************************************************************************/ static void gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason, tBT_TRANSPORT transport) { tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); BOOLEAN check_srv_chg = FALSE; tGATTS_SRV_CHG *p_srv_chg_clt=NULL; /* ignore all fixed channel connect/disconnect on BR/EDR link for GATT */ if (transport == BT_TRANSPORT_BR_EDR) return; GATT_TRACE_DEBUG ("GATT ATT protocol channel with BDA: %08x%04x is %s", (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], (bd_addr[4]<<8)+bd_addr[5], (connected) ? "connected" : "disconnected"); if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) { check_srv_chg = TRUE; } else { if (btm_sec_is_a_bonded_dev(bd_addr)) gatt_add_a_bonded_dev_for_srv_chg(bd_addr); } if (connected) { /* do we have a channel initiating a connection? */ if (p_tcb) { /* we are initiating connection */ if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN) { /* send callback */ gatt_set_ch_state(p_tcb, GATT_CH_OPEN); p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; gatt_send_conn_cback(p_tcb); } if (check_srv_chg) gatt_chk_srv_chg (p_srv_chg_clt); } /* this is incoming connection or background connection callback */ else { if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, BT_TRANSPORT_LE)) != NULL) { p_tcb->att_lcid = L2CAP_ATT_CID; gatt_set_ch_state(p_tcb, GATT_CH_OPEN); p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; gatt_send_conn_cback (p_tcb); if (check_srv_chg) { gatt_chk_srv_chg (p_srv_chg_clt); } } else { GATT_TRACE_ERROR("CCB max out, no rsources"); } } } else { gatt_cleanup_upon_disc(bd_addr, reason, transport); GATT_TRACE_DEBUG ("ATT disconnected"); } }
/******************************************************************************* ** ** Function gatt_le_connect_cback ** ** Description This callback function is called by L2CAP to indicate that ** the ATT fixed channel for LE is ** connected (conn = TRUE)/disconnected (conn = FALSE). ** *******************************************************************************/ static void gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason) { tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr); BOOLEAN check_srv_chg = FALSE; tGATTS_SRV_CHG *p_srv_chg_clt=NULL; BOOLEAN is_bg_conn = FALSE; GATT_TRACE_DEBUG3 ("GATT ATT protocol channel with BDA: %08x%04x is %s", (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], (bd_addr[4]<<8)+bd_addr[5], (connected) ? "connected" : "disconnected"); if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) { check_srv_chg = TRUE; } else { if (btm_sec_is_a_bonded_dev(bd_addr)) gatt_add_a_bonded_dev_for_srv_chg(bd_addr); } if (connected) { GATT_TRACE_DEBUG1("connected is TRUE reason=%d",reason ); /* BR/EDR lik, ignore this callback */ if (reason == 0) return; /* do we have a channel initiating a connection? */ if (p_tcb) { if (check_srv_chg) gatt_chk_srv_chg (p_srv_chg_clt); /* we are initiating connection */ if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN) { /* send callback */ gatt_set_ch_state(p_tcb, GATT_CH_OPEN); p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; gatt_send_conn_cback(FALSE, p_tcb); } else /* there was an exisiting link, ignore the callback */ { GATT_TRACE_ERROR0("connection already up, ignore it"); return; } } /* this is incoming connection or background connection callback */ else { if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL) { p_tcb->att_lcid = L2CAP_ATT_CID; gatt_set_ch_state(p_tcb, GATT_CH_OPEN); p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; if (L2CA_GetBleConnRole(p_tcb->peer_bda)== HCI_ROLE_MASTER) { is_bg_conn = TRUE; } gatt_send_conn_cback (is_bg_conn, p_tcb); if (check_srv_chg) { gatt_chk_srv_chg (p_srv_chg_clt); } } else { GATT_TRACE_ERROR0("CCB max out, no rsources"); } } } else { gatt_cleanup_upon_disc(bd_addr, reason); GATT_TRACE_DEBUG0 ("ATT disconnected"); } }