/******************************************************************************* ** ** 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_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_l2cif_data_ind_cback ** ** Description This is the L2CAP data indication callback function. ** ** ** Returns void ** *******************************************************************************/ static void gatt_l2cif_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) { tGATT_TCB *p_tcb; /* look up clcb for this channel */ if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL && gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { /* process the data */ gatt_data_process(p_tcb, p_buf); } else /* prevent buffer leak */ GKI_freebuf(p_buf); }
/******************************************************************************* ** ** 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_config_cfm_cback ** ** Description This is the L2CAP config confirm callback function. ** ** ** Returns void ** *******************************************************************************/ void gatt_l2cif_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) { tGATT_TCB *p_tcb; tGATTS_SRV_CHG *p_srv_chg_clt=NULL; /* look up clcb for this channel */ if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { /* if in correct state */ if ( gatt_get_ch_state(p_tcb) == GATT_CH_CFG) { /* if result successful */ if (p_cfg->result == L2CAP_CFG_OK) { /* update flags */ p_tcb->ch_flags |= GATT_L2C_CFG_CFM_DONE; /* if configuration complete */ if (p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) { gatt_set_ch_state(p_tcb, GATT_CH_OPEN); if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { gatt_chk_srv_chg(p_srv_chg_clt); } else { if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); } /* send callback */ gatt_send_conn_cback(p_tcb); } } /* else failure */ else { /* Send L2CAP disconnect req */ L2CA_DisconnectReq(lcid); } } } }
/******************************************************************************* ** ** 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_connect ** ** Description This function is called to initiate a connection to a peer device. ** ** Parameter rem_bda: remote device address to connect to. ** ** Returns TRUE if connection is started, otherwise return FALSE. ** *******************************************************************************/ BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb, tBT_TRANSPORT transport) { BOOLEAN gatt_ret = FALSE; if (gatt_get_ch_state(p_tcb) != GATT_CH_OPEN) gatt_set_ch_state(p_tcb, GATT_CH_CONN); if (transport == BT_TRANSPORT_LE) { p_tcb->att_lcid = L2CAP_ATT_CID; gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda); } else { if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) != 0) gatt_ret = TRUE; } return gatt_ret; }
/******************************************************************************* ** ** Function gatt_l2c_connect_cfm_cback ** ** Description This is the L2CAP connect confirm callback function. ** ** ** Returns void ** *******************************************************************************/ static void gatt_l2cif_connect_cfm_cback(UINT16 lcid, UINT16 result) { tGATT_TCB *p_tcb; tL2CAP_CFG_INFO cfg; /* look up clcb for this channel */ if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { GATT_TRACE_DEBUG("gatt_l2c_connect_cfm_cback result: %d ch_state: %d, lcid:0x%x", result, gatt_get_ch_state(p_tcb), p_tcb->att_lcid); /* if in correct state */ if (gatt_get_ch_state(p_tcb) == GATT_CH_CONN) { /* if result successful */ if (result == L2CAP_CONN_OK) { /* set channel 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); } /* else initiating connection failure */ else { gatt_cleanup_upon_disc(p_tcb->peer_bda, result, GATT_TRANSPORT_BR_EDR); } } else /* wrong state, disconnect it */ { if (result == L2CAP_CONN_OK) { /* just in case the peer also accepts our connection - Send L2CAP disconnect req */ L2CA_DisconnectReq(lcid); } } } }
/******************************************************************************* ** ** Function gatt_connect ** ** Description This function is called to initiate a connection to a peer device. ** ** Parameter rem_bda: remote device address to connect to. ** ** Returns TRUE if connection is started, otherwise return FALSE. ** *******************************************************************************/ BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb) { BOOLEAN gatt_ret = FALSE; if (gatt_get_ch_state(p_tcb) != GATT_CH_OPEN) gatt_set_ch_state(p_tcb, GATT_CH_CONN); /* select the physical link for GATT connection */ if (BTM_UseLeLink(rem_bda)) { p_tcb->att_lcid = L2CAP_ATT_CID; gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda); } else { if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) != 0) gatt_ret = TRUE; } return gatt_ret; }
/******************************************************************************* ** ** 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"); } }