/******************************************************************************* ** ** Function gatt_check_enc_req ** ** Description check link security. ** ** Returns TRUE if encrypted, otherwise FALSE. ** *******************************************************************************/ BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb) { tGATT_TCB *p_tcb = p_clcb->p_tcb; tGATT_SEC_ACTION gatt_sec_act; tBTM_BLE_SEC_ACT btm_ble_sec_act; BOOLEAN status = TRUE; #if (SMP_INCLUDED == TRUE) tBTM_STATUS btm_status; #endif ///SMP_INCLUDED == TRUE tGATT_SEC_ACTION sec_act_old = gatt_get_sec_act(p_tcb); gatt_sec_act = gatt_determine_sec_act(p_clcb); if (sec_act_old == GATT_SEC_NONE) { gatt_set_sec_act(p_tcb, gatt_sec_act); } switch (gatt_sec_act ) { case GATT_SEC_SIGN_DATA: #if (SMP_INCLUDED == TRUE) GATT_TRACE_DEBUG("gatt_security_check_start: Do data signing"); gatt_sign_data(p_clcb); #endif ///SMP_INCLUDED == TRUE break; case GATT_SEC_ENCRYPT: case GATT_SEC_ENCRYPT_NO_MITM: case GATT_SEC_ENCRYPT_MITM: if (sec_act_old < GATT_SEC_ENCRYPT) { GATT_TRACE_DEBUG("gatt_security_check_start: Encrypt now or key upgreade first"); gatt_convert_sec_action(gatt_sec_act, &btm_ble_sec_act); #if (SMP_INCLUDED == TRUE) btm_status = BTM_SetEncryption(p_tcb->peer_bda, p_tcb->transport , gatt_enc_cmpl_cback, &btm_ble_sec_act); if ( (btm_status != BTM_SUCCESS) && (btm_status != BTM_CMD_STARTED)) { GATT_TRACE_ERROR("gatt_security_check_start BTM_SetEncryption failed btm_status=%d", btm_status); status = FALSE; } #endif ///SMP_INCLUDED == TRUE } if (status) { gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); } break; case GATT_SEC_ENC_PENDING: gatt_add_pending_enc_channel_clcb (p_tcb, p_clcb); /* wait for link encrypotion to finish */ break; default: gatt_sec_check_complete(TRUE, p_clcb, gatt_sec_act); break; } if (status == FALSE) { gatt_set_sec_act(p_tcb, GATT_SEC_NONE); gatt_set_ch_state(p_tcb, GATT_CH_OPEN); } return status; }
/******************************************************************************* ** ** Function gatt_l2cif_config_ind_cback ** ** Description This is the L2CAP config indication callback function. ** ** ** Returns void ** *******************************************************************************/ void gatt_l2cif_config_ind_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) { /* GATT uses the smaller of our MTU and peer's MTU */ if ( p_cfg->mtu_present && (p_cfg->mtu >= GATT_MIN_BR_MTU_SIZE && p_cfg->mtu < L2CAP_DEFAULT_MTU)) p_tcb->payload_size = p_cfg->mtu; else p_tcb->payload_size = L2CAP_DEFAULT_MTU; /* send L2CAP configure response */ memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); p_cfg->result = L2CAP_CFG_OK; L2CA_ConfigRsp(lcid, p_cfg); /* if first config ind */ if ((p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) == 0) { /* update flags */ p_tcb->ch_flags |= GATT_L2C_CFG_IND_DONE; /* if configuration complete */ if (p_tcb->ch_flags & GATT_L2C_CFG_CFM_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) && btm_sec_is_le_capable_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); } } } }
/******************************************************************************* ** ** 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_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_sign_data ** ** Description This function sign the data for write command. ** ** Returns TRUE if encrypted, otherwise FALSE. ** *******************************************************************************/ static BOOLEAN gatt_sign_data (tGATT_CLCB *p_clcb) { tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; UINT8 *p_data = NULL, *p; UINT16 payload_size = p_clcb->p_tcb->payload_size; BOOLEAN status = FALSE; UINT8 *p_signature; /* do not need to mark channel securoty activity for data signing */ gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_OK); p_data = (UINT8 *)GKI_getbuf((UINT16)(p_attr->len + 3)); /* 3 = 2 byte handle + opcode */ if (p_data != NULL) { p = p_data; UINT8_TO_STREAM(p, GATT_SIGN_CMD_WRITE); UINT16_TO_STREAM(p, p_attr->handle); ARRAY_TO_STREAM(p, p_attr->value, p_attr->len); /* sign data length should be attribulte value length plus 2B handle + 1B op code */ if ((payload_size - GATT_AUTH_SIGN_LEN - 3) < p_attr->len) p_attr->len = payload_size - GATT_AUTH_SIGN_LEN - 3; p_signature = p_attr->value + p_attr->len; if (BTM_BleDataSignature(p_clcb->p_tcb->peer_bda, p_data, (UINT16)(p_attr->len + 3), /* 3 = 2 byte handle + opcode */ p_signature)) { p_attr->len += BTM_BLE_AUTH_SIGN_LEN; gatt_set_ch_state(p_clcb->p_tcb, GATT_CH_OPEN); gatt_act_write(p_clcb, GATT_SEC_SIGN_DATA); } else { gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL); } GKI_freebuf(p_data); } return status; }
/******************************************************************************* ** ** 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_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 = TRUE; tBT_DEVICE_TYPE dev_type; tBLE_ADDR_TYPE addr_type; BTM_ReadDevInfo(rem_bda, &dev_type, &addr_type); gatt_set_ch_state(p_tcb, GATT_CH_CONN); if (dev_type == BT_DEVICE_TYPE_BLE) { 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 = FALSE; } return gatt_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"); } }