summaryrefslogtreecommitdiff
path: root/lib/bt/host/bluedroid/stack/gatt
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
committerjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
commitee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch)
tree8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/gatt
parent239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff)
downloadtangara-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.c638
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_api.c1805
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_attr.c797
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_auth.c522
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_cl.c1241
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_db.c1620
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_main.c1250
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_sr.c1900
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_sr_hash.c256
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/gatt_utils.c2948
-rw-r--r--lib/bt/host/bluedroid/stack/gatt/include/gatt_int.h788
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 *)&notif))
+ != 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 *)&notif);
+ 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