/******************************************************************************* ** ** Function gatt_update_app_use_link_flag ** ** Description Update the application use link flag and optional to check the acl link ** if the link is up then set the idle time out accordingly ** ** Returns void. ** *******************************************************************************/ void gatt_update_app_use_link_flag (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link) { GATT_TRACE_DEBUG2("gatt_update_app_use_link_flag is_add=%d chk_link=%d", is_add, check_acl_link); gatt_update_app_hold_link_status(gatt_if, p_tcb, is_add); if (check_acl_link && p_tcb && (BTM_GetHCIConnHandle(p_tcb->peer_bda) != GATT_INVALID_ACL_HANDLE)) { if (is_add) { GATT_TRACE_DEBUG0("GATT disables link idle timer"); /* acl link is connected disable the idle timeout */ GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT); } else { if (!gatt_num_apps_hold_link(p_tcb)) { /* acl link is connected but no application needs to use the link so set the timeout value to GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP seconds */ GATT_TRACE_DEBUG1("GATT starts link idle timer =%d sec", GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP); GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP); } } } }
/******************************************************************************* ** ** Function srvc_eng_init ** ** Description Initializa the GATT Service engine. ** *******************************************************************************/ tGATT_STATUS srvc_eng_init (void) { tBT_UUID app_uuid = {LEN_UUID_16, {UUID_SERVCLASS_DEVICE_INFO}}; if (srvc_eng_cb.enabled) { GATT_TRACE_ERROR0("DIS already initalized"); } else { memset(&srvc_eng_cb, 0, sizeof(tDIS_CB)); /* Create a GATT profile service */ srvc_eng_cb.gatt_if = GATT_Register(&app_uuid, &srvc_gatt_cback); GATT_StartIf(srvc_eng_cb.gatt_if); GATT_TRACE_DEBUG1 ("Srvc_Init: gatt_if=%d ", srvc_eng_cb.gatt_if); srvc_eng_cb.enabled = TRUE; //#if DIS_INCLUDED == TRUE dis_cb.dis_read_uuid_idx = 0xff; //#endif } return GATT_SUCCESS; }
/******************************************************************************* ** ** Function gatt_chk_srv_chg ** ** Description Check sending service chnaged Indication is required or not ** if required then send the Indication ** ** Returns void ** *******************************************************************************/ void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt) { GATT_TRACE_DEBUG1("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed ); if (p_srv_chg_clt->srv_changed) { gatt_send_srv_chg_ind(p_srv_chg_clt->bda); } }
/******************************************************************************* ** ** Function gatt_get_ch_state ** ** Description This function get the ch_state in tcb ** ** Returns none ** *******************************************************************************/ tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb) { tGATT_CH_STATE ch_state = GATT_CH_CLOSE; if (p_tcb) { GATT_TRACE_DEBUG1 ("gatt_get_ch_state: ch_state=%d", p_tcb->ch_state); ch_state = p_tcb->ch_state; } return ch_state; }
/******************************************************************************* ** ** 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 ** *******************************************************************************/ void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf) { GATT_TRACE_DEBUG0("gatt_data_process"); UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; UINT8 op_code, pseudo_op_code; UINT16 msg_len; if (p_buf->len > 0) { msg_len = p_buf->len - 1; STREAM_TO_UINT8(op_code, p); GATT_TRACE_DEBUG1("op_code = %d", op_code); /* remove the two MSBs associated with sign write and write cmd */ pseudo_op_code = op_code & (~GATT_WRITE_CMD_MASK); if (pseudo_op_code < GATT_OP_CODE_MAX) { if (op_code == GATT_SIGN_CMD_WRITE) { GATT_TRACE_DEBUG0("op_code == GATT_SIGN_CMD_WRITE"); gatt_verify_signature(p_tcb, p_buf); return; } else { /* message from client */ if ((op_code % 2) == 0) { GATT_TRACE_DEBUG0("gatt_server_handle_client_req"); gatt_server_handle_client_req (p_tcb, op_code, msg_len, p); } else { GATT_TRACE_DEBUG0("gatt_client_handle_server_rsp"); gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p); } } } else { GATT_TRACE_ERROR1 ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code); } } else { GATT_TRACE_ERROR0 ("invalid data length, ignore"); } GKI_freebuf (p_buf); }
/******************************************************************************* ** ** Function gatt_send_prepare_write ** ** Description Send prepare write. ** ** Returns void. ** *******************************************************************************/ void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb) { tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; UINT16 to_send, offset; UINT8 rt = GATT_SUCCESS; UINT8 type = p_clcb->op_subtype; GATT_TRACE_DEBUG1("gatt_send_prepare_write type=0x%x", type ); to_send = p_attr->len - p_attr->offset; if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) /* 2 = UINT16 offset bytes */ to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE; p_clcb->s_handle = p_attr->handle; offset = p_attr->offset; if (type == GATT_WRITE_PREPARE) { offset += p_clcb->start_offset; } GATT_TRACE_DEBUG2("offset =0x%x len=%d", offset, to_send ); rt = gatt_send_write_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_PREPARE_WRITE, p_attr->handle, to_send, /* length */ offset, /* used as offset */ p_attr->value + p_attr->offset); /* data */ /* remember the write long attribute length */ p_clcb->counter = to_send; if (rt != GATT_SUCCESS ) { gatt_end_operation(p_clcb, rt, NULL); } }
/******************************************************************************* ** ** Function gatt_init_srv_chg ** ** Description This function is used to initialize the service changed ** attribute value ** ** Returns void ** *******************************************************************************/ void gatt_init_srv_chg (void) { tGATTS_SRV_CHG_REQ req; tGATTS_SRV_CHG_RSP rsp; BOOLEAN status; UINT8 num_clients,i; tGATTS_SRV_CHG srv_chg_clt; GATT_TRACE_DEBUG0("gatt_init_srv_chg"); if (gatt_cb.cb_info.p_srv_chg_callback) { status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_NUM_CLENTS, NULL, &rsp); if (status && rsp.num_clients) { GATT_TRACE_DEBUG1("gatt_init_srv_chg num_srv_chg_clt_clients=%d", rsp.num_clients); num_clients = rsp.num_clients; i = 1; /* use one based index */ while ((i <= num_clients) && status) { req.client_read_index = i; if ((status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_CLENT, &req, &rsp))) { memcpy(&srv_chg_clt, &rsp.srv_chg ,sizeof(tGATTS_SRV_CHG)); if (gatt_add_srv_chg_clt(&srv_chg_clt) == NULL) { GATT_TRACE_ERROR0("Unable to add a service change client"); status = FALSE; } } i++; } } } else { GATT_TRACE_DEBUG0("gatt_init_srv_chg callback not registered yet"); } }
/******************************************************************************* ** ** Function gatt_act_read ** ** Description GATT read operation. ** ** Returns void. ** *******************************************************************************/ void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset) { tGATT_TCB *p_tcb = p_clcb->p_tcb; UINT8 rt = GATT_INTERNAL_ERROR; tGATT_CL_MSG msg; UINT8 op_code = 0; memset (&msg, 0, sizeof(tGATT_CL_MSG)); switch (p_clcb->op_subtype) { case GATT_READ_CHAR_VALUE: case GATT_READ_BY_TYPE: op_code = GATT_REQ_READ_BY_TYPE; msg.browse.s_handle = p_clcb->s_handle; msg.browse.e_handle = p_clcb->e_handle; if (p_clcb->op_subtype == GATT_READ_BY_TYPE) memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); else { msg.browse.uuid.len = LEN_UUID_16; msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE; } break; case GATT_READ_CHAR_VALUE_HDL: case GATT_READ_BY_HANDLE: if (!p_clcb->counter) { op_code = GATT_REQ_READ; msg.handle = p_clcb->s_handle; } else { if (!p_clcb->first_read_blob_after_read) p_clcb->first_read_blob_after_read = TRUE; else p_clcb->first_read_blob_after_read = FALSE; GATT_TRACE_DEBUG1("gatt_act_read first_read_blob_after_read=%d", p_clcb->first_read_blob_after_read); op_code = GATT_REQ_READ_BLOB; msg.read_blob.offset = offset; msg.read_blob.handle = p_clcb->s_handle; } p_clcb->op_subtype &= ~ 0x80; break; case GATT_READ_PARTIAL: op_code = GATT_REQ_READ_BLOB; msg.read_blob.handle = p_clcb->s_handle; msg.read_blob.offset = offset; break; case GATT_READ_MULTIPLE: op_code = GATT_REQ_READ_MULTI; memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI)); break; case GATT_READ_INC_SRV_UUID128: op_code = GATT_REQ_READ; msg.handle = p_clcb->s_handle; p_clcb->op_subtype &= ~ 0x90; break; default: GATT_TRACE_ERROR1("Unknown read type: %d", p_clcb->op_subtype); break; } if ( op_code == 0 || (rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg)) != GATT_SUCCESS) { gatt_end_operation(p_clcb, rt, NULL); } }
/******************************************************************************* ** ** 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"); } }