diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
| commit | ee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch) | |
| tree | 8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/gatt | |
| parent | 239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff) | |
| download | tangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz | |
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/host/bluedroid/stack/gatt')
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/att_protocol.c | 638 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_api.c | 1805 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_attr.c | 797 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_auth.c | 522 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_cl.c | 1241 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_db.c | 1620 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_main.c | 1250 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_sr.c | 1900 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c | 256 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/gatt_utils.c | 2948 | ||||
| -rw-r--r-- | lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h | 788 |
11 files changed, 13765 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/gatt/att_protocol.c b/lib/bt/host/bluedroid/stack/gatt/att_protocol.c new file mode 100644 index 00000000..310a9756 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/att_protocol.c @@ -0,0 +1,638 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains ATT protocol functions + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE + +#include "gatt_int.h" +#include "stack/l2c_api.h" + +#define GATT_HDR_FIND_TYPE_VALUE_LEN 21 +#define GATT_OP_CODE_SIZE 1 +/********************************************************************** +** ATT protocl message building utility * +***********************************************************************/ +/******************************************************************************* +** +** Function attp_build_mtu_exec_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, rx_mtu); + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */ + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_exec_write_cmd +** +** Description Build a execute write request or response. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(GATT_DATA_BUF_SIZE)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE; + + UINT8_TO_STREAM (p, op_code); + + if (op_code == GATT_REQ_EXEC_WRITE) { + flag &= GATT_PREP_WRITE_EXEC; + UINT8_TO_STREAM (p, flag); + p_buf->len += 1; + } + + } + + return p_buf; +} + +/******************************************************************************* +** +** Function attp_build_err_cmd +** +** Description Build a exchange MTU request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, GATT_RSP_ERROR); + UINT8_TO_STREAM (p, cmd_code); + UINT16_TO_STREAM(p, err_handle); + UINT8_TO_STREAM (p, reason); + + p_buf->offset = L2CAP_MIN_OFFSET; + /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code + 1B status */ + p_buf->len = GATT_HDR_SIZE + 1 + 1; + } + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_browse_cmd +** +** Description Build a read information request or read by type request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + /* length of ATT_READ_BY_TYPE_REQ PDU: opcode(1) + start_handle (2) + end_handle (2) + uuid (2 or 16) */ + const UINT8 payload_size = 1 + 2 + 2 + ((uuid.len == LEN_UUID_16) ? LEN_UUID_16 : LEN_UUID_128); + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + /* Describe the built message location and size */ + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = GATT_OP_CODE_SIZE + 4; + + UINT8_TO_STREAM (p, op_code); + UINT16_TO_STREAM (p, s_hdl); + UINT16_TO_STREAM (p, e_hdl); + p_buf->len += gatt_build_uuid_to_stream(&p, uuid); + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_handles_cmd +** +** Description Build a read by type and value request. +** +** Returns pointer to the command buffer. +** +*******************************************************************************/ +BT_HDR *attp_build_read_by_type_value_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + UINT16 len = p_value_type->value_len; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 5; /* opcode + s_handle + e_handle */ + + UINT8_TO_STREAM (p, GATT_REQ_FIND_TYPE_VALUE); + UINT16_TO_STREAM (p, p_value_type->s_handle); + UINT16_TO_STREAM (p, p_value_type->e_handle); + + p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid); + + if (p_value_type->value_len + p_buf->len > payload_size ) { + len = payload_size - p_buf->len; + } + + memcpy (p, p_value_type->value, len); + p_buf->len += len; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_read_multi_cmd +** +** Description Build a read multiple request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_read_multi_cmd(UINT8 op_code, UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, i = 0; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + UINT8_TO_STREAM (p, op_code); + + for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) { + UINT16_TO_STREAM (p, *(p_handle + i)); + p_buf->len += 2; + } + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_handle_cmd +** +** Description Build a read /read blob request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + + if (op_code == GATT_REQ_READ_BLOB) { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_opcode_cmd +** +** Description Build a request/response with opcode only. +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_opcode_cmd(UINT8 op_code) +{ + BT_HDR *p_buf = NULL; + UINT8 *p; + + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL) { + p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + p_buf->offset = L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->len = 1; + } + + return p_buf; +} +/******************************************************************************* +** +** Function attp_build_value_cmd +** +** Description Build a attribute value request +** +** Returns None. +** +*******************************************************************************/ +BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle, + UINT16 offset, UINT16 len, UINT8 *p_data) +{ + BT_HDR *p_buf = NULL; + UINT8 *p, *pp, pair_len, *p_pair_len; + + if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) { + p = pp = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET; + + UINT8_TO_STREAM (p, op_code); + p_buf->offset = L2CAP_MIN_OFFSET; + p_buf->len = 1; + + if (op_code == GATT_RSP_READ_BY_TYPE) { + p_pair_len = p; + pair_len = len + 2; + UINT8_TO_STREAM (p, pair_len); + p_buf->len += 1; + } + if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ && op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + UINT16_TO_STREAM (p, handle); + p_buf->len += 2; + } + + if (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_RSP_PREPARE_WRITE ) { + UINT16_TO_STREAM (p, offset); + p_buf->len += 2; + } + + if(payload_size < GATT_DEF_BLE_MTU_SIZE || payload_size > GATT_MAX_MTU_SIZE) { + GATT_TRACE_ERROR("invalid payload_size %d", payload_size); + osi_free(p_buf); + return NULL; + } + + if (len > 0 && p_data != NULL) { + /* ensure data not exceed MTU size */ + if (payload_size - p_buf->len < len) { + len = payload_size - p_buf->len; + /* update handle value pair length */ + if (op_code == GATT_RSP_READ_BY_TYPE) { + *p_pair_len = (len + 2); + } + + GATT_TRACE_WARNING("attribute value too long, to be truncated to %d", len); + } + + ARRAY_TO_STREAM (p, p_data, len); + p_buf->len += len; + } + } + return p_buf; +} + +/******************************************************************************* +** +** Function attp_send_msg_to_l2cap +** +** Description Send message to L2CAP. +** +*******************************************************************************/ +tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP) +{ + UINT16 l2cap_ret; + + + if (p_tcb->att_lcid == L2CAP_ATT_CID) { + l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP); + } else { +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP); +#else + l2cap_ret = L2CAP_DW_FAILED; +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + + if (l2cap_ret == L2CAP_DW_FAILED) { + GATT_TRACE_DEBUG("ATT failed to pass msg:0x%0x to L2CAP", + *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset)); + return GATT_INTERNAL_ERROR; + } else if (l2cap_ret == L2CAP_DW_CONGESTED) { + GATT_TRACE_DEBUG("ATT congested, message accepted"); + return GATT_CONGESTED; + } + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function attp_build_sr_msg +** +** Description Build ATT Server PDUs. +** +*******************************************************************************/ +BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg) +{ + BT_HDR *p_cmd = NULL; + UINT16 offset = 0; + + switch (op_code) { + case GATT_RSP_READ_BLOB: + case GATT_RSP_PREPARE_WRITE: + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ: + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + case GATT_RSP_ERROR: + case GATT_RSP_MTU: + /* Need to check the validation of parameter p_msg*/ + if (p_msg == NULL) { + GATT_TRACE_ERROR("Invalid parameters in %s, op_code=0x%x, the p_msg should not be NULL.", __func__, op_code); + return NULL; + } + break; + + default: + break; + } + + switch (op_code) { + case GATT_RSP_READ_BLOB: + case GATT_RSP_PREPARE_WRITE: + GATT_TRACE_EVENT ("ATT_RSP_READ_BLOB/GATT_RSP_PREPARE_WRITE: len = %d offset = %d", + p_msg->attr_value.len, p_msg->attr_value.offset); + offset = p_msg->attr_value.offset; + /* Coverity: [FALSE-POSITIVE error] intended fall through */ + /* Missing break statement between cases in switch statement */ + /* fall through */ + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ: + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + p_cmd = attp_build_value_cmd(p_tcb->payload_size, + op_code, + p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + break; + + case GATT_RSP_WRITE: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_RSP_ERROR: + p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason); + break; + + case GATT_RSP_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, 0); + break; + + case GATT_RSP_MTU: + p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu); + break; + + default: + GATT_TRACE_DEBUG("attp_build_sr_msg: unknown op code = %d", op_code); + break; + } + + if (!p_cmd) { + GATT_TRACE_ERROR("No resources"); + } + + return p_cmd; +} + +/******************************************************************************* +** +** Function attp_send_sr_msg +** +** Description This function sends the server response or indication message +** to client. +** +** Parameter p_tcb: pointer to the connecton control block. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_NO_RESOURCES; + + if (p_tcb != NULL) { + if (p_msg != NULL) { + p_msg->offset = L2CAP_MIN_OFFSET; + cmd_sent = attp_send_msg_to_l2cap (p_tcb, p_msg); + } + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function attp_cl_send_cmd +** +** Description Send a ATT command or enqueue it. +** +** Returns GATT_SUCCESS if command sent +** GATT_CONGESTED if command sent but channel congested +** GATT_CMD_STARTED if command queue up in GATT +** GATT_ERROR if command sending failure +** +*******************************************************************************/ +tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd) +{ + tGATT_STATUS att_ret = GATT_SUCCESS; + + if (p_tcb != NULL) { + cmd_code &= ~GATT_AUTH_SIGN_MASK; + + /* no pending request or value confirmation */ + if (p_tcb->pending_cl_req == p_tcb->next_slot_inq || + cmd_code == GATT_HANDLE_VALUE_CONF) { + att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd); + if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS) { + /* do not enq cmd if handle value confirmation or set request */ + if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) { + gatt_start_rsp_timer (clcb_idx); + gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL); + } + } else { + att_ret = GATT_INTERNAL_ERROR; + } + } else { + att_ret = GATT_CMD_STARTED; + gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd); + } + } else { + att_ret = GATT_ERROR; + } + + return att_ret; +} +/******************************************************************************* +** +** Function attp_send_cl_msg +** +** Description This function sends the client request or confirmation message +** to server. +** +** Parameter p_tcb: pointer to the connection control block. +** clcb_idx: clcb index +** op_code: message op code. +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +** +*******************************************************************************/ +tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + BT_HDR *p_cmd = NULL; + UINT16 offset = 0, handle; + + if (p_tcb != NULL) { + switch (op_code) { + case GATT_REQ_MTU: + if (p_msg->mtu <= GATT_MAX_MTU_SIZE) { + p_tcb->payload_size = p_msg->mtu; + p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_READ_BY_GRP_TYPE: + if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) && + GATT_HANDLE_IS_VALID (p_msg->browse.e_handle) && + p_msg->browse.s_handle <= p_msg->browse.e_handle) { + p_cmd = attp_build_browse_cmd(op_code, + p_msg->browse.s_handle, + p_msg->browse.e_handle, + p_msg->browse.uuid); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_READ_BLOB: + offset = p_msg->read_blob.offset; + /* fall through */ + case GATT_REQ_READ: + handle = (op_code == GATT_REQ_READ) ? p_msg->handle : p_msg->read_blob.handle; + /* handle checking */ + if (GATT_HANDLE_IS_VALID (handle)) { + p_cmd = attp_build_handle_cmd(op_code, handle, offset); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_HANDLE_VALUE_CONF: + p_cmd = attp_build_opcode_cmd(op_code); + break; + + case GATT_REQ_PREPARE_WRITE: + offset = p_msg->attr_value.offset; + /* fall through */ + case GATT_REQ_WRITE: + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) { + p_cmd = attp_build_value_cmd (p_tcb->payload_size, + op_code, p_msg->attr_value.handle, + offset, + p_msg->attr_value.len, + p_msg->attr_value.value); + } else { + status = GATT_ILLEGAL_PARAMETER; + } + break; + + case GATT_REQ_EXEC_WRITE: + p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write); + break; + + case GATT_REQ_FIND_TYPE_VALUE: + p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value); + break; + + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + p_cmd = attp_build_read_multi_cmd(op_code, p_tcb->payload_size, + p_msg->read_multi.num_handles, + p_msg->read_multi.handles); + break; + + default: + break; + } + + if (p_cmd != NULL) { + status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd); + } + + } else { + GATT_TRACE_ERROR("Peer device not connected"); + } + + return status; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_api.c b/lib/bt/host/bluedroid/stack/gatt/gatt_api.c new file mode 100644 index 00000000..8d8056f6 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_api.c @@ -0,0 +1,1805 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT interface functions + * + ******************************************************************************/ +#include "common/bt_target.h" + + +#if defined(BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE) + +#include "osi/allocator.h" +#include <string.h> +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" +#include "stack/sdpdefs.h" +#include "stack/sdp_api.h" + +/******************************************************************************* +** +** Function GATT_SetTraceLevel +** +** Description This function sets the trace level. If called with +** a value of 0xFF, it simply returns the current trace level. +** +** Input Parameters: +** level: The level to set the GATT tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new or current trace level +** +*******************************************************************************/ +UINT8 GATT_SetTraceLevel (UINT8 new_level) +{ + if (new_level != 0xFF) { + gatt_cb.trace_level = new_level; + } + + return (gatt_cb.trace_level); +} + + +#if (GATTS_INCLUDED == TRUE) +/***************************************************************************** +** +** GATT SERVER API +** +******************************************************************************/ +/******************************************************************************* +** +** Function GATTS_AddHandleRange +** +** Description This function add the allocated handles range for the specifed +** application UUID, service UUID and service instance +** +** Parameter p_hndl_range: pointer to allocated handles information +** +** Returns TRUE if handle range is added successfully; otherwise FALSE. +** +*******************************************************************************/ + +BOOLEAN GATTS_AddHandleRange(tGATTS_HNDL_RANGE *p_hndl_range) +{ + tGATT_HDL_LIST_ELEM *p_buf; + BOOLEAN status = FALSE; + + if ((p_buf = gatt_alloc_hdl_buffer()) != NULL) { + p_buf->asgn_range = *p_hndl_range; + status = gatt_add_an_item_to_list(&gatt_cb.hdl_list_info, p_buf); + } + return status; +} + + +/******************************************************************************* +** +** Function GATTS_NVRegister +** +** Description Application manager calls this function to register for +** NV save callback function. There can be one and only one +** NV save callback function. +** +** Parameter p_cb_info : callback informaiton +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN GATTS_NVRegister (const tGATT_APPL_INFO *p_cb_info) +{ + BOOLEAN status = FALSE; + if (p_cb_info) { + gatt_cb.cb_info = *p_cb_info; + status = TRUE; + gatt_init_srv_chg(); + } + + return status; +} + +#if GATTS_ROBUST_CACHING_ENABLED +static void gatt_update_for_database_change(void) +{ + UINT8 i; + + gatts_calculate_datebase_hash(gatt_cb.database_hash); + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i++) { + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(i); + if (p_tcb && p_tcb->in_use) { + gatt_sr_update_cl_status(p_tcb, false); + } + } +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/******************************************************************************* +** +** Function GATTS_CreateService +** +** Description This function is called to reserve a block of handles for a service. +** +** *** It should be called only once per service instance *** +** +** Parameter gatt_if : application if +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** num_handles : number of handles needed by the service. +** is_pri : is a primary service or not. +** +** Returns service handle if sucessful, otherwise 0. +** +*******************************************************************************/ +UINT16 GATTS_CreateService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, + UINT16 svc_inst, UINT16 num_handles, BOOLEAN is_pri) +{ + + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT16 s_hdl = 0; + BOOLEAN save_hdl = FALSE; + tGATTS_PENDING_NEW_SRV_START *p_buf = NULL; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + + GATT_TRACE_API ("GATTS_CreateService\n" ); + + if (p_reg == NULL) { + GATT_TRACE_ERROR ("Inavlid gatt_if=%d\n", gatt_if); + return (0); + } + + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) != NULL) { + s_hdl = p_list->asgn_range.s_handle; + GATT_TRACE_DEBUG ("Service already been created!!\n"); + } else { + if ( (p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GATT_SERVER)) { + s_hdl = gatt_cb.hdl_cfg.gatt_start_hdl; + save_hdl = TRUE; + } else if ((p_svc_uuid->len == LEN_UUID_16) && (p_svc_uuid->uu.uuid16 == UUID_SERVCLASS_GAP_SERVER)) { + s_hdl = gatt_cb.hdl_cfg.gap_start_hdl; + save_hdl = TRUE; + } else { + p_list = p_list_info->p_first; + + if (p_list) { + s_hdl = p_list->asgn_range.e_handle + 1; + } + + if (s_hdl < gatt_cb.hdl_cfg.app_start_hdl) { + + s_hdl = gatt_cb.hdl_cfg.app_start_hdl; + } + save_hdl = TRUE; + } + + /* check for space */ + if (num_handles > (0xFFFF - s_hdl + 1)) { + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u\n", s_hdl, num_handles); + return (0); + } + + if ( (p_list = gatt_alloc_hdl_buffer()) == NULL) { + /* No free entry */ + GATT_TRACE_ERROR ("GATTS_ReserveHandles: no free handle blocks\n"); + return (0); + } + + p_list->asgn_range.app_uuid128 = *p_app_uuid128; + p_list->asgn_range.svc_uuid = *p_svc_uuid; + p_list->asgn_range.svc_inst = svc_inst; + p_list->asgn_range.s_handle = s_hdl; + p_list->asgn_range.e_handle = s_hdl + num_handles - 1; + p_list->asgn_range.is_primary = is_pri; + + gatt_add_an_item_to_list(p_list_info, p_list); + + if (save_hdl) { + if (gatt_cb.cb_info.p_nv_save_callback) { + (*gatt_cb.cb_info.p_nv_save_callback)(TRUE, &p_list->asgn_range); + } + /* add a pending new service change item to the list */ + if ( (p_buf = gatt_add_pending_new_srv_start(&p_list->asgn_range)) == NULL) { + /* No free entry */ + GATT_TRACE_ERROR ("gatt_add_pending_new_srv_start: no free blocks\n"); + + if (p_list) { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + } + return (0); + } + + GATT_TRACE_DEBUG ("Add a new srv chg item\n"); + } + } + + if (!gatts_init_service_db(&p_list->svc_db, p_svc_uuid, is_pri, s_hdl , num_handles)) { + GATT_TRACE_ERROR ("GATTS_ReserveHandles: service DB initialization failed\n"); + if (p_list) { + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + } + + if (p_buf) { + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } + return (0); + } + + return (s_hdl); +} + +/******************************************************************************* +** +** Function GATTS_AddIncludeService +** +** Description This function is called to add an included service. +** +** Parameter service_handle : To which service this included service is added to. +** include_svc_handle : included service handle. +** +** Returns included service attribute handle. If 0, add included service +** fail. +** +*******************************************************************************/ +UINT16 GATTS_AddIncludeService (UINT16 service_handle, UINT16 include_svc_handle) + +{ + tGATT_HDL_LIST_ELEM *p_decl, *p_incl_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created"); + return 0; + } + if ((p_incl_decl = gatt_find_hdl_buffer_by_handle(include_svc_handle)) == NULL) { + GATT_TRACE_DEBUG("Included Service not created"); + return 0; + } + + return gatts_add_included_service(&p_decl->svc_db, + p_incl_decl->asgn_range.s_handle, + p_incl_decl->asgn_range.e_handle, + p_incl_decl->asgn_range.svc_uuid); +} +/******************************************************************************* +** +** Function GATTS_AddCharacteristic +** +** Description This function is called to add a characteristic into a service. +** It will add a characteristic declaration and characteristic +** value declaration into the service database identified by the +** service handle. +** +** Parameter service_handle : To which service this included service is added to. +** char_uuid : Characteristic UUID. +** perm : Characteristic value declaration attribute permission. +** property : Characteristic Properties +** +** Returns Characteristic value declaration attribute handle. 0 if failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharacteristic (UINT16 service_handle, tBT_UUID *p_char_uuid, + tGATT_PERM perm, tGATT_CHAR_PROP property, + tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return 0; + } + /* data validity checking */ + if ( ((property & GATT_CHAR_PROP_BIT_AUTH) && !(perm & GATT_WRITE_SIGNED_PERM)) || + ((perm & GATT_WRITE_SIGNED_PERM) && !(property & GATT_CHAR_PROP_BIT_AUTH)) ) { + GATT_TRACE_DEBUG("Invalid configuration property=0x%x perm=0x%x\n ", property, perm); + return 0; + } + + return gatts_add_characteristic(&p_decl->svc_db, + perm, + property, + p_char_uuid, + attr_val, control); +} +/******************************************************************************* +** +** Function GATTS_AddCharDescriptor +** +** Description This function is called to add a characteristic descriptor +** into a service database. Add descriptor should follow add char +** to which it belongs, and next add char should be done only +** after all add descriptors for the previous char. +** +** Parameter service_handle : To which service this characteristic descriptor +** is added to. +** perm : Characteristic value declaration attribute +** permission. +** p_descr_uuid : Characteristic descriptor UUID +** +** Returns Characteristic descriptor attribute handle. 0 if add +** characteristic descriptor failed. +** +*******************************************************************************/ +UINT16 GATTS_AddCharDescriptor (UINT16 service_handle, + tGATT_PERM perm, + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_HDL_LIST_ELEM *p_decl; + + if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created"); + return 0; + } + if (p_descr_uuid == NULL || + (p_descr_uuid->len != LEN_UUID_128 && p_descr_uuid->len != LEN_UUID_16 + && p_descr_uuid->len != LEN_UUID_32)) { + GATT_TRACE_DEBUG("Illegal parameter"); + return 0; + } + + return gatts_add_char_descr(&p_decl->svc_db, + perm, + p_descr_uuid, + attr_val, control); + +} +/******************************************************************************* +** +** Function GATTS_DeleteService +** +** Description This function is called to delete a service. +** +** Parameter gatt_if : application interface +** p_svc_uuid : service UUID +** svc_inst : instance of the service inside the application +** +** Returns TRUE if operation succeed, FALSE if handle block was not found. +** +*******************************************************************************/ +BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT8 i_sreg; + tGATTS_PENDING_NEW_SRV_START *p_buf; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tBT_UUID *p_app_uuid128; + + GATT_TRACE_DEBUG ("GATTS_DeleteService"); + + if (p_reg == NULL) { + GATT_TRACE_ERROR ("Application not foud"); + return (FALSE); + } + p_app_uuid128 = &p_reg->app_uuid128; + + if ((p_list = gatt_find_hdl_buffer_by_app_id(p_app_uuid128, p_svc_uuid, svc_inst)) == NULL) { + GATT_TRACE_ERROR ("No Service found"); + return (FALSE); + } + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) { + GATT_TRACE_DEBUG ("Delete a new service changed item - the service has not yet started"); + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } else { +#if GATTS_ROBUST_CACHING_ENABLED + gatt_update_for_database_change(); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + gatt_proc_srv_chg(); + } + } + + if ((i_sreg = gatt_sr_find_i_rcb_by_app_id (p_app_uuid128, + p_svc_uuid, + svc_inst)) != GATT_MAX_SR_PROFILES) { + GATTS_StopService(gatt_cb.sr_reg[i_sreg].s_hdl); + } + + GATT_TRACE_DEBUG ("released handles s_hdl=%u e_hdl=%u", + p_list->asgn_range.s_handle , p_list->asgn_range.e_handle ); + + if ( (p_list->asgn_range.s_handle >= gatt_cb.hdl_cfg.app_start_hdl) + && gatt_cb.cb_info.p_nv_save_callback) { + (*gatt_cb.cb_info.p_nv_save_callback)(FALSE, &p_list->asgn_range); + } + + gatt_remove_an_item_from_list(p_list_info, p_list); + gatt_free_attr_value_buffer(p_list); + gatt_free_hdl_buffer(p_list); + + return (TRUE); +} + +/******************************************************************************* +** +** Function GATTS_StartService +** +** Description This function is called to start a service with GATT +** +** Parameter gatt_if : service handle. +** p_cback : application service callback functions. +** sup_transport : supported transport(s) for this primary service +** +** return GATT_SUCCESS if successfully started; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle, + tGATT_TRANSPORT sup_transport) +{ + tGATT_SR_REG *p_sreg; + tGATT_HDL_LIST_ELEM *p_list = NULL; + UINT8 i_sreg; +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + tBT_UUID *p_uuid; +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_API ("GATTS_StartService"); + + if (p_reg == NULL) { + /* Not found */ + GATT_TRACE_ERROR ("Application not found "); + return GATT_NOT_FOUND; + } + + if ((p_list = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL) { + /* Not found */ + GATT_TRACE_ERROR ("no service found"); + return GATT_NOT_FOUND; + } + + if (gatt_sr_find_i_rcb_by_app_id (&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst) != GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR ("Duplicate Service start - Service already started"); + return GATT_SERVICE_STARTED; + } + + /*this is a new application servoce start */ + if ((i_sreg = gatt_sr_alloc_rcb(p_list)) == GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR ("GATTS_StartService: no free server registration block"); + return GATT_NO_RESOURCES; + } + + p_sreg = &gatt_cb.sr_reg[i_sreg]; + p_sreg->gatt_if = gatt_if; + + switch (sup_transport) { + case GATT_TRANSPORT_BR_EDR: + case GATT_TRANSPORT_LE_BR_EDR: + if (p_sreg->type == GATT_UUID_PRI_SERVICE) { +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + p_uuid = gatts_get_service_uuid (p_sreg->p_db); + p_sreg->sdp_handle = gatt_add_sdp_record(p_uuid, p_sreg->s_hdl, p_sreg->e_hdl); +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + } + break; + default: + break; + } + + gatts_update_srv_list_elem(i_sreg, p_sreg->s_hdl, + p_list->asgn_range.is_primary); + + gatt_add_a_srv_to_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[i_sreg]); + + GATT_TRACE_DEBUG ("allocated i_sreg=%d\n", i_sreg); + + GATT_TRACE_DEBUG ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x\n", + p_sreg->s_hdl, p_sreg->e_hdl, + p_sreg->type, p_sreg->service_instance, + p_sreg->sdp_handle); + + + if ( (p_buf = gatt_sr_is_new_srv_chg(&p_list->asgn_range.app_uuid128, + &p_list->asgn_range.svc_uuid, + p_list->asgn_range.svc_inst)) != NULL) { + + #if GATTS_ROBUST_CACHING_ENABLED + gatt_update_for_database_change(); + #endif /* GATTS_ROBUST_CACHING_ENABLED */ + + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + gatt_proc_srv_chg(); + } + /* remove the new service element after the srv changed processing is completed*/ + + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf)); + } + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function GATTS_StopService +** +** Description This function is called to stop a service +** +** Parameter service_handle : this is the start handle of a service +** +** Returns None. +** +*******************************************************************************/ +void GATTS_StopService (UINT16 service_handle) +{ + UINT8 ii = gatt_sr_find_i_rcb_by_handle(service_handle); + + GATT_TRACE_API("GATTS_StopService %u", service_handle); + + /* Index 0 is reserved for GATT, and is never stopped */ + if ( (ii > 0) && (ii < GATT_MAX_SR_PROFILES) && (gatt_cb.sr_reg[ii].in_use) ) { +#if(SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) + if (gatt_cb.sr_reg[ii].sdp_handle) { + SDP_DeleteRecord(gatt_cb.sr_reg[ii].sdp_handle); + } +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + gatt_remove_a_srv_from_list(&gatt_cb.srv_list_info, &gatt_cb.srv_list[ii]); + gatt_cb.srv_list[ii].in_use = FALSE; + memset (&gatt_cb.sr_reg[ii], 0, sizeof(tGATT_SR_REG)); + } else { + GATT_TRACE_ERROR("GATTS_StopService service_handle: %u is not in use", service_handle); + } +} +/******************************************************************************* +** +** Function GATTs_HandleValueIndication +** +** Description This function sends a handle value indication to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent or queued; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueIndication (UINT16 conn_id, UINT16 attr_handle, UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_status = GATT_NO_RESOURCES; + + tGATT_VALUE indication; + BT_HDR *p_msg; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + + GATT_TRACE_API ("GATTS_HandleValueIndication"); + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleValueIndication Unknown conn_id: %u ", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (! GATT_HANDLE_IS_VALID (attr_handle)) { + return GATT_ILLEGAL_PARAMETER; + } + + indication.conn_id = conn_id; + indication.handle = attr_handle; + indication.len = val_len; + memcpy (indication.value, p_val, val_len); + indication.auth_req = GATT_AUTH_REQ_NONE; + + if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle)) { + /* TODO: need to further check whether deleting pending queue here cause reducing transport performance */ + /* + GATT_TRACE_DEBUG ("Add a pending indication"); + if ((p_buf = gatt_add_pending_ind(p_tcb, &indication)) != NULL) { + cmd_status = GATT_SUCCESS; + } else { + cmd_status = GATT_NO_RESOURCES; + } + */ + return GATT_BUSY; + } else { + + if ( (p_msg = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_IND, (tGATT_SR_MSG *)&indication)) != NULL) { + cmd_status = attp_send_sr_msg (p_tcb, p_msg); + + if (cmd_status == GATT_SUCCESS || cmd_status == GATT_CONGESTED) { + p_tcb->indicate_handle = indication.handle; + gatt_start_conf_timer(p_tcb); + } + } + } + return cmd_status; +} + +/******************************************************************************* +** +** Function GATTS_HandleValueNotification +** +** Description This function sends a handle value notification to a client. +** +** Parameter conn_id: connection identifier. +** attr_handle: Attribute handle of this handle value indication. +** val_len: Length of the indicated attribute value. +** p_val: Pointer to the indicated attribute value data. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_HandleValueNotification (UINT16 conn_id, UINT16 attr_handle, + UINT16 val_len, UINT8 *p_val) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + BT_HDR *p_buf; + tGATT_VALUE notif; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API ("GATTS_HandleValueNotification"); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleValueNotification Unknown conn_id: %u \n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (GATT_HANDLE_IS_VALID (attr_handle)) { + notif.handle = attr_handle; + notif.len = val_len; + memcpy (notif.value, p_val, val_len); + notif.auth_req = GATT_AUTH_REQ_NONE; + + if ((p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_NOTIF, (tGATT_SR_MSG *)¬if)) + != NULL) { + cmd_sent = attp_send_sr_msg (p_tcb, p_buf); + } else { + cmd_sent = GATT_NO_RESOURCES; + } + } + return cmd_sent; +} + +/******************************************************************************* +** +** Function GATTS_SendRsp +** +** Description This function sends the server response to client. +** +** Parameter conn_id: connection identifier. +** trans_id: transaction id +** status: response status +** p_msg: pointer to message parameters structure. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SendRsp (UINT16 conn_id, UINT32 trans_id, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_API ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x\n", + conn_id, trans_id, status); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_SendRsp Unknown conn_id: %u\n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (p_tcb->sr_cmd.trans_id != trans_id) { + GATT_TRACE_ERROR ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x\n", + conn_id, p_tcb->sr_cmd.op_code); + + return (GATT_WRONG_STATE); + } + /* Process App response */ + cmd_sent = gatt_sr_process_app_rsp (p_tcb, gatt_if, trans_id, p_tcb->sr_cmd.op_code, status, p_msg); + + return cmd_sent; +} + + +/******************************************************************************* +** +** Function GATTS_SetAttributeValue +** +** Description This function sends to set the attribute value . +** +** Parameter attr_handle:the attribute handle +** length: the attribute length +** value: the value to be set to the attribute in the database +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_SetAttributeValue(UINT16 attr_handle, UINT16 length, UINT8 *value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl = NULL; + + GATT_TRACE_DEBUG("GATTS_SetAttributeValue: attr_handle: %u length: %u \n", + attr_handle, length); + if (length <= 0){ + return GATT_INVALID_ATTR_LEN; + } + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return GATT_INVALID_HANDLE; + } + + status = gatts_set_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; + +} + + +/******************************************************************************* +** +** Function GATTS_GetAttributeValue +** +** Description This function sends to get the attribute value . +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValue(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + tGATT_STATUS status; + tGATT_HDL_LIST_ELEM *p_decl; + + GATT_TRACE_DEBUG("GATTS_GetAttributeValue: attr_handle: %u\n", + attr_handle); + + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_ERROR("Service not created\n"); + *length = 0; + return GATT_INVALID_HANDLE; + } + + status = gatts_get_attribute_value(&p_decl->svc_db, attr_handle, length, value); + return status; +} + +/******************************************************************************* +** +** Function GATTS_GetAttributeValueInternal +** +** Description This function sends to get the attribute value of internal gatt and gap service. +** +** Parameter attr_handle: the attribute handle +** length:the attribute value length in the database +** value: the attribute value out put +* +** +** Returns tGATT_STATUS - GATT status indicating success or failure in +** retrieving the attribute value. +** +*******************************************************************************/ +tGATT_STATUS GATTS_GetAttributeValueInternal(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + return gatts_get_attr_value_internal(attr_handle, length, value); +} +#endif ///GATTS_INCLUDED == TRUE + + +#if (GATTC_INCLUDED == TRUE) +/*******************************************************************************/ +/* GATT Profile Srvr Functions */ +/*******************************************************************************/ + +/*******************************************************************************/ +/* */ +/* GATT CLIENT APIs */ +/* */ +/*******************************************************************************/ + + +/******************************************************************************* +** +** Function GATTC_ConfigureMTU +** +** Description This function is called to configure the ATT MTU size. +** +** Parameters conn_id: connection identifier. +** mtu - attribute MTU size.. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ConfigureMTU (UINT16 conn_id) +{ + UINT8 ret = GATT_NO_RESOURCES; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + tGATT_CLCB *p_clcb; + uint16_t mtu = gatt_get_local_mtu(); + + GATT_TRACE_API ("GATTC_ConfigureMTU conn_id=%d mtu=%d", conn_id, mtu ); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (mtu < GATT_DEF_BLE_MTU_SIZE) || (mtu > GATT_MAX_MTU_SIZE)) { + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + /* Validate that the link is BLE, not BR/EDR */ + if (p_tcb->transport != BT_TRANSPORT_LE) { + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_ConfigureMTU GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) { + p_clcb->p_tcb->payload_size = mtu; + p_clcb->operation = GATTC_OPTYPE_CONFIG; + + ret = attp_send_cl_msg (p_clcb->p_tcb, p_clcb->clcb_idx, GATT_REQ_MTU, (tGATT_CL_MSG *)&mtu); + } + + return ret; +} + +/******************************************************************************* +** +** Function GATTC_Discover +** +** Description This function is called to do a discovery procedure on ATT server. +** +** Parameters conn_id: connection identifier. +** disc_type:discovery type. +** p_param: parameters of discovery requirement. +** +** Returns GATT_SUCCESS if command received/sent successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Discover (UINT16 conn_id, tGATT_DISC_TYPE disc_type, + tGATT_DISC_PARAM *p_param) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API ("GATTC_Discover conn_id=%d disc_type=%d", conn_id, disc_type); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_param == NULL) || + (disc_type >= GATT_DISC_MAX)) { + GATT_TRACE_ERROR("GATTC_Discover Illegal param: disc_type %d conn_id = %d", disc_type, conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Discover GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + if (!GATT_HANDLE_IS_VALID(p_param->s_handle) || + !GATT_HANDLE_IS_VALID(p_param->e_handle) || + /* search by type does not have a valid UUID param */ + (disc_type == GATT_DISC_SRVC_BY_UUID && + p_param->service.len == 0)) { + gatt_clcb_dealloc(p_clcb); + return GATT_ILLEGAL_PARAMETER; + } + + p_clcb->operation = GATTC_OPTYPE_DISCOVERY; + p_clcb->op_subtype = disc_type; + p_clcb->s_handle = p_param->s_handle; + p_clcb->e_handle = p_param->e_handle; + p_clcb->uuid = p_param->service; + + gatt_act_discovery(p_clcb); + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Read +** +** Description This function is called to read the value of an attribute from +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute read type. +** p_read - read operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, tGATT_READ_PARAM *p_read) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_READ_MULTI *p_read_multi; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + + GATT_TRACE_API ("GATTC_Read conn_id=%d type=%d", conn_id, type); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_read == NULL) || ((type >= GATT_READ_MAX) || (type == 0))) { + GATT_TRACE_ERROR("GATT_Read Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Read GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ( (p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + p_clcb->operation = GATTC_OPTYPE_READ; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_read->by_handle.auth_req; + p_clcb->counter = 0; + + switch (type) { + case GATT_READ_BY_TYPE: + case GATT_READ_CHAR_VALUE: + p_clcb->s_handle = p_read->service.s_handle; + p_clcb->e_handle = p_read->service.e_handle; + memcpy(&p_clcb->uuid, &p_read->service.uuid, sizeof(tBT_UUID)); + break; + case GATT_READ_MULTIPLE: + case GATT_READ_MULTIPLE_VAR: + p_clcb->s_handle = 0; + /* copy multiple handles in CB */ + p_read_multi = (tGATT_READ_MULTI *)osi_malloc(sizeof(tGATT_READ_MULTI)); + p_clcb->p_attr_buf = (UINT8 *)p_read_multi; + memcpy (p_read_multi, &p_read->read_multiple, sizeof(tGATT_READ_MULTI)); + case GATT_READ_BY_HANDLE: + case GATT_READ_PARTIAL: + memset(&p_clcb->uuid, 0, sizeof(tBT_UUID)); + p_clcb->s_handle = p_read->by_handle.handle; + + if (type == GATT_READ_PARTIAL) { + p_clcb->counter = p_read->partial.offset; + } + + break; + default: + break; + } + /* start security check */ + if (gatt_security_check_start(p_clcb) == FALSE) { + status = GATT_NO_RESOURCES; + gatt_clcb_dealloc(p_clcb); + } + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_Write +** +** Description This function is called to write the value of an attribute to +** the server. +** +** Parameters conn_id: connection identifier. +** type - attribute write type. +** p_write - write operation parameters. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_VALUE *p; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if ( (p_tcb == NULL) || (p_reg == NULL) || (p_write == NULL) || + ((type != GATT_WRITE) && (type != GATT_WRITE_PREPARE) && (type != GATT_WRITE_NO_RSP)) ) { + GATT_TRACE_ERROR("GATT_Write Illegal param: conn_id %d, type 0%d,", conn_id, type); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL ) { + p_clcb->operation = GATTC_OPTYPE_WRITE; + p_clcb->op_subtype = type; + p_clcb->auth_req = p_write->auth_req; + + if (( p_clcb->p_attr_buf = (UINT8 *)osi_malloc((UINT16)sizeof(tGATT_VALUE))) != NULL) { + memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE)); + + p = (tGATT_VALUE *)p_clcb->p_attr_buf; + if (type == GATT_WRITE_PREPARE) { + p_clcb->start_offset = p_write->offset; + p->offset = 0; + } + + if (gatt_security_check_start(p_clcb) == FALSE) { + status = GATT_NO_RESOURCES; + } + } else { + status = GATT_NO_RESOURCES; + } + + if (status == GATT_NO_RESOURCES) { + gatt_clcb_dealloc(p_clcb); + } + } else { + status = GATT_NO_RESOURCES; + } + return status; +} + + +/******************************************************************************* +** +** Function GATTC_ExecuteWrite +** +** Description This function is called to send an Execute write request to +** the server. +** +** Parameters conn_id: connection identifier. +** is_execute - to execute or cancel the prepare write request(s) +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute) +{ + tGATT_STATUS status = GATT_SUCCESS; + tGATT_CLCB *p_clcb; + tGATT_EXEC_FLAG flag; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + GATT_TRACE_API ("GATTC_ExecuteWrite conn_id=%d is_execute=%d", conn_id, is_execute); + + if ( (p_tcb == NULL) || (p_reg == NULL) ) { + GATT_TRACE_ERROR("GATTC_ExecuteWrite Illegal param: conn_id %d", conn_id); + return GATT_ILLEGAL_PARAMETER; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_ERROR; + } + + if (gatt_is_clcb_allocated(conn_id)) { + GATT_TRACE_ERROR("GATTC_Write GATT_BUSY conn_id = %d", conn_id); + return GATT_BUSY; + } + + if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL) { + p_clcb->operation = GATTC_OPTYPE_EXE_WRITE; + flag = is_execute ? GATT_PREP_WRITE_EXEC : GATT_PREP_WRITE_CANCEL; + gatt_send_queue_write_cancel (p_clcb->p_tcb, p_clcb, flag); + } else { + GATT_TRACE_ERROR("Unable to allocate client CB for conn_id %d ", conn_id); + status = GATT_NO_RESOURCES; + } + return status; +} + +/******************************************************************************* +** +** Function GATTC_SendHandleValueConfirm +** +** Description This function is called to send a handle value confirmation +** as response to a handle value notification from server. +** +** Parameters conn_id: connection identifier. +** handle: the handle of the attribute confirmation. +** +** Returns GATT_SUCCESS if command started successfully. +** +*******************************************************************************/ +tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(GATT_GET_TCB_IDX(conn_id)); + + GATT_TRACE_API ("GATTC_SendHandleValueConfirm conn_id=%d handle=0x%x", conn_id, handle); + + if (p_tcb) { + if (p_tcb->ind_count > 0 ) { + btu_stop_timer (&p_tcb->ind_ack_timer_ent); + + GATT_TRACE_DEBUG ("notif_count=%d ", p_tcb->ind_count); + /* send confirmation now */ + ret = attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, (tGATT_CL_MSG *)&handle); + + p_tcb->ind_count = 0; + + } else { + GATT_TRACE_DEBUG ("GATTC_SendHandleValueConfirm - conn_id: %u - ignored not waiting for indicaiton ack", conn_id); + ret = GATT_SUCCESS; + } + } else { + GATT_TRACE_ERROR ("GATTC_SendHandleValueConfirm - Unknown conn_id: %u", conn_id); + } + return ret; +} + +tGATT_STATUS GATTC_AutoDiscoverEnable(UINT8 enable) +{ + gatt_cb.auto_disc = (enable > 0) ? TRUE : FALSE; + return GATT_SUCCESS; +} + +#endif ///GATTC_INCLUDED == TRUE + +/*******************************************************************************/ +/* */ +/* GATT APIs */ +/* */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function GATT_SetIdleTimeout +** +** Description This function (common to both client and server) sets the idle +** timeout for a tansport connection +** +** Parameter bd_addr: target device bd address. +** idle_tout: timeout value in seconds. +** +** Returns void +** +*******************************************************************************/ +void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb; + BOOLEAN status = FALSE; + + if ((p_tcb = gatt_find_tcb_by_addr (bd_addr, transport)) != NULL) { + if (p_tcb->att_lcid == L2CAP_ATT_CID) { + status = L2CA_SetFixedChannelTout (bd_addr, L2CAP_ATT_CID, idle_tout); + + if (idle_tout == GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP) { + L2CA_SetIdleTimeoutByBdAddr(p_tcb->peer_bda, + GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP, BT_TRANSPORT_LE); + } + } else { + status = L2CA_SetIdleTimeout (p_tcb->att_lcid, idle_tout, FALSE); + } + } + +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GATT_TRACE_API ("GATT_SetIdleTimeout idle_tout=%d status=%d(1-OK 0-not performed)", + idle_tout, status); +} + + +/******************************************************************************* +** +** Function GATT_Register +** +** Description This function is called to register an application +** with GATT +** +** Parameter p_app_uuid128: Application UUID +** p_cb_info: callback functions. +** +** Returns 0 for error, otherwise the index of the client registered with GATT +** +*******************************************************************************/ +tGATT_IF GATT_Register (tBT_UUID *p_app_uuid128, const tGATT_CBACK *p_cb_info) +{ + tGATT_REG *p_reg; + UINT8 i_gatt_if = 0; + tGATT_IF gatt_if = 0; + + GATT_TRACE_API ("GATT_Register"); + gatt_dbg_display_uuid(*p_app_uuid128); + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { + if (p_reg->in_use && !memcmp(p_app_uuid128->uu.uuid128, p_reg->app_uuid128.uu.uuid128, LEN_UUID_128)) { + GATT_TRACE_ERROR("application already registered."); + return 0; + } + } + + for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { + if (!p_reg->in_use) { + memset(p_reg, 0 , sizeof(tGATT_REG)); + i_gatt_if++; /* one based number */ + p_reg->app_uuid128 = *p_app_uuid128; + gatt_if = + p_reg->gatt_if = (tGATT_IF)i_gatt_if; + p_reg->app_cb = *p_cb_info; + p_reg->in_use = TRUE; + + break; + } + } + GATT_TRACE_API ("allocated gatt_if=%d\n", gatt_if); + return gatt_if; +} + + +/******************************************************************************* +** +** Function GATT_Deregister +** +** Description This function deregistered the application from GATT. +** +** Parameters gatt_if: application interface. +** +** Returns None. +** +*******************************************************************************/ +void GATT_Deregister (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb; + tGATT_CLCB *p_clcb; + list_node_t *p_node = NULL; + list_node_t *p_next = NULL; +#if (GATTS_INCLUDED == TRUE) + UINT8 ii; + tGATT_SR_REG *p_sreg; +#endif ///GATTS_INCLUDED == TRUE + GATT_TRACE_API ("GATT_Deregister gatt_if=%d", gatt_if); + /* Index 0 is GAP and is never deregistered */ + if ( (gatt_if == 0) || (p_reg == NULL) ) { + GATT_TRACE_ERROR ("GATT_Deregister with invalid gatt_if: %u", gatt_if); + return; + } + + /* stop all services */ + /* todo an applcaiton can not be deregistered if its services is also used by other application + deregisteration need to bed performed in an orderly fashion + no check for now */ +#if (GATTS_INCLUDED == TRUE) + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) { + if (p_sreg->in_use && (p_sreg->gatt_if == gatt_if)) { + GATTS_StopService(p_sreg->s_hdl); + } + } + /* free all services db buffers if owned by this application */ + gatt_free_srvc_db_buffer_app_id(&p_reg->app_uuid128); +#endif ///GATTS_INCLUDED == TRUE + /* When an application deregisters, check remove the link associated with the app */ + + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = p_next) { + p_tcb = list_node(p_node); + p_next = list_next(p_node); + if (p_tcb->in_use) { + if (gatt_get_ch_state(p_tcb) != GATT_CH_CLOSE) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + /* this will disconnect the link or cancel the pending connect request at lower layer*/ + gatt_disconnect(p_tcb); + } + } + + list_node_t *p_node_clcb = NULL; + list_node_t *p_node_next = NULL; + for(p_node_clcb = list_begin(gatt_cb.p_clcb_list); p_node_clcb; p_node_clcb = p_node_next) { + p_clcb = list_node(p_node_clcb); + p_node_next = list_next(p_node_clcb); + if (p_clcb->in_use && + (p_clcb->p_reg->gatt_if == gatt_if) && + (p_clcb->p_tcb->tcb_idx == p_tcb->tcb_idx)) { + btu_stop_timer(&p_clcb->rsp_timer_ent); + gatt_clcb_dealloc (p_clcb); + break; + } + } + } + } + + gatt_deregister_bgdev_list(gatt_if); + /* update the listen mode */ +#if (defined(BLE_PERIPHERAL_MODE_SUPPORT) && (BLE_PERIPHERAL_MODE_SUPPORT == TRUE)) + GATT_Listen(gatt_if, FALSE, NULL); +#endif + + memset (p_reg, 0, sizeof(tGATT_REG)); +} + + +/******************************************************************************* +** +** Function GATT_StartIf +** +** Description This function is called after registration to start receiving +** callbacks for registered interface. Function may call back +** with connection status and queued notifications +** +** Parameter gatt_if: application interface. +** +** Returns None. +** +*******************************************************************************/ +void GATT_StartIf (tGATT_IF gatt_if) +{ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + BD_ADDR bda; + UINT8 start_idx, found_idx; + UINT16 conn_id; + tGATT_TRANSPORT transport ; + + GATT_TRACE_API ("GATT_StartIf gatt_if=%d", gatt_if); + if ((p_reg = gatt_get_regcb(gatt_if)) != NULL) { + start_idx = 0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) { + p_tcb = gatt_find_tcb_by_addr(bda, transport); + if (p_reg->app_cb.p_conn_cb && p_tcb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + (*p_reg->app_cb.p_conn_cb)(gatt_if, bda, conn_id, TRUE, 0, transport); + } + start_idx = ++found_idx; + } + } +} + + +/******************************************************************************* +** +** Function GATT_Connect +** +** Description This function initiate a connection to a remote device on GATT +** channel. +** +** Parameters gatt_if: application interface +** bd_addr: peer device address. +** bd_addr_type: peer device address type. +** is_direct: is a direct connection or a background auto connection +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_Connect (tGATT_IF gatt_if, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, + BOOLEAN is_direct, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + tGATT_REG *p_reg; + BOOLEAN status = FALSE; + + GATT_TRACE_API ("GATT_Connect gatt_if=%d", gatt_if); + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("GATT_Connect - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (is_direct) { + status = gatt_act_connect (p_reg, bd_addr, bd_addr_type, transport, is_aux); + } else { + if (transport == BT_TRANSPORT_LE) { + status = gatt_update_auto_connect_dev(gatt_if, TRUE, bd_addr, TRUE); + } else { + GATT_TRACE_ERROR("Unsupported transport for background connection"); + } + } + + return status; + +} + +/******************************************************************************* +** +** Function GATT_CancelConnect +** +** Description This function terminate the connection initaition to a remote +** device on GATT channel. +** +** Parameters gatt_if: client interface. If 0 used as unconditionally disconnect, +** typically used for direct connection cancellation. +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN GATT_CancelConnect (tGATT_IF gatt_if, BD_ADDR bd_addr, BOOLEAN is_direct) +{ + tGATT_REG *p_reg; + tGATT_TCB *p_tcb; + BOOLEAN status = TRUE; + tGATT_IF temp_gatt_if; + UINT8 start_idx, found_idx; + + GATT_TRACE_API ("GATT_CancelConnect gatt_if=%d", gatt_if); + + if ((gatt_if != 0) && ((p_reg = gatt_get_regcb(gatt_if)) == NULL)) { + GATT_TRACE_ERROR("GATT_CancelConnect - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (is_direct) { + if (!gatt_if) { + GATT_TRACE_DEBUG("GATT_CancelConnect - unconditional"); + start_idx = 0; + /* only LE connection can be cancelled */ + p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + if (p_tcb && gatt_num_apps_hold_link(p_tcb)) { + while (status && gatt_find_app_hold_link(p_tcb, start_idx, &found_idx, &temp_gatt_if)) { + status = gatt_cancel_open(temp_gatt_if, bd_addr); + start_idx = ++found_idx; + } + } else { + GATT_TRACE_ERROR("GATT_CancelConnect - no app found"); + status = FALSE; + } + } else { + status = gatt_cancel_open(gatt_if, bd_addr); + } + } else { + if (!gatt_if) { + if (gatt_get_num_apps_for_bg_dev(bd_addr)) { + while (gatt_find_app_for_bg_dev(bd_addr, &temp_gatt_if)) { + gatt_remove_bg_dev_for_app(temp_gatt_if, bd_addr); + } + } else { + GATT_TRACE_ERROR("GATT_CancelConnect -no app associated with the bg device for unconditional removal"); + status = FALSE; + } + } else { + status = gatt_remove_bg_dev_for_app(gatt_if, bd_addr); + } + } + + return status; +} + +/******************************************************************************* +** +** Function GATT_Disconnect +** +** Description This function disconnect the GATT channel for this registered +** application. +** +** Parameters conn_id: connection identifier. +** +** Returns GATT_SUCCESS if disconnected. +** +*******************************************************************************/ +tGATT_STATUS GATT_Disconnect (UINT16 conn_id) +{ + tGATT_STATUS ret = GATT_ILLEGAL_PARAMETER; + tGATT_TCB *p_tcb = NULL; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + + GATT_TRACE_API ("GATT_Disconnect conn_id=%d ", conn_id); + + p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + if (p_tcb) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + gatt_disconnect(p_tcb); + } + ret = GATT_SUCCESS; + } + return ret; +} + +/******************************************************************************* +** +** Function GATT_SendServiceChangeIndication +** +** Description This function is to send a service change indication +** +** Parameters bd_addr: peer device address. +** +** Returns None. +** +*******************************************************************************/ +tGATT_STATUS GATT_SendServiceChangeIndication (BD_ADDR bd_addr) +{ + UINT8 start_idx, found_idx; + BOOLEAN srv_chg_ind_pending = FALSE; + tGATT_TCB *p_tcb; + tBT_TRANSPORT transport; + tGATT_STATUS status = GATT_NOT_FOUND; + + if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) { + status = GATT_WRONG_STATE; + GATT_TRACE_ERROR ("%s can't send service change indication manually, please configure the option through menuconfig", __func__); + return status; + } + + if(bd_addr) { + status = gatt_send_srv_chg_ind(bd_addr); + } else { + start_idx = 0; + BD_ADDR addr; + while (gatt_find_the_connected_bda(start_idx, addr, &found_idx, &transport)) { + p_tcb = gatt_get_tcb_by_idx(found_idx); + srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); + + if (!srv_chg_ind_pending) { + status = gatt_send_srv_chg_ind(addr); + } else { + status = GATT_BUSY; + GATT_TRACE_DEBUG("discard srv chg - already has one in the queue"); + } + start_idx = ++found_idx; + } + } + + return status; +} + +/******************************************************************************* +** +** Function GATT_GetConnectionInfor +** +** Description This function use conn_id to find its associated BD address and applciation +** interface +** +** Parameters conn_id: connection id (input) +** p_gatt_if: application interface (output) +** bd_addr: peer device address. (output) +** +** Returns TRUE the logical link information is found for conn_id +** +*******************************************************************************/ +BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, BD_ADDR bd_addr, + tBT_TRANSPORT *p_transport) +{ + + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + BOOLEAN status = FALSE; + + GATT_TRACE_API ("GATT_GetConnectionInfor conn_id=%d", conn_id); + + if (p_tcb && p_reg ) { + memcpy(bd_addr, p_tcb->peer_bda, BD_ADDR_LEN); + *p_gatt_if = gatt_if; + *p_transport = p_tcb->transport; + status = TRUE; + } + return status; +} + + +/******************************************************************************* +** +** Function GATT_GetConnIdIfConnected +** +** Description This function find the conn_id if the logical link for BD address +** and applciation interface is connected +** +** Parameters gatt_if: application interface (input) +** bd_addr: peer device address. (input) +** p_conn_id: connection id (output) +** transport: transport option +** +** Returns TRUE the logical link is connected +** +*******************************************************************************/ +BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, UINT16 *p_conn_id, + tBT_TRANSPORT transport) +{ + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); + BOOLEAN status = FALSE; + + if (p_reg && p_tcb && (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) ) { + *p_conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + status = TRUE; + } + + GATT_TRACE_API ("GATT_GetConnIdIfConnected status=%d\n", status); + return status; +} + + +/******************************************************************************* +** +** Function GATT_Listen +** +** Description This function start or stop LE advertisement and listen for +** connection. +** +** Parameters gatt_if: application interface +** p_bd_addr: listen for specific address connection, or NULL for +** listen to all device connection. +** start: start or stop listening. +** +** Returns TRUE if advertisement is started; FALSE if adv start failure. +** +*******************************************************************************/ +BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr) +{ + tGATT_REG *p_reg; + + GATT_TRACE_API ("GATT_Listen gatt_if=%d", gatt_if); + + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("GATT_Listen - gatt_if =%d is not registered", gatt_if); + return (FALSE); + } + + if (bd_addr != NULL) { + gatt_update_auto_connect_dev(gatt_if, start, bd_addr, FALSE); + } else { + p_reg->listening = start ? GATT_LISTEN_TO_ALL : GATT_LISTEN_TO_NONE; + } + + return gatt_update_listen_mode(); +} + +tGATT_STATUS GATTS_SetServiceChangeMode(UINT8 mode) +{ + if (mode > GATTS_SEND_SERVICE_CHANGE_MANUAL) { + GATT_TRACE_ERROR("%s invalid service change mode %u", __func__, mode); + return GATT_VALUE_NOT_ALLOWED; + } + + gatt_cb.srv_chg_mode = mode; + return GATT_SUCCESS; +} + +tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tuples, UINT16 num_tuples) +{ + tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER; + BT_HDR *p_buf; + tGATT_VALUE notif; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + UINT8 *p = notif.value; + tGATT_HLV *p_hlv = tuples; + + GATT_TRACE_API ("GATTS_HandleMultiValueNotification"); + + if ( (p_reg == NULL) || (p_tcb == NULL)) { + GATT_TRACE_ERROR ("GATTS_HandleMultiValueNotification Unknown conn_id: %u \n", conn_id); + return (tGATT_STATUS) GATT_INVALID_CONN_ID; + } + + if (!gatt_check_connection_state_by_tcb(p_tcb)) { + GATT_TRACE_ERROR("connection not established\n"); + return GATT_WRONG_STATE; + } + + if (tuples == NULL) { + return GATT_ILLEGAL_PARAMETER; + } + + notif.len = 0; + + while (num_tuples) { + if (!GATT_HANDLE_IS_VALID (p_hlv->handle)) { + return GATT_ILLEGAL_PARAMETER; + } + + UINT16_TO_STREAM(p, p_hlv->handle); //handle + UINT16_TO_STREAM(p, p_hlv->length); //length + memcpy (p, p_hlv->value, p_hlv->length); //value + GATT_TRACE_DEBUG("%s handle %x, length %u", __func__, p_hlv->handle, p_hlv->length); + p += p_hlv->length; + notif.len += 4 + p_hlv->length; + num_tuples--; + p_hlv++; + } + + notif.auth_req = GATT_AUTH_REQ_NONE; + + p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_MULTI_VALUE_NOTIF, (tGATT_SR_MSG *)¬if); + if (p_buf != NULL) { + cmd_sent = attp_send_sr_msg (p_tcb, p_buf); + } else { + cmd_sent = GATT_NO_RESOURCES; + } + + return cmd_sent; +} + +tGATT_STATUS GATTS_ShowLocalDatabase(void) +{ + gatts_show_local_database(); + return GATT_SUCCESS; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c b/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c new file mode 100644 index 00000000..3ab57342 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_attr.c @@ -0,0 +1,797 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT server attributes access request + * handling functions. + * + ******************************************************************************/ + +#include "common/bt_target.h" +//#include "bt_utils.h" + +#include "stack/gatt_api.h" +#include "gatt_int.h" +#include "stack/sdpdefs.h" +#include "bta/bta_gatts_co.h" + +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +#define BLE_GATT_SR_SUPP_FEAT_EATT_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK 0x01 +#define BLE_GATT_CL_SUPP_FEAT_EATT_BITMASK 0x02 +#define BLE_GATT_CL_SUPP_FEAT_MULTI_NOTIF_BITMASK 0x04 +#define BLE_GATT_CL_SUPP_FEAT_BITMASK 0x07 + +#define GATTP_MAX_NUM_INC_SVR 0 + +#if GATTS_ROBUST_CACHING_ENABLED +#define GATTP_MAX_CHAR_NUM 5 +#else +#define GATTP_MAX_CHAR_NUM 2 +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + +#define GATTP_MAX_ATTR_NUM (GATTP_MAX_CHAR_NUM * 2 + GATTP_MAX_NUM_INC_SVR + 1) +#define GATTP_MAX_CHAR_VALUE_SIZE 50 + +#ifndef GATTP_ATTR_DB_SIZE +#define GATTP_ATTR_DB_SIZE GATT_DB_MEM_SIZE(GATTP_MAX_NUM_INC_SVR, GATTP_MAX_CHAR_NUM, GATTP_MAX_CHAR_VALUE_SIZE) +#endif + +static void gatt_request_cback(UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); +static void gatt_connect_cback(tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, + tGATT_DISCONN_REASON reason, tBT_TRANSPORT transport); +static void gatt_disc_res_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data); +static void gatt_disc_cmpl_cback(UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status); +static void gatt_cl_op_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data); + +static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb); + + +static const tGATT_CBACK gatt_profile_cback = { + gatt_connect_cback, + gatt_cl_op_cmpl_cback, + gatt_disc_res_cback, + gatt_disc_cmpl_cback, + gatt_request_cback, + NULL, + NULL +} ; + +/******************************************************************************* +** +** Function gatt_profile_find_conn_id_by_bd_addr +** +** Description Find the connection ID by remote address +** +** Returns Connection ID +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR remote_bda) +{ + UINT16 conn_id = GATT_INVALID_CONN_ID; + GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &conn_id, BT_TRANSPORT_LE); + return conn_id; +} +#endif ///GATTS_INCLUDED == TRUE +/******************************************************************************* +** +** Function gatt_profile_find_clcb_by_conn_id +** +** Description find clcb by Connection ID +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->conn_id == conn_id) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_find_clcb_by_bd_addr +** +** Description The function searches all LCBs with macthing bd address. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +static tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i_clcb; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (p_clcb->in_use && p_clcb->transport == transport && + p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) { + return p_clcb; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, BD_ADDR bda, tBT_TRANSPORT tranport) +{ + UINT8 i_clcb = 0; + tGATT_PROFILE_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb = gatt_cb.profile_clcb; i_clcb < GATT_MAX_APPS; i_clcb++, p_clcb++) { + if (!p_clcb->in_use) { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + p_clcb->transport = tranport; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + if (i_clcb < GATT_MAX_APPS) { + return p_clcb; + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_profile_clcb_dealloc +** +** Description The function deallocates a GATT profile connection link control block +** +** Returns void +** +*******************************************************************************/ +void gatt_profile_clcb_dealloc (tGATT_PROFILE_CLCB *p_clcb) +{ + memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB)); +} + +/******************************************************************************* +** +** Function gatt_proc_read +** +** Description GATT Attributes Database Read/Read Blob Request process +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS gatt_proc_read (UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp) +{ + tGATT_STATUS status = GATT_NO_RESOURCES; + UINT16 len = 0; + UINT8 *value; + UNUSED(type); + + GATT_TRACE_DEBUG("%s handle %x", __func__, p_data->handle); + + if (p_data->is_long) { + p_rsp->attr_value.offset = p_data->offset; + } + + p_rsp->attr_value.handle = p_data->handle; +#if GATTS_ROBUST_CACHING_ENABLED + + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *tcb = gatt_get_tcb_by_idx(tcb_idx); + + /* handle request for reading client supported features */ + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + if (tcb == NULL) { + return GATT_INSUF_RESOURCE; + } + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &tcb->cl_supp_feat, 1); + return GATT_SUCCESS; + } + + /* handle request for reading database hash */ + if (p_data->handle == gatt_cb.handle_of_database_hash) { + p_rsp->attr_value.len = BT_OCTET16_LEN; + memcpy(p_rsp->attr_value.value, gatt_cb.database_hash, BT_OCTET16_LEN); + gatt_sr_update_cl_status(tcb, true); + return GATT_SUCCESS; + } + + /* handle request for reading server supported features */ + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + p_rsp->attr_value.len = 1; + memcpy(p_rsp->attr_value.value, &gatt_cb.gatt_sr_supported_feat_mask, 1); + return GATT_SUCCESS; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + /* handle request for reading service changed des and the others */ + status = GATTS_GetAttributeValue(p_data->handle, &len, &value); + if(status == GATT_SUCCESS && len > 0 && value) { + if(len > GATT_MAX_ATTR_LEN) { + len = GATT_MAX_ATTR_LEN; + } + p_rsp->attr_value.len = len; + memcpy(p_rsp->attr_value.value, value, len); + } + return status; +} +#if GATTS_ROBUST_CACHING_ENABLED +static tGATT_STATUS gatt_sr_write_cl_supp_feat(UINT16 conn_id, tGATT_WRITE_REQ *p_data) +{ + UINT8 val_new; + UINT8 val_old; + UINT8 val_xor; + UINT8 val_and; + UINT8 *p = p_data->value; + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + + GATT_TRACE_DEBUG("%s len %u, feat %x", __func__, p_data->len, *p); + + if (p_tcb == NULL) { + GATT_TRACE_ERROR("%s no conn", __func__); + return GATT_NOT_FOUND; + } + + if (p_data->len != 1) { + GATT_TRACE_ERROR("%s len %u", __func__, p_data->len); + return GATT_INVALID_PDU; + } + + STREAM_TO_UINT8(val_new, p); + val_new = (val_new & BLE_GATT_CL_SUPP_FEAT_BITMASK); + + if (val_new == 0) { + GATT_TRACE_ERROR("%s bit cannot be all zero", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + val_old = p_tcb->cl_supp_feat; + val_xor = val_old ^ val_new; + val_and = val_xor & val_new; + if (val_and != val_xor) { + GATT_TRACE_ERROR("%s bit cannot be reset", __func__); + return GATT_VALUE_NOT_ALLOWED; + } + + p_tcb->cl_supp_feat = val_new; +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_cl_feat_save(p_tcb->peer_bda, &p_tcb->cl_supp_feat); +#endif + return GATT_SUCCESS; +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/****************************************************************************** +** +** Function gatt_proc_write_req +** +** Description GATT server process a write request. +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code. +** +*******************************************************************************/ +tGATT_STATUS gatt_proc_write_req(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_WRITE_REQ *p_data) +{ + if(p_data->len > GATT_MAX_ATTR_LEN) { + p_data->len = GATT_MAX_ATTR_LEN; + } +#if GATTS_ROBUST_CACHING_ENABLED + if (p_data->handle == gatt_cb.handle_of_h_r) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_cl_supported_feat) { + return gatt_sr_write_cl_supp_feat(conn_id, p_data); + } + + if (p_data->handle == gatt_cb.handle_of_database_hash) { + return GATT_WRITE_NOT_PERMIT; + } + + if (p_data->handle == gatt_cb.handle_of_sr_supported_feat) { + return GATT_WRITE_NOT_PERMIT; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + return GATTS_SetAttributeValue(p_data->handle, + p_data->len, + p_data->value); + +} + +/******************************************************************************* +** +** Function gatt_request_cback +** +** Description GATT profile attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +static void gatt_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, + tGATTS_DATA *p_data) +{ + UINT8 status = GATT_INVALID_PDU; + tGATTS_RSP rsp_msg ; + BOOLEAN ignore = FALSE; + GATT_TRACE_DEBUG("%s",__func__); + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + switch (type) { + case GATTS_REQ_TYPE_READ: + status = gatt_proc_read(conn_id, type, &p_data->read_req, &rsp_msg); + break; + + case GATTS_REQ_TYPE_WRITE: + if (!p_data->write_req.need_rsp) { + ignore = TRUE; + } + status = gatt_proc_write_req(conn_id, type, &p_data->write_req); + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + case GATT_CMD_WRITE: + ignore = TRUE; + GATT_TRACE_EVENT("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); + break; + + case GATTS_REQ_TYPE_MTU: + GATT_TRACE_EVENT("Get MTU exchange new mtu size: %d", p_data->mtu); + ignore = TRUE; + break; + + default: + GATT_TRACE_EVENT("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + if (!ignore) { + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + } + +} + +/******************************************************************************* +** +** Function gatt_connect_cback +** +** Description Gatt profile connection callback. +** +** Returns void +** +*******************************************************************************/ +static void gatt_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason, + tBT_TRANSPORT transport) +{ + UNUSED(gatt_if); + + GATT_TRACE_DEBUG ("%s: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", __FUNCTION__, + (bda[0] << 24) + (bda[1] << 16) + (bda[2] << 8) + bda[3], + (bda[4] << 8) + bda[5], connected, conn_id, reason); + + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr(bda, transport); + if (p_clcb == NULL) { + p_clcb = gatt_profile_clcb_alloc (conn_id, bda, transport); + } + + if (p_clcb == NULL) { + return; + } + + if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, bda, &p_clcb->conn_id, transport)) { + p_clcb->connected = TRUE; + p_clcb->conn_id = conn_id; + } + + + if (!p_clcb->connected) { + /* wait for connection */ + return; + } + + if (connected) { + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + + } else { + gatt_profile_clcb_dealloc(p_clcb); + } +} + +/******************************************************************************* +** +** Function gatt_profile_db_init +** +** Description Initializa the GATT profile attribute database. +** +*******************************************************************************/ +void gatt_profile_db_init (void) +{ + tBT_UUID app_uuid = {LEN_UUID_128, {0}}; + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}}; + UINT16 service_handle = 0; + tGATT_STATUS status; + + /* Fill our internal UUID with a fixed pattern 0x81 */ + memset (&app_uuid.uu.uuid128, 0x81, LEN_UUID_128); + + + /* Create a GATT profile service */ + gatt_cb.gatt_if = GATT_Register(&app_uuid, &gatt_profile_cback); + GATT_StartIf(gatt_cb.gatt_if); + + service_handle = GATTS_CreateService (gatt_cb.gatt_if , &uuid, 0, GATTP_MAX_ATTR_NUM, TRUE); + GATT_TRACE_DEBUG ("GATTS_CreateService: handle of service handle%x", service_handle); + + /* add Service Changed characteristic + */ + uuid.uu.uuid16 = gatt_cb.gattp_attr.uuid = GATT_UUID_GATT_SRV_CHGD; + gatt_cb.gattp_attr.service_change = 0; + gatt_cb.gattp_attr.handle = + gatt_cb.handle_of_h_r = GATTS_AddCharacteristic(service_handle, &uuid, 0, GATT_CHAR_PROP_BIT_INDICATE, + NULL, NULL); + + GATT_TRACE_DEBUG ("gatt_profile_db_init: handle of service changed%d\n", + gatt_cb.handle_of_h_r); + + tBT_UUID descr_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}}; + uint8_t ccc_value[2] ={ 0x00, 0x00}; + + tGATT_ATTR_VAL attr_val = { + .attr_max_len = sizeof(UINT16), + .attr_len = sizeof(UINT16), + .attr_val = ccc_value, + }; + + GATTS_AddCharDescriptor (service_handle, GATT_PERM_READ | GATT_PERM_WRITE , &descr_uuid, &attr_val, NULL); +#if GATTS_ROBUST_CACHING_ENABLED + /* add Client Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_CLIENT_SUP_FEAT; + gatt_cb.handle_of_cl_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ | GATT_PERM_WRITE, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE, NULL, NULL); + + /* add Database Hash characteristic */ + uuid.uu.uuid16 = GATT_UUID_GATT_DATABASE_HASH; + gatt_cb.handle_of_database_hash = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); + + /* add Server Supported Features characteristic */ + uuid.uu.uuid16 = GATT_UUID_SERVER_SUP_FEAT; + gatt_cb.handle_of_sr_supported_feat = GATTS_AddCharacteristic(service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ, NULL, NULL); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + /* start service */ + status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATTP_TRANSPORT_SUPPORTED ); + +#if (CONFIG_BT_STACK_NO_LOG) + (void) status; +#endif + + GATT_TRACE_DEBUG ("gatt_profile_db_init: gatt_if=%d start status%d\n", + gatt_cb.gatt_if, status); +} + +/******************************************************************************* +** +** Function gatt_disc_res_cback +** +** Description Gatt profile discovery result callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_DISC_RES *p_data) +{ + GATT_TRACE_DEBUG("%s, disc_type = %d",__func__, disc_type); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + switch (disc_type) { + case GATT_DISC_SRVC_BY_UUID:/* stage 1 */ + p_clcb->e_handle = p_data->value.group_value.e_handle; + p_clcb->ccc_result ++; + break; + + case GATT_DISC_CHAR:/* stage 2 */ + p_clcb->s_handle = p_data->value.dclr_value.val_handle; + p_clcb->ccc_result ++; + break; + + case GATT_DISC_CHAR_DSCPT: /* stage 3 */ + if (p_data->type.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) { + p_clcb->s_handle = p_data->handle; + p_clcb->ccc_result ++; + } + break; + } +} + +/******************************************************************************* +** +** Function gatt_disc_cmpl_cback +** +** Description Gatt profile discovery complete callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_STATUS status) +{ + GATT_TRACE_DEBUG("%s",__func__); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + if (status == GATT_SUCCESS && p_clcb->ccc_result > 0) { + p_clcb->ccc_result = 0; + p_clcb->ccc_stage ++; + gatt_cl_start_config_ccc(p_clcb); + } else { + GATT_TRACE_ERROR("%s() - Register for service changed indication failure", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function gatt_cl_op_cmpl_cback +** +** Description Gatt profile client operation complete callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_cl_op_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) +{ + GATT_TRACE_DEBUG("%s",__func__); + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_conn_id(conn_id); + + if (p_clcb == NULL) { + return; + } + + if (op == GATTC_OPTYPE_WRITE) { + GATT_TRACE_DEBUG("%s() - ccc write status : %d", __FUNCTION__, status); + } + +} + +/******************************************************************************* +** +** Function gatt_cl_start_config_ccc +** +** Description Gatt profile start configure service change CCC +** +** Returns void +** +*******************************************************************************/ +static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB *p_clcb) +{ + tGATT_DISC_PARAM srvc_disc_param; + tGATT_VALUE ccc_value; + + GATT_TRACE_DEBUG("%s() - stage: %d", __FUNCTION__, p_clcb->ccc_stage); + + memset (&srvc_disc_param, 0 , sizeof(tGATT_DISC_PARAM)); + memset (&ccc_value, 0 , sizeof(tGATT_VALUE)); + + switch (p_clcb->ccc_stage) { + case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */ + srvc_disc_param.s_handle = 1; + srvc_disc_param.e_handle = 0xffff; + srvc_disc_param.service.len = 2; + srvc_disc_param.service.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc service error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */ + srvc_disc_param.s_handle = 1; + srvc_disc_param.e_handle = p_clcb->e_handle; + srvc_disc_param.service.len = 2; + srvc_disc_param.service.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc char error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */ + srvc_disc_param.s_handle = p_clcb->s_handle; + srvc_disc_param.e_handle = p_clcb->e_handle; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Discover (p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - ccc char descriptor error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + + case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */ + ccc_value.handle = p_clcb->s_handle; + ccc_value.len = 2; + ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION; +#if (GATTC_INCLUDED == TRUE) + if (GATTC_Write (p_clcb->conn_id, GATT_WRITE, &ccc_value) != GATT_SUCCESS) { + GATT_TRACE_ERROR("%s() - write ccc error", __FUNCTION__); + } +#endif ///GATTC_INCLUDED == TRUE + break; + } +} + +/******************************************************************************* +** +** Function GATT_ConfigServiceChangeCCC +** +** Description Configure service change indication on remote device +** +** Returns none +** +*******************************************************************************/ +void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable, tBT_TRANSPORT transport) +{ + tGATT_PROFILE_CLCB *p_clcb = gatt_profile_find_clcb_by_bd_addr (remote_bda, transport); + + if (p_clcb == NULL) { + p_clcb = gatt_profile_clcb_alloc (0, remote_bda, transport); + } + + if (p_clcb == NULL) { + return; + } + + if (GATT_GetConnIdIfConnected (gatt_cb.gatt_if, remote_bda, &p_clcb->conn_id, transport)) { + p_clcb->connected = TRUE; + } + /* hold the link here */ + GATT_Connect(gatt_cb.gatt_if, remote_bda, BLE_ADDR_UNKNOWN_TYPE, TRUE, transport, FALSE); + p_clcb->ccc_stage = GATT_SVC_CHANGED_CONNECTING; + + if (!p_clcb->connected) { + /* wait for connection */ + return; + } + + p_clcb->ccc_stage ++; + gatt_cl_start_config_ccc(p_clcb); +} + +#if GATTS_ROBUST_CACHING_ENABLED +/******************************************************************************* +** +** Function gatt_sr_is_cl_robust_caching_supported +** +** Description Check if Robust Caching is supported for the connection +** +** Returns true if enabled by client side, otherwise false +** +*******************************************************************************/ +static BOOLEAN gatt_sr_is_cl_robust_caching_supported(tGATT_TCB *p_tcb) +{ + return (p_tcb->cl_supp_feat & BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK); +} +/******************************************************************************* +** +** Function gatt_sr_is_cl_change_aware +** +** Description Check if the connection is change-aware +** +** Returns true if change aware, otherwise false +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb) +{ + // If robust caching is not supported, should always return true by default + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return true; + } + + return p_tcb->is_robust_cache_change_aware; +} + +/******************************************************************************* +** +** Function gatt_sr_init_cl_status +** +** Description Restore status for trusted device +** +** Returns none +** +*******************************************************************************/ +void gatt_sr_init_cl_status(tGATT_TCB *p_tcb) +{ +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_cl_feat_load(p_tcb->peer_bda, &p_tcb->cl_supp_feat); +#endif + + // This is used to reset bit when robust caching is disabled + if (!GATTS_ROBUST_CACHING_ENABLED) { + p_tcb->cl_supp_feat &= ~BLE_GATT_CL_SUPP_FEAT_ROBUST_CACHING_BITMASK; + } + + if (gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + BT_OCTET16 stored_hash = {0}; +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_db_hash_load(p_tcb->peer_bda, stored_hash); +#endif + p_tcb->is_robust_cache_change_aware = (memcmp(stored_hash, gatt_cb.database_hash, BT_OCTET16_LEN) == 0); + } else { + p_tcb->is_robust_cache_change_aware = true; + } + + GATT_TRACE_DEBUG("%s feat %x aware %d", __func__, p_tcb->cl_supp_feat, p_tcb->is_robust_cache_change_aware); +} + +/******************************************************************************* +** +** Function gatt_sr_update_cl_status +** +** Description Update change-aware status for the remote device +** +** Returns none +** +*******************************************************************************/ +void gatt_sr_update_cl_status(tGATT_TCB *p_tcb, BOOLEAN chg_aware) +{ + if (p_tcb == NULL) { + return; + } + + // if robust caching is not supported, do nothing + if (!gatt_sr_is_cl_robust_caching_supported(p_tcb)) { + return; + } + + // only when client status is changed from unaware to aware, we should store database hash + if (!p_tcb->is_robust_cache_change_aware && chg_aware) { +#if (SMP_INCLUDED == TRUE) + bta_gatts_co_db_hash_save(p_tcb->peer_bda, gatt_cb.database_hash); +#endif + } + + p_tcb->is_robust_cache_change_aware = chg_aware; + + GATT_TRACE_DEBUG("%s status %d", __func__, chg_aware); +} +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c b/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c new file mode 100644 index 00000000..4db0a554 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_auth.c @@ -0,0 +1,522 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT authentication handling functions + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE +#include <string.h> + +#include "gatt_int.h" +#include "stack/gatt_api.h" +#include "btm_int.h" + +/******************************************************************************* +** +** Function gatt_sign_data +** +** Description This function sign the data for write command. +** +** Returns TRUE if encrypted, otherwise FALSE. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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 *)osi_malloc((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); +#if (GATTC_INCLUDED == TRUE) + gatt_act_write(p_clcb, GATT_SEC_SIGN_DATA); +#endif ///GATTC_INCLUDED == TRUE + } else { + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL); + } + + osi_free(p_data); + } + + return status; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_verify_signature +** +** Description This function start to verify the sign data when receiving +** the data from peer device. +** +** Returns +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf) +{ + UINT16 cmd_len; +#if (GATTS_INCLUDED == TRUE) + UINT8 op_code; +#endif ///GATTS_INCLUDED == TRUE + UINT8 *p, *p_orig = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT32 counter; + + if (p_buf->len < GATT_AUTH_SIGN_LEN + 4) { + GATT_TRACE_ERROR("%s: Data length %u less than expected %u", + __func__, p_buf->len, GATT_AUTH_SIGN_LEN + 4); + return; + } + cmd_len = p_buf->len - GATT_AUTH_SIGN_LEN + 4; + p = p_orig + cmd_len - 4; + STREAM_TO_UINT32(counter, p); + + if (BTM_BleVerifySignature(p_tcb->peer_bda, p_orig, cmd_len, counter, p)) { +#if (GATTS_INCLUDED == TRUE) + STREAM_TO_UINT8(op_code, p_orig); + gatt_server_handle_client_req (p_tcb, op_code, (UINT16)(p_buf->len - 1), p_orig); +#endif ///GATTS_INCLUDED == TRUE + } else { + /* if this is a bad signature, assume from attacker, ignore it */ + GATT_TRACE_ERROR("Signature Verification Failed, data ignored"); + } + + return; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_sec_check_complete +** +** Description security check complete and proceed to data sending action. +** +** Returns void. +** +*******************************************************************************/ +void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB *p_clcb, UINT8 sec_act) +{ + if (p_clcb && p_clcb->p_tcb) { + if (fixed_queue_is_empty(p_clcb->p_tcb->pending_enc_clcb)) { + gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE); + } +#if (GATTC_INCLUDED == TRUE) + if (!sec_check_ok) { + gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL); + } else if (p_clcb->operation == GATTC_OPTYPE_WRITE) { + gatt_act_write(p_clcb, sec_act); + } else if (p_clcb->operation == GATTC_OPTYPE_READ) { + gatt_act_read(p_clcb, p_clcb->counter); + } +#endif ///GATTC_INCLUDED == TRUE + } +} +/******************************************************************************* +** +** 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; + 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; + } + tGATT_PENDING_ENC_CLCB *p_buf = + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != 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); + osi_free(p_buf); + /* start all other pending operation in queue */ + for (size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); + count > 0; count--) { + p_buf = (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != NULL) { + gatt_security_check_start(p_buf->p_clcb); + osi_free(p_buf); + } else { + break; + } + } + } else { + GATT_TRACE_ERROR("Unknown operation encryption completed"); + } + } else { + GATT_TRACE_ERROR("enc callback for unknown bd_addr"); + } +} + +/******************************************************************************* +** +** 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; + 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); + + size_t count = fixed_queue_length(p_tcb->pending_enc_clcb); + for (; count > 0; count--) { + tGATT_PENDING_ENC_CLCB *p_buf = + (tGATT_PENDING_ENC_CLCB *)fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0); + if (p_buf != NULL) { + gatt_security_check_start(p_buf->p_clcb); + osi_free(p_buf); + } else { + break; + } + } + } + } else { + GATT_TRACE_DEBUG("notify GATT for encryption completion of unknown device"); + } + return; +} +/******************************************************************************* +** +** Function gatt_set_sec_act +** +** Description This function set the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act) +{ + if (p_tcb) { + p_tcb->sec_act = sec_act; + } +} +/******************************************************************************* +** +** Function gatt_get_sec_act +** +** Description This function get the sec_act in clcb +** +** Returns none +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb) +{ + tGATT_SEC_ACTION sec_act = GATT_SEC_NONE; + if (p_tcb) { + sec_act = p_tcb->sec_act; + } + return sec_act; +} +/******************************************************************************* +** +** Function gatt_determine_sec_act +** +** Description This routine determine the security action based on auth_request and +** current link status +** +** Returns tGATT_SEC_ACTION security action +** +*******************************************************************************/ +tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ) +{ + tGATT_SEC_ACTION act = GATT_SEC_OK; + UINT8 sec_flag; + tGATT_TCB *p_tcb = p_clcb->p_tcb; + tGATT_AUTH_REQ auth_req = p_clcb->auth_req; + BOOLEAN is_link_encrypted = FALSE; + BOOLEAN is_link_key_known = FALSE; + BOOLEAN is_key_mitm = FALSE; +#if (SMP_INCLUDED == TRUE) + UINT8 key_type; + tBTM_BLE_SEC_REQ_ACT sec_act = BTM_LE_SEC_NONE; +#endif ///SMP_INCLUDED == TRUE + if (auth_req == GATT_AUTH_REQ_NONE ) { + return act; + } + + BTM_GetSecurityFlagsByTransport(p_tcb->peer_bda, &sec_flag, p_clcb->p_tcb->transport); +#if (SMP_INCLUDED == TRUE) + btm_ble_link_sec_check(p_tcb->peer_bda, auth_req, &sec_act); +#endif ///SMP_INCLUDED == TRUE + /* if a encryption is pending, need to wait */ + if ( +#if (SMP_INCLUDED == TRUE) + sec_act == BTM_BLE_SEC_REQ_ACT_DISCARD && +#endif ///SMP_INCLUDED == TRUE + auth_req != GATT_AUTH_REQ_NONE) { + return GATT_SEC_ENC_PENDING; + } + + if (sec_flag & (BTM_SEC_FLAG_ENCRYPTED | BTM_SEC_FLAG_LKEY_KNOWN)) { + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { + is_link_encrypted = TRUE; + } + + is_link_key_known = TRUE; + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + is_key_mitm = TRUE; + } + } + + /* first check link key upgrade required or not */ + switch (auth_req) { + case GATT_AUTH_REQ_MITM: + case GATT_AUTH_REQ_SIGNED_MITM: + if (!is_key_mitm) { + act = GATT_SEC_ENCRYPT_MITM; + } + break; + + case GATT_AUTH_REQ_NO_MITM: + case GATT_AUTH_REQ_SIGNED_NO_MITM: + if (!is_link_key_known) { + act = GATT_SEC_ENCRYPT_NO_MITM; + } + break; + default: + break; + } + + /* now check link needs to be encrypted or not if the link key upgrade is not required */ + if (act == GATT_SEC_OK) { + if (p_tcb->transport == BT_TRANSPORT_LE && + (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { + /* this is a write command request + check data signing required or not */ + if (!is_link_encrypted) { +#if (SMP_INCLUDED == TRUE) + btm_ble_get_enc_key_type(p_tcb->peer_bda, &key_type); +#endif ///SMP_INCLUDED == TRUE + if ( +#if (SMP_INCLUDED == TRUE) + (key_type & BTM_LE_KEY_LCSRK) && +#endif ///SMP_INCLUDED == TRUE + ((auth_req == GATT_AUTH_REQ_SIGNED_NO_MITM) || + (auth_req == GATT_AUTH_REQ_SIGNED_MITM))) { + act = GATT_SEC_SIGN_DATA; + } else { + act = GATT_SEC_ENCRYPT; + } + } + } else { + if (!is_link_encrypted) { + act = GATT_SEC_ENCRYPT; + } + } + + } + + return act ; + +} + + + +/******************************************************************************* +** +** Function gatt_get_link_encrypt_status +** +** Description This routine get the encryption status of the specified link +** +** +** Returns tGATT_STATUS link encryption status +** +*******************************************************************************/ +tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb) +{ + tGATT_STATUS encrypt_status = GATT_NOT_ENCRYPTED; + UINT8 sec_flag = 0; + + BTM_GetSecurityFlagsByTransport(p_tcb->peer_bda, &sec_flag, p_tcb->transport); + + if ((sec_flag & BTM_SEC_FLAG_ENCRYPTED) && (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) { + encrypt_status = GATT_ENCRYPED_NO_MITM; + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + encrypt_status = GATT_ENCRYPED_MITM; + } + } + + GATT_TRACE_DEBUG("gatt_get_link_encrypt_status status=0x%x", encrypt_status); + return encrypt_status ; +} + + +/******************************************************************************* +** +** Function gatt_convert_sec_action +** +** Description Convert GATT security action enum into equivalent BTM BLE security action enum +** +** Returns BOOLEAN TRUE - conversation is successful +** +*******************************************************************************/ +static BOOLEAN gatt_convert_sec_action(tGATT_SEC_ACTION gatt_sec_act, tBTM_BLE_SEC_ACT *p_btm_sec_act ) +{ + BOOLEAN status = TRUE; + switch (gatt_sec_act) { + case GATT_SEC_ENCRYPT: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + case GATT_SEC_ENCRYPT_NO_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + case GATT_SEC_ENCRYPT_MITM: + *p_btm_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + default: + status = FALSE; + break; + } + + return status; +} +/******************************************************************************* +** +** 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; +} + + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c b/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c new file mode 100644 index 00000000..12168dc0 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_cl.c @@ -0,0 +1,1241 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main GATT client functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE + +#include <string.h> +#include "osi/allocator.h" +#include "gatt_int.h" +#include "l2c_int.h" + +#define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */ +#define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80) +#define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90) + +#define GATT_PREP_WRITE_RSP_MIN_LEN 4 +#define GATT_NOTIFICATION_MIN_LEN 2 +#define GATT_WRITE_RSP_MIN_LEN 2 +#define GATT_INFO_RSP_MIN_LEN 1 +#define GATT_MTU_RSP_MIN_LEN 2 +#define GATT_READ_BY_TYPE_RSP_MIN_LEN 1 + +/******************************************************************************** +** G L O B A L G A T T D A T A * +*********************************************************************************/ +void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb); + +static const UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = { + 0, + GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */ + GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */ + GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR_BY_UUID, */ + GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */ +}; + +static const UINT16 disc_type_to_uuid[GATT_DISC_MAX] = { + 0, /* reserved */ + GATT_UUID_PRI_SERVICE, /* <service> DISC_SRVC_ALL */ + GATT_UUID_PRI_SERVICE, /* <service> for DISC_SERVC_BY_UUID */ + GATT_UUID_INCLUDE_SERVICE, /* <include_service> for DISC_INC_SRVC */ + GATT_UUID_CHAR_DECLARE, /* <characteristic> for DISC_CHAR */ + 0 /* no type filtering for DISC_CHAR_DSCPT */ +}; + +// Use for GATTC discover infomation print +#define GATT_DISC_INFO(fmt, args...) {if (gatt_cb.auto_disc == FALSE) BT_PRINT_I("BT_GATT", fmt, ## args);} + +/******************************************************************************* +** +** Function gatt_act_discovery +** +** Description GATT discovery operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_discovery(tGATT_CLCB *p_clcb) +{ + UINT8 op_code = disc_type_to_att_opcode[p_clcb->op_subtype]; + tGATT_CL_MSG cl_req; + tGATT_STATUS st; + + if (p_clcb->s_handle <= p_clcb->e_handle && p_clcb->s_handle != 0) { + memset(&cl_req, 0, sizeof(tGATT_CL_MSG)); + + cl_req.browse.s_handle = p_clcb->s_handle; + cl_req.browse.e_handle = p_clcb->e_handle; + + if (disc_type_to_uuid[p_clcb->op_subtype] != 0) { + cl_req.browse.uuid.len = 2; + cl_req.browse.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + } + + if (p_clcb->op_subtype == GATT_DISC_SRVC_BY_UUID) { /* fill in the FindByTypeValue request info*/ + cl_req.find_type_value.uuid.len = 2; + cl_req.find_type_value.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + cl_req.find_type_value.s_handle = p_clcb->s_handle; + cl_req.find_type_value.e_handle = p_clcb->e_handle; + cl_req.find_type_value.value_len = p_clcb->uuid.len; + /* if service type is 32 bits UUID, convert it now */ + if (p_clcb->uuid.len == LEN_UUID_32) { + cl_req.find_type_value.value_len = LEN_UUID_128; + gatt_convert_uuid32_to_uuid128(cl_req.find_type_value.value, p_clcb->uuid.uu.uuid32); + } else { + memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len); + } + } + + if (p_clcb->op_subtype == GATT_DISC_CHAR_BY_UUID) { + memcpy(&cl_req.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); + } + + st = attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req); + + if (st != GATT_SUCCESS && st != GATT_CMD_STARTED) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + } else { /* end of handle range */ + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); + } +} + +/******************************************************************************* +** +** 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_DEBUG("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_MULTIPLE_VAR: + op_code = GATT_REQ_READ_MULTI_VAR; + 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_ERROR("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); + } + + if ( op_code == 0 || (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)) { + gatt_end_operation(p_clcb, rt, NULL); + } +} + +/******************************************************************************* +** +** Function gatt_act_write +** +** Description GATT write operation. +** +** Returns void. +** +*******************************************************************************/ +void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act) +{ + tGATT_TCB *p_tcb = p_clcb->p_tcb; + UINT8 rt = GATT_SUCCESS, op_code = 0; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + if (p_attr) { + switch (p_clcb->op_subtype) { + case GATT_WRITE_NO_RSP: + l2ble_update_att_acl_pkt_num(L2CA_DECREASE_BTU_NUM, NULL); + p_clcb->s_handle = p_attr->handle; + op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE; + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + op_code, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + break; + + case GATT_WRITE: + if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) { + p_clcb->s_handle = p_attr->handle; + + rt = gatt_send_write_msg(p_tcb, + p_clcb->clcb_idx, + GATT_REQ_WRITE, + p_attr->handle, + p_attr->len, + 0, + p_attr->value); + } else { /* prepare write for long attribute */ + gatt_send_prepare_write(p_tcb, p_clcb); + } + break; + + case GATT_WRITE_PREPARE: + gatt_send_prepare_write(p_tcb, p_clcb); + break; + + default: + rt = GATT_INTERNAL_ERROR; + GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype); + break; + } + } else { + rt = GATT_INTERNAL_ERROR; + } + + if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) + || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { + if (rt != GATT_SUCCESS) { + GATT_TRACE_DEBUG("gatt_act_write() failed op_code=0x%x rt=%d", op_code, rt); + } + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_send_queue_write_cancel +** +** Description send queue write cancel +** +** Returns void. +** +*******************************************************************************/ +void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag) +{ + UINT8 rt ; + + GATT_TRACE_DEBUG("gatt_send_queue_write_cancel "); + + rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag); + + if (rt != GATT_SUCCESS) { + gatt_end_operation(p_clcb, rt, NULL); + } +} +/******************************************************************************* +** +** Function gatt_check_write_long_terminate +** +** Description To terminate write long or not. +** +** Returns TRUE: write long is terminated; FALSE keep sending. +** +*******************************************************************************/ +BOOLEAN gatt_check_write_long_terminate(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_VALUE *p_rsp_value) +{ + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + BOOLEAN exec = FALSE; + tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC; + + GATT_TRACE_DEBUG("gatt_check_write_long_terminate "); + /* check the first write response status */ + if (p_rsp_value != NULL) { + if (p_rsp_value->handle != p_attr->handle || + p_rsp_value->len != p_clcb->counter || + memcmp(p_rsp_value->value, p_attr->value + p_attr->offset, p_rsp_value->len)) { + /* data does not match */ + p_clcb->status = GATT_ERROR; + flag = GATT_PREP_WRITE_CANCEL; + exec = TRUE; + } else { /* response checking is good */ + p_clcb->status = GATT_SUCCESS; + /* update write offset and check if end of attribute value */ + if ((p_attr->offset += p_rsp_value->len) >= p_attr->len) { + exec = TRUE; + } + } + } + if (exec) { + gatt_send_queue_write_cancel (p_tcb, p_clcb, flag); + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** 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_DEBUG("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_DEBUG("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 && rt != GATT_CMD_STARTED) { + gatt_end_operation(p_clcb, rt, NULL); + } +} + + +/******************************************************************************* +** +** Function gatt_process_find_type_value_rsp +** +** Description This function is called to handle find by type value response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + UINT8 *p = p_data; + + UNUSED(p_tcb); + + GATT_TRACE_DEBUG("gatt_process_find_type_value_rsp "); + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) { + return; + } + + memset (&result, 0, sizeof(tGATT_DISC_RES)); + result.type.len = 2; + result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE; + + /* returns a series of handle ranges */ + while (len >= 4) { + STREAM_TO_UINT16 (result.handle, p); + STREAM_TO_UINT16 (result.value.group_value.e_handle, p); + GATT_DISC_INFO("%s handle %x, end handle %x", __func__, result.handle, result.value.group_value.e_handle); + memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID)); + + len -= 4; + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + /* last handle + 1 */ + p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_process_read_info_rsp +** +** Description This function is called to handle the read information +** response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + UINT8 *p = p_data, uuid_len = 0, type; + + UNUSED(p_tcb); + UNUSED(op_code); + + if (len < GATT_INFO_RSP_MIN_LEN) { + GATT_TRACE_ERROR("invalid Info Response PDU received, discard."); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); + return; + } + /* unexpected response */ + if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT) { + return; + } + + STREAM_TO_UINT8(type, p); + len -= 1; + + if (type == GATT_INFO_TYPE_PAIR_16) { + uuid_len = LEN_UUID_16; + } else if (type == GATT_INFO_TYPE_PAIR_128) { + uuid_len = LEN_UUID_128; + } + + while (len >= uuid_len + 2) { + STREAM_TO_UINT16 (result.handle, p); + + if (uuid_len > 0) { + if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) { + break; + } + } else { + memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID)); + } + + len -= (uuid_len + 2); + + GATT_DISC_INFO("%s handle %x, uuid %s", __func__, result.handle, gatt_uuid_to_str(&result.type)); + + if (p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + p_clcb->s_handle = (result.handle == 0) ? 0 : (result.handle + 1); + /* initiate another request */ + gatt_act_discovery(p_clcb) ; +} +/******************************************************************************* +** +** Function gatt_proc_disc_error_rsp +** +** Description This function process the read by type response and send another +** request if needed. +** +** Returns void. +** +*******************************************************************************/ +void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode, + UINT16 handle, UINT8 reason) +{ + tGATT_STATUS status = (tGATT_STATUS) reason; + + UNUSED(p_tcb); + UNUSED(handle); + + GATT_TRACE_DEBUG("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode); + + switch (opcode) { + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_READ_BY_TYPE: + case GATT_REQ_FIND_INFO: + if (reason == GATT_NOT_FOUND) { + status = GATT_SUCCESS; + GATT_DISC_INFO("Discovery completed"); + } + break; + default: + GATT_TRACE_ERROR("Incorrect discovery opcode %04x", opcode); + break; + } + + gatt_end_operation(p_clcb, status, NULL); +} + +/******************************************************************************* +** +** Function gatt_process_error_rsp +** +** Description This function is called to handle the error response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT8 opcode, reason, * p = p_data; + UINT16 handle; + tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; + + UNUSED(op_code); + UNUSED(len); + + GATT_TRACE_DEBUG("%s", __func__); + STREAM_TO_UINT8(opcode, p); + STREAM_TO_UINT16(handle, p); + STREAM_TO_UINT8(reason, p); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + gatt_proc_disc_error_rsp(p_tcb, p_clcb, opcode, handle, reason); + } else { + if ( (p_clcb->operation == GATTC_OPTYPE_WRITE) && + (p_clcb->op_subtype == GATT_WRITE) && + (opcode == GATT_REQ_PREPARE_WRITE) && + (p_attr) && + (handle == p_attr->handle) ) { + if (reason == GATT_SUCCESS){ + reason = GATT_ERROR; + } + p_clcb->status = reason; + gatt_send_queue_write_cancel(p_tcb, p_clcb, GATT_PREP_WRITE_CANCEL); + } else if ((p_clcb->operation == GATTC_OPTYPE_READ) && + ((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) || + (p_clcb->op_subtype == GATT_READ_BY_HANDLE)) && + (opcode == GATT_REQ_READ_BLOB) && + p_clcb->first_read_blob_after_read && + (reason == GATT_NOT_LONG)) { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } else { + gatt_end_operation(p_clcb, reason, NULL); + } + } +} +/******************************************************************************* +** +** Function gatt_process_prep_write_rsp +** +** Description This function is called to handle the read response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + UINT8 *p = p_data; + + GATT_TRACE_DEBUG("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); + + if (len < GATT_PREP_WRITE_RSP_MIN_LEN) { + GATT_TRACE_ERROR("illegal prepare write response length, discard"); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value); + return; + } + + STREAM_TO_UINT16 (value.handle, p); + STREAM_TO_UINT16 (value.offset, p); + + value.len = len - 4; + + memcpy (value.value, p, value.len); + + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) { + p_clcb->status = GATT_SUCCESS; + /* application should verify handle offset + and value are matched or not */ + + gatt_end_operation(p_clcb, p_clcb->status, &value); + } else if (p_clcb->op_subtype == GATT_WRITE ) { + if (!gatt_check_write_long_terminate(p_tcb, p_clcb, &value)) { + gatt_send_prepare_write(p_tcb, p_clcb); + } + } + +} +/******************************************************************************* +** +** Function gatt_process_notification +** +** Description This function is called to handle the handle value indication +** or handle value notification. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_VALUE value = {0}; + tGATT_REG *p_reg; + UINT16 conn_id; + tGATT_STATUS encrypt_status; + UINT8 *p = p_data, i, + event = (op_code == GATT_HANDLE_VALUE_IND) ? GATTC_OPTYPE_INDICATION: GATTC_OPTYPE_NOTIFICATION; + + GATT_TRACE_DEBUG("gatt_process_notification "); + + if (len < GATT_NOTIFICATION_MIN_LEN) { + GATT_TRACE_ERROR("illegal notification PDU length, discard"); + return; + } + + STREAM_TO_UINT16 (value.handle, p); + + if (!GATT_HANDLE_IS_VALID(value.handle)) { + /* illegal handle, send ack now */ + if (op_code == GATT_HANDLE_VALUE_IND) { + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + } + return; + } + + if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) { + if (len < GATT_NOTIFICATION_MIN_LEN + 2) { + GATT_TRACE_ERROR("illegal notification PDU length, discard"); + return; + } + + STREAM_TO_UINT16(value.len, p); + if (value.len > len - 4) { + return; + } + } else { + value.len = len - 2; + } + + if (value.len > GATT_MAX_ATTR_LEN) { + GATT_TRACE_ERROR("value length larger than GATT_MAX_ATTR_LEN, discard"); + return; + } + + memcpy(value.value, p, value.len); + p += value.len; + + if (event == GATTC_OPTYPE_INDICATION) { + if (p_tcb->ind_count) { + /* this is an error case that receiving an indication but we + still has an indication not being acked yet. + For now, just log the error reset the counter. + Later we need to disconnect the link unconditionally. + */ + GATT_TRACE_ERROR("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count); + } + p_tcb->ind_count = 0; + + /* should notify all registered client with the handle value notificaion/indication + Note: need to do the indication count and start timer first then do callback + */ + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION)) { + p_tcb->ind_count++; + } + } + + /* start a timer for app confirmation */ + if (p_tcb->ind_count > 0) { + gatt_start_ind_ack_timer(p_tcb); + } else { /* no app to indicate, or invalid handle */ + attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); + } + } + + encrypt_status = gatt_get_link_encrypt_status(p_tcb); + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); + } + } + + if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + return; + } + + if (len < (4 + value.len)) { + GATT_TRACE_ERROR("no remain data for multi notification"); + return; + } + + len -= (4 + value.len); + + while (len > 4) { + STREAM_TO_UINT16(value.handle, p); + STREAM_TO_UINT16(value.len, p); + len -= 4; + value.len = MIN(len, value.len); + memcpy(value.value, p, value.len); + p += value.len; + len -= value.len; + + for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_process_read_by_type_rsp +** +** Description This function is called to handle the read by type response. +** read by type can be used for discovery, or read by type or +** read characteristic value. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_DISC_RES result; + tGATT_DISC_VALUE record_value; + UINT8 *p = p_data, value_len, handle_len = 2; + UINT16 handle = 0; + + /* discovery procedure and no callback function registered */ + if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) { + return; + } + + if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) { + GATT_TRACE_ERROR("Illegal ReadByType/ReadByGroupType Response length, discard"); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); + return; + } + + STREAM_TO_UINT8(value_len, p); + + if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len - 1)) ) { + /* this is an error case that server's response containing a value length which is larger than MTU-2 + or value_len > message total length -1 */ + GATT_TRACE_ERROR("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)", + op_code, value_len, (p_tcb->payload_size - 2), (len - 1)); + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + return; + } + + if (op_code == GATT_RSP_READ_BY_GRP_TYPE) { + handle_len = 4; + } + + value_len -= handle_len; /* substract the handle pairs bytes */ + len -= 1; + + while (len >= (handle_len + value_len)) { + STREAM_TO_UINT16(handle, p); + GATT_DISC_INFO("%s op %x, handle %x", __func__, op_code, handle); + + if (!GATT_HANDLE_IS_VALID(handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + memset(&result, 0, sizeof(tGATT_DISC_RES)); + memset(&record_value, 0, sizeof(tGATT_DISC_VALUE)); + + result.handle = handle; + result.type.len = 2; + result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; + + /* discover all services */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL && + op_code == GATT_RSP_READ_BY_GRP_TYPE) { + STREAM_TO_UINT16(handle, p); + + if (!GATT_HANDLE_IS_VALID(handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } else { + record_value.group_value.e_handle = handle; + if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) { + GATT_TRACE_ERROR("discover all service response parsing failure"); + break; + } + } + GATT_DISC_INFO("DISC ALL SVC end handle %x, uuid %s", record_value.group_value.e_handle, gatt_uuid_to_str(&record_value.group_value.service_type)); + } + /* discover included service */ + else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) { + STREAM_TO_UINT16(record_value.incl_service.s_handle, p); + STREAM_TO_UINT16(record_value.incl_service.e_handle, p); + + if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) || + !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + + if (value_len == 6) { + STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p); + record_value.incl_service.service_type.len = LEN_UUID_16; + GATT_DISC_INFO("DISC INC SVC start handle %x, end handle %x, uuid %s", + record_value.incl_service.s_handle, record_value.incl_service.e_handle, gatt_uuid_to_str(&record_value.incl_service.service_type)); + } else if (value_len == 4) { + p_clcb->s_handle = record_value.incl_service.s_handle; + p_clcb->read_uuid128.wait_for_read_rsp = TRUE; + p_clcb->read_uuid128.next_disc_start_hdl = handle + 1; + memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result)); + memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value)); + p_clcb->op_subtype |= 0x90; + gatt_act_read(p_clcb, 0); // read 128-bit uuid of include service + return; + } else { + GATT_TRACE_ERROR("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len); + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + return; + } + } + /* read by type */ + else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE) { + p_clcb->counter = len - 2; + p_clcb->s_handle = handle; + if ( p_clcb->counter == (p_clcb->p_tcb->payload_size - 4)) { + p_clcb->op_subtype = GATT_READ_BY_HANDLE; + if (!p_clcb->p_attr_buf) { + p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); + } + if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN) { + memcpy(p_clcb->p_attr_buf, p, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } else { + gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p); + } + } else { + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } + return; + } else { /* discover characterisitic */ + STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); + STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); + if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) { + gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); + return; + } + if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p)) { + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); + /* invalid format, and skip the result */ + return; + } + + /* UUID not matching */ + if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid)) { + len -= (value_len + 2); + continue; /* skip the result, and look for next one */ + } else if (p_clcb->operation == GATTC_OPTYPE_READ) + /* UUID match for read characteristic value */ + { + /* only read the first matching UUID characteristic value, and + discard the rest results */ + p_clcb->s_handle = record_value.dclr_value.val_handle; + p_clcb->op_subtype |= 0x80; + gatt_act_read(p_clcb, 0); + return; + } + } + len -= (value_len + handle_len); + + /* result is (handle, 16bits UUID) pairs */ + memcpy (&result.value, &record_value, sizeof (result.value)); + + /* send callback if is discover procedure */ + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); + } + } + + p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1); + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + /* initiate another request */ + gatt_act_discovery(p_clcb) ; + } else { /* read characteristic value */ + gatt_act_read(p_clcb, 0); + } +} + +/******************************************************************************* +** +** Function gatt_process_read_rsp +** +** Description This function is called to handle the read BLOB response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 offset = p_clcb->counter; + UINT8 *p = p_data; + + UNUSED(op_code); + + if (p_clcb->operation == GATTC_OPTYPE_READ) { + if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) { + p_clcb->counter = len; + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); + } else { + + /* allocate GKI buffer holding up long attribute value */ + if (!p_clcb->p_attr_buf) { + p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); + } + + /* copy attrobute value into cb buffer */ + if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN) { + if ((len + offset) > GATT_MAX_ATTR_LEN) { + len = GATT_MAX_ATTR_LEN - offset; + } + + p_clcb->counter += len; + + memcpy(p_clcb->p_attr_buf + offset, p, len); + + /* send next request if needed */ + + if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ + len + offset < GATT_MAX_ATTR_LEN) { + GATT_TRACE_DEBUG("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", + offset, len, p_clcb->counter); + gatt_act_read(p_clcb, p_clcb->counter); + } else { /* end of request, send callback */ + gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); + } + } else { /* exception, should not happen */ + GATT_TRACE_ERROR("attr offset = %d p_attr_buf = %p ", offset, p_clcb->p_attr_buf); + gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf); + } + } + } else { + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_INC_SRVC && + p_clcb->read_uuid128.wait_for_read_rsp ) { + p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl; + p_clcb->read_uuid128.wait_for_read_rsp = FALSE; + if (len == LEN_UUID_128) { + + memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len); + p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128; + tGATT_INCL_SRVC *inc_srvc = &p_clcb->read_uuid128.result.value.incl_service; + GATT_DISC_INFO("DISC INC SRVC start handle %x, end handle %x, uuid %s", + inc_srvc->s_handle, inc_srvc->e_handle, gatt_uuid_to_str(&inc_srvc->service_type)); + if ( p_clcb->p_reg->app_cb.p_disc_res_cb) { + (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result); + } + gatt_act_discovery(p_clcb) ; + } else { + gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); + } + } + } + +} + + +/******************************************************************************* +** +** Function gatt_process_handle_rsp +** +** Description This function is called to handle the write response +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_handle_rsp(tGATT_CLCB *p_clcb) +{ + gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); +} +/******************************************************************************* +** +** Function gatt_process_mtu_rsp +** +** Description This function is called to process the configure MTU response. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu; + tGATT_STATUS status = GATT_SUCCESS; + + if (len < GATT_MTU_RSP_MIN_LEN) { + GATT_TRACE_ERROR("invalid MTU response PDU received, discard."); + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(mtu, p_data); + + if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE) { + p_tcb->payload_size = mtu; + } + } + /* host will set packet data length to 251 automatically if remote device support set packet data length, + so l2cble_set_fixed_channel_tx_data_length() is not necessary. + l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); + */ + gatt_end_operation(p_clcb, status, NULL); +} +/******************************************************************************* +** +** Function gatt_cmd_to_rsp_code +** +** Description The function convert a ATT command op code into the corresponding +** response code assume no error occurs. +** +** Returns response code. +** +*******************************************************************************/ +UINT8 gatt_cmd_to_rsp_code (UINT8 cmd_code) +{ + UINT8 rsp_code = 0; + + if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) { + rsp_code = cmd_code + 1; + } + return rsp_code; +} +/******************************************************************************* +** +** Function gatt_cl_send_next_cmd_inq +** +** Description Find next command in queue and sent to server +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + BOOLEAN sent = FALSE; + UINT8 rsp_code; + tGATT_CLCB *p_clcb = NULL; + tGATT_STATUS att_ret = GATT_SUCCESS; + + while (!sent && + p_tcb->pending_cl_req != p_tcb->next_slot_inq && + p_cmd->to_send && p_cmd->p_cmd != NULL) { + att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd->p_cmd); + + if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) { + sent = TRUE; + p_cmd->to_send = FALSE; + if(p_cmd->p_cmd) { + osi_free(p_cmd->p_cmd); + p_cmd->p_cmd = NULL; + } + + /* dequeue the request if is write command or sign write */ + if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) { + gatt_start_rsp_timer (p_cmd->clcb_idx); + } else { + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); + + /* if no ack needed, keep sending */ + if (att_ret == GATT_SUCCESS) { + sent = FALSE; + } + + p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + /* send command complete callback here */ + gatt_end_operation(p_clcb, att_ret, NULL); + } + } else { + GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error"); + + memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); + p_tcb->pending_cl_req ++; + p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + } + + } + return sent; +} + +/******************************************************************************* +** +** Function gatt_client_handle_server_rsp +** +** Description This function is called to handle the server response to +** client. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + tGATT_CLCB *p_clcb = NULL; + UINT8 rsp_code; + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); + + rsp_code = gatt_cmd_to_rsp_code(rsp_code); + + if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) { + GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \ + Request(%02x) Ignored", op_code, rsp_code); + + return; + } else { + btu_stop_timer (&p_clcb->rsp_timer_ent); + p_clcb->retry_count = 0; + } + } + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not count op_code */ + if (len >= p_tcb->payload_size) { + GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size); + if (op_code != GATT_HANDLE_VALUE_NOTIF && op_code != GATT_HANDLE_VALUE_IND && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + } + } else { + switch (op_code) { + case GATT_RSP_ERROR: + gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_MTU: /* 2 bytes mtu */ + gatt_process_mtu_rsp(p_tcb, p_clcb, len , p_data); + break; + + case GATT_RSP_FIND_INFO: + gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ_BY_TYPE: + case GATT_RSP_READ_BY_GRP_TYPE: + gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_READ: + case GATT_RSP_READ_BLOB: + case GATT_RSP_READ_MULTI: + case GATT_RSP_READ_MULTI_VAR: + gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */ + gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data); + break; + + case GATT_RSP_WRITE: + gatt_process_handle_rsp(p_clcb); + break; + + case GATT_RSP_PREPARE_WRITE: + gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data); + break; + + case GATT_RSP_EXEC_WRITE: + gatt_end_operation(p_clcb, p_clcb->status, NULL); + break; + + case GATT_HANDLE_VALUE_NOTIF: + case GATT_HANDLE_VALUE_IND: + case GATT_HANDLE_MULTI_VALUE_NOTIF: + gatt_process_notification(p_tcb, op_code, len, p_data); + break; + + default: + GATT_TRACE_ERROR("Unknown opcode = %d", op_code); + break; + } + } + + if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF && + op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) { + gatt_cl_send_next_cmd_inq(p_tcb); + } +} + +#endif /* BLE_INCLUDED == TRUE && GATTC_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_db.c b/lib/bt/host/bluedroid/stack/gatt/gatt_db.c new file mode 100644 index 00000000..e84580bc --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_db.c @@ -0,0 +1,1620 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT database building and query functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE + +#include "common/bt_trace.h" +#include "osi/allocator.h" + +//#include <stdio.h> +#include <string.h> +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" + +extern tGATT_STATUS gap_proc_read(tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp); +extern tGATT_STATUS gatt_proc_read(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp); + +/******************************************************************************** +** L O C A L F U N C T I O N P R O T O T Y P E S * +*********************************************************************************/ +static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db); +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PERM perm); +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr); +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len); + +static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri); +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp); +static BOOLEAN gatts_add_char_desc_value_check (tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control); + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, + UINT16 s_hdl, UINT16 num_handle) +{ + if (p_db->svc_buffer == NULL) { //in case already alloc + p_db->svc_buffer = fixed_queue_new(QUEUE_SIZE_MAX); + } + + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("gatts_init_service_db failed, no resources\n"); + return FALSE; + } + + GATT_TRACE_DEBUG("gatts_init_service_db\n"); + GATT_TRACE_DEBUG("s_hdl = %d num_handle = %d\n", s_hdl, num_handle ); + + /* update service database information */ + p_db->next_handle = s_hdl; + p_db->end_handle = s_hdl + num_handle; + + return gatts_db_add_service_declaration(p_db, p_service, is_pri); +} + +/******************************************************************************* +** +** Function gatts_init_service_db +** +** Description This function initialize a memory space to be a service database. +** +** Parameter p_db: database pointer. +** len: size of the memory space. +** +** Returns Status of te operation. +** +*******************************************************************************/ +tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db) +{ + if (!p_db || !p_db->p_attr_list) { + GATT_TRACE_ERROR("service DB empty\n"); + + return NULL; + } else { + return &((tGATT_ATTR16 *)p_db->p_attr_list)->p_value->uuid; + } +} + +/******************************************************************************* +** +** Function gatts_check_attr_readability +** +** Description check attribute readability +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_check_attr_readability(tGATT_ATTR16 *p_attr, + UINT16 offset, + BOOLEAN read_long, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 min_key_size; + tGATT_PERM perm = p_attr->permission; + + UNUSED(offset); + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) { + min_key_size += 6; + } + + if (!(perm & GATT_READ_ALLOWED)) { + GATT_TRACE_ERROR( "GATT_READ_NOT_PERMIT\n"); + return GATT_READ_NOT_PERMIT; + } + + if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED) && + !(sec_flag & BTM_SEC_FLAG_ENCRYPTED)) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION\n"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHENTICATION: MITM Required\n"); + return GATT_INSUF_AUTHENTICATION; + } + + if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + GATT_TRACE_ERROR( "GATT_INSUF_ENCRYPTION\n"); + return GATT_INSUF_ENCRYPTION; + } + + if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { + GATT_TRACE_ERROR( "GATT_INSUF_KEY_SIZE\n"); + return GATT_INSUF_KEY_SIZE; + } + /* LE Authorization check*/ + if ((perm & GATT_READ_AUTHORIZATION) && (!(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED) || !(sec_flag & GATT_SEC_FLAG_AUTHORIZATION))) { + GATT_TRACE_ERROR( "GATT_INSUF_AUTHORIZATION\n"); + return GATT_INSUF_AUTHORIZATION; + } + + if (read_long) { + switch (p_attr->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + case GATT_UUID_CHAR_EXT_PROP: + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + case GATT_UUID_CHAR_PRESENT_FORMAT: + GATT_TRACE_ERROR("GATT_NOT_LONG\n"); + return GATT_NOT_LONG; + + default: + break; + } + } + + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function read_attr_value +** +** Description Utility function to read an attribute value. +** +** Parameter p_attr: pointer to the attribute to read. +** offset: read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter to carry out the attribute length. +** read_long: this is a read blob request. +** mtu: MTU +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS read_attr_value (void *p_attr, + UINT16 offset, + UINT8 **p_data, + BOOLEAN read_long, + UINT16 mtu, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + UINT16 len = 0, uuid16 = 0; + UINT8 *p = *p_data; + tGATT_STATUS status; + tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; + + GATT_TRACE_DEBUG("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d\n", + p_attr16->uuid, + p_attr16->permission, + sec_flag, + offset, + read_long); + + status = gatts_check_attr_readability((tGATT_ATTR16 *)p_attr, offset, read_long, sec_flag, key_size); + + if (status != GATT_SUCCESS) { + return status; + } + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + uuid16 = p_attr16->uuid; + } + + status = GATT_NO_RESOURCES; + + if (uuid16 == GATT_UUID_PRI_SERVICE || uuid16 == GATT_UUID_SEC_SERVICE) { + len = p_attr16->p_value->uuid.len; + if (mtu >= p_attr16->p_value->uuid.len) { + gatt_build_uuid_to_stream(&p, p_attr16->p_value->uuid); + status = GATT_SUCCESS; + } + } else if (uuid16 == GATT_UUID_CHAR_DECLARE) { + len = (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) ? 5 : 19; + + if (mtu >= len) { + UINT8_TO_STREAM(p, p_attr16->p_value->char_decl.property); + UINT16_TO_STREAM(p, p_attr16->p_value->char_decl.char_val_handle); + + if (((tGATT_ATTR16 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_16) { + UINT16_TO_STREAM(p, ((tGATT_ATTR16 *)(p_attr16->p_next))->uuid); + } + /* convert a 32bits UUID to 128 bits */ + else if (((tGATT_ATTR32 *)(p_attr16->p_next))->uuid_type == GATT_ATTR_UUID_TYPE_32) { + gatt_convert_uuid32_to_uuid128 (p, ((tGATT_ATTR32 *)(p_attr16->p_next))->uuid); + p += LEN_UUID_128; + } else { + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *)(p_attr16->p_next))->uuid, LEN_UUID_128); + } + status = GATT_SUCCESS; + } + + } else if (uuid16 == GATT_UUID_INCLUDE_SERVICE) { + if (p_attr16->p_value->incl_handle.service_type.len == LEN_UUID_16) { + len = 6; + } else { + len = 4; + } + + if (mtu >= len) { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.s_handle); + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.e_handle); + + if (p_attr16->p_value->incl_handle.service_type.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.service_type.uu.uuid16); + } + status = GATT_SUCCESS; + } + } else { /* characteristic description or characteristic value */ + if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { + if (p_attr16->p_value == NULL || p_attr16->p_value->attr_val.attr_val == NULL) { + status = GATT_UNKNOWN_ERROR; + } + else if (offset > p_attr16->p_value->attr_val.attr_len){ + /*if offset equal to max_len, should respond with zero byte value + //if offset is greater than max_len, should respond with an error*/ + status = GATT_INVALID_OFFSET; + } else { + UINT8 *value = (UINT8 *)(p_attr16->p_value->attr_val.attr_val) + offset; + UINT16 len_left = p_attr16->p_value->attr_val.attr_len - offset; + len = (mtu >= len_left) ? (len_left) : mtu; + ARRAY_TO_STREAM(p, value, len); + status = GATT_STACK_RSP; + } + + } else { + status = GATT_PENDING; + } + } + + *p_len = len; + *p_data = p; + return status; +} + +/******************************************************************************* +** +** Function gatts_db_read_attr_value_by_type +** +** Description Query attribute value by attribute type. +** +** Parameter p_db: pointer to the attribute database. +** p_rsp: Read By type response data. +** s_handle: starting handle of the range we are looking for. +** e_handle: ending handle of the range we are looking for. +** type: Attribute type. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size. +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + BT_HDR *p_rsp, + UINT16 s_handle, + UINT16 e_handle, + tBT_UUID type, + UINT16 *p_len, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id, + UINT16 *p_cur_handle) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 len = 0; + UINT8 *p = (UINT8 *)(p_rsp + 1) + p_rsp->len + L2CAP_MIN_OFFSET; + tBT_UUID attr_uuid; +#if (defined(BLE_DELAY_REQUEST_ENC) && (BLE_DELAY_REQUEST_ENC == TRUE)) + UINT8 flag; +#endif + BOOLEAN need_rsp; + BOOLEAN have_send_request = false; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && p_attr->handle <= e_handle) { + if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + attr_uuid.len = LEN_UUID_16; + attr_uuid.uu.uuid16 = p_attr->uuid; + } else if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + attr_uuid.len = LEN_UUID_32; + attr_uuid.uu.uuid32 = ((tGATT_ATTR32 *)p_attr)->uuid; + } else { + attr_uuid.len = LEN_UUID_128; + memcpy(attr_uuid.uu.uuid128, ((tGATT_ATTR128 *)p_attr)->uuid, LEN_UUID_128); + } + + if (p_attr->handle >= s_handle && gatt_uuid_compare(type, attr_uuid)) { + if (*p_len <= 2) { + status = GATT_NO_RESOURCES; + break; + } + + UINT16_TO_STREAM (p, p_attr->handle); + + status = read_attr_value ((void *)p_attr, 0, &p, FALSE, (UINT16)(*p_len - 2), &len, sec_flag, key_size); + if (status == GATT_PENDING) { + + + need_rsp = TRUE; + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); + + /* one callback at a time */ + break; + } else if (status == GATT_SUCCESS || status == GATT_STACK_RSP) { + if (status == GATT_STACK_RSP){ + need_rsp = FALSE; + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id, need_rsp); + if(status == GATT_BUSY) + break; + + if (!have_send_request){ + have_send_request = true; + trans_id = p_tcb->sr_cmd.trans_id; + } + } + + if (p_rsp->offset == 0) { + p_rsp->offset = len + 2; + } + + if (p_rsp->offset == len + 2) { + p_rsp->len += (len + 2); + *p_len -= (len + 2); + } else { + GATT_TRACE_WARNING("format mismatch"); + status = GATT_NO_RESOURCES; + break; + } + } else { + *p_cur_handle = p_attr->handle; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + +#if (defined(BLE_DELAY_REQUEST_ENC) && (BLE_DELAY_REQUEST_ENC == TRUE)) + if (BTM_GetSecurityFlags(p_tcb->peer_bda, &flag)) { + if ((p_tcb->att_lcid == L2CAP_ATT_CID) && (status == GATT_PENDING) && + (type.uu.uuid16 == GATT_UUID_GAP_DEVICE_NAME)) { + if ((flag & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_FLAG_ENCRYPTED)) == + BTM_SEC_LINK_KEY_KNOWN) { + tACL_CONN *p; + p = btm_bda_to_acl(p_tcb->peer_bda, BT_TRANSPORT_LE); + if ((p != NULL) && (p->link_role == BTM_ROLE_MASTER)) { + tBTM_BLE_SEC_ACT sec_act = BTM_BLE_SEC_ENCRYPT; + btm_ble_set_encryption(p_tcb->peer_bda, &sec_act, p->link_role); + } + } + } + } +#endif + return status; +} + +/******************************************************************************* +** +** Function gatts_add_included_service +** +** Description This function adds an included service into a database. +** +** Parameter p_db: database pointer. +** inc_srvc_type: included service type. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, + tBT_UUID service) +{ + tGATT_ATTR16 *p_attr; + tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_INCLUDE_SERVICE}}; + + GATT_TRACE_DEBUG("gatts_add_included_service: s_hdl = 0x%04x e_hdl = 0x%04x uuid = 0x%04x", + s_handle, e_handle, service.uu.uuid16); + + if (service.len == 0 || s_handle == 0 || e_handle == 0) { + GATT_TRACE_ERROR("gatts_add_included_service Illegal Params."); + return 0; + } + + BOOLEAN is_include_service_allowed = TRUE; + // service declaration + tGATT_ATTR16 *first_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + if (p_db->p_attr_list != NULL) { + tGATT_ATTR16 *next_attr = (tGATT_ATTR16 *)first_attr->p_next; + /* This service already has other attributes */ + while (next_attr != NULL) { + if (!(next_attr->uuid_type == GATT_ATTR_UUID_TYPE_16 && next_attr->uuid == GATT_UUID_INCLUDE_SERVICE)) { + is_include_service_allowed = FALSE; + break; + } + next_attr = (tGATT_ATTR16 *)next_attr->p_next; + } + + } + if (!is_include_service_allowed) { + GATT_TRACE_ERROR("%s error, The include service should be added before adding the characteristics", __func__); + return 0; + } + + if ((p_attr = (tGATT_ATTR16 *) allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { + if (copy_extra_byte_in_db(p_db, (void **)&p_attr->p_value, sizeof(tGATT_INCL_SRVC))) { + p_attr->p_value->incl_handle.s_handle = s_handle; + p_attr->p_value->incl_handle.e_handle = e_handle; + memcpy(&p_attr->p_value->incl_handle.service_type, &service, sizeof(tBT_UUID)); + + return p_attr->handle; + } else { + deallocate_attr_in_db(p_db, p_attr); + } + } + + return 0; +} + +/******************************************************************************* +** +** Function gatts_add_characteristic +** +** Description This function add a characteristics and its descriptor into +** a servce identified by the service database pointer. +** +** Parameter p_db: database pointer. +** perm: permission (authentication and key size requirements) +** property: property of the characteristic. +** p_char: characteristic value information. +** +** Returns Status of te operation. +** +*******************************************************************************/ +UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_ATTR16 *p_char_decl, *p_char_val; + tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; + BOOLEAN status; + + GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); + /* parameter validation check */ + status = gatts_add_char_desc_value_check(attr_val, control); + if (status == FALSE){ + return 0; + } + + + if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_val = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_char_uuid, perm); + + if (p_char_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + return 0; + } + + p_char_decl->p_value->char_decl.property = property; + p_char_decl->p_value->char_decl.char_val_handle = p_char_val->handle; + if (control != NULL) { + p_char_val->control.auto_rsp = control->auto_rsp; + } else { + p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; + } + + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_val); + return 0; + } + GATT_TRACE_DEBUG("attr_val->attr_len = %x, attr_val->attr_max_len = %x\n", attr_val->attr_len, attr_val->attr_max_len); + GATT_TRACE_DEBUG("attribute handle = %x\n", p_char_val->handle); + p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + p_char_val->p_value->attr_val.attr_val = osi_malloc(attr_val->attr_max_len); + if (p_char_val->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + deallocate_attr_in_db(p_db, p_char_val); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for attribute value\n", __func__, __LINE__); + return 0; + } + else { + //add mask to indicate that p_value->attr_val.attr_val is dynamic allocated + p_char_val->mask |= GATT_ATTR_VALUE_ALLOCATED; + } + + //initiate characteristic attribute value part + memset(p_char_val->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if (attr_val->attr_val != NULL) { + if (attr_val->attr_max_len < attr_val->attr_len){ + GATT_TRACE_ERROR("Error in %s, Line=%d, attribute actual length (%d) should not larger than max size (%d)\n", + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + } + UINT16 actual_len = (attr_val->attr_max_len < attr_val->attr_len) ? (attr_val->attr_max_len) : (attr_val->attr_len); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, actual_len); + } + } + + return p_char_val->handle; + } + + return 0; +} + +/******************************************************************************* +** +** Function gatt_convertchar_descr_type +** +** Description This function convert a char descript UUID into descriptor type. +** +** Returns descriptor type. +** +*******************************************************************************/ +UINT8 gatt_convertchar_descr_type(tBT_UUID *p_descr_uuid) +{ + tBT_UUID std_descr = {LEN_UUID_16, {GATT_UUID_CHAR_EXT_PROP}}; + + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_EXT_DSCPTOR; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_USER_DSCPTOR; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_CLT_CONFIG; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_SVR_CONFIG; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_PRES_FORMAT; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_AGGR_FORMAT; + } + + std_descr.uu.uuid16 ++; + if (gatt_uuid_compare(std_descr, * p_descr_uuid)) { + return GATT_DESCR_VALID_RANGE; + } + + + return GATT_DESCR_UNKNOWN; +} + +/******************************************************************************* +** +** Function gatts_add_char_descr +** +** Description This function add a characteristics descriptor. +** +** Parameter p_db: database pointer. +** perm: characteristic descriptor permission type. +** char_dscp_tpye: the characteristic descriptor masks. +** p_dscp_params: characteristic descriptors values. +** +** Returns Status of the operation. +** +*******************************************************************************/ +UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_descr_uuid, tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + tGATT_ATTR16 *p_char_dscptr; + BOOLEAN status; + + GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); + + /* parameter validation check */ + status = gatts_add_char_desc_value_check(attr_val, control); + if (status == FALSE){ + return 0; + } + + /* Add characteristic descriptors */ + if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); + return 0; + } else { + p_char_dscptr->control.auto_rsp = (control == NULL) ? GATT_RSP_DEFAULT : (control->auto_rsp); + if (attr_val != NULL) { + if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { + deallocate_attr_in_db(p_db, p_char_dscptr); + return 0; + } + p_char_dscptr->p_value->attr_val.attr_len = attr_val->attr_len; + p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; + if (attr_val->attr_max_len != 0) { + p_char_dscptr->p_value->attr_val.attr_val = osi_malloc(attr_val->attr_max_len); + if (p_char_dscptr->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for descriptor value\n", __func__, __LINE__); + return 0; + } + else { + //add mask to indicate that p_value->attr_val.attr_val is dynamic allocated + p_char_dscptr->mask |= GATT_ATTR_VALUE_ALLOCATED; + } + + //initiate characteristic attribute value part + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if(attr_val->attr_val != NULL) { + memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + } + } + } + return p_char_dscptr->handle; + } +} + + +/******************************************************************************* +** +** Function gatts_set_attribute_value +** +** Description This function add the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be set to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value) +{ + tGATT_ATTR16 *p_cur; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db is NULL.\n"); + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return GATT_INVALID_PDU; + } + if ((length > 0) && (value == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, value should not be NULL here\n",__func__, __LINE__); + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + if (p_cur->handle == attr_handle) { + /* for characteristic should not be set, return GATT_NOT_FOUND */ + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + return GATT_NOT_FOUND; + break; + } + } + + /* in other cases, value can be set*/ + if ((p_cur->p_value == NULL) || (p_cur->p_value->attr_val.attr_val == NULL) \ + || (p_cur->p_value->attr_val.attr_max_len == 0)){ + GATT_TRACE_ERROR("Error in %s, line=%d, attribute value should not be NULL here\n", __func__, __LINE__); + return GATT_NOT_FOUND; + } else if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_value failed:Invalid value length"); + return GATT_INVALID_ATTR_LEN; + } else{ + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } + break; + } + p_cur = p_cur->p_next; + } + + return GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatts_get_attr_value_internal +** +** Description This function get the attribute value in gap service and gatt service +** +** Parameter attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attr_value_internal(UINT16 attr_handle, UINT16 *length, UINT8 **value) +{ + UINT8 i; + tGATT_READ_REQ read_req; + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + UINT8 service_uuid[LEN_UUID_128] = {0}; + + if (length == NULL){ + GATT_TRACE_ERROR("gatts_get_attr_value_internal Fail:length is NULL.\n"); + return GATT_INVALID_PDU; + } + + if (value == NULL){ + GATT_TRACE_ERROR("gatts_get_attr_value_internal Fail:value is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + + // find the service by handle + for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_rcb++) { + if (p_rcb->in_use && p_rcb->s_hdl <= attr_handle && p_rcb->e_hdl >= attr_handle) { + break; + } + } + + // service cb not found + if (i == GATT_MAX_SR_PROFILES) { + return status; + } + + if (p_rcb->app_uuid.len != LEN_UUID_128) { + return status; + } + + memset(&read_req, 0, sizeof(tGATT_READ_REQ)); + read_req.handle = attr_handle; + + // read gatt service attribute value + memset(service_uuid, 0x81, LEN_UUID_128); + if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) { + status = gatt_proc_read(0, GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp); + } + + // read gap service attribute value + memset(service_uuid, 0x82, LEN_UUID_128); + if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) { + status = gap_proc_read(GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp); + } + + if (status == GATT_SUCCESS) { + *length = gatt_cb.rsp.attr_value.len; + *value = gatt_cb.rsp.attr_value.value; + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_get_attribute_value +** +** Description This function get the attribute value in the database +** +** Parameter p_db: database pointer. +** attr_handle: the attribute handle +** length: the attribute value length +** value: the pointer to the data to be get to the attribute value in the database +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value) +{ + tGATT_ATTR16 *p_cur; + + GATT_TRACE_DEBUG("attr_handle = %x\n", attr_handle); + + if (p_db == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + if (length == NULL){ + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:length is NULL.\n"); + return GATT_INVALID_PDU; + } + if (value == NULL){ + GATT_TRACE_ERROR("gatts_get_attribute_value Fail:value is NULL.\n"); + *length = 0; + return GATT_INVALID_PDU; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_cur != NULL) { + if (p_cur->handle == attr_handle) { + + if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_cur->uuid) { + case GATT_UUID_CHAR_DECLARE: + case GATT_UUID_INCLUDE_SERVICE: + break; + default: + if (p_cur->p_value && p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + *length = 0; + return GATT_SUCCESS; + } + break; + } + } else { + if (p_cur->p_value && p_cur->p_value->attr_val.attr_len != 0) { + *length = p_cur->p_value->attr_val.attr_len; + *value = p_cur->p_value->attr_val.attr_val; + return GATT_SUCCESS; + } else { + *length = 0; + return GATT_SUCCESS; + } + + } + + break; + + } + + p_cur = p_cur->p_next; + } + + return GATT_NOT_FOUND; +} + +BOOLEAN gatts_is_auto_response(UINT16 attr_handle) +{ + tGATT_HDL_LIST_ELEM *p_decl = NULL; + BOOLEAN rsp = FALSE; + tGATT_SVC_DB *p_db = NULL; + if ((p_decl = gatt_find_hdl_buffer_by_attr_handle(attr_handle)) == NULL) { + GATT_TRACE_DEBUG("Service not created\n"); + return rsp; + } + + p_db = &p_decl->svc_db; + + tGATT_ATTR16 *p_cur, *p_next; + + if (p_db == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db is NULL.\n"); + return rsp; + } + if (p_db->p_attr_list == NULL) { + GATT_TRACE_DEBUG("gatts_get_attribute_value Fail:p_db->p_attr_list is NULL.\n"); + return rsp; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_cur->handle == attr_handle) { + if (p_cur->p_value != NULL && p_cur->control.auto_rsp == GATT_RSP_BY_STACK) { + rsp = true; + return rsp; + } + + } + + } + + return rsp; + +} + +/*******************************************************************************/ +/* Service Attribute Database Query Utility Functions */ +/*******************************************************************************/ +/******************************************************************************* +** +** Function gatts_read_attr_value_by_handle +** +** Description Query attribute value by attribute handle. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, + tGATT_SVC_DB *p_db, + UINT8 op_code, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, + UINT16 mtu, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size, + UINT32 trans_id) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT8 *pp = p_value; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle) { + status = read_attr_value (p_attr, offset, &pp, + (BOOLEAN)(op_code == GATT_REQ_READ_BLOB), + mtu, p_len, sec_flag, key_size); + + if ((status == GATT_PENDING) || (status == GATT_STACK_RSP)) { + BOOLEAN need_rsp = (status != GATT_STACK_RSP); + status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id, need_rsp); + } + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + + + return status; +} + +tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + return GATT_APP_RSP; + } + + if ((p_attr->p_value != NULL) && + (p_attr->p_value->attr_val.attr_max_len >= offset + len) && + p_attr->p_value->attr_val.attr_val != NULL) { + memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); + p_attr->p_value->attr_val.attr_len = len + offset; + return GATT_SUCCESS; + } else if (p_attr->p_value && p_attr->p_value->attr_val.attr_max_len < offset + len){ + GATT_TRACE_DEBUG("Remote device try to write with a length larger then attribute's max length\n"); + return GATT_INVALID_ATTR_LEN; + } else if ((p_attr->p_value == NULL) || (p_attr->p_value->attr_val.attr_val == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, %s should not be NULL here\n", __func__, __LINE__, \ + (p_attr->p_value == NULL) ? "p_value" : "attr_val.attr_val"); + return GATT_UNKNOWN_ERROR; + } + } + + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatts_read_attr_perm_check +** +** Description Check attribute readability. +** +** Parameter p_db: pointer to the attribute database. +** handle: Attribute handle to read. +** offset: Read offset. +** p_value: output parameter to carry out the attribute value. +** p_len: output parameter as attribute length read. +** read_long: this is a read blob request. +** mtu: MTU. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, + BOOLEAN is_long, + UINT16 handle, + tGATT_SEC_FLAG sec_flag, + UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle) { + status = gatts_check_attr_readability (p_attr, 0, + is_long, + sec_flag, key_size); + break; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + + return status; +} + + +/******************************************************************************* +** +** Function gatts_write_attr_perm_check +** +** Description Write attribute value into database. +** +** Parameter p_db: pointer to the attribute database. +** op_code:op code of this write. +** handle: handle of the attribute to write. +** offset: Write offset if write op code is write blob. +** p_data: Attribute value to write. +** len: attribute data length. +** sec_flag: current link security status. +** key_size: encryption key size +** +** Returns Status of the operation. +** +*******************************************************************************/ +tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + tGATT_ATTR16 *p_attr; + UINT16 max_size = 0; + tGATT_PERM perm; + UINT16 min_key_size; + + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check op_code=0x%0x handle=0x%04x offset=%d len=%d sec_flag=0x%0x key_size=%d", + op_code, handle, offset, len, sec_flag, key_size); + + if (p_db != NULL) { + p_attr = (tGATT_ATTR16 *) p_db->p_attr_list; + + while (p_attr != NULL) { + if (p_attr->handle == handle) { + perm = p_attr->permission; + min_key_size = (((perm & GATT_ENCRYPT_KEY_SIZE_MASK) >> 12)); + if (min_key_size != 0 ) { + min_key_size += 6; + } + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check p_attr->permission =0x%04x min_key_size==0x%04x", + p_attr->permission, + min_key_size); + + if ((op_code == GATT_CMD_WRITE || op_code == GATT_REQ_WRITE) + && (perm & GATT_WRITE_SIGNED_PERM)) { + /* use the rules for the mixed security see section 10.2.3*/ + /* use security mode 1 level 2 when the following condition follows */ + /* LE security mode 2 level 1 and LE security mode 1 level 2 */ + if ((perm & GATT_PERM_WRITE_SIGNED) && (perm & GATT_PERM_WRITE_ENCRYPTED)) { + perm = GATT_PERM_WRITE_ENCRYPTED; + } + /* use security mode 1 level 3 when the following condition follows */ + /* LE security mode 2 level 2 and security mode 1 and LE */ + else if (((perm & GATT_PERM_WRITE_SIGNED_MITM) && (perm & GATT_PERM_WRITE_ENCRYPTED)) || + /* LE security mode 2 and security mode 1 level 3 */ + ((perm & GATT_WRITE_SIGNED_PERM) && (perm & GATT_PERM_WRITE_ENC_MITM))) { + perm = GATT_PERM_WRITE_ENC_MITM; + } + } + + if ((op_code == GATT_SIGN_CMD_WRITE) && !(perm & GATT_WRITE_SIGNED_PERM)) { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_DEBUG( "gatts_write_attr_perm_check - sign cmd write not allowed,handle:0x%04x",handle); + } + if ((op_code == GATT_SIGN_CMD_WRITE) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + status = GATT_INVALID_PDU; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - Error!! sign cmd write sent on a encypted link,handle:0x%04x",handle); + } else if (!(perm & GATT_WRITE_ALLOWED)) { + status = GATT_WRITE_NOT_PERMIT; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_WRITE_NOT_PERMIT,handle:0x%04x",handle); + } + /* require authentication, but not been authenticated */ + else if ((perm & GATT_WRITE_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED)) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED)) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: MITM required,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED)) { + status = GATT_INSUF_ENCRYPTION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_ENCRYPTION,handle:0x%04x",handle); + } else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size)) { + status = GATT_INSUF_KEY_SIZE; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_KEY_SIZE,handle:0x%04x",handle); + } + /* LE Authorization check*/ + else if ((perm & GATT_WRITE_AUTHORIZATION) && (!(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED) || !(sec_flag & GATT_SEC_FLAG_AUTHORIZATION))){ + status = GATT_INSUF_AUTHORIZATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHORIZATION,handle:0x%04x",handle); + } + /* LE security mode 2 attribute */ + else if (perm & GATT_WRITE_SIGNED_PERM && op_code != GATT_SIGN_CMD_WRITE && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED) + && (perm & GATT_WRITE_ALLOWED) == 0) { + status = GATT_INSUF_AUTHENTICATION; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: LE security mode 2 required,handle:0x%04x",handle); + } else { /* writable: must be char value declaration or char descritpors */ + if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + switch (p_attr->uuid) { + case GATT_UUID_CHAR_PRESENT_FORMAT:/* should be readable only */ + case GATT_UUID_CHAR_EXT_PROP:/* should be readable only */ + case GATT_UUID_CHAR_AGG_FORMAT: /* should be readable only */ + case GATT_UUID_CHAR_VALID_RANGE: + status = GATT_WRITE_NOT_PERMIT; + break; + + case GATT_UUID_CHAR_CLIENT_CONFIG: + /* coverity[MISSING_BREAK] */ + /* intnended fall through, ignored */ + /* fall through */ + case GATT_UUID_CHAR_SRVR_CONFIG: + max_size = 2; + case GATT_UUID_CHAR_DESCRIPTION: + default: /* any other must be character value declaration */ + status = GATT_SUCCESS; + break; + } + } else if (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128 || + p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + status = GATT_SUCCESS; + } else { + status = GATT_INVALID_PDU; + } + + if (p_data == NULL && len > 0) { + status = GATT_INVALID_PDU; + } + /* these attribute does not allow write blob */ +// btla-specific ++ + else if ( (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) && + (p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CLIENT_SUP_FEAT || + p_attr->uuid == GATT_UUID_GAP_ICON + ) ) +// btla-specific -- + { + if (op_code == GATT_REQ_PREPARE_WRITE) { /* does not allow write blob */ + status = GATT_REQ_NOT_SUPPORTED; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_REQ_NOT_SUPPORTED,handle:0x%04x",handle); + } else if (len != max_size) { /* data does not match the required format */ + status = GATT_INVALID_ATTR_LEN; + GATT_TRACE_ERROR( "gatts_write_attr_perm_check - GATT_INVALID_ATTR_LEN,handle:0x%04x",handle); + } else { + status = GATT_SUCCESS; + } + } + } + break; + } else { + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function allocate_attr_in_db +** +** Description Allocate a memory space for a new attribute, and link this +** attribute into the database attribute list. +** +** +** Parameter p_db : database pointer. +** p_uuid: pointer to attribute UUID +** service : type of attribute to be added. +** +** Returns pointer to the newly allocated attribute. +** +*******************************************************************************/ +static void *allocate_attr_in_db(tGATT_SVC_DB *p_db, tBT_UUID *p_uuid, tGATT_PERM perm) +{ + tGATT_ATTR16 *p_attr16 = NULL, *p_last; + tGATT_ATTR32 *p_attr32 = NULL; + tGATT_ATTR128 *p_attr128 = NULL; + UINT16 len = sizeof(tGATT_ATTR128); + + if (p_uuid == NULL) { + GATT_TRACE_ERROR("illegal UUID\n"); + return NULL; + } + + if (p_uuid->len == LEN_UUID_16) { + len = sizeof(tGATT_ATTR16); + } else if (p_uuid->len == LEN_UUID_32) { + len = sizeof(tGATT_ATTR32); + } + + GATT_TRACE_DEBUG("allocate attr %d bytes\n", len); + + if (p_db->end_handle <= p_db->next_handle) { + GATT_TRACE_DEBUG("handle space full. handle_max = %d next_handle = %d\n", + p_db->end_handle, p_db->next_handle); + return NULL; + } + + if (p_db->mem_free < len) { + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("allocate_attr_in_db failed, no resources\n"); + return NULL; + } + } + memset(p_db->p_free_mem, 0, len); + p_attr16 = (tGATT_ATTR16 *) p_db->p_free_mem; + + if (p_uuid->len == LEN_UUID_16 && p_uuid->uu.uuid16 != GATT_ILLEGAL_UUID) { + p_attr16->uuid_type = GATT_ATTR_UUID_TYPE_16; + p_attr16->uuid = p_uuid->uu.uuid16; + } else if (p_uuid->len == LEN_UUID_32) { + p_attr32 = (tGATT_ATTR32 *) p_db->p_free_mem; + p_attr32->uuid_type = GATT_ATTR_UUID_TYPE_32; + p_attr32->uuid = p_uuid->uu.uuid32; + } else if (p_uuid->len == LEN_UUID_128) { + p_attr128 = (tGATT_ATTR128 *) p_db->p_free_mem; + p_attr128->uuid_type = GATT_ATTR_UUID_TYPE_128; + memcpy(p_attr128->uuid, p_uuid->uu.uuid128, LEN_UUID_128); + } + + p_db->p_free_mem += len; + p_db->mem_free -= len; + + p_attr16->handle = p_db->next_handle++; + p_attr16->permission = perm; + p_attr16->p_next = NULL; + + /* link the attribute record into the end of DB */ + if (p_db->p_attr_list == NULL) { + p_db->p_attr_list = p_attr16; + } else { + p_last = (tGATT_ATTR16 *)p_db->p_attr_list; + + while (p_last != NULL && p_last->p_next != NULL) { + p_last = (tGATT_ATTR16 *)p_last->p_next; + } + + p_last->p_next = p_attr16; + } + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid16 = [0x%04x] perm=0x%02x\n", + p_attr16->handle, p_attr16->uuid, p_attr16->permission); + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid32 = [0x%08x] perm=0x%02x\n", + p_attr32->handle, p_attr32->uuid, p_attr32->permission); + } else { + GATT_TRACE_DEBUG("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x\n", + p_attr128->handle, p_attr128->uuid[0], p_attr128->uuid[1], + p_attr128->permission); + } + return (void *)p_attr16; +} + + + +/******************************************************************************* +** +** Function deallocate_attr_in_db +** +** Description Free an attribute within the database. +** +** Parameter p_db: database pointer. +** p_attr: pointer to the attribute record to be freed. +** +** Returns BOOLEAN: success +** +*******************************************************************************/ +static BOOLEAN deallocate_attr_in_db(tGATT_SVC_DB *p_db, void *p_attr) +{ + tGATT_ATTR16 *p_cur, *p_next; + BOOLEAN found = FALSE; + + if (p_db->p_attr_list == NULL) { + return found; + } + + p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; + p_next = (tGATT_ATTR16 *) p_cur->p_next; + + for (; p_cur != NULL && p_next != NULL; + p_cur = p_next, p_next = (tGATT_ATTR16 *)p_next->p_next) { + if (p_next == p_attr) { + p_cur->p_next = p_next->p_next; + found = TRUE; + } + } + if (p_cur == p_attr && p_cur == p_db->p_attr_list) { + p_db->p_attr_list = p_cur->p_next; + found = TRUE; + } + /* else attr not found */ + if ( found) { + p_db->next_handle --; + } + + return found; +} + +/******************************************************************************* +** +** Function copy_extra_byte_in_db +** +** Description Utility function to allocate extra bytes memory in DB and copy +** the value from a source place. +** +** +** Parameter p_db: database pointer. +** p_dst: destination data pointer. +** p_src: source data pointer. +** len: data length to be copied. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN copy_extra_byte_in_db(tGATT_SVC_DB *p_db, void **p_dst, UINT16 len) +{ + UINT8 *p = (UINT8 *)*p_dst; + + if (p_db->mem_free < len) { + if (!allocate_svc_db_buf(p_db)) { + GATT_TRACE_ERROR("copy_extra_byte_in_db failed, no resources\n"); + return FALSE; + } + } + + p = p_db->p_free_mem; + p_db->p_free_mem += len; + p_db->mem_free -= len; + memset((void *)p, 0, len); + *p_dst = (void *)p; + + return TRUE; +} + +/******************************************************************************* +** +** Function allocate_svc_db_buf +** +** Description Utility function to allocate extra buffer for service database. +** +** Returns TRUE if allocation succeed, otherwise FALSE. +** +*******************************************************************************/ +static BOOLEAN allocate_svc_db_buf(tGATT_SVC_DB *p_db) +{ + BT_HDR *p_buf; + + GATT_TRACE_DEBUG("allocate_svc_db_buf allocating extra buffer"); + + if ((p_buf = (BT_HDR *)osi_calloc(GATT_DB_BUF_SIZE)) == NULL) { + GATT_TRACE_ERROR("allocate_svc_db_buf failed, no resources"); + return FALSE; + } + + p_db->p_free_mem = (UINT8 *) p_buf; + p_db->mem_free = GATT_DB_BUF_SIZE; + + fixed_queue_enqueue(p_db->svc_buffer, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + return TRUE; + +} + +/******************************************************************************* +** +** Function gatts_send_app_read_request +** +** Description Send application read request callback +** +** Returns status of operation. +** +*******************************************************************************/ +static tGATT_STATUS gatts_send_app_read_request(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 handle, UINT16 offset, UINT32 trans_id, BOOLEAN need_rsp) +{ + tGATTS_DATA sr_data; + UINT8 i_rcb; + tGATT_SR_REG *p_sreg; + UINT16 conn_id; + + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + if (i_rcb == GATT_MAX_SR_PROFILES) { + GATT_TRACE_ERROR("Failed to find i_rcb,Error in %s, line=%d, \n", __func__, __LINE__); + return (tGATT_STATUS) GATT_ERROR; + } + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + + if (trans_id == 0) { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + gatt_sr_update_cback_cnt(p_tcb, p_sreg->gatt_if, TRUE, TRUE); + } + + if (trans_id != 0 ) { + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + sr_data.read_req.handle = handle; + sr_data.read_req.is_long = (BOOLEAN)(op_code == GATT_REQ_READ_BLOB); + sr_data.read_req.offset = offset; + sr_data.read_req.need_rsp = need_rsp; + + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_READ, &sr_data); + + if (need_rsp) { + return (tGATT_STATUS) GATT_PENDING; + } + else{ + return (tGATT_STATUS) GATT_STACK_RSP; + } + } else { + return (tGATT_STATUS) GATT_BUSY; /* max pending command, application error */ + } + +} + +/******************************************************************************* +** +** Function gatts_db_add_service_declaration +** +** Description Update a service database service declaration record. +** +** Parameter p_db: database pointer. +** service: UUID of the service. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri) +{ + tGATT_ATTR16 *p_attr; + tBT_UUID uuid = {LEN_UUID_16, {0}}; + BOOLEAN rt = FALSE; + + GATT_TRACE_DEBUG( "add_service_declaration"); + + if (is_pri) { + uuid.uu.uuid16 = GATT_UUID_PRI_SERVICE; + } else { + uuid.uu.uuid16 = GATT_UUID_SEC_SERVICE; + } + + /* add service declration record */ + if ((p_attr = (tGATT_ATTR16 *)(allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ))) != NULL) { + if (copy_extra_byte_in_db (p_db, (void **)&p_attr->p_value, sizeof(tBT_UUID))) { + if (p_service->len == LEN_UUID_16) { + p_attr->p_value->uuid.len = LEN_UUID_16; + p_attr->p_value->uuid.uu.uuid16 = p_service->uu.uuid16; + } else if (p_service->len == LEN_UUID_32) { + p_attr->p_value->uuid.len = LEN_UUID_128; + gatt_convert_uuid32_to_uuid128(p_attr->p_value->uuid.uu.uuid128, p_service->uu.uuid32); + } else { + p_attr->p_value->uuid.len = LEN_UUID_128; + memcpy(p_attr->p_value->uuid.uu.uuid128, p_service->uu.uuid128, LEN_UUID_128); + } + rt = TRUE; + } + + } + return rt; +} + +/******************************************************************************* +** +** Function gatts_add_char_desc_value_check +** +** Description parameters validation check for gatts add char/descriptor functions +** +** Parameter attr_val: attribute value for char/descriptor. +** control: control variable for char/descriptor. +** +** Returns void +** +*******************************************************************************/ +static BOOLEAN gatts_add_char_desc_value_check (tGATT_ATTR_VAL *attr_val, tGATTS_ATTR_CONTROL *control) +{ + if ((control != NULL) && ((control->auto_rsp != GATT_RSP_BY_APP) && (control->auto_rsp != GATT_RSP_BY_STACK))){ + GATT_TRACE_ERROR("Error in %s, line=%d, control->auto_rsp should be set to GATT_RSP_BY_APP or GATT_RSP_BY_STACK here\n",\ + __func__, __LINE__); + return FALSE; + } + + if ((control != NULL) && (control->auto_rsp == GATT_RSP_BY_STACK)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return FALSE; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return FALSE; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length should not be larger than max length\n",\ + __func__, __LINE__); + return FALSE; + } + } + + return TRUE ; +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_main.c b/lib/bt/host/bluedroid/stack/gatt/gatt_main.c new file mode 100644 index 00000000..d76aefe9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_main.c @@ -0,0 +1,1250 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main ATT functions + * + ******************************************************************************/ + +#include "common/bt_target.h" + +#if BLE_INCLUDED == TRUE + +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "btm_int.h" +#include "btm_ble_int.h" +#include "osi/allocator.h" + +/* Configuration flags. */ +#define GATT_L2C_CFG_IND_DONE (1<<0) +#define GATT_L2C_CFG_CFM_DONE (1<<1) + +/* minimum GATT MTU size over BR/EDR link +*/ +#define GATT_MIN_BR_MTU_SIZE 48 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected, + UINT16 reason, tBT_TRANSPORT transport); +static void gatt_le_data_ind (UINT16 chan, BD_ADDR bd_addr, BT_HDR *p_buf); +static void gatt_le_cong_cback(BD_ADDR remote_bda, BOOLEAN congest); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +static void gatt_l2cif_connect_ind_cback (BD_ADDR bd_addr, UINT16 l2cap_cid, + UINT16 psm, UINT8 l2cap_id); +static void gatt_l2cif_connect_cfm_cback (UINT16 l2cap_cid, UINT16 result); +static void gatt_l2cif_config_ind_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gatt_l2cif_config_cfm_cback (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg); +static void gatt_l2cif_disconnect_ind_cback (UINT16 l2cap_cid, BOOLEAN ack_needed); +static void gatt_l2cif_disconnect_cfm_cback (UINT16 l2cap_cid, UINT16 result); +static void gatt_l2cif_data_ind_cback (UINT16 l2cap_cid, BT_HDR *p_msg); +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE +static void gatt_send_conn_cback (tGATT_TCB *p_tcb); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +static void gatt_l2cif_congest_cback (UINT16 cid, BOOLEAN congested); +static const tL2CAP_APPL_INFO dyn_info = { + gatt_l2cif_connect_ind_cback, + gatt_l2cif_connect_cfm_cback, + NULL, + gatt_l2cif_config_ind_cback, + gatt_l2cif_config_cfm_cback, + gatt_l2cif_disconnect_ind_cback, + gatt_l2cif_disconnect_cfm_cback, + NULL, + gatt_l2cif_data_ind_cback, + gatt_l2cif_congest_cback, + NULL +} ; +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + +#if GATT_DYNAMIC_MEMORY == FALSE +tGATT_CB gatt_cb; +#else +tGATT_CB *gatt_cb_ptr; +#endif + +tGATT_DEFAULT gatt_default; + +/******************************************************************************* +** +** Function gatt_init +** +** Description This function is enable the GATT profile on the device. +** It clears out the control blocks, and registers with L2CAP. +** +** Returns void +** +*******************************************************************************/ +void gatt_init (void) +{ + tL2CAP_FIXED_CHNL_REG fixed_reg; + +#if GATT_DYNAMIC_MEMORY + gatt_cb_ptr = (tGATT_CB *)osi_malloc(sizeof(tGATT_CB)); +#endif /* #if GATT_DYNAMIC_MEMORY */ + memset (&gatt_cb, 0, sizeof(tGATT_CB)); + memset (&fixed_reg, 0, sizeof(tL2CAP_FIXED_CHNL_REG)); + + gatt_cb.auto_disc = TRUE; + gatt_cb.p_clcb_list = list_new(osi_free_func); + gatt_cb.p_tcb_list = list_new(osi_free_func); +#if defined(GATT_INITIAL_TRACE_LEVEL) + gatt_cb.trace_level = GATT_INITIAL_TRACE_LEVEL; +#else + gatt_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + gatt_cb.def_mtu_size = GATT_DEF_BLE_MTU_SIZE; + gatt_cb.sign_op_queue = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.srv_chg_clt_q = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.pending_new_srv_start_q = fixed_queue_new(QUEUE_SIZE_MAX); + gatt_cb.srv_chg_mode = GATTS_SEND_SERVICE_CHANGE_MODE; + + /* First, register fixed L2CAP channel for ATT over BLE */ + fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE; + fixed_reg.fixed_chnl_opts.max_transmit = 0xFF; + fixed_reg.fixed_chnl_opts.rtrans_tout = 2000; + fixed_reg.fixed_chnl_opts.mon_tout = 12000; + fixed_reg.fixed_chnl_opts.mps = 670; + fixed_reg.fixed_chnl_opts.tx_win_sz = 1; + + fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback; + fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind; + fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback; /* congestion callback */ + fixed_reg.default_idle_tout = 0xffff; /* 0xffff default idle timeout */ + + L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg); + +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + /* Now, register with L2CAP for ATT PSM over BR/EDR */ + if (!L2CA_Register (BT_PSM_ATT, (tL2CAP_APPL_INFO *) &dyn_info)) { + GATT_TRACE_ERROR ("ATT Dynamic Registration failed"); + } +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_ATT, BTM_SEC_NONE, BT_PSM_ATT, 0, 0); + + gatt_cb.hdl_cfg.gatt_start_hdl = GATT_GATT_START_HANDLE; + gatt_cb.hdl_cfg.gap_start_hdl = GATT_GAP_START_HANDLE; + gatt_cb.hdl_cfg.app_start_hdl = GATT_APP_START_HANDLE; +#if (GATTS_INCLUDED == TRUE) + gatt_profile_db_init(); +#endif ///GATTS_INCLUDED == TRUE + //init local MTU size + gatt_default.local_mtu = GATT_MAX_MTU_SIZE; +} + + +/******************************************************************************* +** +** Function gatt_free +** +** Description This function frees resources used by the GATT profile. +** +** Returns void +** +*******************************************************************************/ +#if (GATT_INCLUDED == TRUE) +void gatt_free(void) +{ + GATT_TRACE_DEBUG("gatt_free()"); + fixed_queue_free(gatt_cb.sign_op_queue, NULL); + gatt_cb.sign_op_queue = NULL; + fixed_queue_free(gatt_cb.srv_chg_clt_q, NULL); + gatt_cb.srv_chg_clt_q = NULL; + fixed_queue_free(gatt_cb.pending_new_srv_start_q, NULL); + gatt_cb.pending_new_srv_start_q = NULL; + + list_node_t *p_node = NULL; + tGATT_TCB *p_tcb = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + p_tcb->pending_enc_clcb = NULL; + + fixed_queue_free(p_tcb->pending_ind_q, NULL); + p_tcb->pending_ind_q = NULL; + + btu_free_timer(&p_tcb->conf_timer_ent); + memset(&p_tcb->conf_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + + btu_free_timer(&p_tcb->ind_ack_timer_ent); + memset(&p_tcb->ind_ack_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + +#if (GATTS_INCLUDED == TRUE) + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); + p_tcb->sr_cmd.multi_rsp_q = NULL; +#endif /* #if (GATTS_INCLUDED == TRUE) */ + } + list_free(gatt_cb.p_tcb_list); + list_free(gatt_cb.p_clcb_list); + +#if (GATTS_INCLUDED == TRUE) + for (int i = 0; i < GATT_MAX_SR_PROFILES; i++) { + gatt_remove_an_item_from_list(&gatt_cb.hdl_list_info, &gatt_cb.hdl_list[i]); + gatt_free_attr_value_buffer(&gatt_cb.hdl_list[i]); + gatt_free_hdl_buffer(&gatt_cb.hdl_list[i]); + } +#endif /* #if (GATTS_INCLUDED == TRUE) */ +#if GATT_DYNAMIC_MEMORY + FREE_AND_RESET(gatt_cb_ptr); +#endif /* #if GATT_DYNAMIC_MEMORY */ +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** 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. +** bd_addr_type: emote device address type. +** Returns TRUE if connection is started, otherwise return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_connect (BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, tGATT_TCB *p_tcb, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + 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, bd_addr_type, is_aux); +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + } else { + if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) != 0) { + gatt_ret = TRUE; + } +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + + } + + return gatt_ret; +} + +/******************************************************************************* +** +** Function gatt_disconnect +** +** Description This function is called to disconnect to an ATT device. +** +** Parameter p_tcb: pointer to the TCB to disconnect. +** +** Returns TRUE: if connection found and to be disconnected; otherwise +** return FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_disconnect (tGATT_TCB *p_tcb) +{ + BOOLEAN ret = FALSE; + tGATT_CH_STATE ch_state; + GATT_TRACE_DEBUG ("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, p_tcb->peer_bda); + } else { + gatt_set_ch_state(p_tcb, GATT_CH_CLOSING); + ret = L2CA_CancelBleConnectReq (p_tcb->peer_bda); + } +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) + } else { + ret = L2CA_DisconnectReq(p_tcb->att_lcid); +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + } + } else { + GATT_TRACE_DEBUG ("gatt_disconnect already in closing state"); + } + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_update_app_hold_link_status +** +** Description Update the application use link status +** +** Returns void. +** +*******************************************************************************/ +void gatt_update_app_hold_link_status (tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add) +{ + UINT8 i; + BOOLEAN found = FALSE; + + if (p_tcb == NULL) { + GATT_TRACE_ERROR("gatt_update_app_hold_link_status p_tcb=NULL"); + return; + } + + + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->app_hold_link[i] == gatt_if) { + found = TRUE; + if (!is_add) { + p_tcb->app_hold_link[i] = 0; + break; + } + } + } + + if (!found && is_add) { + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->app_hold_link[i] == 0) { + p_tcb->app_hold_link[i] = gatt_if; + found = TRUE; + break; + } + } + } + + GATT_TRACE_DEBUG("gatt_update_app_hold_link_status found=%d[1-found] idx=%d gatt_if=%d is_add=%d", found, i, gatt_if, is_add); + +} + +/******************************************************************************* +** +** 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_DEBUG("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 && + p_tcb->att_lcid == L2CAP_ATT_CID && /* only update link idle timer for fixed channel */ + (BTM_GetHCIConnHandle(p_tcb->peer_bda, p_tcb->transport) != GATT_INVALID_ACL_HANDLE)) { + if (is_add) { + GATT_TRACE_DEBUG("GATT disables link idle timer"); + /* acl link is connected disable the idle timeout */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport); + } 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_DEBUG("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, p_tcb->transport); + } + + } + } +} + +/******************************************************************************* +** +** Function gatt_act_connect +** +** Description GATT connection initiation. +** +** Returns void. +** +*******************************************************************************/ +BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, + tBLE_ADDR_TYPE bd_addr_type, tBT_TRANSPORT transport, BOOLEAN is_aux) +{ + 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, bd_addr_type, p_tcb, transport, is_aux)) { + ret = FALSE; + } + } else if (st == GATT_CH_CLOSING) { + /* need to complete the closing first */ + ret = FALSE; + } else { + GATT_TRACE_WARNING("gatt_connect wrong state %d", st); + } + } else { + if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr, transport)) != NULL) { + if (!gatt_connect(bd_addr, bd_addr_type, p_tcb, transport, is_aux)) { + GATT_TRACE_ERROR("gatt_connect failed"); + + // code enter here if create connection failed. if disconnect after connection, code will not enter here + + // p_tcb, p_tcb->pending_enc_clcb, and p_tcb->pending_ind_q have been freed in gatt_cleanup_upon_disc(), + // but here p_tcb is get from gatt_allocate_tcb_by_bdaddr(), is too old, so we get p_tcb again + p_tcb = gatt_find_tcb_by_addr(bd_addr, transport); + if(p_tcb != NULL) { + if(p_tcb->pending_enc_clcb != NULL) { + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + } + if(p_tcb->pending_ind_q != NULL) { + fixed_queue_free(p_tcb->pending_ind_q, NULL); + } + gatt_tcb_free(p_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_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 (UINT16 chan, 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) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg (p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } + } + /* 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) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg (p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } + } 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_channel_congestion +** +** Description This function is called to process the congestion callback +** from lcb +** +** Returns void +** +*******************************************************************************/ +static void gatt_channel_congestion(tGATT_TCB *p_tcb, BOOLEAN congested) +{ + UINT8 i = 0; + tGATT_REG *p_reg = NULL; + UINT16 conn_id; +#if (GATTC_INCLUDED == TRUE) + /* if uncongested, check to see if there is any more pending data */ + if (p_tcb != NULL && congested == FALSE) { + gatt_cl_send_next_cmd_inq(p_tcb); + } +#endif ///GATTC_INCLUDED == TRUE + /* notifying all applications for the connection up event */ + for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use) { + if (p_reg->app_cb.p_congestion_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_congestion_cb)(conn_id, congested); + } + } + } +} + +/******************************************************************************* +** +** 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_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 (UINT16 chan, 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 { + osi_free (p_buf); + + if (p_tcb != NULL) { + GATT_TRACE_WARNING ("ATT - Ignored L2CAP data while in state: %d\n", + gatt_get_ch_state(p_tcb)); + } + } +} + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (CLASSIC_BT_GATT_INCLUDED == TRUE) +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_default.local_mtu; + + L2CA_ConfigReq(lcid, &cfg); + } + +} + +/******************************************************************************* +** +** 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_default.local_mtu; + 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_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) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg(p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } 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_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) { +#if (GATTS_INCLUDED == TRUE) + gatt_chk_srv_chg(p_srv_chg_clt); +#endif ///GATTS_INCLUDED == TRUE + } 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); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_l2cif_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + if (ack_needed) { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + if (gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda) == NULL) { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda, p_tcb->transport)) == 0) { + reason = GATT_CONN_TERMINATE_PEER_USER; + } + + /* send disconnect callback */ + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason, GATT_TRANSPORT_BR_EDR); + } +} + +/******************************************************************************* +** +** Function gatt_l2cif_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tGATT_TCB *p_tcb; + UINT16 reason; + UNUSED(result); + + /* look up clcb for this channel */ + if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL) { + /* If the device is not in the service changed client list, add it... */ + if (gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda) == NULL) { + if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda)) { + gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda); + } + } + + /* send disconnect callback */ + /* if ACL link is still up, no reason is logged, l2cap is disconnect from peer */ + if ((reason = L2CA_GetDisconnectReason(p_tcb->peer_bda, p_tcb->transport)) == 0) { + reason = GATT_CONN_TERMINATE_LOCAL_HOST; + } + + gatt_cleanup_upon_disc(p_tcb->peer_bda, reason, GATT_TRANSPORT_BR_EDR); + } +} + +/******************************************************************************* +** +** 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 */ + osi_free(p_buf); + } + +} + +/******************************************************************************* +** +** Function gatt_l2cif_congest_cback +** +** Description L2CAP congestion callback +** +** Returns void +** +*******************************************************************************/ +static void gatt_l2cif_congest_cback (UINT16 lcid, BOOLEAN congested) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_cid(lcid); + + if (p_tcb != NULL) { + gatt_channel_congestion(p_tcb, congested); + } + +} +#endif ///CLASSIC_BT_GATT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_send_conn_cback +** +** Description Callback used to notify layer above about a connection. +** +** +** Returns void +** +*******************************************************************************/ +static void gatt_send_conn_cback(tGATT_TCB *p_tcb) +{ + UINT8 i; + tGATT_REG *p_reg; + tGATT_BG_CONN_DEV *p_bg_dev = NULL; + UINT16 conn_id; + + p_bg_dev = gatt_find_bg_dev(p_tcb->peer_bda); + + /* notifying all applications for the connection up event */ + for (i = 0, p_reg = gatt_cb.cl_rcb ; i < GATT_MAX_APPS; i++, p_reg++) { + if (p_reg->in_use) { + if (p_bg_dev && gatt_is_bg_dev_for_app(p_bg_dev, p_reg->gatt_if)) { + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, TRUE); + } + + if (p_reg->app_cb.p_conn_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, + TRUE, 0, p_tcb->transport); + } + } + } + + + if (gatt_num_apps_hold_link(p_tcb) && p_tcb->att_lcid == L2CAP_ATT_CID ) { + /* disable idle timeout if one or more clients are holding the link disable the idle timer */ + GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport); + } +} + +/******************************************************************************* +** +** 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) +{ + UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; + UINT8 op_code, pseudo_op_code; +#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + UINT16 msg_len; +#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + + + if (p_buf->len > 0) { +#if (GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + msg_len = p_buf->len - 1; +#endif ///(GATTS_INCLUDED == TRUE) || (GATTC_INCLUDED == TRUE) + STREAM_TO_UINT8(op_code, p); + + /* 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) { +#if (SMP_INCLUDED == TRUE) + gatt_verify_signature(p_tcb, p_buf); +#endif ///SMP_INCLUDED == TRUE + } else { + /* message from client */ + if ((op_code % 2) == 0) { +#if (GATTS_INCLUDED == TRUE) + gatt_server_handle_client_req (p_tcb, op_code, msg_len, p); +#endif ///GATTS_INCLUDED == TRUE + } else { +#if (GATTC_INCLUDED == TRUE) + gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p); +#endif ///GATTC_INCLUDED == TRUE + } + } + } else { + if (op_code & GATT_COMMAND_FLAG) { + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x\n", op_code); + } else { + GATT_TRACE_ERROR ("ATT - Rcvd L2CAP data, unknown req: 0x%x\n", op_code); + gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, op_code, 0, FALSE); + } + } + } else { + GATT_TRACE_ERROR ("invalid data length, ignore\n"); + } + + osi_free (p_buf); +} + +/******************************************************************************* +** +** Function gatt_add_a_bonded_dev_for_srv_chg +** +** Description Add a bonded dev to the service changed client list +** +** Returns void +** +*******************************************************************************/ +void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG srv_chg_clt; + + memcpy(srv_chg_clt.bda, bda, BD_ADDR_LEN); + srv_chg_clt.srv_changed = FALSE; + if (gatt_add_srv_chg_clt(&srv_chg_clt) != NULL) { + memcpy(req.srv_chg.bda, bda, BD_ADDR_LEN); + req.srv_chg.srv_changed = FALSE; + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_ADD_CLIENT, &req, NULL); + } + } +} + +/******************************************************************************* +** +** Function gatt_send_srv_chg_ind +** +** Description This function is called to send a service changed indication to +** the specified bd address +** +** Returns GATT_SUCCESS if successfully sent; otherwise error code +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda) +{ + UINT8 handle_range[GATT_SIZE_OF_SRV_CHG_HNDL_RANGE]; + UINT8 *p = handle_range; + UINT16 conn_id; + tGATT_STATUS status = GATT_ERROR; + GATT_TRACE_DEBUG("gatt_send_srv_chg_ind"); + + if (gatt_cb.handle_of_h_r) { + if ((conn_id = gatt_profile_find_conn_id_by_bd_addr(peer_bda)) != GATT_INVALID_CONN_ID) { + UINT16_TO_STREAM (p, 1); + UINT16_TO_STREAM (p, 0xFFFF); + status = GATTS_HandleValueIndication (conn_id, + gatt_cb.handle_of_h_r, + GATT_SIZE_OF_SRV_CHG_HNDL_RANGE, + handle_range); + } else { + status = GATT_NOT_FOUND; + GATT_TRACE_ERROR("Unable to find conn_id for %02x%02x%02x%02x%02x%02x ", + peer_bda[0], peer_bda[1], peer_bda[2], peer_bda[3], peer_bda[4], peer_bda[5]); + } + } + return status; +} + + +/******************************************************************************* +** +** Function gatt_chk_srv_chg +** +** Description Check sending service changed 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_DEBUG("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); + } +} +#endif ///GATTS_INCLUDED == TRUE + + +/******************************************************************************* +** +** 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_DEBUG("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_DEBUG("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)) == TRUE) { + memcpy(&srv_chg_clt, &rsp.srv_chg , sizeof(tGATTS_SRV_CHG)); + if (gatt_add_srv_chg_clt(&srv_chg_clt) == NULL) { + GATT_TRACE_ERROR("Unable to add a service change client"); + status = FALSE; + } + } + i++; + } + } + } else { + GATT_TRACE_DEBUG("gatt_init_srv_chg callback not registered yet"); + } +} + +/******************************************************************************* +** +** Function gatt_proc_srv_chg +** +** Description This function is process the service changed request +** +** Returns void +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +void gatt_proc_srv_chg (void) +{ + UINT8 start_idx, found_idx; + BD_ADDR bda; + BOOLEAN srv_chg_ind_pending = FALSE; + tGATT_TCB *p_tcb; + tBT_TRANSPORT transport; + + GATT_TRACE_DEBUG ("gatt_proc_srv_chg"); + + if (gatt_cb.cb_info.p_srv_chg_callback && gatt_cb.handle_of_h_r) { + gatt_set_srv_chg(); + start_idx = 0; + while (gatt_find_the_connected_bda(start_idx, bda, &found_idx, &transport)) { + p_tcb = gatt_get_tcb_by_idx(found_idx); + srv_chg_ind_pending = gatt_is_srv_chg_ind_pending(p_tcb); + + if (!srv_chg_ind_pending) { + gatt_send_srv_chg_ind(bda); + } else { + GATT_TRACE_DEBUG ("discard srv chg - already has one in the queue"); + } + start_idx = ++found_idx; + } + } +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_set_ch_state +** +** Description This function set the ch_state in tcb +** +** Returns none +** +*******************************************************************************/ +void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state) +{ + if (p_tcb) { + GATT_TRACE_DEBUG ("gatt_set_ch_state: old=%d new=%d", p_tcb->ch_state, ch_state); + p_tcb->ch_state = ch_state; + } +} + +/******************************************************************************* +** +** 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_DEBUG ("gatt_get_ch_state: ch_state=%d", p_tcb->ch_state); + ch_state = p_tcb->ch_state; + } + return ch_state; +} + +uint16_t gatt_get_local_mtu(void) +{ + return gatt_default.local_mtu; +} + +void gatt_set_local_mtu(uint16_t mtu) +{ + gatt_default.local_mtu = mtu; +} + +uint8_t gatt_tcb_active_count(void) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + uint8_t count = 0; + + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb && p_tcb->in_use && (p_tcb->ch_state != GATT_CH_CLOSE)) { + count++; + } + } + + return count; +} + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c b/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c new file mode 100644 index 00000000..79e161c1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_sr.c @@ -0,0 +1,1900 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the GATT server functions + * + ******************************************************************************/ + +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE +#include <string.h> +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#define GATT_MTU_REQ_MIN_LEN 2 + + + +/******************************************************************************* +** +** Function gatt_send_packet +** +** Description This function is called to send gatt packets directly + +** +** Returns status +** +*******************************************************************************/ +tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_msg = NULL; + UINT8 *p_m = NULL; + UINT16 buf_len; + tGATT_STATUS status; + + if (len > p_tcb->payload_size){ + return GATT_ILLEGAL_PARAMETER; + } + + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + if ((p_msg = (BT_HDR *)osi_malloc(buf_len)) == NULL) { + return GATT_NO_RESOURCES; + } + + memset(p_msg, 0, buf_len); + p_msg->len = len; + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + memcpy(p_m, p_data, len); + + status = attp_send_sr_msg(p_tcb, p_msg); + return status; +} + +/******************************************************************************* +** +** Function gatt_sr_enqueue_cmd +** +** Description This function enqueue the request from client which needs a +** application response, and update the transaction ID. +** +** Returns void +** +*******************************************************************************/ +UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle) +{ + tGATT_SR_CMD *p_cmd = &p_tcb->sr_cmd; + UINT32 trans_id = 0; + + if ( (p_cmd->op_code == 0) || + (op_code == GATT_HANDLE_VALUE_CONF)) { /* no pending request */ + if (op_code == GATT_CMD_WRITE || + op_code == GATT_SIGN_CMD_WRITE || + op_code == GATT_REQ_MTU || + op_code == GATT_HANDLE_VALUE_CONF) { + trans_id = ++p_tcb->trans_id; + } else { + p_cmd->trans_id = ++p_tcb->trans_id; + p_cmd->op_code = op_code; + p_cmd->handle = handle; + p_cmd->status = GATT_NOT_FOUND; + p_tcb->trans_id %= GATT_TRANS_ID_MAX; + trans_id = p_cmd->trans_id; + } + } + + return trans_id; +} + +/******************************************************************************* +** +** Function gatt_sr_cmd_empty +** +** Description This function check the server command queue is empty or not. +** +** Returns TRUE if empty, FALSE if there is pending command. +** +*******************************************************************************/ +BOOLEAN gatt_sr_cmd_empty (tGATT_TCB *p_tcb) +{ + return (p_tcb->sr_cmd.op_code == 0); +} + +/******************************************************************************* +** +** Function gatt_dequeue_sr_cmd +** +** Description This function dequeue the request from command queue. +** +** Returns void +** +*******************************************************************************/ +void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb) +{ + /* Double check in case any buffers are queued */ + GATT_TRACE_DEBUG("gatt_dequeue_sr_cmd" ); + if (p_tcb->sr_cmd.p_rsp_msg) { + GATT_TRACE_ERROR("%s free msg %p", __func__, p_tcb->sr_cmd.p_rsp_msg); + + osi_free(p_tcb->sr_cmd.p_rsp_msg); + p_tcb->sr_cmd.p_rsp_msg = NULL; + } + + if (p_tcb->sr_cmd.multi_rsp_q) { + while (!fixed_queue_is_empty(p_tcb->sr_cmd.multi_rsp_q)) { + osi_free(fixed_queue_dequeue(p_tcb->sr_cmd.multi_rsp_q, 0)); + } + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, NULL); + } + + memset( &p_tcb->sr_cmd, 0, sizeof(tGATT_SR_CMD)); +} + +/******************************************************************************* +** +** Function process_read_multi_rsp +** +** Description This function check the read multiple response. +** +** Returns BOOLEAN if all replies have been received +** +*******************************************************************************/ +static BOOLEAN process_read_multi_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, + tGATTS_RSP *p_msg, UINT16 mtu) +{ + UINT16 ii, total_len, len; + UINT8 *p; + BOOLEAN is_overflow = FALSE; + + GATT_TRACE_DEBUG ("process_read_multi_rsp status=%d mtu=%d", status, mtu); + + if (p_cmd->multi_rsp_q == NULL) { + p_cmd->multi_rsp_q = fixed_queue_new(QUEUE_SIZE_MAX); + } + + /* Enqueue the response */ + BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(tGATTS_RSP)); + if (p_buf == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return FALSE; + } + memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); + + fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + p_cmd->status = status; + if (status == GATT_SUCCESS) { + GATT_TRACE_DEBUG ("Multi read count=%d num_hdls=%d", + fixed_queue_length(p_cmd->multi_rsp_q), + p_cmd->multi_req.num_handles); + /* Wait till we get all the responses */ + if (fixed_queue_length(p_cmd->multi_rsp_q) == p_cmd->multi_req.num_handles) { + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; + if ((p_buf = (BT_HDR *)osi_calloc(len)) == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return (TRUE); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First byte in the response is the opcode */ + *p++ = GATT_RSP_READ_MULTI; + p_buf->len = 1; + + /* Now walk through the buffers puting the data into the response in order */ + list_t *list = NULL; + const list_node_t *node = NULL; + if (! fixed_queue_is_empty(p_cmd->multi_rsp_q)) { + list = fixed_queue_get_list(p_cmd->multi_rsp_q); + } + for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) { + tGATTS_RSP *p_rsp = NULL; + if (list != NULL) { + if (ii == 0) { + node = list_begin(list); + } else { + node = list_next(node); + } + if (node != list_end(list)) { + p_rsp = (tGATTS_RSP *)list_node(node); + } + } + + if (p_rsp != NULL) { + + total_len = (p_buf->len + p_rsp->attr_value.len); + + if (total_len > mtu) { + /* just send the partial response for the overflow case */ + len = p_rsp->attr_value.len - (total_len - mtu); + is_overflow = TRUE; + GATT_TRACE_DEBUG ("multi read overflow available len=%d val_len=%d", len, p_rsp->attr_value.len ); + } else { + len = p_rsp->attr_value.len; + } + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) { + memcpy (p, p_rsp->attr_value.value, len); + if (!is_overflow) { + p += len; + } + p_buf->len += len; + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + if (is_overflow) { + break; + } + + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + } /* loop through all handles*/ + + + /* Sanity check on the buffer length */ + if (p_buf->len == 0) { + GATT_TRACE_ERROR("process_read_multi_rsp - nothing found!!"); + p_cmd->status = GATT_NOT_FOUND; + osi_free (p_buf); + GATT_TRACE_DEBUG(" osi_free (p_buf)"); + } else if (p_cmd->p_rsp_msg != NULL) { + osi_free (p_buf); + } else { + p_cmd->p_rsp_msg = p_buf; + } + + return (TRUE); + } + } else { /* any handle read exception occurs, return error */ + return (TRUE); + } + + /* If here, still waiting */ + return (FALSE); +} + +static BOOLEAN process_read_multi_var_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status, + tGATTS_RSP *p_msg, UINT16 mtu) +{ + UINT16 ii; + UINT16 total_len; + UINT16 len; + UINT8 *p; + + GATT_TRACE_DEBUG ("process_read_multi_var rsp status=%d mtu=%d", status, mtu); + + if (p_cmd->multi_rsp_q == NULL) { + p_cmd->multi_rsp_q = fixed_queue_new(QUEUE_SIZE_MAX); + } + + /* Enqueue the response */ + BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(tGATTS_RSP)); + if (p_buf == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return FALSE; + } + memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP)); + + fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + + p_cmd->status = status; + if (status == GATT_SUCCESS) { + GATT_TRACE_DEBUG ("Multi var read count=%d num_hdls=%d", + fixed_queue_length(p_cmd->multi_rsp_q), + p_cmd->multi_req.num_handles); + /* Wait till we get all the responses */ + if (fixed_queue_length(p_cmd->multi_rsp_q) == p_cmd->multi_req.num_handles) { + len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu; + if ((p_buf = (BT_HDR *)osi_calloc(len)) == NULL) { + p_cmd->status = GATT_INSUF_RESOURCE; + return (TRUE); + } + + p_buf->offset = L2CAP_MIN_OFFSET; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + + /* First byte in the response is the opcode */ + *p++ = GATT_RSP_READ_MULTI_VAR; + p_buf->len = 1; + + /* Now walk through the buffers puting the data into the response in order */ + list_t *list = NULL; + const list_node_t *node = NULL; + if (! fixed_queue_is_empty(p_cmd->multi_rsp_q)) { + list = fixed_queue_get_list(p_cmd->multi_rsp_q); + } + for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) { + tGATTS_RSP *p_rsp = NULL; + if (list != NULL) { + if (ii == 0) { + node = list_begin(list); + } else { + node = list_next(node); + } + if (node != list_end(list)) { + p_rsp = (tGATTS_RSP *)list_node(node); + } + } + + if (p_rsp != NULL) { + + total_len = (p_buf->len + 2); // value length + + if (total_len > mtu) { + GATT_TRACE_DEBUG ("multi read variable overflow available len=%d val_len=%d", len, p_rsp->attr_value.len ); + break; + } + len = MIN(p_rsp->attr_value.len, (mtu - total_len)); // attribute value length + + if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) { + GATT_TRACE_DEBUG("%s handle %x len %u", __func__, p_rsp->attr_value.handle, p_rsp->attr_value.len); + UINT16_TO_STREAM(p, p_rsp->attr_value.len); + memcpy (p, p_rsp->attr_value.value, len); + p += len; + p_buf->len += (2+len); + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + } else { + p_cmd->status = GATT_NOT_FOUND; + break; + } + + } /* loop through all handles*/ + + /* Sanity check on the buffer length */ + if (p_buf->len == 0) { + GATT_TRACE_ERROR("%s - nothing found!!", __func__); + p_cmd->status = GATT_NOT_FOUND; + osi_free (p_buf); + } else if (p_cmd->p_rsp_msg != NULL) { + osi_free (p_buf); + } else { + p_cmd->p_rsp_msg = p_buf; + } + + return (TRUE); + } + } else { /* any handle read exception occurs, return error */ + return (TRUE); + } + + /* If here, still waiting */ + return (FALSE); +} + +/******************************************************************************* +** +** Function gatt_sr_process_app_rsp +** +** Description This function checks whether the response message from application +** match any pending request or not. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, + UINT32 trans_id, UINT8 op_code, + tGATT_STATUS status, tGATTS_RSP *p_msg) +{ + tGATT_STATUS ret_code = GATT_SUCCESS; + UNUSED(trans_id); + + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp gatt_if=%d\n", gatt_if); + + gatt_sr_update_cback_cnt(p_tcb, gatt_if, FALSE, FALSE); + + if (op_code == GATT_REQ_READ_MULTI) { + /* If no error and still waiting, just return */ + if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) { + return (GATT_SUCCESS); + } + } else if (op_code == GATT_REQ_READ_MULTI_VAR) { + if (!process_read_multi_var_rsp(&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) { + return (GATT_SUCCESS); + } + } else { + if (op_code == GATT_REQ_PREPARE_WRITE && status == GATT_SUCCESS) { + gatt_sr_update_prep_cnt(p_tcb, gatt_if, TRUE, FALSE); + } + + if (op_code == GATT_REQ_EXEC_WRITE && status != GATT_SUCCESS) { + gatt_sr_reset_cback_cnt(p_tcb); + } + + p_tcb->sr_cmd.status = status; + + if (gatt_sr_is_cback_cnt_zero(p_tcb) + && status == GATT_SUCCESS) { + if (p_tcb->sr_cmd.p_rsp_msg == NULL) { + p_tcb->sr_cmd.p_rsp_msg = attp_build_sr_msg (p_tcb, (UINT8)(op_code + 1), (tGATT_SR_MSG *)p_msg); + } else { + GATT_TRACE_ERROR("Exception!!! already has respond message\n"); + } + } + } + if (gatt_sr_is_cback_cnt_zero(p_tcb)) { + if ( (p_tcb->sr_cmd.status == GATT_SUCCESS) && (p_tcb->sr_cmd.p_rsp_msg) ) { + ret_code = attp_send_sr_msg (p_tcb, p_tcb->sr_cmd.p_rsp_msg); + p_tcb->sr_cmd.p_rsp_msg = NULL; + } else { + if (p_tcb->sr_cmd.status == GATT_SUCCESS){ + status = GATT_UNKNOWN_ERROR; + } + ret_code = gatt_send_error_rsp (p_tcb, status, op_code, p_tcb->sr_cmd.handle, FALSE); + } + + gatt_dequeue_sr_cmd(p_tcb); + } + + GATT_TRACE_DEBUG("gatt_sr_process_app_rsp ret_code=%d\n", ret_code); + + return ret_code; +} + +/******************************************************************************* +** +** Function gatt_process_exec_write_req +** +** Description This function is called to process the execute write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 *p = p_data, flag, i = 0; + UINT32 trans_id = 0; + tGATT_IF gatt_if; + UINT16 conn_id; + UINT16 queue_num = 0; + BOOLEAN is_first = TRUE; + BOOLEAN is_prepare_write_valid = FALSE; + BOOLEAN is_need_dequeue_sr_cmd = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + UNUSED(len); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rspv for Execute Write: error status=%d", + gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, gatt_cb.handle, FALSE); + + return; + } +#endif + + STREAM_TO_UINT8(flag, p); + + /* mask the flag */ + flag &= GATT_PREP_WRITE_EXEC; + + prepare_record = &(p_tcb->prepare_write_record); + queue_num = fixed_queue_length(prepare_record->queue); + + //if received prepare_write packets include stack_rsp and app_rsp, + //stack respond to execute_write only when stack_rsp handle has invalid_offset + //or invalid_length error; + //app need to respond to execute_write if it has received app_rsp handle packets + if (((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num == queue_num)) + || (flag == GATT_PREP_WRITE_CANCEL)){ + tGATT_EXEC_WRITE_RSP gatt_exec_write_rsp; + gatt_exec_write_rsp.op_code = GATT_RSP_EXEC_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_exec_write_rsp), sizeof(gatt_exec_write_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + if (flag != GATT_PREP_WRITE_CANCEL){ + is_prepare_write_valid = TRUE; + } + GATT_TRACE_DEBUG("Send execute_write_rsp\n"); + } else if ((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num > queue_num)){ + //No error for stack_rsp's handles and there exist some app_rsp's handles, + //so exec_write_rsp depends to app's response; but stack_rsp's data is valid + //TODO: there exist problem if stack_rsp's data is valid but app_rsp's data is not valid. + is_prepare_write_valid = TRUE; + } else if(prepare_record->total_num < queue_num) { + GATT_TRACE_ERROR("Error in %s, line=%d, prepare write total number (%d) \ + should not smaller than prepare queue number (%d)\n", \ + __func__, __LINE__,prepare_record->total_num, queue_num); + } else if (prepare_record->error_code_app != GATT_SUCCESS){ + GATT_TRACE_DEBUG("Send error code for execute_write, code=0x%x\n", prepare_record->error_code_app); + is_need_dequeue_sr_cmd = (prepare_record->total_num == queue_num) ? TRUE : FALSE; + gatt_send_error_rsp(p_tcb, prepare_record->error_code_app, GATT_REQ_EXEC_WRITE, 0, is_need_dequeue_sr_cmd); + } + + //dequeue prepare write data + while(fixed_queue_try_peek_first(prepare_record->queue)) { + queue_data = fixed_queue_dequeue(prepare_record->queue, FIXED_QUEUE_MAX_TIMEOUT); + if (is_prepare_write_valid){ + if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ + if(is_first) { + //clear attr_val.attr_len before handle prepare write data + queue_data->p_attr->p_value->attr_val.attr_len = 0; + is_first = FALSE; + } + memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); + //don't forget to increase the attribute value length in the gatts database. + queue_data->p_attr->p_value->attr_val.attr_len += queue_data->len; + } + } + osi_free(queue_data); + } + fixed_queue_free(prepare_record->queue, NULL); + prepare_record->queue = NULL; + + /* according to ble spec, even if there is no prep write queued, + * need to respond execute_write_response + * Note: exec_write_rsp callback should be called after all data has been written*/ + if (!gatt_sr_is_prep_cnt_zero(p_tcb)) { + if (prepare_record->total_num > queue_num){ + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, 0); + gatt_sr_copy_prep_cnt_to_cback_cnt(p_tcb); + } + + for (i = 0; i < GATT_MAX_APPS; i++) { + if (p_tcb->prep_cnt[i]) { + gatt_if = (tGATT_IF) (i + 1); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_if); + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE_EXEC, + (tGATTS_DATA *)&flag); + p_tcb->prep_cnt[i] = 0; + } + } + } + + prepare_record->total_num = 0; + prepare_record->error_code_app = GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatt_process_read_multi_req +** +** Description This function is called to process the read multiple request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT32 trans_id; + UINT16 handle = 0, ll = len; + UINT8 *p = p_data, i_rcb; + tGATT_STATUS err = GATT_SUCCESS; + UINT8 sec_flag, key_size; + tGATTS_RSP *p_msg; + + GATT_TRACE_DEBUG("gatt_process_read_multi_req" ); + p_tcb->sr_cmd.multi_req.num_handles = 0; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rspvofr ReadMultiple: error status=%d\n", gatt_cb.err_status); + + STREAM_TO_UINT16(handle, p); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + while (ll >= 2 && p_tcb->sr_cmd.multi_req.num_handles < GATT_MAX_READ_MULTI_HANDLES) { + STREAM_TO_UINT16(handle, p); + + if ((i_rcb = gatt_sr_find_i_rcb_by_handle(handle)) < GATT_MAX_SR_PROFILES) { + p_tcb->sr_cmd.multi_req.handles[p_tcb->sr_cmd.multi_req.num_handles++] = handle; + + /* check read permission */ + if ((err = gatts_read_attr_perm_check( gatt_cb.sr_reg[i_rcb].p_db, + FALSE, + handle, + sec_flag, + key_size)) + != GATT_SUCCESS) { + GATT_TRACE_ERROR("read permission denied : 0x%02x", err); + break; + } + } else { + /* invalid handle */ + err = GATT_INVALID_HANDLE; + break; + } + ll -= 2; + } + + if (err == GATT_SUCCESS) { + if (ll != 0) { + GATT_TRACE_ERROR("max attribute handle reached in ReadMultiple Request."); + err = GATT_INVALID_HANDLE; + } + + if (p_tcb->sr_cmd.multi_req.num_handles == 0) { + err = GATT_INVALID_HANDLE; + } + } + + if (err == GATT_SUCCESS) { + if ((trans_id = gatt_sr_enqueue_cmd (p_tcb, op_code, p_tcb->sr_cmd.multi_req.handles[0])) != 0) { + gatt_sr_reset_cback_cnt(p_tcb); /* read multiple use multi_rsp_q's count*/ + + for (ll = 0; ll < p_tcb->sr_cmd.multi_req.num_handles; ll ++) { + if ((p_msg = (tGATTS_RSP *)osi_malloc(sizeof(tGATTS_RSP))) != NULL) { + memset(p_msg, 0, sizeof(tGATTS_RSP)) + ; + handle = p_tcb->sr_cmd.multi_req.handles[ll]; + i_rcb = gatt_sr_find_i_rcb_by_handle(handle); + + p_msg->attr_value.handle = handle; + err = gatts_read_attr_value_by_handle(p_tcb, + gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + 0, + p_msg->attr_value.value, + &p_msg->attr_value.len, + GATT_MAX_ATTR_LEN, + sec_flag, + key_size, + trans_id); + + if (err == GATT_SUCCESS || err == GATT_STACK_RSP) { + gatt_sr_process_app_rsp(p_tcb, gatt_cb.sr_reg[i_rcb].gatt_if , trans_id, op_code, GATT_SUCCESS, p_msg); + } + /* either not using or done using the buffer, release it now */ + osi_free(p_msg); + } else { + err = GATT_NO_RESOURCES; + gatt_dequeue_sr_cmd(p_tcb); + break; + } + } + } else { + err = GATT_NO_RESOURCES; + } + } + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (err != GATT_SUCCESS && err != GATT_STACK_RSP && err != GATT_PENDING && err != GATT_BUSY) { + gatt_send_error_rsp(p_tcb, err, op_code, handle, FALSE); + } +} + +/******************************************************************************* +** +** Function gatt_build_primary_service_rsp +** +** Description Primamry service request processed internally. Theretically +** only deal with ReadByTypeVAlue and ReadByGroupType. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_primary_service_rsp (BT_HDR *p_msg, tGATT_TCB *p_tcb, + UINT8 op_code, UINT16 s_hdl, + UINT16 e_hdl, UINT8 *p_data, tBT_UUID value) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 handle_len = 4, *p ; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + tBT_UUID *p_uuid; + + UNUSED(p_data); + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + p_rcb->s_hdl >= s_hdl && + p_rcb->s_hdl <= e_hdl && + p_rcb->type == GATT_UUID_PRI_SERVICE) { + if ((p_uuid = gatts_get_service_uuid (p_rcb->p_db)) != NULL) { + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + handle_len = 4 + p_uuid->len; + } + + /* get the length byte in the repsonse */ + if (p_msg->offset == 0) { + *p ++ = op_code + 1; + p_msg->len ++; + p_msg->offset = handle_len; + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + *p ++ = (UINT8)p_msg->offset; /* length byte */ + p_msg->len ++; + } + } + + if (p_msg->len + p_msg->offset <= p_tcb->payload_size && + handle_len == p_msg->offset) { + if (op_code != GATT_REQ_FIND_TYPE_VALUE || + gatt_uuid_compare(value, *p_uuid)) { + UINT16_TO_STREAM(p, p_rcb->s_hdl); + + if (p_list->p_last_primary == p_srv && + p_list->p_last_primary == p_list->p_last) { + GATT_TRACE_DEBUG("Use 0xFFFF for the last primary attribute"); + UINT16_TO_STREAM(p, 0xFFFF); /* see GATT ERRATA 4065, 4063, ATT ERRATA 4062 */ + } else { + UINT16_TO_STREAM(p, p_rcb->e_hdl); + } + + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + gatt_build_uuid_to_stream(&p, *p_uuid); + } + + status = GATT_SUCCESS; + p_msg->len += p_msg->offset; + } + } else { + break; + } + } + } + p_srv = p_srv->p_next; + } + p_msg->offset = L2CAP_MIN_OFFSET; + + return status; +} + +/******************************************************************************* +** +** Function gatt_build_find_info_rsp +** +** Description fill the find information response information in the given +** buffer. +** +** Returns TRUE: if data filled successfully. +** FALSE: packet full, or format mismatch. +** +*******************************************************************************/ +static tGATT_STATUS gatt_build_find_info_rsp(tGATT_SR_REG *p_rcb, BT_HDR *p_msg, UINT16 *p_len, + UINT16 s_hdl, UINT16 e_hdl) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 *p; + UINT16 len = *p_len; + tGATT_ATTR16 *p_attr = NULL; + UINT8 info_pair_len[2] = {4, 18}; + + if (!p_rcb->p_db || !p_rcb->p_db->p_attr_list) { + return status; + } + + /* check the attribute database */ + p_attr = (tGATT_ATTR16 *) p_rcb->p_db->p_attr_list; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET + p_msg->len; + + while (p_attr) { + if (p_attr->handle > e_hdl) { + break; + } + + if (p_attr->handle >= s_hdl) { + if (p_msg->offset == 0) { + p_msg->offset = (p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) ? GATT_INFO_TYPE_PAIR_16 : GATT_INFO_TYPE_PAIR_128; + } + + if (len >= info_pair_len[p_msg->offset - 1]) { + if (p_msg->offset == GATT_INFO_TYPE_PAIR_16 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_16) { + UINT16_TO_STREAM(p, p_attr->handle); + UINT16_TO_STREAM(p, p_attr->uuid); + } else if (p_msg->offset == GATT_INFO_TYPE_PAIR_128 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_128 ) { + UINT16_TO_STREAM(p, p_attr->handle); + ARRAY_TO_STREAM (p, ((tGATT_ATTR128 *) p_attr)->uuid, LEN_UUID_128); + } else if (p_msg->offset == GATT_INFO_TYPE_PAIR_128 && p_attr->uuid_type == GATT_ATTR_UUID_TYPE_32) { + UINT16_TO_STREAM(p, p_attr->handle); + gatt_convert_uuid32_to_uuid128(p, ((tGATT_ATTR32 *) p_attr)->uuid); + p += LEN_UUID_128; + } else { + GATT_TRACE_ERROR("format mismatch"); + status = GATT_NO_RESOURCES; + break; + /* format mismatch */ + } + p_msg->len += info_pair_len[p_msg->offset - 1]; + len -= info_pair_len[p_msg->offset - 1]; + status = GATT_SUCCESS; + + } else { + status = GATT_NO_RESOURCES; + break; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + + *p_len = len; + return status; +} + +/******************************************************************************* +** +** Function gatts_internal_read_by_type_req +** +** Description check to see if the ReadByType request can be handled internally. +** +** Returns void +** +*******************************************************************************/ +static tGATT_STATUS gatts_validate_packet_format(UINT8 op_code, UINT16 *p_len, + UINT8 **p_data, tBT_UUID *p_uuid_filter, + UINT16 *p_s_hdl, UINT16 *p_e_hdl) +{ + tGATT_STATUS reason = GATT_SUCCESS; + UINT16 uuid_len, s_hdl = 0, e_hdl = 0; + UINT16 len = *p_len; + UINT8 *p = *p_data; + + if (len >= 4) { + /* obtain starting handle, and ending handle */ + STREAM_TO_UINT16(s_hdl, p); + STREAM_TO_UINT16(e_hdl, p); + len -= 4; + + if (s_hdl > e_hdl || !GATT_HANDLE_IS_VALID(s_hdl) || !GATT_HANDLE_IS_VALID(e_hdl)) { + reason = GATT_INVALID_HANDLE; + } + /* for these PDUs, uuid filter must present */ + else if (op_code == GATT_REQ_READ_BY_GRP_TYPE || + op_code == GATT_REQ_FIND_TYPE_VALUE || + op_code == GATT_REQ_READ_BY_TYPE) { + if (len >= 2 && p_uuid_filter != NULL) { + uuid_len = (op_code == GATT_REQ_FIND_TYPE_VALUE) ? 2 : len; + + /* parse uuid now */ + if (gatt_parse_uuid_from_cmd (p_uuid_filter, uuid_len, &p) == FALSE || + p_uuid_filter->len == 0) { + GATT_TRACE_DEBUG("UUID filter does not exsit"); + reason = GATT_INVALID_PDU; + } else { + len -= p_uuid_filter->len; + } + } else { + reason = GATT_INVALID_PDU; + } + } + } else { + reason = GATT_INVALID_PDU; + } + + *p_data = p; + *p_len = len; + *p_s_hdl = s_hdl; + *p_e_hdl = e_hdl; + + return reason; +} + +/******************************************************************************* +** +** Function gatts_process_primary_service_req +** +** Description process ReadByGroupType/ReadByTypeValue request, for discover +** all primary services or discover primary service by UUID request. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_primary_service_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU; + UINT16 s_hdl = 0, e_hdl = 0; + tBT_UUID uuid, value, primary_service = {LEN_UUID_16, {GATT_UUID_PRI_SERVICE}}; + BT_HDR *p_msg = NULL; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + memset (&value, 0, sizeof(tBT_UUID)); + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) { + if (gatt_uuid_compare(uuid, primary_service)) { + if (op_code == GATT_REQ_FIND_TYPE_VALUE) { + if (gatt_parse_uuid_from_cmd(&value, len, &p_data) == FALSE) { + reason = GATT_INVALID_PDU; + } + } + + if (reason == GATT_SUCCESS) { + if ((p_msg = (BT_HDR *)osi_calloc(msg_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_primary_service_req failed. no resources."); + reason = GATT_NO_RESOURCES; + } else { + reason = gatt_build_primary_service_rsp (p_msg, p_tcb, op_code, s_hdl, e_hdl, p_data, value); + } + } + } else { + if (op_code == GATT_REQ_READ_BY_GRP_TYPE) { + reason = GATT_UNSUPPORT_GRP_TYPE; + GATT_TRACE_DEBUG("unexpected ReadByGrpType Group: 0x%04x", uuid.uu.uuid16); + } else { + /* we do not support ReadByTypeValue with any non-primamry_service type */ + reason = GATT_NOT_FOUND; + GATT_TRACE_DEBUG("unexpected ReadByTypeValue type: 0x%04x", uuid.uu.uuid16); + } + } + } + + if (reason != GATT_SUCCESS) { + if (p_msg) { + osi_free(p_msg); + } + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } else { + attp_send_sr_msg(p_tcb, p_msg); + } + +} + +/******************************************************************************* +** +** Function gatts_process_find_info +** +** Description process find information request, for discover character +** descriptors. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_find_info(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + UINT8 reason = GATT_INVALID_PDU, *p; + UINT16 s_hdl = 0, e_hdl = 0, buf_len; + BT_HDR *p_msg = NULL; + tGATT_SR_REG *p_rcb; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, NULL, &s_hdl, &e_hdl); + + if (reason == GATT_SUCCESS) { + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + + if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) { + reason = GATT_NO_RESOURCES; + } else { + reason = GATT_NOT_FOUND; + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 2; + + buf_len = p_tcb->payload_size - 2; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) { + reason = gatt_build_find_info_rsp(p_rcb, p_msg, &buf_len, s_hdl, e_hdl); + if (reason == GATT_NO_RESOURCES) { + reason = GATT_SUCCESS; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + + if (reason != GATT_SUCCESS) { + if (p_msg) { + osi_free(p_msg); + } + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } else { + attp_send_sr_msg(p_tcb, p_msg); + } + +} + +/******************************************************************************* +** +** Function gatts_process_mtu_req +** +** Description This function is called to process excahnge MTU request. +** Only used on LE. +** +** Returns void +** +*******************************************************************************/ +static void gatts_process_mtu_req (tGATT_TCB *p_tcb, UINT16 len, UINT8 *p_data) +{ + UINT16 mtu = 0; + UINT8 *p = p_data, i; + BT_HDR *p_buf; + UINT16 conn_id; + + /* BR/EDR conenction, send error response */ + if (p_tcb->att_lcid != L2CAP_ATT_CID) { + gatt_send_error_rsp (p_tcb, GATT_REQ_NOT_SUPPORTED, GATT_REQ_MTU, 0, FALSE); + } else if (len < GATT_MTU_REQ_MIN_LEN) { + GATT_TRACE_ERROR("invalid MTU request PDU received.\n"); + gatt_send_error_rsp (p_tcb, GATT_INVALID_PDU, GATT_REQ_MTU, 0, FALSE); + } else { + STREAM_TO_UINT16 (mtu, p); + /* mtu must be greater than default MTU which is 23/48 */ + if (mtu < GATT_DEF_BLE_MTU_SIZE) { + p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE; + } else if (mtu > gatt_default.local_mtu) { + p_tcb->payload_size = gatt_default.local_mtu; + } else { + p_tcb->payload_size = mtu; + } + + /* host will set packet data length to 251 automatically if remote device support set packet data length, + so l2cble_set_fixed_channel_tx_data_length() is not necessary. + l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); + */ + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_MTU, (tGATT_SR_MSG *) &p_tcb->payload_size)) != NULL) { + attp_send_sr_msg (p_tcb, p_buf); + + /* Notify all registered application with new MTU size. Us a transaction ID */ + /* of 0, as no response is allowed from applcations */ + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (gatt_cb.cl_rcb[i].in_use ) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, gatt_cb.cl_rcb[i].gatt_if); + gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, + (tGATTS_DATA *)&p_tcb->payload_size); + } + } + + } + } +} + +/******************************************************************************* +** +** Function gatts_process_read_by_type_req +** +** Description process Read By type request. +** This PDU can be used to perform: +** - read characteristic value +** - read characteristic descriptor value +** - discover characteristic +** - discover characteristic by UUID +** - relationship discovery +** +** Returns void +** +*******************************************************************************/ +void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tBT_UUID uuid; + tGATT_SR_REG *p_rcb; + UINT16 msg_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET), + buf_len, + s_hdl, e_hdl, err_hdl = 0; + BT_HDR *p_msg = NULL; + tGATT_STATUS reason, ret; + UINT8 *p; + UINT8 sec_flag, key_size; + tGATT_SRV_LIST_INFO *p_list = &gatt_cb.srv_list_info; + tGATT_SRV_LIST_ELEM *p_srv = NULL; + + reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + GATT_TRACE_DEBUG("%s, op_code =%x, len = %x\n", __func__, op_code, len); +#if GATT_CONFORMANCE_TESTING == TRUE + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rsp for ReadByType: error status=%d\n", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, s_hdl, FALSE); + + return; + } +#endif + + if (reason == GATT_SUCCESS) { + if ((p_msg = (BT_HDR *)osi_calloc(msg_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); + + reason = GATT_NO_RESOURCES; + } else { + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + + *p ++ = op_code + 1; + /* reserve length byte */ + p_msg->len = 2; + buf_len = p_tcb->payload_size - 2; + + reason = GATT_NOT_FOUND; + + p_srv = p_list->p_first; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + if (p_rcb->in_use && + !(p_rcb->s_hdl > e_hdl || + p_rcb->e_hdl < s_hdl)) { + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + ret = gatts_db_read_attr_value_by_type(p_tcb, + p_rcb->p_db, + op_code, + p_msg, + s_hdl, + e_hdl, + uuid, + &buf_len, + sec_flag, + key_size, + 0, + &err_hdl); + if (ret != GATT_NOT_FOUND) { + reason = ret; + + if (ret == GATT_NO_RESOURCES) { + reason = GATT_SUCCESS; + } + } + if (ret != GATT_SUCCESS && ret != GATT_NOT_FOUND) { + s_hdl = err_hdl; + break; + } + } + p_srv = p_srv->p_next; + } + *p = (UINT8)p_msg->offset; + p_msg->offset = L2CAP_MIN_OFFSET; + } + } + if (reason != GATT_SUCCESS && reason != GATT_STACK_RSP) { + if (p_msg) { + osi_free(p_msg); + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_PENDING && reason != GATT_BUSY) { + gatt_send_error_rsp (p_tcb, reason, op_code, s_hdl, FALSE); + } + } else { + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } + +} + +/******************************************************************************* +** +** Function gatts_process_write_req +** +** Description This function is called to process the write request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATTS_DATA sr_data; + UINT32 trans_id; + tGATT_STATUS status; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + sr_data.write_req.need_rsp = FALSE; + + switch (op_code) { + case GATT_SIGN_CMD_WRITE: + if (op_code == GATT_SIGN_CMD_WRITE) { + GATT_TRACE_DEBUG("Write CMD with data signing" ); + len -= GATT_AUTH_SIGN_LEN; + } + /* fall through */ + case GATT_CMD_WRITE: + case GATT_REQ_WRITE: + sr_data.write_req.handle = handle; + sr_data.write_req.len = len; + if (len != 0 && p != NULL) { + memcpy (sr_data.write_req.value, p, len); + } + break; + } + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS) { + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, + handle, offset, p, len); + if((op_code == GATT_REQ_WRITE) && (status == GATT_APP_RSP)){ + sr_data.write_req.need_rsp = TRUE; + status = GATT_PENDING; + } + + gatt_sr_send_req_callback(conn_id, + trans_id, + GATTS_REQ_TYPE_WRITE, + &sr_data); + } else { + GATT_TRACE_ERROR("Error in %s, line=%d, max pending command, send error\n", __func__, __LINE__); + status = GATT_BUSY; /* max pending command, application error */ + } + } + + /* response should be sent only for write_request */ + if ((op_code == GATT_REQ_WRITE) && (sr_data.write_req.need_rsp == FALSE)){ + if (status == GATT_SUCCESS){ + tGATT_WRITE_REQ_RSP gatt_write_req_rsp; + gatt_write_req_rsp.op_code = GATT_RSP_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_write_req_rsp), sizeof(gatt_write_req_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + } else if (status != GATT_PENDING){ + /* note: in case of GATT_BUSY, will respond this application error to remote device */ + gatt_send_error_rsp (p_tcb, status, op_code, handle, TRUE); + } + } + + return; +} + + +/******************************************************************************* + ** + ** Function gatts_attr_process_preapre_write + ** + ** Description This function is called to process the prepare write request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATT_STATUS status; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + tGATT_ATTR16 *p_attr; + tGATT_ATTR16 *p_attr_temp; + tGATTS_DATA sr_data; + UINT32 trans_id = 0; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + tGATT_SVC_DB *p_db; + BOOLEAN is_need_prepare_write_rsp = FALSE; + BOOLEAN is_need_queue_data = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + if (len < 2) { + GATT_TRACE_ERROR("%s: Prepare write request was invalid - missing offset, sending error response", __func__); + gatt_send_error_rsp(p_tcb, GATT_INVALID_PDU, op_code, handle, FALSE); + return; + } + //get offset from p_data + STREAM_TO_UINT16(offset, p); + len -= 2; + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + //prepare_record = &(prepare_write_record); + prepare_record = &(p_tcb->prepare_write_record); + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + status = gatts_write_attr_perm_check (gatt_cb.sr_reg[i_rcb].p_db, + op_code, + handle, + sr_data.write_req.offset, + p, + len, + sec_flag, + key_size); + + if (status == GATT_SUCCESS){ + if ((trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle)) != 0) { + p_db = gatt_cb.sr_reg[i_rcb].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr && handle >= p_attr->handle) { + if (p_attr->handle == handle ) { + p_attr_temp = p_attr; + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + status = GATT_APP_RSP; + } else if (p_attr->p_value != NULL && + offset > p_attr->p_value->attr_val.attr_max_len) { + status = GATT_INVALID_OFFSET; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value != NULL && + ((offset + len) > p_attr->p_value->attr_val.attr_max_len)){ + status = GATT_INVALID_ATTR_LEN; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value == NULL) { + GATT_TRACE_ERROR("Error in %s, attribute of handle 0x%x not allocate value buffer\n", + __func__, handle); + status = GATT_UNKNOWN_ERROR; + } else { + //valid prepare write request, need to send response and queue the data + //status: GATT_SUCCESS + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } else{ + status = GATT_UNKNOWN_ERROR; + GATT_TRACE_ERROR("Error in %s, Line %d: GATT BUSY\n", __func__, __LINE__); + } + } + + if (is_need_queue_data){ + queue_data = (tGATT_PREPARE_WRITE_QUEUE_DATA *)osi_malloc(len + sizeof(tGATT_PREPARE_WRITE_QUEUE_DATA)); + if (queue_data == NULL){ + status = GATT_PREPARE_Q_FULL; + } else { + queue_data->p_attr = p_attr_temp; + queue_data->len = len; + queue_data->handle = handle; + queue_data->offset = offset; + memcpy(queue_data->value, p, len); + if (prepare_record->queue == NULL) { + prepare_record->queue = fixed_queue_new(QUEUE_SIZE_MAX); + } + fixed_queue_enqueue(prepare_record->queue, queue_data, FIXED_QUEUE_MAX_TIMEOUT); + } + } + + if (is_need_prepare_write_rsp){ + //send prepare write response + if (queue_data != NULL){ + queue_data->op_code = op_code + 1; + //5: op_code 1 + handle 2 + offset 2 + tGATT_STATUS rsp_send_status = gatt_send_packet(p_tcb, &(queue_data->op_code), queue_data->len + 5); + gatt_sr_update_prep_cnt(p_tcb, p_sreg->gatt_if, TRUE, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + + if (rsp_send_status != GATT_SUCCESS){ + GATT_TRACE_ERROR("Error in %s, line=%d, fail to send prepare_write_rsp, status=0x%x\n", + __func__, __LINE__, rsp_send_status); + } + } else{ + GATT_TRACE_ERROR("Error in %s, line=%d, queue_data should not be NULL here, fail to send prepare_write_rsp\n", + __func__, __LINE__); + } + } + + if ((status == GATT_APP_RSP) || (is_need_prepare_write_rsp)){ + prepare_record->total_num++; + memset(&sr_data, 0, sizeof(sr_data)); + sr_data.write_req.is_prep = TRUE; + sr_data.write_req.handle = handle; + sr_data.write_req.offset = offset; + sr_data.write_req.len = len; + sr_data.write_req.need_rsp = (status == GATT_APP_RSP) ? TRUE : FALSE; + memcpy(sr_data.write_req.value, p, len); + gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); + } else{ + gatt_send_error_rsp(p_tcb, status, GATT_REQ_PREPARE_WRITE, handle, TRUE); + } + + if ((prepare_record->error_code_app == GATT_SUCCESS) + // update prepare write status for excute write request + && (status == GATT_INVALID_OFFSET || status == GATT_INVALID_ATTR_LEN || status == GATT_REQ_NOT_SUPPORTED)) { + prepare_record->error_code_app = status; + } + +} + +/******************************************************************************* + ** + ** Function gatts_process_read_req + ** + ** Description This function is called to process the read request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +static void gatts_process_read_req(tGATT_TCB *p_tcb, tGATT_SR_REG *p_rcb, UINT8 op_code, + UINT16 handle, UINT16 len, UINT8 *p_data) +{ + UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + tGATT_STATUS reason; + BT_HDR *p_msg = NULL; + UINT8 sec_flag, key_size, *p; + UINT16 offset = 0, value_len = 0; + + UNUSED (len); + if ((p_msg = (BT_HDR *)osi_calloc(buf_len)) == NULL) { + GATT_TRACE_ERROR("gatts_process_find_info failed. no resources.\n"); + + reason = GATT_NO_RESOURCES; + } else { + if (op_code == GATT_REQ_READ_BLOB) { + STREAM_TO_UINT16(offset, p_data); + } + + p = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + *p ++ = op_code + 1; + p_msg->len = 1; + buf_len = p_tcb->payload_size - 1; + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &sec_flag, + &key_size); + + reason = gatts_read_attr_value_by_handle(p_tcb, + p_rcb->p_db, + op_code, + handle, + offset, + p, + &value_len, + buf_len, + sec_flag, + key_size, + 0); + + p_msg->len += value_len; + } + + + if (reason != GATT_SUCCESS && reason != GATT_PENDING && reason != GATT_STACK_RSP) { + if (p_msg) { + osi_free(p_msg); + } + + /* in theroy BUSY is not possible(should already been checked), protected check */ + if (reason != GATT_BUSY) { + gatt_send_error_rsp (p_tcb, reason, op_code, handle, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + } + } else if (reason == GATT_SUCCESS || reason == GATT_STACK_RSP) { + attp_send_sr_msg(p_tcb, p_msg); + gatt_dequeue_sr_cmd(p_tcb); + } else { + if (p_msg) { + osi_free(p_msg); + } + } + +} + +/******************************************************************************* +** +** Function gatts_process_attribute_req +** +** Description This function is called to process the per attribute handle request +** from client. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + UINT16 handle = 0; + UINT8 *p = p_data, i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + tGATT_STATUS status = GATT_INVALID_HANDLE; + tGATT_ATTR16 *p_attr; + + if (len < 2) { + GATT_TRACE_ERROR("Illegal PDU length, discard request\n"); + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(handle, p); + len -= 2; + } + +#if GATT_CONFORMANCE_TESTING == TRUE + gatt_cb.handle = handle; + if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code) { + GATT_TRACE_DEBUG("Conformance tst: forced err rsp: error status=%d\n", gatt_cb.err_status); + + gatt_send_error_rsp (p_tcb, gatt_cb.err_status, gatt_cb.req_op_code, handle, FALSE); + + return; + } +#endif + + if (GATT_HANDLE_IS_VALID(handle)) { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) { + p_attr = (tGATT_ATTR16 *)p_rcb->p_db->p_attr_list; + + while (p_attr) { + if (p_attr->handle == handle) { + switch (op_code) { + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + gatts_process_read_req(p_tcb, p_rcb, op_code, handle, len, p); + break; + + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + gatts_process_write_req(p_tcb, i, handle, op_code, len, p); + break; + + case GATT_REQ_PREPARE_WRITE: + gatt_attr_process_prepare_write (p_tcb, i, handle, op_code, len, p); + default: + break; + } + status = GATT_SUCCESS; + break; + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + break; + } + } + } + + if (status != GATT_SUCCESS && op_code != GATT_CMD_WRITE && op_code != GATT_SIGN_CMD_WRITE) { + gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); + } +} + +/******************************************************************************* +** +** Function gatts_proc_srv_chg_ind_ack +** +** Description This function process the service changed indicaiton ACK +** +** Returns void +** +*******************************************************************************/ +static void gatts_proc_srv_chg_ind_ack(tGATT_TCB *p_tcb ) +{ + tGATTS_SRV_CHG_REQ req; + tGATTS_SRV_CHG *p_buf = NULL; + + GATT_TRACE_DEBUG("gatts_proc_srv_chg_ind_ack"); + + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL) { + GATT_TRACE_DEBUG("NV update set srv chg = FALSE"); + p_buf->srv_changed = FALSE; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT, &req, NULL); + } + } +} + +/******************************************************************************* +** +** Function gatts_chk_pending_ind +** +** Description This function check any pending indication needs to be sent if +** there is a pending indication then sent the indication +** +** Returns void +** +*******************************************************************************/ +static void gatts_chk_pending_ind(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + tGATT_VALUE *p_buf = (tGATT_VALUE *)fixed_queue_try_peek_first(p_tcb->pending_ind_q); + GATT_TRACE_DEBUG("gatts_chk_pending_ind"); + + if (p_buf ) { + GATTS_HandleValueIndication (p_buf->conn_id, + p_buf->handle, + p_buf->len, + p_buf->value); + osi_free(fixed_queue_try_remove_from_queue(p_tcb->pending_ind_q, + p_buf)); + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatts_proc_ind_ack +** +** Description This function process the Indication ack +** +** Returns TRUE continue to process the indication ack by the aaplication +** if the ACk is not a Service Changed Indication Ack +** +*******************************************************************************/ +static BOOLEAN gatts_proc_ind_ack(tGATT_TCB *p_tcb, UINT16 ack_handle) +{ + BOOLEAN continue_processing = TRUE; + + GATT_TRACE_DEBUG ("gatts_proc_ind_ack ack handle=%d", ack_handle); + + if (ack_handle == gatt_cb.handle_of_h_r) { + gatts_proc_srv_chg_ind_ack(p_tcb); + /* there is no need to inform the application since srv chg is handled internally by GATT */ + continue_processing = FALSE; +#if GATTS_ROBUST_CACHING_ENABLED + /* after receiving ack of svc_chg_ind, reset client status */ + gatt_sr_update_cl_status(p_tcb, true); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + } + + gatts_chk_pending_ind(p_tcb); + return continue_processing; +} + +/******************************************************************************* +** +** Function gatts_process_value_conf +** +** Description This function is called to process the handle value confirmation. +** +** Returns void +** +*******************************************************************************/ +void gatts_process_value_conf(tGATT_TCB *p_tcb, UINT8 op_code) +{ + UINT16 handle = p_tcb->indicate_handle; + UINT32 trans_id; + UINT8 i; + tGATT_SR_REG *p_rcb = gatt_cb.sr_reg; + BOOLEAN continue_processing; + UINT16 conn_id; + + btu_stop_timer (&p_tcb->conf_timer_ent); + if (GATT_HANDLE_IS_VALID(handle)) { + p_tcb->indicate_handle = 0; + continue_processing = gatts_proc_ind_ack(p_tcb, handle); + + if (continue_processing) { + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_rcb ++) { + if (p_rcb->in_use && p_rcb->s_hdl <= handle && p_rcb->e_hdl >= handle) { + trans_id = gatt_sr_enqueue_cmd(p_tcb, op_code, handle); + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_rcb->gatt_if); + tGATTS_DATA p_data = {0}; + p_data.handle = handle; + gatt_sr_send_req_callback(conn_id, + trans_id, GATTS_REQ_TYPE_CONF, &p_data); + } + } + } + } else { + GATT_TRACE_ERROR("unexpected handle value confirmation"); + } +} + +#if GATTS_ROBUST_CACHING_ENABLED +static BOOLEAN gatts_handle_db_out_of_sync(tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + if (gatt_sr_is_cl_change_aware(p_tcb)) { + return false; + } + + bool should_ignore = true; + bool should_rsp = true; + + switch (op_code) { + case GATT_REQ_READ_BY_TYPE: + { + tBT_UUID uuid; + UINT16 s_hdl = 0; + UINT16 e_hdl = 0; + UINT16 db_hash_handle = gatt_cb.handle_of_database_hash; + tGATT_STATUS reason = gatts_validate_packet_format(op_code, &len, &p_data, &uuid, &s_hdl, &e_hdl); + if (reason == GATT_SUCCESS && + (s_hdl <= db_hash_handle && db_hash_handle <= e_hdl) && + (uuid.uu.uuid16 == GATT_UUID_GATT_DATABASE_HASH)) { + should_ignore = false; + } + break; + } + case GATT_REQ_READ: + // for pts don't process read request + #if 0 + { + UINT16 handle = 0; + UINT8 *p = p_data; + tGATT_STATUS status = GATT_SUCCESS; + + if (len < 2) { + status = GATT_INVALID_PDU; + } else { + STREAM_TO_UINT16(handle, p); + len -= 2; + } + + if (status == GATT_SUCCESS && handle == gatt_cb.handle_of_database_hash) { + should_ignore = false; + } + break; + } + #endif + case GATT_REQ_READ_BY_GRP_TYPE: + case GATT_REQ_FIND_TYPE_VALUE: + case GATT_REQ_FIND_INFO: + case GATT_REQ_READ_BLOB: + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + case GATT_REQ_WRITE: + case GATT_REQ_PREPARE_WRITE: + break; + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + should_rsp = false; + break; + case GATT_REQ_MTU: + case GATT_REQ_EXEC_WRITE: + case GATT_HANDLE_VALUE_CONF: + default: + should_ignore = false; + break; + } + + if (should_ignore) { + if (should_rsp) { + gatt_send_error_rsp(p_tcb, GATT_DATABASE_OUT_OF_SYNC, op_code, 0x0000, false); + } + + GATT_TRACE_ERROR("database out of sync op_code %x, should_rsp %d", op_code, should_rsp); + gatt_sr_update_cl_status(p_tcb, should_rsp); + } + + return should_ignore; +} + +#endif /* GATTS_ROBUST_CACHING_ENABLED */ +/******************************************************************************* +** +** Function gatt_server_handle_client_req +** +** Description This function is called to handle the client requests to +** server. +** +** +** Returns void +** +*******************************************************************************/ +void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data) +{ + /* there is pending command, discard this one */ + if (!gatt_sr_cmd_empty(p_tcb) && op_code != GATT_HANDLE_VALUE_CONF) { + return; + } + + /* the size of the message may not be bigger than the local max PDU size*/ + /* The message has to be smaller than the agreed MTU, len does not include op code */ + if (len >= p_tcb->payload_size) { + GATT_TRACE_ERROR("server receive invalid PDU size:%d pdu size:%d", len + 1, p_tcb->payload_size ); + /* for invalid request expecting response, send it now */ + if (op_code != GATT_CMD_WRITE && + op_code != GATT_SIGN_CMD_WRITE && + op_code != GATT_HANDLE_VALUE_CONF) { + gatt_send_error_rsp (p_tcb, GATT_INVALID_PDU, op_code, 0, FALSE); + } + /* otherwise, ignore the pkt */ + } else { +#if GATTS_ROBUST_CACHING_ENABLED + // handle database out of sync + if (gatts_handle_db_out_of_sync(p_tcb, op_code, len, p_data)) { + return; + } +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + switch (op_code) { + case GATT_REQ_READ_BY_GRP_TYPE: /* discover primary services */ + case GATT_REQ_FIND_TYPE_VALUE: /* discover service by UUID */ + gatts_process_primary_service_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_FIND_INFO: /* discover char descrptor */ + gatts_process_find_info(p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_BY_TYPE: /* read characteristic value, char descriptor value */ + /* discover characteristic, discover char by UUID */ + gatts_process_read_by_type_req(p_tcb, op_code, len, p_data); + break; + + + case GATT_REQ_READ: /* read char/char descriptor value */ + case GATT_REQ_READ_BLOB: + case GATT_REQ_WRITE: /* write char/char descriptor value */ + case GATT_CMD_WRITE: + case GATT_SIGN_CMD_WRITE: + case GATT_REQ_PREPARE_WRITE: + gatts_process_attribute_req (p_tcb, op_code, len, p_data); + break; + + case GATT_HANDLE_VALUE_CONF: + gatts_process_value_conf (p_tcb, op_code); + break; + + case GATT_REQ_MTU: + gatts_process_mtu_req (p_tcb, len, p_data); + break; + + case GATT_REQ_EXEC_WRITE: + gatt_process_exec_write_req (p_tcb, op_code, len, p_data); + break; + + case GATT_REQ_READ_MULTI: + case GATT_REQ_READ_MULTI_VAR: + gatt_process_read_multi_req (p_tcb, op_code, len, p_data); + break; + + default: + break; + } + } +} + +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c b/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c new file mode 100644 index 00000000..79d09d7a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c @@ -0,0 +1,256 @@ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#include <string.h> +#include "gatt_int.h" +#include "stack/l2c_api.h" +#include "l2c_int.h" +#include "smp_int.h" + +#if (BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + +const char *const gatt_attr_name[] = { + "primary service", + "secondary service", + "included service", + "characteristic", +}; + +const char *const gatt_char_desc_name[] = { + "characteristic extended properties", + "characteristic user description", + "client characteristic configuration", + "server characteristic configuration", + "characteristic presentation format", + "characteristic aggregate format", +}; + +static const char *gatt_get_attr_name(UINT16 uuid) +{ + if (uuid >= GATT_UUID_PRI_SERVICE && uuid <= GATT_UUID_CHAR_DECLARE) { + return gatt_attr_name[uuid - GATT_UUID_PRI_SERVICE]; + } + + if (uuid >= GATT_UUID_CHAR_EXT_PROP && uuid <= GATT_UUID_CHAR_AGG_FORMAT) { + return gatt_char_desc_name[uuid - GATT_UUID_CHAR_EXT_PROP]; + } + + return "Unknown Attribute"; +} + +static void attr_uuid_to_bt_uuid(void *p_attr, tBT_UUID *p_uuid) +{ + tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr; + + if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16) { + p_uuid->len = LEN_UUID_16; + p_uuid->uu.uuid16 = p_attr16->uuid; + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_32) { + tGATT_ATTR32 *p_attr32 = (tGATT_ATTR32 *)p_attr; + p_uuid->len = LEN_UUID_32; + p_uuid->uu.uuid32 = p_attr32->uuid; + } else if (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_128) { + tGATT_ATTR128 *p_attr128 = (tGATT_ATTR128 *)p_attr; + p_uuid->len = LEN_UUID_128; + memcpy(p_uuid->uu.uuid128, p_attr128->uuid, LEN_UUID_128); + } +} + +static size_t calculate_database_info_size(void) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + size_t len = 0; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + if (p_attr->uuid == GATT_UUID_PRI_SERVICE || + p_attr->uuid == GATT_UUID_SEC_SERVICE) { + // Service declaration + len += 4 + p_attr->p_value->uuid.len; + } else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) { + // Included service declaration + len += 8 + p_attr->p_value->incl_handle.service_type.len; + } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { + tBT_UUID char_uuid = {0}; + // Characteristic declaration + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); + // Increment 1 to fetch characteristic uuid from value declaration attribute + len += 7 + char_uuid.len; + } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || + p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT || + p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) { + // Descriptor + len += 4; + } else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) { + // Descriptor + len += 6; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } + + return len; +} + +static void fill_database_info(UINT8 *p_data) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + if (p_attr->uuid == GATT_UUID_PRI_SERVICE || + p_attr->uuid == GATT_UUID_SEC_SERVICE) { + // Service declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + gatt_build_uuid_to_stream(&p_data, p_attr->p_value->uuid); + } else if (p_attr->uuid == GATT_UUID_INCLUDE_SERVICE) { + // Included service declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, GATT_UUID_INCLUDE_SERVICE); + UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.s_handle); + UINT16_TO_STREAM(p_data, p_attr->p_value->incl_handle.e_handle); + gatt_build_uuid_to_stream(&p_data, p_attr->p_value->incl_handle.service_type); + } else if (p_attr->uuid == GATT_UUID_CHAR_DECLARE) { + tBT_UUID char_uuid = {0}; + // Characteristic declaration + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, GATT_UUID_CHAR_DECLARE); + UINT8_TO_STREAM(p_data, p_attr->p_value->char_decl.property); + UINT16_TO_STREAM(p_data, p_attr->p_value->char_decl.char_val_handle); + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_attr, &char_uuid); + // Increment 1 to fetch characteristic uuid from value declaration attribute + gatt_build_uuid_to_stream(&p_data, char_uuid); + } else if (p_attr->uuid == GATT_UUID_CHAR_DESCRIPTION || + p_attr->uuid == GATT_UUID_CHAR_CLIENT_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_SRVR_CONFIG || + p_attr->uuid == GATT_UUID_CHAR_PRESENT_FORMAT || + p_attr->uuid == GATT_UUID_CHAR_AGG_FORMAT) { + // Descriptor + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + } else if (p_attr->uuid == GATT_UUID_CHAR_EXT_PROP) { + // Descriptor + UINT16_TO_STREAM(p_data, p_attr->handle); + UINT16_TO_STREAM(p_data, p_attr->uuid); + // TODO: process extended properties descriptor + if (p_attr->p_value->attr_val.attr_len == 2) { + memcpy(p_data, p_attr->p_value->attr_val.attr_val, 2); + } else { + UINT16_TO_STREAM(p_data, 0x0000); + } + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } +} + +tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash) +{ + UINT8 tmp; + UINT16 i; + UINT16 j; + size_t len; + UINT8 *data_buf = NULL; + + len = calculate_database_info_size(); + + data_buf = (UINT8 *)osi_malloc(len); + if (data_buf == NULL) { + GATT_TRACE_ERROR ("%s failed to allocate buffer (%u)\n", __func__, len); + return GATT_NO_RESOURCES; + } + + fill_database_info(data_buf); + + // reverse database info + for (i = 0, j = len-1; i < j; i++, j--) { + tmp = data_buf[i]; + data_buf[i] = data_buf[j]; + data_buf[j] = tmp; + } + +#if SMP_INCLUDED == TRUE + BT_OCTET16 key = {0}; + aes_cipher_msg_auth_code(key, data_buf, len, 16, hash); + //ESP_LOG_BUFFER_HEX("db hash", hash, BT_OCTET16_LEN); +#endif + + osi_free(data_buf); + return GATT_SUCCESS; +} + +void gatts_show_local_database(void) +{ + UINT8 i; + tGATT_SVC_DB *p_db; + tGATT_ATTR16 *p_attr; + + printf("\n================= GATTS DATABASE DUMP START =================\n"); + for (i = 0; i < GATT_MAX_SR_PROFILES; i++) { + p_db = gatt_cb.sr_reg[i].p_db; + if (p_db && p_db->p_attr_list) { + p_attr = (tGATT_ATTR16 *)p_db->p_attr_list; + while (p_attr) { + switch (p_attr->uuid) { + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + // Service declaration + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\n", gatt_uuid_to_str(&p_attr->p_value->uuid)); + printf("\thandle %d\n", p_attr->handle); + printf("\tend_handle %d\n",p_db->end_handle-1); + break; + case GATT_UUID_INCLUDE_SERVICE: + // Included service declaration + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\t", gatt_uuid_to_str(&p_attr->p_value->incl_handle.service_type)); + printf("\thandle %d\n", p_attr->p_value->incl_handle.s_handle); + printf("\tend_handle %d\n", p_attr->p_value->incl_handle.e_handle); + break; + case GATT_UUID_CHAR_DECLARE: { + tBT_UUID char_uuid = {0}; + tGATT_ATTR16 *p_char_val; + p_char_val = (tGATT_ATTR16 *)p_attr->p_next; + attr_uuid_to_bt_uuid((void *)p_char_val, &char_uuid); + + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\tuuid %s\n", gatt_uuid_to_str(&char_uuid)); + printf("\tdef_handle %d\n", p_attr->handle); + printf("\tval_handle %d\n", p_attr->p_value->char_decl.char_val_handle); + printf("\tperm 0x%04x, prop 0x%02x\n", p_char_val->permission, p_attr->p_value->char_decl.property); + break; + } + case GATT_UUID_CHAR_EXT_PROP: + case GATT_UUID_CHAR_DESCRIPTION: + case GATT_UUID_CHAR_CLIENT_CONFIG: + case GATT_UUID_CHAR_SRVR_CONFIG: + case GATT_UUID_CHAR_PRESENT_FORMAT: + case GATT_UUID_CHAR_AGG_FORMAT: + printf("%s\n", gatt_get_attr_name(p_attr->uuid)); + printf("\thandle %d\n", p_attr->handle); + break; + } + p_attr = (tGATT_ATTR16 *) p_attr->p_next; + } + } + } + printf("================= GATTS DATABASE DUMP END =================\n"); +} +#endif /* BLE_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ diff --git a/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c b/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c new file mode 100644 index 00000000..621b2468 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/gatt_utils.c @@ -0,0 +1,2948 @@ +/****************************************************************************** + * + * Copyright (C) 2009-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains GATT utility functions + * + ******************************************************************************/ +#include "common/bt_target.h" +#include "osi/allocator.h" + +#if BLE_INCLUDED == TRUE +#include <string.h> +#include <stdio.h> + +#include "stack/l2cdefs.h" +#include "gatt_int.h" +#include "stack/gatt_api.h" +#include "stack/gattdefs.h" +#include "stack/sdp_api.h" +#include "btm_int.h" +/* check if [x, y] and [a, b] have overlapping range */ +#define GATT_VALIDATE_HANDLE_RANGE(x, y, a, b) (y >= a && x <= b) + +#define GATT_GET_NEXT_VALID_HANDLE(x) (((x)/10 + 1) * 10) + +const char *const op_code_name[] = { + "UNKNOWN", + "ATT_RSP_ERROR", + "ATT_REQ_MTU", + "ATT_RSP_MTU", + "ATT_REQ_READ_INFO", + "ATT_RSP_READ_INFO", + "ATT_REQ_FIND_TYPE_VALUE", + "ATT_RSP_FIND_TYPE_VALUE", + "ATT_REQ_READ_BY_TYPE", + "ATT_RSP_READ_BY_TYPE", + "ATT_REQ_READ", + "ATT_RSP_READ", + "ATT_REQ_READ_BLOB", + "ATT_RSP_READ_BLOB", + "GATT_REQ_READ_MULTI", + "GATT_RSP_READ_MULTI", + "GATT_REQ_READ_BY_GRP_TYPE", + "GATT_RSP_READ_BY_GRP_TYPE", + "ATT_REQ_WRITE", + "ATT_RSP_WRITE", + "ATT_CMD_WRITE", + "ATT_SIGN_CMD_WRITE", + "ATT_REQ_PREPARE_WRITE", + "ATT_RSP_PREPARE_WRITE", + "ATT_REQ_EXEC_WRITE", + "ATT_RSP_EXEC_WRITE", + "Reserved", + "ATT_HANDLE_VALUE_NOTIF", + "Reserved", + "ATT_HANDLE_VALUE_IND", + "ATT_HANDLE_VALUE_CONF", + "ATT_OP_CODE_MAX" +}; + +static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + +static UINT32 gatt_tcb_id; + +/******************************************************************************* +** +** Function gatt_free_pending_ind +** +** Description Free all pending indications +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_ind(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_ind"); + if (p_tcb->pending_ind_q == NULL) { + return; + } + + /* release all queued indications */ + while (!fixed_queue_is_empty(p_tcb->pending_ind_q)) { + osi_free(fixed_queue_dequeue(p_tcb->pending_ind_q, 0)); + } + fixed_queue_free(p_tcb->pending_ind_q, NULL); + p_tcb->pending_ind_q = NULL; +} + +/******************************************************************************* +** +** Function gatt_free_pending_enc_queue +** +** Description Free all buffers in pending encyption queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_enc_queue"); + if (p_tcb->pending_enc_clcb == NULL) { + return; + } + + /* release all queued indications */ + while (!fixed_queue_is_empty(p_tcb->pending_enc_clcb)) { + osi_free(fixed_queue_dequeue(p_tcb->pending_enc_clcb, 0)); + } + fixed_queue_free(p_tcb->pending_enc_clcb, NULL); + p_tcb->pending_enc_clcb = NULL; +} + +/******************************************************************************* +** +** Function gatt_free_pending_prepare_write_queue +** +** Description Free all buffers in pending prepare write packets queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_prepare_write_queue"); + + if (p_tcb->prepare_write_record.queue) { + /* release all queued prepare write packets */ + while (!fixed_queue_is_empty(p_tcb->prepare_write_record.queue)) { + osi_free(fixed_queue_dequeue(p_tcb->prepare_write_record.queue, FIXED_QUEUE_MAX_TIMEOUT)); + } + fixed_queue_free(p_tcb->prepare_write_record.queue, NULL); + p_tcb->prepare_write_record.queue = NULL; + } + + p_tcb->prepare_write_record.total_num = 0; + p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS; +} + +/******************************************************************************* +** +** Function gatt_delete_dev_from_srv_chg_clt_list +** +** Description Delete a device from the service changed client lit +** +** Returns None +** +*******************************************************************************/ +void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr) +{ + tGATTS_SRV_CHG *p_buf; + tGATTS_SRV_CHG_REQ req; + + GATT_TRACE_DEBUG ("gatt_delete_dev_from_srv_chg_clt_list"); + if ((p_buf = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL) { + if (gatt_cb.cb_info.p_srv_chg_callback) { + /* delete from NV */ + memcpy(req.srv_chg.bda, bd_addr, BD_ADDR_LEN); + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_REMOVE_CLIENT, &req, NULL); + } + osi_free(fixed_queue_try_remove_from_queue(gatt_cb.srv_chg_clt_q, + p_buf)); + } + +} + +/******************************************************************************* +** +** Function gatt_set_srv_chg +** +** Description Set the service changed flag to TRUE +** +** Returns None +** +*******************************************************************************/ +void gatt_set_srv_chg(void) +{ + GATT_TRACE_DEBUG ("gatt_set_srv_chg"); + + if (fixed_queue_is_empty(gatt_cb.srv_chg_clt_q)) { + return; + } + + list_t *list = fixed_queue_get_list(gatt_cb.srv_chg_clt_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + GATT_TRACE_DEBUG ("found a srv_chg clt"); + + tGATTS_SRV_CHG *p_buf = (tGATTS_SRV_CHG *)list_node(node); + if (!p_buf->srv_changed) { + GATT_TRACE_DEBUG("set srv_changed to TRUE"); + p_buf->srv_changed = TRUE; + tGATTS_SRV_CHG_REQ req; + memcpy(&req.srv_chg, p_buf, sizeof(tGATTS_SRV_CHG)); + if (gatt_cb.cb_info.p_srv_chg_callback) { + (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_UPDATE_CLIENT,&req, NULL); + } + } + } +} + +/******************************************************************************* +** +** Function gatt_sr_is_new_srv_chg +** +** Description Find the app id in on the new service changed list +** +** Returns Pointer to the found new service changed item othwerwise NULL +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + tGATTS_PENDING_NEW_SRV_START *p_buf = NULL; + + if (fixed_queue_is_empty(gatt_cb.pending_new_srv_start_q)) { + return NULL; + } + + list_t *list = fixed_queue_get_list(gatt_cb.pending_new_srv_start_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + p_buf = (tGATTS_PENDING_NEW_SRV_START *)list_node(node); + tGATTS_HNDL_RANGE *p = p_buf->p_new_srv_start; + if (gatt_uuid_compare(*p_app_uuid128, p->app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p->svc_uuid) + && (svc_inst == p->svc_inst)) { + GATT_TRACE_DEBUG("gatt_sr_is_new_srv_chg: Yes"); + break; + } + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_ind +** +** Description Add a pending indication +** +** Returns Pointer to the current pending indication buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind) +{ + tGATT_VALUE *p_buf; + GATT_TRACE_DEBUG ("gatt_add_pending_ind"); + if ((p_buf = (tGATT_VALUE *)osi_malloc((UINT16)sizeof(tGATT_VALUE))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a pending indication"); + memcpy(p_buf, p_ind, sizeof(tGATT_VALUE)); + fixed_queue_enqueue(p_tcb->pending_ind_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_pending_new_srv_start +** +** Description Add a pending new srv start to the new service start queue +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start(tGATTS_HNDL_RANGE *p_new_srv_start) +{ + tGATTS_PENDING_NEW_SRV_START *p_buf; + + GATT_TRACE_DEBUG ("gatt_add_pending_new_srv_start"); + if ((p_buf = (tGATTS_PENDING_NEW_SRV_START *)osi_malloc((UINT16)sizeof(tGATTS_PENDING_NEW_SRV_START))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a new pending new srv start"); + p_buf->p_new_srv_start = p_new_srv_start; + fixed_queue_enqueue(gatt_cb.pending_new_srv_start_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_add_srv_chg_clt +** +** Description Add a service chnage client to the service change client queue +** +** Returns Pointer to the service change client buffer; Null no buffer available +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg) +{ + tGATTS_SRV_CHG *p_buf; + GATT_TRACE_DEBUG ("gatt_add_srv_chg_clt"); + if ((p_buf = (tGATTS_SRV_CHG *)osi_malloc((UINT16)sizeof(tGATTS_SRV_CHG))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a srv chg client"); + memcpy(p_buf, p_srv_chg, sizeof(tGATTS_SRV_CHG)); + fixed_queue_enqueue(gatt_cb.srv_chg_clt_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_alloc_hdl_buffer +** +** Description Allocate a handle buufer +** +** Returns Pointer to the allocated buffer, NULL no buffer available +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void) +{ + UINT8 i; + tGATT_CB *p_cb = &gatt_cb; + tGATT_HDL_LIST_ELEM *p_elem = &p_cb->hdl_list[0]; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_elem ++) { + if (!p_cb->hdl_list[i].in_use) { + memset(p_elem, 0, sizeof(tGATT_HDL_LIST_ELEM)); + p_elem->in_use = TRUE; + p_elem->svc_db.svc_buffer = fixed_queue_new(QUEUE_SIZE_MAX); + return p_elem; + } + } + + return NULL; +} + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_handle +** +** Description Find handle range buffer by service handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && p_list->asgn_range.s_handle == handle) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_attr_handle +** +** Description Find handle range buffer by attribute handle. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if (p_list->in_use && (p_list->asgn_range.s_handle <= attr_handle) + && (p_list->asgn_range.e_handle >= attr_handle)) { + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} + + +/******************************************************************************* +** +** Function gatt_find_hdl_buffer_by_app_id +** +** Description Find handle range buffer by app ID, service and service instance ID. +** +** Returns Pointer to the buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, + tBT_UUID *p_svc_uuid, + UINT16 svc_inst) +{ + tGATT_HDL_LIST_INFO *p_list_info = &gatt_cb.hdl_list_info; + tGATT_HDL_LIST_ELEM *p_list = NULL; + + p_list = p_list_info->p_first; + + while (p_list != NULL) { + if ( gatt_uuid_compare (*p_app_uuid128, p_list->asgn_range.app_uuid128) + && gatt_uuid_compare (*p_svc_uuid, p_list->asgn_range.svc_uuid) + && (svc_inst == p_list->asgn_range.svc_inst) ) { + GATT_TRACE_DEBUG ("Already allocated handles for this service before!!"); + return (p_list); + } + p_list = p_list->p_next; + } + return NULL; +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_free_attr_value_buffer +** +** Description free characteristic attribute value buffer in a service +** +** Returns None +** +*******************************************************************************/ +void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p) +{ + if (p){ + tGATT_SVC_DB *p_db = &(p->svc_db); + tGATT_ATTR16 *p_attr = p_db->p_attr_list; + tGATT_ATTR_VALUE *p_value = NULL; + + while(p_attr){ + if (p_attr->mask & GATT_ATTR_VALUE_ALLOCATED){ + p_value = p_attr->p_value; + if ((p_value != NULL) && (p_value->attr_val.attr_val != NULL)){ + osi_free(p_value->attr_val.attr_val); + } + } + p_attr = p_attr->p_next; + } + } +} +/******************************************************************************* +** +** Function gatt_free_hdl_buffer +** +** Description free a handle buffer +** +** Returns None +** +*******************************************************************************/ +void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p) +{ + + if (p) { + while (!fixed_queue_is_empty(p->svc_db.svc_buffer)) { + osi_free(fixed_queue_dequeue(p->svc_db.svc_buffer, 0)); + } + fixed_queue_free(p->svc_db.svc_buffer, NULL); + memset(p, 0, sizeof(tGATT_HDL_LIST_ELEM)); + } +} +/******************************************************************************* +** +** Function gatt_free_srvc_db_buffer_app_id +** +** Description free the service attribute database buffers by the owner of the +** service app ID. +** +** Returns None +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id) +{ + tGATT_HDL_LIST_ELEM *p_elem = &gatt_cb.hdl_list[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_SR_PROFILES; i ++, p_elem ++) { + if (memcmp(p_app_id, &p_elem->asgn_range.app_uuid128, sizeof(tBT_UUID)) == 0) { + gatt_free_attr_value_buffer(p_elem); + while (!fixed_queue_is_empty(p_elem->svc_db.svc_buffer)) { + osi_free(fixed_queue_dequeue(p_elem->svc_db.svc_buffer, 0)); + } + fixed_queue_free(p_elem->svc_db.svc_buffer, NULL); + p_elem->svc_db.svc_buffer = NULL; + + p_elem->svc_db.mem_free = 0; + p_elem->svc_db.p_attr_list = p_elem->svc_db.p_free_mem = NULL; + } + } +} +/******************************************************************************* +** +** Function gatt_is_last_attribute +** +** Description Check this is the last attribute of the specified value or not +** +** Returns TRUE - yes this is the last attribute +** +*******************************************************************************/ +BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value) +{ + tGATT_SRV_LIST_ELEM *p_srv = p_start->p_next; + BOOLEAN is_last_attribute = TRUE; + tGATT_SR_REG *p_rcb = NULL; + tBT_UUID *p_svc_uuid; + + p_list->p_last_primary = NULL; + + while (p_srv) { + p_rcb = GATT_GET_SR_REG_PTR(p_srv->i_sreg); + + p_svc_uuid = gatts_get_service_uuid (p_rcb->p_db); + + if (gatt_uuid_compare(value, *p_svc_uuid)) { + is_last_attribute = FALSE; + break; + + } + p_srv = p_srv->p_next; + } + + return is_last_attribute; + +} +/******************************************************************************* +** +** Function gatt_update_last_pri_srv_info +** +** Description Update the the last primary info for the service list info +** +** Returns None +** +*******************************************************************************/ +void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list) +{ + tGATT_SRV_LIST_ELEM *p_srv = p_list->p_first; + + p_list->p_last_primary = NULL; + + while (p_srv) { + if (p_srv->is_primary) { + p_list->p_last_primary = p_srv; + } + p_srv = p_srv->p_next; + } + +} +/******************************************************************************* +** +** Function gatts_update_srv_list_elem +** +** Description update an element in the service list. +** +** Returns None. +** +*******************************************************************************/ +void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary) +{ + UNUSED(handle); + + gatt_cb.srv_list[i_sreg].in_use = TRUE; + gatt_cb.srv_list[i_sreg].i_sreg = i_sreg; + gatt_cb.srv_list[i_sreg].s_hdl = gatt_cb.sr_reg[i_sreg].s_hdl; + gatt_cb.srv_list[i_sreg].is_primary = is_primary; + + return; +} +#endif ///GATTS_INCLUDED == TRUE + +/******************************************************************************* +** +** Function gatt_add_a_srv_to_list +** +** Description add an service to the list in ascending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new) +{ + tGATT_SRV_LIST_ELEM *p_old; + + if (!p_new) { + GATT_TRACE_DEBUG("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } else { + p_old = p_list->p_first; + while (1) { + if (p_old == NULL) { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + break; + } else { + if (p_new->s_hdl < p_old->s_hdl) { + /* if not the first in list */ + if (p_old->p_prev != NULL) { + p_old->p_prev->p_next = p_new; + } else { + p_list->p_first = p_new; + } + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_a_srv_from_list +** +** Description Remove a service from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) { + GATT_TRACE_DEBUG("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) { + p_remove->p_next->p_prev = NULL; + } + } else if (p_remove->p_next == NULL) { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } else { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + gatt_update_last_pri_srv_info(p_list); + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_add_an_item_to_list +** +** Description add an service handle range to the list in decending +** order of the start handle +** +** Returns BOOLEAN TRUE-if add is successful +** +*******************************************************************************/ +BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new) +{ + tGATT_HDL_LIST_ELEM *p_old; + if (!p_new) { + GATT_TRACE_DEBUG("p_new==NULL"); + return FALSE; + } + + if (!p_list->p_first) { + /* this is an empty list */ + p_list->p_first = + p_list->p_last = p_new; + p_new->p_next = + p_new->p_prev = NULL; + } else { + p_old = p_list->p_first; + while (1) { + if (p_old == NULL) { + p_list->p_last->p_next = p_new; + p_new->p_prev = p_list->p_last; + p_new->p_next = NULL; + p_list->p_last = p_new; + + break; + + } else { + if (p_new->asgn_range.s_handle > p_old->asgn_range.s_handle) { + if (p_old == p_list->p_first) { + p_list->p_first = p_new; + } + + p_new->p_prev = p_old->p_prev; + p_new->p_next = p_old; + + + p_old->p_prev = p_new; + break; + } + } + p_old = p_old->p_next; + } + } + p_list->count++; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_remove_an_item_from_list +** +** Description Remove an service handle range from the list +** +** Returns BOOLEAN TRUE-if remove is successful +** +*******************************************************************************/ +BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove) +{ + if (!p_remove || !p_list->p_first) { + GATT_TRACE_DEBUG("p_remove==NULL || p_list->p_first==NULL"); + return FALSE; + } + + if (p_remove->p_prev == NULL) { + p_list->p_first = p_remove->p_next; + if (p_remove->p_next) { + p_remove->p_next->p_prev = NULL; + } + } else if (p_remove->p_next == NULL) { + p_list->p_last = p_remove->p_prev; + p_remove->p_prev->p_next = NULL; + } else { + p_remove->p_next->p_prev = p_remove->p_prev; + p_remove->p_prev->p_next = p_remove->p_next; + } + p_list->count--; + return TRUE; + +} + +/******************************************************************************* +** +** Function gatt_find_the_connected_bda +** +** Description This function find the connected bda +** +** Returns TRUE if found +** +*******************************************************************************/ +BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx, + tBT_TRANSPORT *p_transport) +{ + BOOLEAN found = FALSE; + GATT_TRACE_DEBUG("gatt_find_the_connected_bda start_idx=%d", start_idx); + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + p_tcb = gatt_get_tcb_by_idx(start_idx); + if (p_tcb) { + for(p_node = list_get_node(gatt_cb.p_tcb_list, p_tcb); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && p_tcb->ch_state == GATT_CH_OPEN) { + memcpy( bda, p_tcb->peer_bda, BD_ADDR_LEN); + *p_found_idx = p_tcb->tcb_idx; + *p_transport = p_tcb->transport; + found = TRUE; + GATT_TRACE_DEBUG("gatt_find_the_connected_bda bda :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + break; + } + } + GATT_TRACE_DEBUG("gatt_find_the_connected_bda found=%d found_idx=%d", found, p_tcb->tcb_idx); + } + return found; +} + + +/******************************************************************************* +** +** Function gatt_is_srv_chg_ind_pending +** +** Description Check whether a service chnaged is in the indication pending queue +** or waiting for an Ack already +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb) +{ + BOOLEAN srv_chg_ind_pending = FALSE; + + GATT_TRACE_DEBUG("gatt_is_srv_chg_ind_pending is_queue_empty=%d", + fixed_queue_is_empty(p_tcb->pending_ind_q)); + + if (p_tcb->indicate_handle == gatt_cb.handle_of_h_r) { + srv_chg_ind_pending = TRUE; + } else if (! fixed_queue_is_empty(p_tcb->pending_ind_q)) { + list_t *list = fixed_queue_get_list(p_tcb->pending_ind_q); + for (const list_node_t *node = list_begin(list); + node != list_end(list); + node = list_next(node)) { + tGATT_VALUE *p_buf = (tGATT_VALUE *)list_node(node); + if (p_buf->handle == gatt_cb.handle_of_h_r) + { + srv_chg_ind_pending = TRUE; + break; + } + } + } + + GATT_TRACE_DEBUG("srv_chg_ind_pending = %d", srv_chg_ind_pending); + return srv_chg_ind_pending; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_in_the_srv_chg_clt_list +** +** Description This function check the specified bda is in the srv chg clinet list or not +** +** Returns pointer to the found elemenet otherwise NULL +** +*******************************************************************************/ +tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda) +{ + tGATTS_SRV_CHG *p_buf = NULL; + + GATT_TRACE_DEBUG("gatt_is_bda_in_the_srv_chg_clt_list :%02x-%02x-%02x-%02x-%02x-%02x", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + if (fixed_queue_is_empty(gatt_cb.srv_chg_clt_q)) { + return NULL; + } + + list_t *list = fixed_queue_get_list(gatt_cb.srv_chg_clt_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); + node = list_next(node)) { + p_buf = (tGATTS_SRV_CHG *)list_node(node); + if (!memcmp( bda, p_buf->bda, BD_ADDR_LEN)) { + GATT_TRACE_DEBUG("bda is in the srv chg clt list"); + break; + } + } + + return p_buf; +} + + +/******************************************************************************* +** +** Function gatt_is_bda_connected +** +** Description +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +BOOLEAN gatt_is_bda_connected(BD_ADDR bda) +{ + BOOLEAN connected = FALSE; + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && + !memcmp(p_tcb->peer_bda, bda, BD_ADDR_LEN)) { + connected = TRUE; + break; + } + } + return connected; +} + +/******************************************************************************* +** +** Function gatt_check_connection_state_by_tcb +** +** Description +** +** Returns TRUE if connected. Otherwise connection not established. +** +*******************************************************************************/ +BOOLEAN gatt_check_connection_state_by_tcb(tGATT_TCB *p_tcb) +{ + BOOLEAN connected = FALSE; + + if(p_tcb && gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { + connected = TRUE; + } + + return connected; +} + +/******************************************************************************* +** +** Function gatt_find_i_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i = 0; + list_node_t *p_node = NULL; + tGATT_TCB *p_tcb = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (!memcmp(p_tcb->peer_bda, bda, BD_ADDR_LEN) && + p_tcb->transport == transport) { + i = p_tcb->tcb_idx; + return i; + } + } + return GATT_INDEX_INVALID; +} + +/******************************************************************************* +** +** Function gatt_get_tcb_by_idx +** +** Description The function get TCB using the TCB index +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_get_tcb_by_idx(UINT8 tcb_idx) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if ( (tcb_idx < GATT_MAX_PHY_CHANNEL) && p_tcb->in_use && p_tcb->tcb_idx == tcb_idx ) { + break; + } else { + p_tcb = NULL; + } + } + + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_find_tcb_by_addr +** +** Description The function searches for an empty tcb entry, and return pointer. +** +** Returns NULL if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_find_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb = NULL; + UINT8 i = 0; + + if ((i = gatt_find_i_tcb_by_addr(bda, transport)) != GATT_INDEX_INVALID) { + p_tcb = gatt_get_tcb_by_idx(i); + } + + return p_tcb; +} +/******************************************************************************* +** +** Function gatt_find_i_tcb_free +** +** Description The function searches for an empty tcb entry, and return the index. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +UINT8 gatt_find_i_tcb_free(void) +{ + UINT8 i = 0, j = GATT_INDEX_INVALID; + + for (i = 0; i < GATT_MAX_PHY_CHANNEL; i ++) { + if (!((1 << i) & gatt_tcb_id)) { + j = i; + break; + } + } + return j; +} +/******************************************************************************* +** +** Function gatt_tcb_alloc +** +** Description The function allocates tcb for given tcb_idx and update tcb_id +** +** Returns Allocated tcb block +** +*******************************************************************************/ +tGATT_TCB *gatt_tcb_alloc(UINT8 tcb_idx) +{ + /* Allocate tcb block */ + tGATT_TCB *p_tcb = (tGATT_TCB *)osi_malloc(sizeof(tGATT_TCB)); + if (p_tcb && list_length(gatt_cb.p_tcb_list) < GATT_MAX_PHY_CHANNEL) { + memset(p_tcb, 0, sizeof(tGATT_TCB)); + /* Add tcb block to list in gatt_cb */ + list_append(gatt_cb.p_tcb_list, p_tcb); + /* Update tcb id */ + gatt_tcb_id |= 1 << tcb_idx; + } else if(p_tcb) { + osi_free(p_tcb); + p_tcb = NULL; + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_tcb_free +** +** Description The function free the given tcb block and update tcb id +** +** Returns void +** +*******************************************************************************/ +void gatt_tcb_free( tGATT_TCB *p_tcb) +{ + UINT8 tcb_idx = p_tcb->tcb_idx; + if (list_remove(gatt_cb.p_tcb_list, p_tcb)) { + gatt_tcb_id &= ~(1 << tcb_idx); + } +} +/******************************************************************************* +** +** Function gatt_allocate_tcb_by_bdaddr +** +** Description The function locate or allocate new tcb entry for matching bda. +** +** Returns GATT_INDEX_INVALID if not found. Otherwise index to the tcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport) +{ + UINT8 i = 0; + BOOLEAN allocated = FALSE; + tGATT_TCB *p_tcb = NULL; + + /* search for existing tcb with matching bda */ + i = gatt_find_i_tcb_by_addr(bda, transport); + /* find free tcb */ + if (i == GATT_INDEX_INVALID) { + i = gatt_find_i_tcb_free(); + allocated = TRUE; + } + if (i != GATT_INDEX_INVALID) { + p_tcb = gatt_tcb_alloc(i); + if (!p_tcb) { + return NULL; + } + if (allocated) { + memset(p_tcb, 0, sizeof(tGATT_TCB)); + p_tcb->pending_enc_clcb = fixed_queue_new(QUEUE_SIZE_MAX); + p_tcb->pending_ind_q = fixed_queue_new(QUEUE_SIZE_MAX); + p_tcb->in_use = TRUE; + p_tcb->tcb_idx = i; + p_tcb->transport = transport; + } + memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN); +#if GATTS_ROBUST_CACHING_ENABLED + gatt_sr_init_cl_status(p_tcb); +#endif /* GATTS_ROBUST_CACHING_ENABLED */ + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_convert_uuid16_to_uuid128 +** +** Description Convert a 16 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT16_TO_STREAM(p, uuid_16); +} + +/******************************************************************************* +** +** Function gatt_convert_uuid32_to_uuid128 +** +** Description Convert a 32 bits UUID to be an standard 128 bits one. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32) +{ + UINT8 *p = &uuid_128[LEN_UUID_128 - 4]; + + memcpy (uuid_128, base_uuid, LEN_UUID_128); + + UINT32_TO_STREAM(p, uuid_32); +} +/******************************************************************************* +** +** Function gatt_uuid_compare +** +** Description Compare two UUID to see if they are the same. +** +** Returns TRUE if two uuid match; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN gatt_uuid_compare (tBT_UUID src, tBT_UUID tar) +{ + UINT8 su[LEN_UUID_128], tu[LEN_UUID_128]; + UINT8 *ps, *pt; + + /* any of the UUID is unspecified */ + if (src.len == 0 || tar.len == 0) { + return TRUE; + } + + /* If both are 16-bit, we can do a simple compare */ + if (src.len == LEN_UUID_16 && tar.len == LEN_UUID_16) { + return src.uu.uuid16 == tar.uu.uuid16; + } + + /* If both are 32-bit, we can do a simple compare */ + if (src.len == LEN_UUID_32 && tar.len == LEN_UUID_32) { + return src.uu.uuid32 == tar.uu.uuid32; + } + + /* One or both of the UUIDs is 128-bit */ + if (src.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(su, src.uu.uuid16); + ps = su; + } else if (src.len == LEN_UUID_32) { + gatt_convert_uuid32_to_uuid128(su, src.uu.uuid32); + ps = su; + } else { + ps = src.uu.uuid128; + } + + if (tar.len == LEN_UUID_16) { + /* convert a 16 bits UUID to 128 bits value */ + gatt_convert_uuid16_to_uuid128(tu, tar.uu.uuid16); + pt = tu; + } else if (tar.len == LEN_UUID_32) { + /* convert a 32 bits UUID to 128 bits value */ + gatt_convert_uuid32_to_uuid128(tu, tar.uu.uuid32); + pt = tu; + } else { + pt = tar.uu.uuid128; + } + + return (memcmp(ps, pt, LEN_UUID_128) == 0); +} + +/******************************************************************************* +** +** Function gatt_build_uuid_to_stream +** +** Description Add UUID into stream. +** +** Returns UUID length. +** +*******************************************************************************/ +UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid) +{ + UINT8 *p = *p_dst; + UINT8 len = 0; + + if (uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM (p, uuid.uu.uuid16); + len = LEN_UUID_16; + } else if (uuid.len == LEN_UUID_32) { /* always convert 32 bits into 128 bits as alwats */ + gatt_convert_uuid32_to_uuid128(p, uuid.uu.uuid32); + p += LEN_UUID_128; + len = LEN_UUID_128; + } else if (uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, uuid.uu.uuid128, LEN_UUID_128); + len = LEN_UUID_128; + } + + *p_dst = p; + return len; +} + +/******************************************************************************* +** +** Function gatt_parse_uuid_from_cmd +** +** Description Convert a 128 bits UUID into a 16 bits UUID. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid_rec, UINT16 uuid_size, UINT8 **p_data) +{ + BOOLEAN is_base_uuid, ret = TRUE; + UINT8 xx; + UINT8 *p_uuid = *p_data; + + memset(p_uuid_rec, 0, sizeof(tBT_UUID)); + + switch (uuid_size) { + case LEN_UUID_16: + p_uuid_rec->len = uuid_size; + STREAM_TO_UINT16 (p_uuid_rec->uu.uuid16, p_uuid); + *p_data += LEN_UUID_16; + break; + + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) { + if (p_uuid[xx] != base_uuid[xx]) { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) { + p_uuid += (LEN_UUID_128 - 4); + p_uuid_rec->len = LEN_UUID_16; + STREAM_TO_UINT16(p_uuid_rec->uu.uuid16, p_uuid); + } else { + p_uuid += (LEN_UUID_128 - LEN_UUID_32); + p_uuid_rec->len = LEN_UUID_32; + STREAM_TO_UINT32(p_uuid_rec->uu.uuid32, p_uuid); + } + } + if (!is_base_uuid) { + p_uuid_rec->len = LEN_UUID_128; + memcpy(p_uuid_rec->uu.uuid128, p_uuid, LEN_UUID_128); + } + *p_data += LEN_UUID_128; + break; + + /* do not allow 32 bits UUID in ATT PDU now */ + case LEN_UUID_32: + GATT_TRACE_ERROR("DO NOT ALLOW 32 BITS UUID IN ATT PDU"); + case 0: + default: + if (uuid_size != 0) { + ret = FALSE; + } + GATT_TRACE_WARNING("gatt_parse_uuid_from_cmd invalid uuid size"); + break; + } + + return ( ret); +} + +/******************************************************************************* +** +** Function gatt_start_rsp_timer +** +** Description Start a wait_for_response timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_rsp_timer(UINT16 clcb_idx) +{ + tGATT_CLCB *p_clcb = gatt_clcb_find_by_idx(clcb_idx); + UINT32 timeout = GATT_WAIT_FOR_RSP_TOUT; + p_clcb->rsp_timer_ent.param = (TIMER_PARAM_TYPE)p_clcb; + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL) { + timeout = GATT_WAIT_FOR_DISC_RSP_TOUT; + } + btu_start_timer (&p_clcb->rsp_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + timeout); +} +/******************************************************************************* +** +** Function gatt_start_conf_timer +** +** Description Start a wait_for_confirmation timer. +** +** Returns TRUE if command sent, otherwise FALSE. +** +*******************************************************************************/ +void gatt_start_conf_timer(tGATT_TCB *p_tcb) +{ + p_tcb->conf_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + btu_start_timer (&p_tcb->conf_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP, + GATT_WAIT_FOR_RSP_TOUT); +} +/******************************************************************************* +** +** Function gatt_start_ind_ack_timer +** +** Description start the application ack timer +** +** Returns void +** +*******************************************************************************/ +void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb) +{ + p_tcb->ind_ack_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb; + /* start notification cache timer */ + btu_start_timer (&p_tcb->ind_ack_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_IND_ACK, + GATT_WAIT_FOR_IND_ACK_TOUT); + +} +/******************************************************************************* +** +** Function gatt_rsp_timeout +** +** Description Called when GATT wait for ATT command response timer expires +** +** Returns void +** +*******************************************************************************/ +void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_CLCB *p_clcb = (tGATT_CLCB *)p_tle->param; + if (p_clcb == NULL || p_clcb->p_tcb == NULL) { + GATT_TRACE_WARNING("gatt_rsp_timeout clcb is already deleted"); + return; + } + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && + p_clcb->op_subtype == GATT_DISC_SRVC_ALL && + p_clcb->retry_count < GATT_REQ_RETRY_LIMIT) { + UINT8 rsp_code; + GATT_TRACE_WARNING("gatt_rsp_timeout retry discovery primary service"); + if (p_clcb != gatt_cmd_dequeue(p_clcb->p_tcb, &rsp_code)) { + GATT_TRACE_ERROR("gatt_rsp_timeout command queue out of sync, disconnect"); + } else { + p_clcb->retry_count++; +#if (GATTC_INCLUDED == TRUE) + gatt_act_discovery(p_clcb); +#endif ///GATTC_INCLUDED == TRUE + return; + } + } + + GATT_TRACE_WARNING("gatt_rsp_timeout disconnecting..."); + gatt_disconnect (p_clcb->p_tcb); +} + +/******************************************************************************* +** +** Function gatt_ind_ack_timeout +** +** Description Called when GATT wait for ATT handle confirmation timeout +** +** Returns void +** +*******************************************************************************/ +void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle) +{ + tGATT_TCB *p_tcb = (tGATT_TCB *)p_tle->param; + + GATT_TRACE_WARNING("gatt_ind_ack_timeout send ack now"); + + if (p_tcb != NULL) { + p_tcb->ind_count = 0; + } + + attp_send_cl_msg(((tGATT_TCB *)p_tle->param), 0, GATT_HANDLE_VALUE_CONF, NULL); +} +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns GATT_MAX_SR_PROFILES if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle) +{ + UINT8 i_rcb = 0; + + for ( ; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++) { + if (gatt_cb.sr_reg[i_rcb].in_use && + gatt_cb.sr_reg[i_rcb].s_hdl <= handle && + gatt_cb.sr_reg[i_rcb].e_hdl >= handle ) { + break; + } + } + return i_rcb; +} + +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns GATT_MAX_SR_PROFILES if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +#if (GATTS_INCLUDED == TRUE) +UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst) +{ + UINT8 i_rcb = 0; + tGATT_SR_REG *p_sreg; + tBT_UUID *p_this_uuid; + + for (i_rcb = 0, p_sreg = gatt_cb.sr_reg; i_rcb < GATT_MAX_SR_PROFILES; i_rcb++, p_sreg++) { + if ( p_sreg->in_use ) { + p_this_uuid = gatts_get_service_uuid (p_sreg->p_db); + + if (p_this_uuid && + gatt_uuid_compare (*p_app_uuid128, p_sreg->app_uuid ) && + gatt_uuid_compare (*p_svc_uuid, *p_this_uuid) && + (svc_inst == p_sreg->service_instance)) { + GATT_TRACE_ERROR ("Active Service Found "); + gatt_dbg_display_uuid(*p_svc_uuid); + + break; + } + } + } + return i_rcb; +} +#endif ///GATTS_INCLUDED == TRUE +/******************************************************************************* +** +** Function gatt_sr_find_i_rcb_by_handle +** +** Description The function searches for a service that owns a specific handle. +** +** Returns 0 if not found. Otherwise index of th eservice. +** +*******************************************************************************/ +UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list ) +{ + UINT8 ii = 0; + tGATT_SR_REG *p_sreg = NULL; + + /*this is a new application servoce start */ + for (ii = 0, p_sreg = gatt_cb.sr_reg; ii < GATT_MAX_SR_PROFILES; ii++, p_sreg++) { + if (!p_sreg->in_use) { + memset (p_sreg, 0, sizeof(tGATT_SR_REG)); + + p_sreg->in_use = TRUE; + memcpy (&p_sreg->app_uuid, &p_list->asgn_range.app_uuid128, sizeof(tBT_UUID)); + + p_sreg->service_instance = p_list->asgn_range.svc_inst; + p_sreg->type = p_list->asgn_range.is_primary ? GATT_UUID_PRI_SERVICE : GATT_UUID_SEC_SERVICE; + p_sreg->s_hdl = p_list->asgn_range.s_handle; + p_sreg->e_hdl = p_list->asgn_range.e_handle; + p_sreg->p_db = &p_list->svc_db; + + GATT_TRACE_DEBUG ("total buffer in db [%d]", fixed_queue_length(p_sreg->p_db->svc_buffer)); + break; + } + } + + return ii; +} +/******************************************************************************* +** +** Function gatt_sr_get_sec_info +** +** Description Get the security flag and key size information for the peer +** device. +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size) +{ + UINT8 sec_flag = 0; + + BTM_GetSecurityFlagsByTransport(rem_bda, &sec_flag, transport); + + sec_flag &= (GATT_SEC_FLAG_LKEY_UNAUTHED | GATT_SEC_FLAG_LKEY_AUTHED | GATT_SEC_FLAG_ENCRYPTED | GATT_SEC_FLAG_AUTHORIZATION); +#if (SMP_INCLUDED == TRUE) + *p_key_size = btm_ble_read_sec_key_size(rem_bda); +#endif ///SMP_INCLUDED == TRUE + *p_sec_flag = sec_flag; +} +/******************************************************************************* +** +** Function gatt_sr_send_req_callback +** +** Description +** +** +** Returns void +** +*******************************************************************************/ +void gatt_sr_send_req_callback(UINT16 conn_id, + UINT32 trans_id, + tGATTS_REQ_TYPE type, tGATTS_DATA *p_data) +{ + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if (!p_reg ) { + GATT_TRACE_ERROR ("p_reg not found discard request"); + return; + } + + if ( p_reg->in_use && + p_reg->app_cb.p_req_cb) { + (*p_reg->app_cb.p_req_cb)(conn_id, trans_id, type, p_data); + } else { + GATT_TRACE_WARNING("Call back not found for application conn_id=%d", conn_id); + } + +} + +/******************************************************************************* +** +** Function gatt_send_error_rsp +** +** Description This function sends an error response. +** +** Returns void +** +*******************************************************************************/ +tGATT_STATUS gatt_send_error_rsp (tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, + UINT16 handle, BOOLEAN deq) +{ + tGATT_ERROR error; + tGATT_STATUS status; + BT_HDR *p_buf; + + error.cmd_code = op_code; + error.reason = err_code; + error.handle = handle; + + if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_ERROR, (tGATT_SR_MSG *)&error)) != NULL) { + status = attp_send_sr_msg (p_tcb, p_buf); + } else { + status = GATT_INSUF_RESOURCE; + } +#if (GATTS_INCLUDED == TRUE) + if (deq) { + gatt_dequeue_sr_cmd(p_tcb); + } +#endif ///GATTS_INCLUDED == TRUE + return status; +} + +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function gatt_add_sdp_record +** +** Description This function add a SDP record for a GATT primary service +** +** Returns 0 if error else sdp handle for the record. +** +*******************************************************************************/ +UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl) +{ + tSDP_PROTOCOL_ELEM proto_elem_list[2]; + UINT32 sdp_handle; + UINT16 list = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + UINT8 buff[60]; + UINT8 *p = buff; + + GATT_TRACE_DEBUG("gatt_add_sdp_record s_hdl=0x%x s_hdl=0x%x", start_hdl, end_hdl); + + if ((sdp_handle = SDP_CreateRecord()) == 0) { + return 0; + } + + switch (p_uuid->len) { + case LEN_UUID_16: + SDP_AddServiceClassIdList(sdp_handle, 1, &p_uuid->uu.uuid16); + break; + + case LEN_UUID_32: + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_FOUR_BYTES); + UINT32_TO_BE_STREAM (p, p_uuid->uu.uuid32); + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff); + break; + + case LEN_UUID_128: + UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES); + ARRAY_TO_BE_STREAM_REVERSE (p, p_uuid->uu.uuid128, LEN_UUID_128); + SDP_AddAttribute (sdp_handle, ATTR_ID_SERVICE_CLASS_ID_LIST, DATA_ELE_SEQ_DESC_TYPE, + (UINT32) (p - buff), buff); + break; + + default: + GATT_TRACE_ERROR("inavlid UUID len=%d", p_uuid->len); + SDP_DeleteRecord(sdp_handle); + return 0; + break; + } + + /*** Fill out the protocol element sequence for SDP ***/ + proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_elem_list[0].num_params = 1; + proto_elem_list[0].params[0] = BT_PSM_ATT; + proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_ATT; + proto_elem_list[1].num_params = 2; + proto_elem_list[1].params[0] = start_hdl; + proto_elem_list[1].params[1] = end_hdl; + + SDP_AddProtocolList(sdp_handle, 2, proto_elem_list); + + /* Make the service browseable */ + SDP_AddUuidSequence (sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &list); + + return (sdp_handle); +} +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE + +#if GATT_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function gatt_set_err_rsp +** +** Description This function is called to set the test confirm value +** +** Returns void +** +*******************************************************************************/ +void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status) +{ + GATT_TRACE_DEBUG("gatt_set_err_rsp enable=%d op_code=%d, err_status=%d", enable, req_op_code, err_status); + gatt_cb.enable_err_rsp = enable; + gatt_cb.req_op_code = req_op_code; + gatt_cb.err_status = err_status; +} +#endif + + + +/******************************************************************************* +** +** Function gatt_get_regcb +** +** Description The function returns the registration control block. +** +** Returns pointer to the registration control block or NULL +** +*******************************************************************************/ +tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if) +{ + UINT8 ii = (UINT8)gatt_if; + tGATT_REG *p_reg = NULL; + + if (ii < 1 || ii > GATT_MAX_APPS) { + GATT_TRACE_WARNING("gatt_if out of range [ = %d]", ii); + return NULL; + } + + // Index for cl_rcb is always 1 less than gatt_if. + p_reg = &gatt_cb.cl_rcb[ii - 1]; + + if (!p_reg->in_use) { + GATT_TRACE_WARNING("gatt_if found but not in use.\n"); + return NULL; + } + + return p_reg; +} + + +/******************************************************************************* +** +** Function gatt_is_clcb_allocated +** +** Description The function check clcb for conn_id is allocated or not +** +** Returns True already allocated +** +*******************************************************************************/ + +BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id) +{ + BOOLEAN is_allocated = FALSE; + + tGATT_CLCB *p_clcb = gatt_clcb_find_by_conn_id(conn_id); + if (p_clcb) { + is_allocated = TRUE; + } + return is_allocated; +} + +/******************************************************************************* +** +** Function gatt_clcb_find_by_conn_id +** +** Description Find clcb block using clcb_idx stored at the time of alloc +** +** Returns pointer to clcb corresponding to conn_id +** +*******************************************************************************/ + +tGATT_CLCB *gatt_clcb_find_by_conn_id(UINT16 conn_id) +{ + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + tGATT_CLCB *p_clcb_ret = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (p_clcb->conn_id == conn_id) { + p_clcb_ret = p_clcb; + break; + } + } + return p_clcb_ret; +} + +/******************************************************************************* +** +** Function gatt_clcb_find_by_idx +** +** Description Find clcb block using clcb_idx stored at the time of alloc +** +** Returns pointer to clcb corresponding to clcb_idx +** +*******************************************************************************/ + +tGATT_CLCB *gatt_clcb_find_by_idx(UINT16 clcb_idx) +{ + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + tGATT_CLCB *p_clcb_ret = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (p_clcb->clcb_idx == clcb_idx) { + p_clcb_ret = p_clcb; + break; + } + } + return p_clcb_ret; +} + +/******************************************************************************* +** +** Function gatt_clcb_alloc +** +** Description The function allocates a GATT connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id) +{ + tGATT_CLCB *p_clcb = NULL; + tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id); + UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id); + tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx); + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + if (list_length(gatt_cb.p_clcb_list) < GATT_CL_MAX_LCB) { + p_clcb = (tGATT_CLCB *)osi_malloc(sizeof(tGATT_CLCB)); + if (p_clcb) { + list_append(gatt_cb.p_clcb_list, p_clcb); + memset(p_clcb, 0, sizeof(tGATT_CLCB)); + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + //Add index of the clcb same as conn_id + p_clcb->clcb_idx = conn_id; + p_clcb->p_reg = p_reg; + p_clcb->p_tcb = p_tcb; + } + } + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_clcb_dealloc +** +** Description The function de allocates a GATT connection link control block +** +** Returns None +** +*******************************************************************************/ +void gatt_clcb_dealloc (tGATT_CLCB *p_clcb) +{ + if (p_clcb && p_clcb->in_use) { + btu_free_timer(&p_clcb->rsp_timer_ent); + memset(p_clcb, 0, sizeof(tGATT_CLCB)); + list_remove(gatt_cb.p_clcb_list, p_clcb); + p_clcb = NULL; + } +} + + + +/******************************************************************************* +** +** Function gatt_find_tcb_by_cid +** +** Description The function searches for an empty entry +** in registration info table for GATT client +** +** Returns NULL if not found. Otherwise pointer to the rcb. +** +*******************************************************************************/ +tGATT_TCB *gatt_find_tcb_by_cid (UINT16 lcid) +{ + tGATT_TCB *p_tcb = NULL; + list_node_t *p_node = NULL; + for(p_node = list_begin(gatt_cb.p_tcb_list); p_node; p_node = list_next(p_node)) { + p_tcb = list_node(p_node); + if (p_tcb->in_use && p_tcb->att_lcid == lcid) { + break; + } + } + return p_tcb; +} + +/******************************************************************************* +** +** Function gatt_num_apps_hold_link +** +** Description The function find the number of applcaitions is holding the link +** +** Returns total number of applications holding this acl link. +** +*******************************************************************************/ +UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb) +{ + UINT8 i, num = 0; + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i]) { + num ++; + } + } + + GATT_TRACE_DEBUG("gatt_num_apps_hold_link num=%d", num); + return num; +} + + +/******************************************************************************* +** +** Function gatt_num_clcb_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 num = 0; + + list_node_t *p_node = NULL; + tGATT_CLCB *p_clcb = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = list_next(p_node)) { + p_clcb = list_node(p_node); + if (memcmp(p_clcb->p_tcb->peer_bda, bda, BD_ADDR_LEN) == 0) { + num++; + } + } + return num; +} + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->prep_cnt[i]) { + p_tcb->sr_cmd.cback_cnt[i] = 1; + } + } + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatt_sr_is_cback_cnt_zero +** +** Description The function searches all LCB with macthing bd address +** +** Returns True if thetotal application callback count is zero +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ) +{ + BOOLEAN status = TRUE; +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->sr_cmd.cback_cnt[i]) { + status = FALSE; + break; + } + } + } else { + status = FALSE; + } +#endif ///GATTS_INCLUDED == TRUE + return status; +} + +/******************************************************************************* +** +** Function gatt_sr_is_prep_cnt_zero +** +** Description Check the prepare write request count is zero or not +** +** Returns True no prepare write request +** +*******************************************************************************/ +BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb) +{ + BOOLEAN status = TRUE; + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->prep_cnt[i]) { + status = FALSE; + break; + } + } + } else { + status = FALSE; + } + return status; +} + + +/******************************************************************************* +** +** Function gatt_sr_reset_cback_cnt +** +** Description Reset the application callback count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 i; + + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_tcb->sr_cmd.cback_cnt[i] = 0; + } + } +#endif ///GATTS_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function gatt_sr_reset_prep_cnt +** +** Description Reset the prep write count to zero +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ) +{ + UINT8 i; + if (p_tcb) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_tcb->prep_cnt[i] = 0; + } + } +} + + +/******************************************************************************* +** +** Function gatt_sr_update_cback_cnt +** +** Description Update the teh application callback count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ +#if (GATTS_INCLUDED == TRUE) + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + if (p_tcb) { + if (is_reset_first) { + gatt_sr_reset_cback_cnt(p_tcb); + } + if (is_inc) { + p_tcb->sr_cmd.cback_cnt[idx]++; + } else { + if ( p_tcb->sr_cmd.cback_cnt[idx]) { + p_tcb->sr_cmd.cback_cnt[idx]--; + } + } + } +#endif ///GATTS_INCLUDED == TRUE +} + + +/******************************************************************************* +** +** Function gatt_sr_update_prep_cnt +** +** Description Update the teh prepare write request count +** +** Returns None +** +*******************************************************************************/ +void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first) +{ + UINT8 idx = ((UINT8) gatt_if) - 1 ; + + GATT_TRACE_DEBUG("gatt_sr_update_prep_cnt tcb idx=%d gatt_if=%d is_inc=%d is_reset_first=%d", + p_tcb->tcb_idx, gatt_if, is_inc, is_reset_first); + + if (p_tcb) { + if (is_reset_first) { + gatt_sr_reset_prep_cnt(p_tcb); + } + if (is_inc) { + p_tcb->prep_cnt[idx]++; + } else { + if (p_tcb->prep_cnt[idx]) { + p_tcb->prep_cnt[idx]--; + } + } + } +} +/******************************************************************************* +** +** Function gatt_cancel_open +** +** Description Cancel open request +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda) +{ + tGATT_TCB *p_tcb = NULL; + BOOLEAN status = TRUE; + + p_tcb = gatt_find_tcb_by_addr(bda, BT_TRANSPORT_LE); + + if (p_tcb) { + if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) { + GATT_TRACE_ERROR("GATT_CancelConnect - link connected Too late to cancel"); + status = FALSE; + } else { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + if (!gatt_num_apps_hold_link(p_tcb)) { + gatt_disconnect(p_tcb); + } + } + } + + return status; +} + +/******************************************************************************* +** +** Function gatt_find_app_hold_link +** +** Description find the application that is holding the specified link +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if) +{ + UINT8 i; + BOOLEAN found = FALSE; + + for (i = start_idx; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i]) { + *p_gatt_if = p_tcb->app_hold_link[i]; + *p_found_idx = i; + found = TRUE; + break; + } + } + return found; +} + +/******************************************************************************* +** +** Function gatt_find_specific_app_in_hold_link +** +** Description find the specific application that is holding the specified link +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_specific_app_in_hold_link(tGATT_TCB *p_tcb, tGATT_IF p_gatt_if) +{ + UINT8 i; + BOOLEAN found = FALSE; + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_tcb->app_hold_link[i] && p_tcb->app_hold_link[i] == p_gatt_if) { + found = TRUE; + break; + } + } + return found; +} + +/******************************************************************************* +** +** Function gatt_cmd_enq +** +** Description Enqueue this command. +** +** Returns None. +** +*******************************************************************************/ +BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->next_slot_inq]; + + p_cmd->to_send = to_send; /* waiting to be sent */ + p_cmd->op_code = op_code; + p_cmd->p_cmd = p_buf; + p_cmd->clcb_idx = clcb_idx; + + if (!to_send) { + p_tcb->pending_cl_req = p_tcb->next_slot_inq; + } + + p_tcb->next_slot_inq ++; + p_tcb->next_slot_inq %= GATT_CL_MAX_LCB; + + return TRUE; +} + +/******************************************************************************* +** +** Function gatt_cmd_dequeue +** +** Description dequeue the command in the client CCB command queue. +** +** Returns total number of clcb found. +** +*******************************************************************************/ +tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_op_code) +{ + tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; + tGATT_CLCB *p_clcb = NULL; + + if (p_tcb->pending_cl_req != p_tcb->next_slot_inq) { + p_clcb = gatt_clcb_find_by_idx(p_cmd->clcb_idx); + + *p_op_code = p_cmd->op_code; + + p_tcb->pending_cl_req ++; + p_tcb->pending_cl_req %= GATT_CL_MAX_LCB; + } + + return p_clcb; +} + +/******************************************************************************* +** +** Function gatt_send_write_msg +** +** Description This real function send out the ATT message for write. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, + UINT16 handle, UINT16 len, + UINT16 offset, UINT8 *p_data) +{ + tGATT_CL_MSG msg; + + msg.attr_value.handle = handle; + msg.attr_value.len = len; + msg.attr_value.offset = offset; + + memcpy (msg.attr_value.value, p_data, len); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg); +} + +/******************************************************************************* +** +** Function gatt_act_send_browse +** +** Description This function ends a browse command request, including read +** information request and read by type request. +** +** Returns status code +** +*******************************************************************************/ +UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, + UINT16 e_handle, tBT_UUID uuid) +{ + tGATT_CL_MSG msg; + + msg.browse.s_handle = s_handle; + msg.browse.e_handle = e_handle; + memcpy(&msg.browse.uuid, &uuid, sizeof(tBT_UUID)); + + /* write by handle */ + return attp_send_cl_msg(p_tcb, index, op, &msg); +} + +/******************************************************************************* +** +** Function gatt_end_operation +** +** Description This function ends a discovery, send callback and finalize +** some control value. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data) +{ + tGATT_CL_COMPLETE cb_data; + tGATT_CMPL_CBACK *p_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_cmpl_cb : NULL; + UINT8 op = p_clcb->operation, disc_type = GATT_DISC_MAX; + tGATT_DISC_CMPL_CB *p_disc_cmpl_cb = (p_clcb->p_reg) ? p_clcb->p_reg->app_cb.p_disc_cmpl_cb : NULL; + UINT16 conn_id; +#if (!CONFIG_BT_STACK_NO_LOG) + UINT8 operation; +#endif + + GATT_TRACE_DEBUG ("gatt_end_operation status=%d op=%d subtype=%d", + status, p_clcb->operation, p_clcb->op_subtype); + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + + if (p_cmpl_cb != NULL && p_clcb->operation != 0) { + if (p_clcb->operation == GATTC_OPTYPE_READ) { + cb_data.att_value.handle = p_clcb->s_handle; + cb_data.att_value.len = p_clcb->counter; + + if (p_data && p_clcb->counter) { + memcpy (cb_data.att_value.value, p_data, cb_data.att_value.len); + } + } + + if (p_clcb->operation == GATTC_OPTYPE_WRITE) { + memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE)); + cb_data.handle = + cb_data.att_value.handle = p_clcb->s_handle; + if (p_clcb->op_subtype == GATT_WRITE_PREPARE) { + if (p_data) { + cb_data.att_value = *((tGATT_VALUE *) p_data); + } else { + GATT_TRACE_DEBUG("Rcv Prepare write rsp but no data"); + } + } + } + + if (p_clcb->operation == GATTC_OPTYPE_CONFIG) { + cb_data.mtu = p_clcb->p_tcb->payload_size; + } + + if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { + disc_type = p_clcb->op_subtype; + } + } + + if (p_clcb->p_attr_buf) { + osi_free(p_clcb->p_attr_buf); + } + +#if !CONFIG_BT_STACK_NO_LOG + operation = p_clcb->operation; +#endif + + conn_id = p_clcb->conn_id; + btu_stop_timer(&p_clcb->rsp_timer_ent); + + gatt_clcb_dealloc(p_clcb); + + if (p_disc_cmpl_cb && (op == GATTC_OPTYPE_DISCOVERY)) { + (*p_disc_cmpl_cb)(conn_id, disc_type, status); + } else if (p_cmpl_cb && op) { + (*p_cmpl_cb)(conn_id, op, status, &cb_data); + } else { + GATT_TRACE_WARNING ("gatt_end_operation not sent out op=%d p_disc_cmpl_cb:%p p_cmpl_cb:%p", + operation, p_disc_cmpl_cb, p_cmpl_cb); + } +} + +/******************************************************************************* +** +** Function gatt_cleanup_upon_disc +** +** Description This function cleans up the control blocks when L2CAP channel +** disconnect. +** +** Returns 16 bits uuid. +** +*******************************************************************************/ +void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) +{ + tGATT_TCB *p_tcb = NULL; + tGATT_CLCB *p_clcb; + UINT8 i; + UINT16 conn_id; + tGATT_REG *p_reg = NULL; + + + GATT_TRACE_DEBUG ("gatt_cleanup_upon_disc "); + + if ((p_tcb = gatt_find_tcb_by_addr(bda, transport)) != NULL) { + GATT_TRACE_DEBUG ("found p_tcb "); + gatt_set_ch_state(p_tcb, GATT_CH_CLOSE); + list_node_t *p_node = NULL; + list_node_t *p_node_next = NULL; + for(p_node = list_begin(gatt_cb.p_clcb_list); p_node; p_node = p_node_next) { + p_clcb = list_node(p_node); + p_node_next = list_next(p_node); + if (p_clcb->in_use && p_clcb->p_tcb == p_tcb) { + btu_stop_timer(&p_clcb->rsp_timer_ent); + GATT_TRACE_DEBUG ("found p_clcb conn_id=%d clcb_idx=%d", p_clcb->conn_id, p_clcb->clcb_idx); + if (p_clcb->operation != GATTC_OPTYPE_NONE) { + gatt_end_operation(p_clcb, GATT_ERROR, NULL); + p_clcb = NULL; + } + gatt_clcb_dealloc(p_clcb); + } + } + + btu_free_timer (&p_tcb->ind_ack_timer_ent); + btu_free_timer (&p_tcb->conf_timer_ent); + gatt_free_pending_ind(p_tcb); + gatt_free_pending_enc_queue(p_tcb); + gatt_free_pending_prepare_write_queue(p_tcb); +#if (GATTS_INCLUDED) + fixed_queue_free(p_tcb->sr_cmd.multi_rsp_q, osi_free_func); + p_tcb->sr_cmd.multi_rsp_q = NULL; +#endif /* #if (GATTS_INCLUDED) */ + for (i = 0; i < GATT_MAX_APPS; i ++) { + p_reg = &gatt_cb.cl_rcb[i]; + if (p_reg->in_use && p_reg->app_cb.p_conn_cb) { + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); + GATT_TRACE_DEBUG ("found p_reg tcb_idx=%d gatt_if=%d conn_id=0x%x", p_tcb->tcb_idx, p_reg->gatt_if, conn_id); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, FALSE, reason, transport); + } + } + gatt_tcb_free(p_tcb); + } else { + GATT_TRACE_DEBUG ("exit gatt_cleanup_upon_disc "); + BTM_Recovery_Pre_State(); + } + gatt_delete_dev_from_srv_chg_clt_list(bda); +} +/******************************************************************************* +** +** Function gatt_dbg_req_op_name +** +** Description Get op code description name, for debug information. +** +** Returns UINT8 *: name of the operation. +** +*******************************************************************************/ +UINT8 *gatt_dbg_op_name(UINT8 op_code) +{ + UINT8 pseduo_op_code_idx = op_code & (~GATT_WRITE_CMD_MASK); + + if (op_code == GATT_CMD_WRITE ) { + pseduo_op_code_idx = 0x14; /* just an index to op_code_name */ + + } + + if (op_code == GATT_SIGN_CMD_WRITE) { + pseduo_op_code_idx = 0x15; /* just an index to op_code_name */ + } + + if (pseduo_op_code_idx <= GATT_OP_CODE_MAX) { + return (UINT8 *) op_code_name[pseduo_op_code_idx]; + } else { + return (UINT8 *)"Op Code Exceed Max"; + } +} + +/******************************************************************************* +** +** Function gatt_dbg_display_uuid +** +** Description Disaplay the UUID +** +** Returns None +** +*******************************************************************************/ +void gatt_dbg_display_uuid(tBT_UUID bt_uuid) +{ + char str_buf[50]; + int x = 0; + + if (bt_uuid.len == LEN_UUID_16) { + sprintf(str_buf, "0x%04x", bt_uuid.uu.uuid16); + } else if (bt_uuid.len == LEN_UUID_32) { + sprintf(str_buf, "0x%08x", (unsigned int)bt_uuid.uu.uuid32); + } else if (bt_uuid.len == LEN_UUID_128) { + x += sprintf(&str_buf[x], "0x%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[15], bt_uuid.uu.uuid128[14], + bt_uuid.uu.uuid128[13], bt_uuid.uu.uuid128[12], + bt_uuid.uu.uuid128[11], bt_uuid.uu.uuid128[10], + bt_uuid.uu.uuid128[9], bt_uuid.uu.uuid128[8]); + sprintf(&str_buf[x], "%02x%02x%02x%02x%02x%02x%02x%02x", + bt_uuid.uu.uuid128[7], bt_uuid.uu.uuid128[6], + bt_uuid.uu.uuid128[5], bt_uuid.uu.uuid128[4], + bt_uuid.uu.uuid128[3], bt_uuid.uu.uuid128[2], + bt_uuid.uu.uuid128[1], bt_uuid.uu.uuid128[0]); + } else { + BCM_STRNCPY_S(str_buf, "Unknown UUID 0", 15); + } + + GATT_TRACE_DEBUG ("UUID=[%s]", str_buf); + +} + + +/******************************************************************************* +** +** Function gatt_is_bg_dev_for_app +** +** Description find is this one of the background devices for the application +** +** Returns TRUE this is one of the background devices for the application +** +*******************************************************************************/ +BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if) +{ + UINT8 i; + + for (i = 0; i < GATT_MAX_APPS; i ++ ) { + if (p_dev->in_use && (p_dev->gatt_if[i] == gatt_if)) { + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function gatt_find_bg_dev +** +** Description find background connection device from the list. +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV *gatt_find_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) { + if (p_dev_list->in_use && !memcmp(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN)) { + return p_dev_list; + } + } + return NULL; +} +/******************************************************************************* +** +** Function gatt_alloc_bg_dev +** +** Description allocate a background connection device record +** +** Returns pointer to the device record +** +*******************************************************************************/ +tGATT_BG_CONN_DEV *gatt_alloc_bg_dev(BD_ADDR remote_bda) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i; + + for (i = 0; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++) { + if (!p_dev_list->in_use) { + p_dev_list->in_use = TRUE; + memcpy(p_dev_list->remote_bda, remote_bda, BD_ADDR_LEN); + + return p_dev_list; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function gatt_add_bg_dev_list +** +** Description add/remove device from the background connection device list +** +** Returns TRUE if device added to the list; FALSE failed +** +*******************************************************************************/ +BOOLEAN gatt_add_bg_dev_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN is_initator) +{ + tGATT_IF gatt_if = p_reg->gatt_if; + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + p_dev = gatt_alloc_bg_dev(bd_addr); + } + + if (p_dev) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (is_initator) { + if (p_dev->gatt_if[i] == gatt_if) { + GATT_TRACE_ERROR("device already in iniator white list"); + return TRUE; + } else if (p_dev->gatt_if[i] == 0) { + p_dev->gatt_if[i] = gatt_if; + if (i == 0) { + ret = BTM_BleUpdateBgConnDev(TRUE, bd_addr); + } else { + ret = TRUE; + } + break; + } + } else { + if (p_dev->listen_gif[i] == gatt_if) { + GATT_TRACE_ERROR("device already in adv white list"); + return TRUE; + } else if (p_dev->listen_gif[i] == 0) { + if (p_reg->listening == GATT_LISTEN_TO_ALL) { + p_reg->listening = GATT_LISTEN_TO_NONE; + } + + p_reg->listening ++; + p_dev->listen_gif[i] = gatt_if; + + if (i == 0) { + // To check, we do not support background connection, code will not be called here + ret = BTM_BleUpdateAdvWhitelist(TRUE, bd_addr, 0, NULL); + } else { + ret = TRUE; + } + break; + } + } + } + } else { + GATT_TRACE_ERROR("no device record available"); + } + + return ret; +} + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_for_app +** +** Description Remove the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr) +{ + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + BOOLEAN status; + + if (p_tcb) { + gatt_update_app_use_link_flag(gatt_if, p_tcb, FALSE, FALSE); + } + status = gatt_update_auto_connect_dev(gatt_if, FALSE, bd_addr, TRUE); + return status; +} + + +/******************************************************************************* +** +** Function gatt_get_num_apps_for_bg_dev +** +** Description Gte the number of applciations for the specified background device +** +** Returns UINT8 total number fo applications +** +*******************************************************************************/ +UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + UINT8 cnt = 0; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) != NULL) { + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_dev->gatt_if[i]) { + cnt++; + } + } + } + return cnt; +} + +/******************************************************************************* +** +** Function gatt_find_app_for_bg_dev +** +** Description find the application interface for the specified background device +** +** Returns Boolean +** +*******************************************************************************/ +BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS; i ++) { + if (p_dev->gatt_if[i] != 0 ) { + *p_gatt_if = p_dev->gatt_if[i]; + ret = TRUE; + break; + } + } + return ret; +} + + +/******************************************************************************* +** +** Function gatt_remove_bg_dev_from_list +** +** Description add/remove device from the background connection device list or +** listening to advertising list. +** +** Returns pointer to the device record +** +*******************************************************************************/ +BOOLEAN gatt_remove_bg_dev_from_list(tGATT_REG *p_reg, BD_ADDR bd_addr, BOOLEAN is_initiator) +{ + tGATT_IF gatt_if = p_reg->gatt_if; + tGATT_BG_CONN_DEV *p_dev = NULL; + UINT8 i, j; + BOOLEAN ret = FALSE; + + if ((p_dev = gatt_find_bg_dev(bd_addr)) == NULL) { + return ret; + } + + for (i = 0; i < GATT_MAX_APPS && (p_dev->gatt_if[i] > 0 || p_dev->listen_gif[i]); i ++) { + if (is_initiator) { + if (p_dev->gatt_if[i] == gatt_if) { + p_dev->gatt_if[i] = 0; + /* move all element behind one forward */ + for (j = i + 1; j < GATT_MAX_APPS; j ++) { + p_dev->gatt_if[j - 1] = p_dev->gatt_if[j]; + } + + if (p_dev->gatt_if[0] == 0) { + ret = BTM_BleUpdateBgConnDev(FALSE, p_dev->remote_bda); + } else { + ret = TRUE; + } + + break; + } + } else { + if (p_dev->listen_gif[i] == gatt_if) { + p_dev->listen_gif[i] = 0; + p_reg->listening --; + /* move all element behind one forward */ + for (j = i + 1; j < GATT_MAX_APPS; j ++) { + p_dev->listen_gif[j - 1] = p_dev->listen_gif[j]; + } + + if (p_dev->listen_gif[0] == 0) { + // To check, we do not support background connection, code will not be called here + ret = BTM_BleUpdateAdvWhitelist(FALSE, p_dev->remote_bda, 0, NULL); + } else { + ret = TRUE; + } + break; + } + } + } + + if (i != GATT_MAX_APPS && p_dev->gatt_if[0] == 0 && p_dev->listen_gif[0] == 0) { + memset(p_dev, 0, sizeof(tGATT_BG_CONN_DEV)); + } + + return ret; +} +/******************************************************************************* +** +** Function gatt_deregister_bgdev_list +** +** Description deregister all related background connection device. +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_deregister_bgdev_list(tGATT_IF gatt_if) +{ + tGATT_BG_CONN_DEV *p_dev_list = &gatt_cb.bgconn_dev[0]; + UINT8 i , j, k; + tGATT_REG *p_reg = gatt_get_regcb(gatt_if); + + /* update the BG conn device list */ + for (i = 0 ; i < GATT_MAX_BG_CONN_DEV; i ++, p_dev_list ++ ) { + if (p_dev_list->in_use) { + for (j = 0; j < GATT_MAX_APPS; j ++) { + if (p_dev_list->gatt_if[j] == 0 && p_dev_list->listen_gif[j] == 0) { + break; + } + + if (p_dev_list->gatt_if[j] == gatt_if) { + for (k = j + 1; k < GATT_MAX_APPS; k ++) { + p_dev_list->gatt_if[k - 1] = p_dev_list->gatt_if[k]; + } + + if (p_dev_list->gatt_if[0] == 0) { + BTM_BleUpdateBgConnDev(FALSE, p_dev_list->remote_bda); + } + } + + if (p_dev_list->listen_gif[j] == gatt_if) { + p_dev_list->listen_gif[j] = 0; + + if (p_reg != NULL && p_reg->listening > 0) { + p_reg->listening --; + } + + /* move all element behind one forward */ + for (k = j + 1; k < GATT_MAX_APPS; k ++) { + p_dev_list->listen_gif[k - 1] = p_dev_list->listen_gif[k]; + } + + if (p_dev_list->listen_gif[0] == 0) { + // To check, we do not support background connection, code will not be called here + BTM_BleUpdateAdvWhitelist(FALSE, p_dev_list->remote_bda, 0, NULL); + } + } + } + } + } +} + + +/******************************************************************************* +** +** Function gatt_reset_bgdev_list +** +** Description reset bg device list +** +** Returns pointer to the device record +** +*******************************************************************************/ +void gatt_reset_bgdev_list(void) +{ + memset(&gatt_cb.bgconn_dev, 0 , sizeof(tGATT_BG_CONN_DEV)*GATT_MAX_BG_CONN_DEV); + +} +/******************************************************************************* +** +** Function gatt_update_auto_connect_dev +** +** Description This function add or remove a device for background connection +** procedure. +** +** Parameters gatt_if: Application ID. +** add: add peer device +** bd_addr: peer device address. +** +** Returns TRUE if connection started; FALSE if connection start failure. +** +*******************************************************************************/ +BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr, BOOLEAN is_initator) +{ + BOOLEAN ret = FALSE; + tGATT_REG *p_reg; + tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE); + + GATT_TRACE_API ("gatt_update_auto_connect_dev "); + /* Make sure app is registered */ + if ((p_reg = gatt_get_regcb(gatt_if)) == NULL) { + GATT_TRACE_ERROR("gatt_update_auto_connect_dev - gatt_if %d is not registered", gatt_if); + return (FALSE); + } + + if (add) { + ret = gatt_add_bg_dev_list(p_reg, bd_addr, is_initator); + + if (ret && p_tcb != NULL) { + /* if a connected device, update the link holding number */ + gatt_update_app_use_link_flag(gatt_if, p_tcb, TRUE, TRUE); + } + } else { + ret = gatt_remove_bg_dev_from_list(p_reg, bd_addr, is_initator); + } + return ret; +} + + + +/******************************************************************************* +** +** Function gatt_add_pending_new_srv_start +** +** Description Add a pending new srv start to the new service start queue +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +tGATT_PENDING_ENC_CLCB *gatt_add_pending_enc_channel_clcb(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb ) +{ + tGATT_PENDING_ENC_CLCB *p_buf; + + GATT_TRACE_DEBUG ("gatt_add_pending_new_srv_start"); + if ((p_buf = (tGATT_PENDING_ENC_CLCB *)osi_malloc((UINT16)sizeof(tGATT_PENDING_ENC_CLCB))) != NULL) { + GATT_TRACE_DEBUG ("enqueue a new pending encryption channel clcb"); + p_buf->p_clcb = p_clcb; + fixed_queue_enqueue(p_tcb->pending_enc_clcb, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + } + return p_buf; +} +/******************************************************************************* +** +** Function gatt_update_listen_mode +** +** Description update peripheral role listening mode +** +** Returns Pointer to the new service start buffer, NULL no buffer available +** +*******************************************************************************/ +BOOLEAN gatt_update_listen_mode(void) +{ + UINT8 ii = 0; + tGATT_REG *p_reg = &gatt_cb.cl_rcb[0]; + UINT8 listening = 0; + UINT16 connectability, window, interval; + BOOLEAN rt = TRUE; + + for (; ii < GATT_MAX_APPS; ii ++, p_reg ++) { + if ( p_reg->in_use && p_reg->listening > listening) { + listening = p_reg->listening; + } + } + + if (listening == GATT_LISTEN_TO_ALL || + listening == GATT_LISTEN_TO_NONE) { + BTM_BleUpdateAdvFilterPolicy (AP_SCAN_CONN_ALL); + } else { + BTM_BleUpdateAdvFilterPolicy (AP_SCAN_CONN_WL); + } + + if (rt) { + connectability = BTM_ReadConnectability (&window, &interval); + + if (listening != GATT_LISTEN_TO_NONE) { + connectability |= BTM_BLE_CONNECTABLE; + } else { + if ((connectability & BTM_BLE_CONNECTABLE) == 0) { + connectability &= ~BTM_BLE_CONNECTABLE; + } + } + /* turning on the adv now */ + btm_ble_set_connectability(connectability); + } + + return rt; + +} + +char *gatt_uuid_to_str(const tBT_UUID *uuid) +{ + static char dst[48] = {0}; + const UINT8 *u8p; + + memset(dst, 0, sizeof(dst)); + + switch (uuid->len) { + case LEN_UUID_16: + sprintf(dst, "0x%04x", uuid->uu.uuid16); + break; + case LEN_UUID_32: + sprintf(dst, "0x%08x", uuid->uu.uuid32); + break; + case LEN_UUID_128: + u8p = uuid->uu.uuid128; + + sprintf(dst, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + u8p[15], u8p[14], u8p[13], u8p[12], + u8p[11], u8p[10], u8p[9], u8p[8], + u8p[7], u8p[6], u8p[5], u8p[4], + u8p[3], u8p[2], u8p[1], u8p[0]); + break; + default: + dst[0] = '\0'; + break; + } + + return dst; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h b/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h new file mode 100644 index 00000000..1161da62 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h @@ -0,0 +1,788 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef GATT_INT_H +#define GATT_INT_H + +#include "common/bt_target.h" +#include "common/bt_trace.h" +#include "stack/gatt_api.h" +#include "stack/btm_ble_api.h" +#include "stack/btu.h" +#include "osi/fixed_queue.h" + +#include <string.h> + + +#define GATT_CREATE_CONN_ID(tcb_idx, gatt_if) ((UINT16) ((((UINT8)(tcb_idx) ) << 8) | ((UINT8) (gatt_if)))) +#define GATT_GET_TCB_IDX(conn_id) ((UINT8) (((UINT16) (conn_id)) >> 8)) +#define GATT_GET_GATT_IF(conn_id) ((tGATT_IF)((UINT8) (conn_id))) + +#define GATT_GET_SR_REG_PTR(index) (&gatt_cb.sr_reg[(UINT8) (index)]); +#define GATT_TRANS_ID_MAX 0x0fffffff /* 4 MSB is reserved */ +#define GATT_RSP_BY_APP 0x00 +#define GATT_RSP_BY_STACK 0x01 +#define GATT_RSP_DEFAULT GATT_RSP_BY_APP //need to rsp by the app. + +/* security action for GATT write and read request */ +#define GATT_SEC_NONE 0 +#define GATT_SEC_OK 1 +#define GATT_SEC_SIGN_DATA 2 /* compute the signature for the write cmd */ +#define GATT_SEC_ENCRYPT 3 /* encrypt the link with current key */ +#define GATT_SEC_ENCRYPT_NO_MITM 4 /* unauthenticated encryption or better */ +#define GATT_SEC_ENCRYPT_MITM 5 /* authenticated encryption */ +#define GATT_SEC_ENC_PENDING 6 /* wait for link encryption pending */ +typedef UINT8 tGATT_SEC_ACTION; + + +#define GATT_ATTR_OP_SPT_MTU (0x00000001 << 0) +#define GATT_ATTR_OP_SPT_FIND_INFO (0x00000001 << 1) +#define GATT_ATTR_OP_SPT_FIND_BY_TYPE (0x00000001 << 2) +#define GATT_ATTR_OP_SPT_READ_BY_TYPE (0x00000001 << 3) +#define GATT_ATTR_OP_SPT_READ (0x00000001 << 4) +#define GATT_ATTR_OP_SPT_MULT_READ (0x00000001 << 5) +#define GATT_ATTR_OP_SPT_READ_BLOB (0x00000001 << 6) +#define GATT_ATTR_OP_SPT_READ_BY_GRP_TYPE (0x00000001 << 7) +#define GATT_ATTR_OP_SPT_WRITE (0x00000001 << 8) +#define GATT_ATTR_OP_SPT_WRITE_CMD (0x00000001 << 9) +#define GATT_ATTR_OP_SPT_PREP_WRITE (0x00000001 << 10) +#define GATT_ATTR_OP_SPT_EXE_WRITE (0x00000001 << 11) +#define GATT_ATTR_OP_SPT_HDL_VALUE_CONF (0x00000001 << 12) +#define GATT_ATTR_OP_SP_SIGN_WRITE (0x00000001 << 13) + +#define GATT_INDEX_INVALID 0xff + +#define GATT_PENDING_REQ_NONE 0 + + +#define GATT_WRITE_CMD_MASK 0xc0 /*0x1100-0000*/ +#define GATT_AUTH_SIGN_MASK 0x80 /*0x1000-0000*/ +#define GATT_AUTH_SIGN_LEN 12 + +#define GATT_HDR_SIZE 3 /* 1B opcode + 2B handle */ + +/* wait for ATT cmd response timeout value */ +#define GATT_WAIT_FOR_RSP_TOUT 30 +#define GATT_WAIT_FOR_DISC_RSP_TOUT 15 +#define GATT_REQ_RETRY_LIMIT 2 +#define GATT_WAIT_FOR_IND_ACK_TOUT 5 + +/* characteristic descriptor type */ +#define GATT_DESCR_EXT_DSCPTOR 1 /* Characteristic Extended Properties */ +#define GATT_DESCR_USER_DSCPTOR 2 /* Characteristic User Description */ +#define GATT_DESCR_CLT_CONFIG 3 /* Client Characteristic Configuration */ +#define GATT_DESCR_SVR_CONFIG 4 /* Server Characteristic Configuration */ +#define GATT_DESCR_PRES_FORMAT 5 /* Characteristic Presentation Format */ +#define GATT_DESCR_AGGR_FORMAT 6 /* Characteristic Aggregate Format */ +#define GATT_DESCR_VALID_RANGE 7 /* Characteristic Valid Range */ +#define GATT_DESCR_UNKNOWN 0xff + +#define GATT_SEC_FLAG_LKEY_UNAUTHED BTM_SEC_FLAG_LKEY_KNOWN +#define GATT_SEC_FLAG_LKEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED +#define GATT_SEC_FLAG_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED +#define GATT_SEC_FLAG_AUTHORIZATION BTM_SEC_FLAG_AUTHORIZED +typedef UINT8 tGATT_SEC_FLAG; + +/* Find Information Response Type +*/ +#define GATT_INFO_TYPE_PAIR_16 0x01 +#define GATT_INFO_TYPE_PAIR_128 0x02 + +#define GATTS_SEND_SERVICE_CHANGE_AUTO 0 +#define GATTS_SEND_SERVICE_CHANGE_MANUAL 1 + +/* GATT client FIND_TYPE_VALUE_Request data */ +typedef struct { + tBT_UUID uuid; /* type of attribute to be found */ + UINT16 s_handle; /* starting handle */ + UINT16 e_handle; /* ending handle */ + UINT16 value_len; /* length of the attribute value */ + UINT8 value[GATT_MAX_MTU_SIZE]; /* pointer to the attribute value to be found */ +} tGATT_FIND_TYPE_VALUE; + +/* client request message to ATT protocol +*/ +typedef union { + tGATT_READ_BY_TYPE browse; /* read by type request */ + tGATT_FIND_TYPE_VALUE find_type_value;/* find by type value */ + tGATT_READ_MULTI read_multi; /* read multiple request */ + tGATT_READ_PARTIAL read_blob; /* read blob */ + tGATT_VALUE attr_value; /* write request */ + /* prepare write */ + /* write blob */ + UINT16 handle; /* read, handle value confirmation */ + UINT16 mtu; + tGATT_EXEC_FLAG exec_write; /* execute write */ +} tGATT_CL_MSG; + +/* error response strucutre */ +typedef struct { + UINT16 handle; + UINT8 cmd_code; + UINT8 reason; +} tGATT_ERROR; + +/* Execute write response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_EXEC_WRITE_RSP; + +/* Write request response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_WRITE_REQ_RSP; + +/* server response message to ATT protocol +*/ +typedef union { + /* data type member event */ + tGATT_VALUE attr_value; /* READ, HANDLE_VALUE_IND, PREPARE_WRITE */ + /* READ_BLOB, READ_BY_TYPE */ + tGATT_ERROR error; /* ERROR_RSP */ + UINT16 handle; /* WRITE, WRITE_BLOB */ + UINT16 mtu; /* exchange MTU request */ +} tGATT_SR_MSG; + +/* Characteristic declaration attribute value +*/ +typedef struct { + tGATT_CHAR_PROP property; + UINT16 char_val_handle; +} tGATT_CHAR_DECL; + +/* attribute value maintained in the server database +*/ +typedef union { + tBT_UUID uuid; /* service declaration */ + tGATT_CHAR_DECL char_decl; /* characteristic declaration */ + tGATT_INCL_SRVC incl_handle; /* included service */ + tGATT_ATTR_VAL attr_val; +} tGATT_ATTR_VALUE; + +/* Attribute UUID type +*/ +#define GATT_ATTR_UUID_TYPE_16 0 +#define GATT_ATTR_UUID_TYPE_128 1 +#define GATT_ATTR_UUID_TYPE_32 2 +typedef UINT8 tGATT_ATTR_UUID_TYPE; + +/* 16 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT16 uuid; +} tGATT_ATTR16; + +/* 32 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16, tGATT_ATTR32 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT32 uuid; +} tGATT_ATTR32; + + +/* 128 bits UUID Attribute in server database +*/ +typedef struct { + void *p_next; /* pointer to the next attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + tGATT_ATTR_VALUE *p_value; + tGATT_ATTR_UUID_TYPE uuid_type; + tGATT_PERM permission; + tGATTS_ATTR_CONTROL control; + tGATT_ATTR_MASK mask; + UINT16 handle; + UINT8 uuid[LEN_UUID_128]; +} tGATT_ATTR128; + +/* Service Database definition +*/ +typedef struct { + void *p_attr_list; /* pointer to the first attribute, either tGATT_ATTR16 or tGATT_ATTR128 */ + UINT8 *p_free_mem; /* Pointer to free memory */ + fixed_queue_t *svc_buffer; /* buffer queue used for service database */ + UINT32 mem_free; /* Memory still available */ + UINT16 end_handle; /* Last handle number */ + UINT16 next_handle; /* Next usable handle value */ +} tGATT_SVC_DB; + +/* Data Structure used for GATT server */ +/* A GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ +typedef struct { + tGATT_SVC_DB *p_db; /* pointer to the service database */ + tBT_UUID app_uuid; /* applicatino UUID */ + UINT32 sdp_handle; /* primamry service SDP handle */ + UINT16 service_instance; /* service instance number */ + UINT16 type; /* service type UUID, primary or secondary */ + UINT16 s_hdl; /* service starting handle */ + UINT16 e_hdl; /* service ending handle */ + tGATT_IF gatt_if; /* this service is belong to which application */ + BOOLEAN in_use; +} tGATT_SR_REG; + +#define GATT_LISTEN_TO_ALL 0xff +#define GATT_LISTEN_TO_NONE 0 + +/* Data Structure used for GATT server */ +/* An GATT registration record consists of a handle, and 1 or more attributes */ +/* A service registration information record consists of beginning and ending */ +/* attribute handle, service UUID and a set of GATT server callback. */ + +typedef struct { + tBT_UUID app_uuid128; + tGATT_CBACK app_cb; + tGATT_IF gatt_if; /* one based */ + BOOLEAN in_use; + UINT8 listening; /* if adv for all has been enabled */ +} tGATT_REG; + + + + +/* command queue for each connection */ +typedef struct { + BT_HDR *p_cmd; + UINT16 clcb_idx; + UINT8 op_code; + BOOLEAN to_send; +} tGATT_CMD_Q; + + +#if GATT_MAX_SR_PROFILES <= 8 +typedef UINT8 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 16 +typedef UINT16 tGATT_APP_MASK; +#elif GATT_MAX_SR_PROFILES <= 32 +typedef UINT32 tGATT_APP_MASK; +#endif + +/* command details for each connection */ +typedef struct { + BT_HDR *p_rsp_msg; + UINT32 trans_id; + tGATT_READ_MULTI multi_req; + fixed_queue_t *multi_rsp_q; + UINT16 handle; + UINT8 op_code; + UINT8 status; + UINT8 cback_cnt[GATT_MAX_APPS]; +} tGATT_SR_CMD; + +#define GATT_CH_CLOSE 0 +#define GATT_CH_CLOSING 1 +#define GATT_CH_CONN 2 +#define GATT_CH_CFG 3 +#define GATT_CH_OPEN 4 + +typedef UINT8 tGATT_CH_STATE; + +#define GATT_GATT_START_HANDLE 1 +#define GATT_GAP_START_HANDLE 20 +#define GATT_APP_START_HANDLE 40 + +typedef struct hdl_cfg { + UINT16 gatt_start_hdl; + UINT16 gap_start_hdl; + UINT16 app_start_hdl; +} tGATT_HDL_CFG; + +typedef struct hdl_list_elem { + struct hdl_list_elem *p_next; + struct hdl_list_elem *p_prev; + tGATTS_HNDL_RANGE asgn_range; /* assigned handle range */ + tGATT_SVC_DB svc_db; + BOOLEAN in_use; +} tGATT_HDL_LIST_ELEM; + +typedef struct { + tGATT_HDL_LIST_ELEM *p_first; + tGATT_HDL_LIST_ELEM *p_last; + UINT16 count; +} tGATT_HDL_LIST_INFO; + + +typedef struct srv_list_elem { + struct srv_list_elem *p_next; + struct srv_list_elem *p_prev; + UINT16 s_hdl; + UINT8 i_sreg; + BOOLEAN in_use; + BOOLEAN is_primary; +} tGATT_SRV_LIST_ELEM; + + +typedef struct { + tGATT_SRV_LIST_ELEM *p_last_primary; + tGATT_SRV_LIST_ELEM *p_first; + tGATT_SRV_LIST_ELEM *p_last; + UINT16 count; +} tGATT_SRV_LIST_INFO; + +/* prepare write queue data */ +typedef struct{ + //len: length of value + tGATT_ATTR16 *p_attr; + UINT16 len; + UINT8 op_code; + UINT16 handle; + UINT16 offset; + UINT8 value[2]; +}__attribute__((packed)) tGATT_PREPARE_WRITE_QUEUE_DATA; + +/* structure to store prepare write packts information */ +typedef struct{ + //only store prepare write packets which need + //to be responded by stack (not by application) + fixed_queue_t *queue; + + //store the total number of prepare write packets + //including that should be responded by stack or by application + UINT16 total_num; + + //store application error code for prepare write, + //invalid offset && invalid length + UINT8 error_code_app; +}tGATT_PREPARE_WRITE_RECORD; + +typedef struct { + fixed_queue_t *pending_enc_clcb; /* pending encryption channel q */ + tGATT_SEC_ACTION sec_act; + BD_ADDR peer_bda; + tBT_TRANSPORT transport; + UINT32 trans_id; + + UINT16 att_lcid; /* L2CAP channel ID for ATT */ + UINT16 payload_size; + + tGATT_CH_STATE ch_state; + UINT8 ch_flags; + + tGATT_IF app_hold_link[GATT_MAX_APPS]; + + /* server needs */ + /* server response data */ +#if (GATTS_INCLUDED == TRUE) + tGATT_SR_CMD sr_cmd; +#endif ///GATTS_INCLUDED == TRUE + UINT16 indicate_handle; + fixed_queue_t *pending_ind_q; + + TIMER_LIST_ENT conf_timer_ent; /* peer confirm to indication timer */ + + UINT8 prep_cnt[GATT_MAX_APPS]; + UINT8 ind_count; + + tGATT_CMD_Q cl_cmd_q[GATT_CL_MAX_LCB]; + TIMER_LIST_ENT ind_ack_timer_ent; /* local app confirm to indication timer */ + UINT8 pending_cl_req; + UINT8 next_slot_inq; /* index of next available slot in queue */ + + /* client supported feature */ + UINT8 cl_supp_feat; + /* server supported feature */ + UINT8 sr_supp_feat; + /* if false, should handle database out of sync */ + BOOLEAN is_robust_cache_change_aware; + + BOOLEAN in_use; + UINT8 tcb_idx; + tGATT_PREPARE_WRITE_RECORD prepare_write_record; /* prepare write packets record */ +} tGATT_TCB; + + +/* logic channel */ +typedef struct { + UINT16 next_disc_start_hdl; /* starting handle for the next inc srvv discovery */ + tGATT_DISC_RES result; + BOOLEAN wait_for_read_rsp; +} tGATT_READ_INC_UUID128; +typedef struct { + tGATT_TCB *p_tcb; /* associated TCB of this CLCB */ + tGATT_REG *p_reg; /* owner of this CLCB */ + UINT8 sccb_idx; + UINT8 *p_attr_buf; /* attribute buffer for read multiple, prepare write */ + tBT_UUID uuid; + UINT16 conn_id; /* connection handle */ + UINT16 clcb_idx; + UINT16 s_handle; /* starting handle of the active request */ + UINT16 e_handle; /* ending handle of the active request */ + UINT16 counter; /* used as offset, attribute length, num of prepare write */ + UINT16 start_offset; + tGATT_AUTH_REQ auth_req; /* authentication requirement */ + UINT8 operation; /* one logic channel can have one operation active */ + UINT8 op_subtype; /* operation subtype */ + UINT8 status; /* operation status */ + BOOLEAN first_read_blob_after_read; + tGATT_READ_INC_UUID128 read_uuid128; + BOOLEAN in_use; + TIMER_LIST_ENT rsp_timer_ent; /* peer response timer */ + UINT8 retry_count; + +} tGATT_CLCB; + +typedef struct { + tGATT_CLCB *p_clcb; +} tGATT_PENDING_ENC_CLCB; + + +#define GATT_SIGN_WRITE 1 +#define GATT_VERIFY_SIGN_DATA 2 + +typedef struct { + BT_HDR hdr; + tGATT_CLCB *p_clcb; +} tGATT_SIGN_WRITE_OP; + +typedef struct { + BT_HDR hdr; + tGATT_TCB *p_tcb; + BT_HDR *p_data; + +} tGATT_VERIFY_SIGN_OP; + + +typedef struct { + UINT16 clcb_idx; + BOOLEAN in_use; +} tGATT_SCCB; + +typedef struct { + UINT16 handle; + UINT16 uuid; + UINT32 service_change; +} tGATT_SVC_CHG; + +typedef struct { + tGATT_IF gatt_if[GATT_MAX_APPS]; + tGATT_IF listen_gif[GATT_MAX_APPS]; + BD_ADDR remote_bda; + BOOLEAN in_use; +} tGATT_BG_CONN_DEV; + +#define GATT_SVC_CHANGED_CONNECTING 1 /* wait for connection */ +#define GATT_SVC_CHANGED_SERVICE 2 /* GATT service discovery */ +#define GATT_SVC_CHANGED_CHARACTERISTIC 3 /* service change char discovery */ +#define GATT_SVC_CHANGED_DESCRIPTOR 4 /* service change CCC discoery */ +#define GATT_SVC_CHANGED_CONFIGURE_CCCD 5 /* config CCC */ + +typedef struct { + UINT16 conn_id; + BOOLEAN in_use; + BOOLEAN connected; + BD_ADDR bda; + tBT_TRANSPORT transport; + + /* GATT service change CCC related variables */ + UINT8 ccc_stage; + UINT8 ccc_result; + UINT16 s_handle; + UINT16 e_handle; +} tGATT_PROFILE_CLCB; + +typedef struct { + list_t *p_tcb_list; + fixed_queue_t *sign_op_queue; + + tGATT_SR_REG sr_reg[GATT_MAX_SR_PROFILES]; + UINT16 next_handle; /* next available handle */ + tGATT_SVC_CHG gattp_attr; /* GATT profile attribute service change */ + tGATT_IF gatt_if; +#if (GATTS_INCLUDED == TRUE) + tGATT_HDL_LIST_INFO hdl_list_info; + tGATT_HDL_LIST_ELEM hdl_list[GATT_MAX_SR_PROFILES]; + tGATT_SRV_LIST_INFO srv_list_info; + tGATT_SRV_LIST_ELEM srv_list[GATT_MAX_SR_PROFILES]; +#endif ///GATTS_INCLUDED == TRUE + fixed_queue_t *srv_chg_clt_q; /* service change clients queue */ + fixed_queue_t *pending_new_srv_start_q; /* pending new service start queue */ + tGATT_REG cl_rcb[GATT_MAX_APPS]; + list_t *p_clcb_list; /* connection link control block*/ + tGATT_SCCB sccb[GATT_MAX_SCCB]; /* sign complete callback function GATT_MAX_SCCB <= GATT_CL_MAX_LCB */ + UINT8 trace_level; + UINT16 def_mtu_size; + +#if GATT_CONFORMANCE_TESTING == TRUE + BOOLEAN enable_err_rsp; + UINT8 req_op_code; + UINT8 err_status; + UINT16 handle; +#endif +#if (GATTS_INCLUDED == TRUE) + tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS]; +#endif ///GATTS_INCLUDED == TRUE + UINT16 handle_of_h_r; /* Handle of the handles reused characteristic value */ +#if GATTS_ROBUST_CACHING_ENABLED + UINT16 handle_of_database_hash; + UINT16 handle_of_cl_supported_feat; + UINT16 handle_of_sr_supported_feat; + BT_OCTET16 database_hash; + UINT8 gatt_sr_supported_feat_mask; + UINT8 gatt_cl_supported_feat_mask; + +#endif + tGATT_APPL_INFO cb_info; + + + + tGATT_HDL_CFG hdl_cfg; + tGATT_BG_CONN_DEV bgconn_dev[GATT_MAX_BG_CONN_DEV]; + + BOOLEAN auto_disc; /* internal use: true for auto discovering after connected */ + UINT8 srv_chg_mode; /* internal use: service change mode */ + tGATTS_RSP rsp; /* use to read internal service attribute */ +} tGATT_CB; + +typedef struct{ + UINT16 local_mtu; +} tGATT_DEFAULT; + +#define GATT_SIZE_OF_SRV_CHG_HNDL_RANGE 4 + +#ifdef __cplusplus +extern "C" { +#endif + +extern tGATT_DEFAULT gatt_default; + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +extern tGATT_CB gatt_cb; +#else +extern tGATT_CB *gatt_cb_ptr; +#define gatt_cb (*gatt_cb_ptr) +#endif + +#if GATT_CONFORMANCE_TESTING == TRUE +extern void gatt_set_err_rsp(BOOLEAN enable, UINT8 req_op_code, UINT8 err_status); +#endif + +#ifdef __cplusplus +} +#endif + +/* internal functions */ +extern void gatt_init (void); +extern void gatt_free(void); + +/* from gatt_main.c */ +extern BOOLEAN gatt_disconnect (tGATT_TCB *p_tcb); +extern BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr, tBLE_ADDR_TYPE bd_addr_type, tBT_TRANSPORT transport, BOOLEAN is_aux); +extern BOOLEAN gatt_connect (BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, tGATT_TCB *p_tcb, tBT_TRANSPORT transport, BOOLEAN is_aux); +extern void gatt_data_process (tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern void gatt_update_app_use_link_flag ( tGATT_IF gatt_if, tGATT_TCB *p_tcb, BOOLEAN is_add, BOOLEAN check_acl_link); + +extern void gatt_profile_db_init(void); +extern void gatt_set_ch_state(tGATT_TCB *p_tcb, tGATT_CH_STATE ch_state); +extern tGATT_CH_STATE gatt_get_ch_state(tGATT_TCB *p_tcb); +extern void gatt_init_srv_chg(void); +extern void gatt_proc_srv_chg (void); +extern tGATT_STATUS gatt_send_srv_chg_ind (BD_ADDR peer_bda); +extern void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt); +extern void gatt_add_a_bonded_dev_for_srv_chg (BD_ADDR bda); + +/* from gatt_attr.c */ +extern UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda); + + +/* Functions provided by att_protocol.c */ +extern tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg); +extern BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg); +extern tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg); +extern tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP); + +/* utility functions */ +extern UINT8 *gatt_dbg_op_name(UINT8 op_code); +#if (SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE) +extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl); +#endif ///SDP_INCLUDED == TRUE && CLASSIC_BT_GATT_INCLUDED == TRUE +extern BOOLEAN gatt_parse_uuid_from_cmd(tBT_UUID *p_uuid, UINT16 len, UINT8 **p_data); +extern UINT8 gatt_build_uuid_to_stream(UINT8 **p_dst, tBT_UUID uuid); +extern BOOLEAN gatt_uuid_compare(tBT_UUID src, tBT_UUID tar); +extern void gatt_convert_uuid32_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT32 uuid_32); +extern char *gatt_uuid_to_str(const tBT_UUID *uuid); +extern void gatt_sr_get_sec_info(BD_ADDR rem_bda, tBT_TRANSPORT transport, UINT8 *p_sec_flag, UINT8 *p_key_size); +extern void gatt_start_rsp_timer(UINT16 clcb_idx); +extern void gatt_start_conf_timer(tGATT_TCB *p_tcb); +extern void gatt_rsp_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_ind_ack_timeout(TIMER_LIST_ENT *p_tle); +extern void gatt_start_ind_ack_timer(tGATT_TCB *p_tcb); +extern tGATT_STATUS gatt_send_error_rsp(tGATT_TCB *p_tcb, UINT8 err_code, UINT8 op_code, UINT16 handle, BOOLEAN deq); +extern void gatt_dbg_display_uuid(tBT_UUID bt_uuid); +extern tGATT_PENDING_ENC_CLCB *gatt_add_pending_enc_channel_clcb(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb ); + +extern tGATTS_PENDING_NEW_SRV_START *gatt_sr_is_new_srv_chg(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); + +extern BOOLEAN gatt_is_srv_chg_ind_pending (tGATT_TCB *p_tcb); +extern tGATTS_SRV_CHG *gatt_is_bda_in_the_srv_chg_clt_list (BD_ADDR bda); + +extern BOOLEAN gatt_find_the_connected_bda(UINT8 start_idx, BD_ADDR bda, UINT8 *p_found_idx, tBT_TRANSPORT *p_transport); +extern void gatt_set_srv_chg(void); +extern void gatt_delete_dev_from_srv_chg_clt_list(BD_ADDR bd_addr); +extern tGATT_VALUE *gatt_add_pending_ind(tGATT_TCB *p_tcb, tGATT_VALUE *p_ind); +extern tGATTS_PENDING_NEW_SRV_START *gatt_add_pending_new_srv_start( tGATTS_HNDL_RANGE *p_new_srv_start); +extern void gatt_free_srvc_db_buffer_app_id(tBT_UUID *p_app_id); +extern BOOLEAN gatt_update_listen_mode(void); +extern BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb); + +/* reserved handle list */ +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_app_id (tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_handle(UINT16 handle); +extern tGATT_HDL_LIST_ELEM *gatt_find_hdl_buffer_by_attr_handle(UINT16 attr_handle); +extern tGATT_HDL_LIST_ELEM *gatt_alloc_hdl_buffer(void); +extern void gatt_free_hdl_buffer(tGATT_HDL_LIST_ELEM *p); +extern void gatt_free_attr_value_buffer(tGATT_HDL_LIST_ELEM *p); +extern BOOLEAN gatt_is_last_attribute(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_start, tBT_UUID value); +extern void gatt_update_last_pri_srv_info(tGATT_SRV_LIST_INFO *p_list); +extern BOOLEAN gatt_add_a_srv_to_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_a_srv_from_list(tGATT_SRV_LIST_INFO *p_list, tGATT_SRV_LIST_ELEM *p_remove); +extern BOOLEAN gatt_add_an_item_to_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_new); +extern BOOLEAN gatt_remove_an_item_from_list(tGATT_HDL_LIST_INFO *p_list, tGATT_HDL_LIST_ELEM *p_remove); +extern tGATTS_SRV_CHG *gatt_add_srv_chg_clt(tGATTS_SRV_CHG *p_srv_chg); + +/* for background connection */ +extern BOOLEAN gatt_update_auto_connect_dev (tGATT_IF gatt_if, BOOLEAN add, BD_ADDR bd_addr, BOOLEAN is_initiator); +extern BOOLEAN gatt_is_bg_dev_for_app(tGATT_BG_CONN_DEV *p_dev, tGATT_IF gatt_if); +extern BOOLEAN gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr); +extern UINT8 gatt_get_num_apps_for_bg_dev(BD_ADDR bd_addr); +extern BOOLEAN gatt_find_app_for_bg_dev(BD_ADDR bd_addr, tGATT_IF *p_gatt_if); +extern tGATT_BG_CONN_DEV *gatt_find_bg_dev(BD_ADDR remote_bda); +extern void gatt_deregister_bgdev_list(tGATT_IF gatt_if); +extern void gatt_reset_bgdev_list(void); + +/* server function */ +extern UINT8 gatt_sr_find_i_rcb_by_handle(UINT16 handle); +extern UINT8 gatt_sr_find_i_rcb_by_app_id(tBT_UUID *p_app_uuid128, tBT_UUID *p_svc_uuid, UINT16 svc_inst); +extern UINT8 gatt_sr_alloc_rcb(tGATT_HDL_LIST_ELEM *p_list); +extern tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if, UINT32 trans_id, UINT8 op_code, tGATT_STATUS status, tGATTS_RSP *p_msg); +extern void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_sr_send_req_callback(UINT16 conn_id, UINT32 trans_id, + UINT8 op_code, tGATTS_DATA *p_req_data); +extern UINT32 gatt_sr_enqueue_cmd (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 handle); +extern BOOLEAN gatt_cancel_open(tGATT_IF gatt_if, BD_ADDR bda); + +/* */ + +extern tGATT_REG *gatt_get_regcb (tGATT_IF gatt_if); +extern BOOLEAN gatt_is_clcb_allocated (UINT16 conn_id); +extern tGATT_CLCB *gatt_clcb_alloc (UINT16 conn_id); +extern void gatt_clcb_dealloc (tGATT_CLCB *p_clcb); +extern tGATT_CLCB *gatt_clcb_find_by_conn_id(UINT16 conn_id); +extern tGATT_CLCB *gatt_clcb_find_by_idx(UINT16 cclcb_idx); + +extern void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_cback_cnt_zero(tGATT_TCB *p_tcb ); +extern BOOLEAN gatt_sr_is_prep_cnt_zero(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_cback_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_reset_prep_cnt(tGATT_TCB *p_tcb ); +extern void gatt_sr_update_cback_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); +extern void gatt_sr_update_prep_cnt(tGATT_TCB *p_tcb, tGATT_IF gatt_if, BOOLEAN is_inc, BOOLEAN is_reset_first); + +extern BOOLEAN gatt_find_app_hold_link(tGATT_TCB *p_tcb, UINT8 start_idx, UINT8 *p_found_idx, tGATT_IF *p_gatt_if); +extern BOOLEAN gatt_find_specific_app_in_hold_link(tGATT_TCB *p_tcb, tGATT_IF p_gatt_if); +extern UINT8 gatt_num_apps_hold_link(tGATT_TCB *p_tcb); +extern UINT8 gatt_num_clcb_by_bd_addr(BD_ADDR bda); +extern tGATT_TCB *gatt_find_tcb_by_cid(UINT16 lcid); +extern tGATT_TCB *gatt_allocate_tcb_by_bdaddr(BD_ADDR bda, tBT_TRANSPORT transport); +extern tGATT_TCB *gatt_get_tcb_by_idx(UINT8 tcb_idx); +extern tGATT_TCB *gatt_find_tcb_by_addr(BD_ADDR bda, tBT_TRANSPORT transport); +extern BOOLEAN gatt_send_ble_burst_data (BD_ADDR remote_bda, BT_HDR *p_buf); +extern void gatt_tcb_free( tGATT_TCB *p_tcb); + +/* GATT client functions */ +extern void gatt_dequeue_sr_cmd (tGATT_TCB *p_tcb); +extern UINT8 gatt_send_write_msg(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, UINT16 handle, + UINT16 len, UINT16 offset, UINT8 *p_data); +extern void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport); +extern void gatt_end_operation(tGATT_CLCB *p_clcb, tGATT_STATUS status, void *p_data); + +extern void gatt_act_discovery(tGATT_CLCB *p_clcb); +extern void gatt_act_read(tGATT_CLCB *p_clcb, UINT16 offset); +extern void gatt_act_write(tGATT_CLCB *p_clcb, UINT8 sec_act); +extern UINT8 gatt_act_send_browse(tGATT_TCB *p_tcb, UINT16 index, UINT8 op, UINT16 s_handle, UINT16 e_handle, + tBT_UUID uuid); +extern tGATT_CLCB *gatt_cmd_dequeue(tGATT_TCB *p_tcb, UINT8 *p_opcode); +extern BOOLEAN gatt_cmd_enq(tGATT_TCB *p_tcb, UINT16 clcb_idx, BOOLEAN to_send, UINT8 op_code, BT_HDR *p_buf); +extern void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, + UINT16 len, UINT8 *p_data); +extern void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag); + +/* gatt_auth.c */ +extern BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb); +extern void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf); +extern tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB *p_clcb ); +extern tGATT_STATUS gatt_get_link_encrypt_status(tGATT_TCB *p_tcb); +extern tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB *p_tcb); +extern void gatt_set_sec_act(tGATT_TCB *p_tcb, tGATT_SEC_ACTION sec_act); + +/* gatt_db.c */ +extern BOOLEAN gatts_init_service_db (tGATT_SVC_DB *p_db, tBT_UUID *p_service, BOOLEAN is_pri, UINT16 s_hdl, UINT16 num_handle); +extern UINT16 gatts_add_included_service (tGATT_SVC_DB *p_db, UINT16 s_handle, UINT16 e_handle, tBT_UUID service); +extern UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tGATT_CHAR_PROP property, + tBT_UUID *p_char_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); +extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, + tBT_UUID *p_dscp_uuid, tGATT_ATTR_VAL *attr_val, + tGATTS_ATTR_CONTROL *control); + +extern tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 length, UINT8 *value); +extern tGATT_STATUS gatts_get_attr_value_internal(UINT16 attr_handle, UINT16 *length, UINT8 **value); +extern tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, + UINT16 *length, UINT8 **value); +extern BOOLEAN gatts_is_auto_response(UINT16 attr_handle); +extern tGATT_STATUS gatts_db_read_attr_value_by_type (tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, BT_HDR *p_rsp, UINT16 s_handle, + UINT16 e_handle, tBT_UUID type, UINT16 *p_len, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id, UINT16 *p_cur_handle); +extern tGATT_STATUS gatts_read_attr_value_by_handle(tGATT_TCB *p_tcb, tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 *p_len, UINT16 mtu, tGATT_SEC_FLAG sec_flag, UINT8 key_size, UINT32 trans_id); +extern tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, + UINT16 handle, UINT16 offset, + UINT8 *p_value, UINT16 len); +extern tGATT_STATUS gatts_write_attr_perm_check (tGATT_SVC_DB *p_db, UINT8 op_code, UINT16 handle, UINT16 offset, UINT8 *p_data, + UINT16 len, tGATT_SEC_FLAG sec_flag, UINT8 key_size); +extern tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB *p_db, BOOLEAN is_long, UINT16 handle, tGATT_SEC_FLAG sec_flag, UINT8 key_size); +extern void gatts_update_srv_list_elem(UINT8 i_sreg, UINT16 handle, BOOLEAN is_primary); +extern tBT_UUID *gatts_get_service_uuid (tGATT_SVC_DB *p_db); + +extern BOOLEAN gatt_check_connection_state_by_tcb(tGATT_TCB *p_tcb); + +extern void gatt_reset_bgdev_list(void); +extern uint16_t gatt_get_local_mtu(void); +extern void gatt_set_local_mtu(uint16_t mtu); + +extern tGATT_STATUS gatts_calculate_datebase_hash(BT_OCTET16 hash); +extern void gatts_show_local_database(void); + +extern BOOLEAN gatt_sr_is_cl_change_aware(tGATT_TCB *p_tcb); +extern void gatt_sr_init_cl_status(tGATT_TCB *p_tcb); +extern void gatt_sr_update_cl_status(tGATT_TCB *tcb, BOOLEAN chg_aware); +#endif |
