summaryrefslogtreecommitdiffstats
path: root/stack/gatt
diff options
context:
space:
mode:
authorAndre Eisenbach <andre@broadcom.com>2012-02-22 13:18:21 -0800
committerMatthew Xie <mattx@google.com>2012-07-14 11:19:11 -0700
commite448862a47c08eb23185aaed574b39264f5005fc (patch)
tree2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/gatt
parenta2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff)
downloadexternal_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip
external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz
external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2
Initial Bluedroid stack commit
Diffstat (limited to 'stack/gatt')
-rw-r--r--stack/gatt/att_protocol.c617
-rw-r--r--stack/gatt/gatt_api.c1542
-rw-r--r--stack/gatt/gatt_attr.c264
-rw-r--r--stack/gatt/gatt_auth.c435
-rw-r--r--stack/gatt/gatt_cl.c1206
-rw-r--r--stack/gatt/gatt_db.c1117
-rw-r--r--stack/gatt/gatt_int.h664
-rw-r--r--stack/gatt/gatt_main.c1101
-rw-r--r--stack/gatt/gatt_sr.c1472
-rw-r--r--stack/gatt/gatt_utils.c2587
10 files changed, 11005 insertions, 0 deletions
diff --git a/stack/gatt/att_protocol.c b/stack/gatt/att_protocol.c
new file mode 100644
index 0000000..f3c3a28
--- /dev/null
+++ b/stack/gatt/att_protocol.c
@@ -0,0 +1,617 @@
+/*****************************************************************************
+**
+** Name: att_protocl.c
+**
+** Description: this file contains ATT protocol functions
+**
+**
+** Copyright (c) 2008-2010, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+
+#include "gatt_int.h"
+#include "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 *)GKI_getbuf(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 *)GKI_getbuf((UINT16)(sizeof(BT_HDR) + 10 + L2CAP_MIN_OFFSET))) != 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 *)GKI_getbuf(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;
+
+ if ((p_buf = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + 8 + 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_handles_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 *)GKI_getbuf((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(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle)
+{
+ BT_HDR *p_buf = NULL;
+ UINT8 *p, i = 0;
+
+ if ((p_buf = (BT_HDR *)GKI_getbuf((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, GATT_REQ_READ_MULTI);
+
+ 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 *)GKI_getbuf(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 *)GKI_getbuf(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 *)GKI_getbuf((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)
+ {
+ 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 (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_WARNING1("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.
+**
+*******************************************************************************/
+BOOLEAN 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
+ l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP);
+
+ if (l2cap_ret == L2CAP_DW_FAILED)
+ {
+ GATT_TRACE_ERROR1("ATT failed to pass msg:0x%0x to L2CAP",
+ *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset));
+ GKI_freebuf(p_toL2CAP);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+/*******************************************************************************
+**
+** 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:
+ GATT_TRACE_EVENT2 ("ATT_RSP_READ_BLOB: len = %d offset = %d", p_msg->attr_value.len, p_msg->attr_value.offset);
+ offset = p_msg->attr_value.offset;
+
+ case GATT_RSP_PREPARE_WRITE:
+ if (offset == 0)
+ offset = p_msg->attr_value.offset;
+
+ case GATT_RSP_READ_BY_TYPE:
+ case GATT_RSP_READ:
+ case GATT_HANDLE_VALUE_NOTIF:
+ case GATT_HANDLE_VALUE_IND:
+ 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_DEBUG1("attp_build_sr_msg: unknown op code = %d", op_code);
+ break;
+ }
+
+ if (!p_cmd)
+ GATT_TRACE_ERROR0("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 sucessfully 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;
+
+ if (attp_send_msg_to_L2CAP (p_tcb, p_msg))
+ cmd_sent = GATT_SUCCESS;
+ else
+ cmd_sent = GATT_INTERNAL_ERROR;
+ }
+ }
+ return cmd_sent;
+}
+
+/*******************************************************************************
+**
+** Function attp_cl_send_cmd
+**
+** Description Send a ATT command or enqueue it.
+**
+** Returns TRUE if command sent, otherwise FALSE.
+**
+*******************************************************************************/
+UINT8 attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
+{
+ UINT8 att_ret = GATT_SUCCESS;
+
+ if (p_tcb != NULL)
+ {
+ cmd_code &= ~GATT_AUTH_SIGN_MASK;
+
+ if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
+ cmd_code == GATT_HANDLE_VALUE_CONF)
+ {
+ /* no penindg request or value confirmation */
+ if (attp_send_msg_to_L2CAP(p_tcb, p_cmd))
+ {
+ /* 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 (p_tcb);
+ gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
+ }
+ }
+ else
+ att_ret = GATT_INTERNAL_ERROR;
+ }
+ else
+ gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
+ }
+ else
+ att_ret = GATT_ILLEGAL_PARAMETER;
+
+ 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 connectino control block.
+** clcb_idx: clcb index
+** op_code: message op code.
+** p_msg: pointer to message parameters structure.
+**
+** Returns GATT_SUCCESS if sucessfully 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_handles_cmd(p_tcb->payload_size, &p_msg->find_type_value);
+ break;
+
+ case GATT_REQ_READ_MULTI:
+ p_cmd = attp_build_read_multi_cmd(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_ERROR0("Peer device not connected");
+ }
+
+ return status;
+}
+#endif
diff --git a/stack/gatt/gatt_api.c b/stack/gatt/gatt_api.c
new file mode 100644
index 0000000..c01d617
--- /dev/null
+++ b/stack/gatt/gatt_api.c
@@ -0,0 +1,1542 @@
+/*****************************************************************************
+**
+** Name: gatt_api.c
+**
+** Description: this file contains GATT interface functions
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+#include "bt_target.h"
+
+
+#if defined(BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE)
+
+#include "gki.h"
+#include <stdio.h>
+#include <string.h>
+#include "gatt_api.h"
+#include "gatt_int.h"
+#include "l2c_api.h"
+#include "btm_int.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);
+}
+
+/*****************************************************************************
+**
+** 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 sucessfully; 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 (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;
+}
+
+/*******************************************************************************
+**
+** 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_API0 ("GATTS_CreateService" );
+
+ if (p_reg == NULL)
+ {
+ GATT_TRACE_ERROR1 ("Inavlid gatt_if=%d", 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_DEBUG0 ("Service already been created!!");
+ }
+ 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;
+ }
+ 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;
+ }
+ 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_ERROR2 ("GATTS_ReserveHandles: no handles, s_hdl: %u needed: %u", s_hdl, num_handles);
+ return(0);
+ }
+
+ if ( (p_list = gatt_alloc_hdl_buffer()) == NULL)
+ {
+ /* No free entry */
+ GATT_TRACE_ERROR0 ("GATTS_ReserveHandles: no free handle blocks");
+ 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_ERROR0 ("gatt_add_pending_new_srv_start: no free blocks");
+
+ if (p_list)
+ {
+ gatt_remove_an_item_from_list(p_list_info, p_list);
+ gatt_free_hdl_buffer(p_list);
+ }
+ return(0);
+ }
+
+ GATT_TRACE_DEBUG0 ("Add a new srv chg item");
+ }
+ }
+
+ if (!gatts_init_service_db(&p_list->svc_db, *p_svc_uuid, is_pri, s_hdl , num_handles))
+ {
+ GATT_TRACE_ERROR0 ("GATTS_ReserveHandles: service DB initialization failed");
+ if (p_list)
+ {
+ gatt_remove_an_item_from_list(p_list_info, p_list);
+ gatt_free_hdl_buffer(p_list);
+ }
+
+ if (p_buf)
+ GKI_freebuf (GKI_remove_from_queue (&gatt_cb.pending_new_srv_start_q, p_buf));
+ return(0);
+ }
+
+ GATT_TRACE_DEBUG6 ("GATTS_CreateService(success): handles needed:%u s_hdl=%u e_hdl=%u %s[%x] is_primary=%d",
+ num_handles, p_list->asgn_range.s_handle , p_list->asgn_range.e_handle,
+ ((p_list->asgn_range.svc_uuid.len == 2) ? "uuid16": "uuid128" ),
+ p_list->asgn_range.svc_uuid.uu.uuid16,
+ p_list->asgn_range.is_primary);
+
+ 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_DEBUG0("Service not created");
+ return 0;
+ }
+ if ((p_incl_decl = gatt_find_hdl_buffer_by_handle(include_svc_handle)) == NULL)
+ {
+ GATT_TRACE_DEBUG0("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_HDL_LIST_ELEM *p_decl;
+
+ if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL)
+ {
+ GATT_TRACE_DEBUG0("Service not created");
+ 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_DEBUG2("Invalid configuration property=0x%x perm=0x%x ", property, perm);
+ return 0;
+ }
+
+ return gatts_add_characteristic(&p_decl->svc_db,
+ perm,
+ property,
+ p_char_uuid);
+}
+/*******************************************************************************
+**
+** 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_HDL_LIST_ELEM *p_decl;
+
+ if ((p_decl = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL)
+ {
+ GATT_TRACE_DEBUG0("Service not created");
+ return 0;
+ }
+ if (p_descr_uuid == NULL ||
+ (p_descr_uuid->len != LEN_UUID_128 && p_descr_uuid->len != LEN_UUID_16))
+ {
+ GATT_TRACE_DEBUG0("Illegal parameter");
+ return 0;
+ }
+
+ return gatts_add_char_descr(&p_decl->svc_db,
+ perm,
+ p_descr_uuid);
+
+}
+/*******************************************************************************
+**
+** 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_DEBUG0 ("GATTS_DeleteService");
+
+ if (p_reg == NULL)
+ {
+ GATT_TRACE_ERROR0 ("Applicaiton 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_ERROR0 ("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_DEBUG0 ("Delete a new service changed item - the service has not yet started");
+ GKI_freebuf (GKI_remove_from_queue (&gatt_cb.pending_new_srv_start_q, p_buf));
+ }
+ else
+ {
+ 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_DEBUG2 ("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_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 sucessfully 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;
+ tBT_UUID *p_uuid;
+ tGATT_REG *p_reg = gatt_get_regcb(gatt_if);
+
+ tGATTS_PENDING_NEW_SRV_START *p_buf;
+
+ GATT_TRACE_API0 ("GATTS_StartService");
+
+ if (p_reg == NULL)
+ {
+ /* Not found */
+ GATT_TRACE_ERROR0 ("Applicaiton not found ");
+ return GATT_NOT_FOUND;
+ }
+
+ if ((p_list = gatt_find_hdl_buffer_by_handle(service_handle)) == NULL)
+ {
+ /* Not found */
+ GATT_TRACE_ERROR0 ("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_ERROR0 ("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_ERROR0 ("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)
+ {
+ 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);
+ }
+ 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_DEBUG1 ("allocated i_sreg=%d ",i_sreg);
+
+ GATT_TRACE_DEBUG5 ("s_hdl=%d e_hdl=%d type=0x%x svc_inst=%d sdp_hdl=0x%x",
+ 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)
+ {
+ gatt_proc_srv_chg();
+ /* remove the new service element after the srv changed processing is completed*/
+
+ GKI_freebuf (GKI_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_API1("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 (gatt_cb.sr_reg[ii].sdp_handle)
+ {
+ SDP_DeleteRecord(gatt_cb.sr_reg[ii].sdp_handle);
+ }
+ 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_ERROR1("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 sucessfully 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_ILLEGAL_PARAMETER;
+
+ tGATT_VALUE indication;
+ BT_HDR *p_msg;
+ tGATT_VALUE *p_buf;
+ 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_API0 ("GATTS_HandleValueIndication");
+ if ( (p_reg == NULL) || (p_tcb == NULL))
+ {
+ GATT_TRACE_ERROR1 ("GATTS_HandleValueIndication Unknown conn_id: %u ", conn_id);
+ return(tGATT_STATUS) GATT_INVALID_CONN_ID;
+ }
+ 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 (attr_handle) )
+ {
+ if (GATT_HANDLE_IS_VALID(p_tcb->indicate_handle))
+ {
+ GATT_TRACE_DEBUG0 ("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;
+ }
+ }
+ 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)
+ {
+ 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 sucessfully 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_API0 ("GATTS_HandleValueNotification");
+
+ if ( (p_reg == NULL) || (p_tcb == NULL))
+ {
+ GATT_TRACE_ERROR1 ("GATTS_HandleValueNotification Unknown conn_id: %u ", conn_id);
+ return(tGATT_STATUS) GATT_INVALID_CONN_ID;
+ }
+
+ 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;;
+
+ p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_VALUE_NOTIF, (tGATT_SR_MSG *)&notif);
+ cmd_sent = attp_send_sr_msg (p_tcb, p_buf);
+ }
+ 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 sucessfully 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_API3 ("GATTS_SendRsp: conn_id: %u trans_id: %u Status: 0x%04x",
+ conn_id, trans_id, status);
+
+ if ( (p_reg == NULL) || (p_tcb == NULL))
+ {
+ GATT_TRACE_ERROR1 ("GATTS_SendRsp Unknown conn_id: %u ", conn_id);
+ return(tGATT_STATUS) GATT_INVALID_CONN_ID;
+ }
+
+ if (p_tcb->sr_cmd.trans_id != trans_id)
+ {
+ GATT_TRACE_ERROR2 ("GATTS_SendRsp conn_id: %u waiting for op_code = %02x",
+ 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;
+}
+
+/*******************************************************************************/
+/* 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, UINT16 mtu)
+{
+ 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;
+
+ GATT_TRACE_API2 ("GATTC_ConfigureMTU conn_id=%d mtu=%d", conn_id, mtu );
+
+ // Validate that the link is BLE, not BR/EDR
+ // ????
+
+ if ( (p_tcb == NULL) || (p_reg==NULL) || (mtu < GATT_DEF_BLE_MTU_SIZE) || (mtu > GATT_MAX_MTU_SIZE))
+ {
+ return GATT_ILLEGAL_PARAMETER;
+ }
+
+ if (gatt_is_clcb_allocated(conn_id))
+ {
+ GATT_TRACE_ERROR1("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_API2 ("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_ERROR2("GATTC_Discover Illegal param: disc_type %d conn_id = %d", disc_type, conn_id);
+ return GATT_ILLEGAL_PARAMETER;
+ }
+
+
+ if (gatt_is_clcb_allocated(conn_id))
+ {
+ GATT_TRACE_ERROR1("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))
+ {
+ 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_API2 ("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_ERROR2("GATT_Read Illegal param: conn_id %d, type 0%d,", conn_id, type);
+ return GATT_ILLEGAL_PARAMETER;
+ }
+
+ if (gatt_is_clcb_allocated(conn_id))
+ {
+ GATT_TRACE_ERROR1("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:
+ p_clcb->s_handle = 0;
+ /* copy multiple handles in CB */
+ p_read_multi = (tGATT_READ_MULTI *)GKI_getbuf(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_ERROR2("GATT_Write Illegal param: conn_id %d, type 0%d,", conn_id, type);
+ return GATT_ILLEGAL_PARAMETER;
+ }
+
+ if (gatt_is_clcb_allocated(conn_id))
+ {
+ GATT_TRACE_ERROR1("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 *)GKI_getbuf((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 requet(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_API2 ("GATTC_ExecuteWrite conn_id=%d is_execute=%d", conn_id, is_execute);
+
+ if ( (p_tcb == NULL) || (p_reg==NULL) )
+ {
+ GATT_TRACE_ERROR1("GATTC_ExecuteWrite Illegal param: conn_id %d", conn_id);
+ return GATT_ILLEGAL_PARAMETER;
+ }
+
+ if (gatt_is_clcb_allocated(conn_id))
+ {
+ GATT_TRACE_ERROR1("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_ERROR1("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_API2 ("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_DEBUG1 ("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_DEBUG1 ("GATTC_SendHandleValueConfirm - conn_id: %u - ignored not waiting for indicaiton ack", conn_id);
+ ret = GATT_SUCCESS;
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR1 ("GATTC_SendHandleValueConfirm - Unknown conn_id: %u", conn_id);
+ }
+ return ret;
+}
+
+
+/*******************************************************************************/
+/* */
+/* 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)
+{
+ tGATT_TCB *p_tcb;
+ BOOLEAN status = FALSE;
+
+ if ((p_tcb = gatt_find_tcb_by_addr (bd_addr)) != NULL)
+ {
+ if (p_tcb->att_lcid == L2CAP_ATT_CID)
+ {
+ status = L2CA_SetFixedChannelTout (bd_addr, L2CAP_ATT_CID, idle_tout);
+ }
+ else
+ {
+ status = L2CA_SetIdleTimeout (p_tcb->att_lcid, idle_tout, FALSE);
+ }
+ }
+
+ GATT_TRACE_API2 ("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, tGATT_CBACK *p_cb_info)
+{
+ tGATT_REG *p_reg;
+ UINT8 i_gatt_if=0;
+ tGATT_IF gatt_if=0;
+
+ GATT_TRACE_API0 ("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_ERROR0("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_API1 ("allocated gatt_if=%d", gatt_if);
+ return gatt_if;
+}
+
+
+/*******************************************************************************
+**
+** Function GATT_Deregister
+**
+** Description This function deregistered the application from GATT.
+**
+** Parameters gatt_if: applicaiton 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;
+ UINT8 i, ii, j;
+ tGATT_SR_REG *p_sreg;
+
+ GATT_TRACE_API1 ("GATT_Deregister gatt_if=%d", gatt_if);
+ /* Index 0 is GAP and is never deregistered */
+ if ( (gatt_if == 0) || (p_reg == NULL) )
+ {
+ GATT_TRACE_ERROR1 ("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 */
+
+ 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);
+
+ /* When an application deregisters, check remove the link associated with the app */
+
+ for (i=0, p_tcb = gatt_cb.tcb; i < GATT_MAX_PHY_CHANNEL; i++, p_tcb++)
+ {
+ 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->peer_bda);
+ }
+ }
+
+ for (j = 0, p_clcb= &gatt_cb.clcb[j]; j < GATT_CL_MAX_LCB; j++, p_clcb++)
+ {
+ if (p_clcb->in_use &&
+ (p_clcb->p_reg->gatt_if == gatt_if) &&
+ (p_clcb->p_tcb->tcb_idx == p_tcb->tcb_idx))
+ {
+ gatt_clcb_dealloc (p_clcb);
+ break;
+ }
+ }
+ }
+ }
+
+ gatt_deregister_bgdev_list(gatt_if);
+ 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: applicaiton interface.
+**
+** Returns 0 for error, otherwise the index of the client registered with GATT
+**
+*******************************************************************************/
+void GATT_StartIf (tGATT_IF gatt_if)
+{
+ tGATT_REG *p_reg;
+ tGATT_TCB *p_tcb;
+ //tGATT_CLCB *p_clcb;
+ BD_ADDR bda;
+ UINT8 start_idx, found_idx;
+ UINT16 conn_id;
+
+ GATT_TRACE_API1 ("GATT_StartIf gatt_if=%d", gatt_if);
+ if ((p_reg = gatt_get_regcb(gatt_if)) != NULL)
+ {
+ p_reg = &gatt_cb.cl_rcb[gatt_if - 1];
+ start_idx = 0;
+ while (gatt_find_the_connected_bda(start_idx, bda, &found_idx))
+ {
+ p_tcb = gatt_find_tcb_by_addr(bda);
+ if (p_reg->app_cb.p_conn_cb)
+ {
+ 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);
+ }
+ start_idx = ++found_idx;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function GATT_Connect
+**
+** Description This function initiate a connecttion to a ATT server.
+**
+** Parameters gatt_if: applicaiton interface
+** bd_addr: peer device address.
+** is_direct: is a direct conenection 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, BOOLEAN is_direct){
+ tGATT_REG *p_reg;
+ BOOLEAN status;
+
+ GATT_TRACE_API1 ("GATT_Connect gatt_if=%d", gatt_if);
+
+ /* Make sure app is registered */
+ if ((p_reg = gatt_get_regcb(gatt_if)) == NULL)
+ {
+ GATT_TRACE_ERROR1("GATT_Connect - gatt_if =%d is not registered", gatt_if);
+ return(FALSE);
+ }
+
+ if (is_direct)
+ status = gatt_act_connect (p_reg, bd_addr);
+ else
+ status = gatt_update_auto_connect_dev(gatt_if,TRUE, bd_addr);
+
+ return status;
+
+}
+
+/*******************************************************************************
+**
+** Function GATT_CancelConnect
+**
+** Description This function initiate a connecttion to a ATT server.
+**
+** 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_API1 ("GATT_CancelConnect gatt_if=%d", gatt_if);
+
+ if ((gatt_if != 0) && ((p_reg = gatt_get_regcb(gatt_if)) == NULL))
+ {
+ GATT_TRACE_ERROR1("GATT_CancelConnect - gatt_if =%d is not registered", gatt_if);
+ return(FALSE);
+ }
+
+ if (is_direct)
+ {
+ if (!gatt_if)
+ {
+ GATT_TRACE_DEBUG0("GATT_CancelConnect - unconditional");
+ start_idx = 0;
+ p_tcb = gatt_find_tcb_by_addr(bd_addr);
+ 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_ERROR0("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_ERROR0("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 a logic channel.
+**
+** 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_API1 ("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->peer_bda);
+ }
+ ret = GATT_SUCCESS;
+ }
+ return ret;
+}
+
+
+/*******************************************************************************
+**
+** 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: applicaiton interface (output)
+** bd_addr: peer device address. (output)
+**
+** Returns TRUE the ligical link information is found for conn_id
+**
+*******************************************************************************/
+BOOLEAN GATT_GetConnectionInfor(UINT16 conn_id, tGATT_IF *p_gatt_if, BD_ADDR bd_addr)
+{
+
+ 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_API1 ("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;
+ 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: applicaiton interface (input)
+** bd_addr: peer device address. (input)
+** p_conn_id: connection id (output)
+**
+** Returns TRUE the ligical link is connected
+**
+*******************************************************************************/
+BOOLEAN GATT_GetConnIdIfConnected(tGATT_IF gatt_if, BD_ADDR bd_addr, UINT16 *p_conn_id)
+{
+ tGATT_REG *p_reg = gatt_get_regcb(gatt_if);
+ tGATT_TCB *p_tcb= gatt_find_tcb_by_addr(bd_addr);
+ 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_API1 ("GATT_GetConnIdIfConnected status=%d", status);
+ return status;
+}
+
+#endif
+
+
diff --git a/stack/gatt/gatt_attr.c b/stack/gatt/gatt_attr.c
new file mode 100644
index 0000000..d8b417f
--- /dev/null
+++ b/stack/gatt/gatt_attr.c
@@ -0,0 +1,264 @@
+/*****************************************************************************
+**
+** Name: gatt_attr.c
+**
+** Description: this file contains the main GATT server attributes access
+** request handling functions.
+**
+** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#include "gatt_api.h"
+#include "gatt_int.h"
+
+#if BLE_INCLUDED == TRUE
+
+#define GATTP_MAX_NUM_INC_SVR 0
+#define GATTP_MAX_CHAR_NUM 2
+#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_profile_request_cback (UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data);
+static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, tGATT_DISCONN_REASON reason);
+
+static tGATT_CBACK gatt_profile_cback =
+{
+ gatt_profile_connect_cback,
+ NULL,
+ NULL,
+ NULL,
+ gatt_profile_request_cback
+} ;
+
+/*******************************************************************************
+**
+** Function gatt_profile_find_conn_id_by_bd_addr
+**
+** Description The function searches all LCB with macthing bd address
+**
+** Returns total number of clcb found.
+**
+*******************************************************************************/
+UINT16 gatt_profile_find_conn_id_by_bd_addr(BD_ADDR bda)
+{
+ 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->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN))
+ {
+ return p_clcb->conn_id;
+ }
+ }
+
+ return GATT_INVALID_CONN_ID;
+}
+
+/*******************************************************************************
+**
+** 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.
+**
+*******************************************************************************/
+tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda)
+{
+ 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->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN))
+ {
+ return p_clcb;
+ }
+ }
+
+ return p_clcb;
+}
+
+/*******************************************************************************
+**
+** 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)
+{
+ 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;
+ memcpy (p_clcb->bda, bda, BD_ADDR_LEN);
+ break;
+ }
+ }
+ return p_clcb;
+}
+/*******************************************************************************
+**
+** Function gatt_profile_clcb_dealloc
+**
+** Description The function deallocates a GATT profile connection link control block
+**
+** Returns NTrue the deallocation is successful
+**
+*******************************************************************************/
+BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id)
+{
+ 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->connected && (p_clcb->conn_id == conn_id))
+ {
+ memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/*******************************************************************************
+**
+** Function gatt_profile_request_cback
+**
+** Description GATT profile attribute access request callback.
+**
+** Returns void.
+**
+*******************************************************************************/
+static void gatt_profile_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;
+
+ memset(&rsp_msg, 0, sizeof(tGATTS_RSP));
+
+ switch (type)
+ {
+ case GATTS_REQ_TYPE_READ:
+ status = GATT_READ_NOT_PERMIT;
+ break;
+
+ case GATTS_REQ_TYPE_WRITE:
+ status = GATT_WRITE_NOT_PERMIT;
+ break;
+
+ case GATTS_REQ_TYPE_WRITE_EXEC:
+ //case GATT_CMD_WRITE:
+ ignore = TRUE;
+ GATT_TRACE_EVENT0("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" );
+ break;
+
+ case GATTS_REQ_TYPE_MTU:
+ GATT_TRACE_EVENT1("Get MTU exchange new mtu size: %d", p_data->mtu);
+ ignore = TRUE;
+ break;
+
+ default:
+ GATT_TRACE_EVENT1("Unknown/unexpected LE GAP ATT request: 0x%02x", type);
+ break;
+ }
+
+ if (!ignore)
+ GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg);
+
+}
+
+/*******************************************************************************
+**
+** Function gatt_profile_connect_cback
+**
+** Description Gatt profile connection callback.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_profile_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id,
+ BOOLEAN connected, tGATT_DISCONN_REASON reason)
+{
+ GATT_TRACE_EVENT5 ("gatt_profile_connect_cback: from %08x%04x connected:%d conn_id=%d reason = 0x%04x",
+ (bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3],
+ (bda[4]<<8)+bda[5], connected, conn_id, reason);
+
+ if (connected)
+ {
+ if (gatt_profile_clcb_alloc(conn_id, bda) == NULL)
+ {
+ GATT_TRACE_ERROR0 ("gatt_profile_connect_cback: no_resource");
+ return;
+ }
+ }
+ else
+ {
+ gatt_profile_clcb_dealloc(conn_id);
+ }
+
+}
+
+/*******************************************************************************
+**
+** 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);
+ /* 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);
+
+ GATT_TRACE_DEBUG1 ("gatt_profile_db_init: handle of service changed%d",
+ gatt_cb.handle_of_h_r );
+
+ /* start service
+ */
+ status = GATTS_StartService (gatt_cb.gatt_if, service_handle, GATT_TRANSPORT_LE_BR_EDR);
+
+ GATT_TRACE_DEBUG2 ("gatt_profile_db_init: gatt_if=%d start status%d",
+ gatt_cb.gatt_if, status);
+}
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_auth.c b/stack/gatt/gatt_auth.c
new file mode 100644
index 0000000..b7fa13f
--- /dev/null
+++ b/stack/gatt/gatt_auth.c
@@ -0,0 +1,435 @@
+/*****************************************************************************
+**
+** Name: gatt_auth.c
+**
+** Description: this file contains GATT authentication handling functions
+**
+**
+** Copyright (c) 1999-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+#include <string.h>
+#include "gki.h"
+
+#include "gatt_int.h"
+#include "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.
+**
+*******************************************************************************/
+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;
+
+ p_data = (UINT8 *)GKI_getbuf((UINT16)(p_attr->len + 3)); /* 3 = 2 byte handle + opcode */
+
+ if (p_data != NULL)
+ {
+ p = p_data;
+ UINT8_TO_STREAM(p, GATT_SIGN_CMD_WRITE);
+ UINT16_TO_STREAM(p, p_attr->handle);
+ ARRAY_TO_STREAM(p, p_attr->value, p_attr->len);
+
+ /* sign data length should be attribulte value length plus 2B handle + 1B op code */
+ if ((payload_size - GATT_AUTH_SIGN_LEN - 3) < p_attr->len)
+ p_attr->len = payload_size - GATT_AUTH_SIGN_LEN - 3;
+
+ p_signature = p_attr->value + p_attr->len;
+ if (BTM_BleDataSignature(p_clcb->p_tcb->peer_bda,
+ p_data,
+ (UINT16)(p_attr->len + 3), /* 3 = 2 byte handle + opcode */
+ p_signature))
+ {
+ p_attr->len += BTM_BLE_AUTH_SIGN_LEN;
+ gatt_set_ch_state(p_clcb->p_tcb, GATT_CH_OPEN);
+ gatt_act_write(p_clcb);
+ }
+ else
+ {
+ gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL);
+ }
+
+ GKI_freebuf(p_data);
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** Function gatt_verify_signature
+**
+** Description This function start to verify the sign data when receiving
+** the data from peer device.
+**
+** Returns
+**
+*******************************************************************************/
+void gatt_verify_signature(tGATT_TCB *p_tcb, BT_HDR *p_buf)
+{
+ UINT16 cmd_len;
+ UINT8 op_code;
+ UINT8 *p, *p_orig = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ UINT32 counter;
+
+ 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))
+ {
+ STREAM_TO_UINT8(op_code, p_orig);
+ gatt_server_handle_client_req (p_tcb, op_code, (UINT16)(p_buf->len - 1), p_orig);
+ }
+ else
+ {
+ /* if this is a bad signature, assume from attacker, ignore it */
+ GATT_TRACE_ERROR0("Signature Verification Failed");
+ gatt_disconnect(p_tcb->peer_bda);
+ }
+
+ return;
+}
+/*******************************************************************************
+**
+** 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)
+{
+ p_clcb->p_tcb->p_clcb = NULL;
+ gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE);
+
+ 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);
+ }
+ else if (p_clcb->operation == GATTC_OPTYPE_READ)
+ {
+ gatt_act_read(p_clcb, p_clcb->counter);
+ }
+}
+/*******************************************************************************
+**
+** Function gatt_enc_cmpl_cback
+**
+** Description link encryption complete callback.
+**
+** Returns
+**
+*******************************************************************************/
+void gatt_enc_cmpl_cback(BD_ADDR bd_addr, void *p_ref_data, tBTM_STATUS result)
+{
+ tGATT_TCB *p_tcb;
+ UINT8 sec_flag;
+ BOOLEAN status = FALSE;
+
+ GATT_TRACE_DEBUG0("gatt_enc_cmpl_cback");
+ if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL)
+ {
+ gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
+
+ if (result == BTM_SUCCESS)
+ {
+ if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM )
+ {
+ BTM_GetSecurityFlags(bd_addr, &sec_flag);
+ if (sec_flag & sec_flag & BTM_SEC_FLAG_LKEY_AUTHED)
+ {
+ status = TRUE;
+ }
+ }
+ else
+ {
+ status = TRUE;
+ }
+ }
+ gatt_sec_check_complete(status , (tGATT_CLCB *)p_tcb->p_clcb);
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("enc callback for unknown bd_addr");
+ }
+}
+
+/*******************************************************************************
+**
+** 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_le_link=FALSE;
+ BOOLEAN is_link_key_known=FALSE;
+ BOOLEAN is_key_mitm=FALSE;
+ UINT8 key_type;
+
+ if (auth_req == GATT_AUTH_REQ_NONE )
+ return act;
+
+ is_le_link = btm_ble_check_link_type(p_tcb->peer_bda);
+ BTM_GetSecurityFlags(p_tcb->peer_bda, &sec_flag);
+
+ if (sec_flag & BTM_SEC_FLAG_ENCRYPTED)
+ {
+ is_link_encrypted = TRUE;
+ }
+ if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)
+ {
+ 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 (is_le_link &&
+ (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)
+ {
+ btm_ble_get_enc_key_type(p_tcb->peer_bda, &key_type);
+
+ if ( (key_type & BTM_LE_KEY_LCSRK) &&
+ ((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_GetSecurityFlags(p_tcb->peer_bda, &sec_flag);
+
+ 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_DEBUG1("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;
+ tBTM_STATUS btm_status;
+
+ if ( gatt_get_ch_state(p_tcb) == GATT_CH_OPEN)
+ {
+ gatt_sec_act = gatt_determine_sec_act(p_clcb);
+ gatt_set_sec_act(p_tcb, gatt_sec_act);
+ switch (gatt_sec_act )
+ {
+ case GATT_SEC_SIGN_DATA:
+ GATT_TRACE_DEBUG0("gatt_security_check_start: Do data signing");
+ gatt_set_ch_state(p_tcb, GATT_CH_W4_DATA_SIGN_COMP);
+ gatt_sign_data(p_clcb);
+ break;
+ case GATT_SEC_ENCRYPT:
+ case GATT_SEC_ENCRYPT_NO_MITM:
+ case GATT_SEC_ENCRYPT_MITM:
+ GATT_TRACE_DEBUG0("gatt_security_check_start: Encrypt now or key upgreade first");
+ gatt_convert_sec_action(p_tcb->sec_act, &btm_ble_sec_act);
+ gatt_set_ch_state(p_tcb, GATT_CH_W4_SEC_COMP);
+ p_tcb->p_clcb = p_clcb; /* keep the clcb pointer in CCB */
+ btm_status = BTM_SetEncryption(p_tcb->peer_bda, gatt_enc_cmpl_cback, &btm_ble_sec_act);
+ if ( (btm_status != BTM_SUCCESS) && (btm_status != BTM_CMD_STARTED))
+ {
+ GATT_TRACE_ERROR1("gatt_security_check_start BTM_SetEncryption failed btm_status=%d", btm_status);
+ p_tcb->p_clcb = NULL;
+ status = FALSE;
+ }
+ break;
+ default:
+ gatt_sec_check_complete(TRUE, p_clcb);
+ break;
+ }
+
+ if (status == FALSE)
+ {
+ gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
+ gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("gatt_security_check_start channel not open");
+ status = FALSE;
+ }
+
+ return status;
+}
+
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_cl.c b/stack/gatt/gatt_cl.c
new file mode 100644
index 0000000..9853694
--- /dev/null
+++ b/stack/gatt/gatt_cl.c
@@ -0,0 +1,1206 @@
+/*****************************************************************************
+**
+** Name: gatt_cl.c
+**
+** Description: this file contains the main GATT client functions
+**
+** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+
+#include <string.h>
+#include "gki.h"
+#include "gatt_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)
+
+/********************************************************************************
+** 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);
+
+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_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */
+};
+
+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 */
+};
+
+
+/*******************************************************************************
+**
+** 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;
+
+ 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;
+ memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len);
+ }
+
+ if (attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req) != GATT_SUCCESS)
+ {
+ 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_DEBUG1("gatt_act_read first_read_blob_after_read=%d",
+ p_clcb->first_read_blob_after_read);
+ op_code = GATT_REQ_READ_BLOB;
+ msg.read_blob.offset = offset;
+ msg.read_blob.handle = p_clcb->s_handle;
+ }
+ p_clcb->op_subtype &= ~ 0x80;
+ break;
+
+ case GATT_READ_PARTIAL:
+ op_code = GATT_REQ_READ_BLOB;
+ msg.read_blob.handle = p_clcb->s_handle;
+ msg.read_blob.offset = offset;
+ break;
+
+ case GATT_READ_MULTIPLE:
+ op_code = GATT_REQ_READ_MULTI;
+ memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
+ break;
+
+ case GATT_READ_INC_SRV_UUID128:
+ op_code = GATT_REQ_READ;
+ msg.handle = p_clcb->s_handle;
+ p_clcb->op_subtype &= ~ 0x90;
+ break;
+
+ default:
+ GATT_TRACE_ERROR1("Unknown read type: %d", p_clcb->op_subtype);
+ break;
+ }
+
+ if ( op_code == 0 ||
+ (rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg)) != GATT_SUCCESS)
+ {
+ gatt_end_operation(p_clcb, rt, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_act_write
+**
+** Description GATT write operation.
+**
+** Returns void.
+**
+*******************************************************************************/
+void gatt_act_write (tGATT_CLCB *p_clcb)
+{
+ tGATT_TCB *p_tcb = p_clcb->p_tcb;
+ UINT8 rt = GATT_SUCCESS, op_code;
+ tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;
+
+ if (p_attr)
+ {
+ switch (p_clcb->op_subtype)
+ {
+ case GATT_WRITE_NO_RSP:
+ p_clcb->s_handle = p_attr->handle;
+ op_code = (p_tcb->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_ERROR1("Unknown write type: %d", p_clcb->op_subtype);
+ break;
+ }
+ }
+ else
+ rt = GATT_INTERNAL_ERROR;
+
+ if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)
+ || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP))
+ {
+ if (rt != GATT_SUCCESS)
+ {
+ GATT_TRACE_ERROR1("gatt_act_write() failed op_code=0x%x", op_code);
+ }
+ 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_DEBUG0("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_DEBUG0("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_DEBUG1("gatt_send_prepare_write type=0x%x", type );
+ to_send = p_attr->len - p_attr->offset;
+
+ if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) /* 2 = UINT16 offset bytes */
+ to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE;
+
+ p_clcb->s_handle = p_attr->handle;
+
+ offset = p_attr->offset;
+ if (type == GATT_WRITE_PREPARE)
+ {
+ offset += p_clcb->start_offset;
+ }
+
+ GATT_TRACE_DEBUG2("offset =0x%x len=%d", offset, to_send );
+
+ rt = gatt_send_write_msg(p_tcb,
+ p_clcb->clcb_idx,
+ GATT_REQ_PREPARE_WRITE,
+ p_attr->handle,
+ to_send, /* length */
+ offset, /* used as offset */
+ p_attr->value + p_attr->offset); /* data */
+
+ /* remember the write long attribute length */
+ p_clcb->counter = to_send;
+
+ if (rt != GATT_SUCCESS )
+ {
+ gatt_end_operation(p_clcb, rt, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_proc_disc_read_by_type_rsp
+**
+** Description This function process the read by type response and send another
+** request if needed.
+**
+** Returns void.
+**
+*******************************************************************************/
+void gatt_proc_disc_read_by_type_rsp(tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data)
+{
+ /*
+ tGATT_TCB *p_tcb = p_clcb->p_tcb;
+ tGATT_DISCOVERY_DB *p_db = p_clcb->p_disc_db;
+ tGATT_DISC_REC *p_rec;
+ tGATT_STATUS status = GATT_INTERNAL_ERROR;
+
+
+ if ((p_rec = gatt_add_record(p_clcb->p_disc_db)) != NULL)
+ {
+ p_rec->handle = handle;
+ p_rec->type = p_db->uuid_filter;
+ p_rec->attr_len = len;
+
+ // copy the attibute value into DB
+ p_rec->p_value = p_db->p_free_mem;
+ memcpy(p_rec->p_value, p_value, len);
+ p_db->p_free_mem += len;
+ p_db->mem_free -= len;
+
+ if (handle < p_clcb->e_handle)
+ {
+ // send another request
+ if (gatt_act_send_browse(p_tcb, p_clcb->conn_id,
+ GATT_REQ_READ_BY_TYPE,
+ (UINT16)(handle + 1), // starting handle
+ p_clcb->e_handle, // end handle
+ p_clcb->p_disc_db->uuid_filter) // uuid filter /
+ == GATT_SUCCESS)
+ {
+ status = GATT_SUCCESS;
+ }
+ }
+ }
+ else
+ status = GATT_DB_FULL;
+
+ if (status != GATT_SUCCESS) // DB full
+ {
+ gatt_end_operation(p_clcb, status, 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;
+ tGATT_DISC_VALUE record_value;
+ UINT8 *p = p_data;
+
+ GATT_TRACE_DEBUG0("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 (&record_value, 0, sizeof(tGATT_DISC_VALUE));
+ 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 (record_value.handle, p);
+ len -= 4;
+
+ memcpy (&result.value, &record_value, sizeof (result.value));;
+
+ 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 = (record_value.handle == 0) ? 0 : (record_value.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;
+
+ /* 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);
+
+ 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;
+
+ GATT_TRACE_DEBUG2("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_TRACE_DEBUG0("Discovery completed");
+ }
+ break;
+ default:
+ GATT_TRACE_ERROR1("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;
+
+ GATT_TRACE_DEBUG0("gatt_process_error_rsp ");
+ 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) )
+ {
+ 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_ERROR2("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len);
+
+ 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_NOTIF) ? GATTC_OPTYPE_NOTIFICATION : GATTC_OPTYPE_INDICATION;
+
+ GATT_TRACE_DEBUG0("gatt_process_notification ");
+
+ STREAM_TO_UINT16 (value.handle, p);
+ value.len = len - 2;
+ memcpy (value.value, p, value.len);
+
+ 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 (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_ERROR1("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++;
+ }
+
+ if (event == GATTC_OPTYPE_INDICATION)
+ {
+ /* 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);
+ }
+
+ 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);
+ encrypt_status = gatt_get_link_encrypt_status(p_tcb);
+ (*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;
+
+ 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_ERROR4("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);
+
+ 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_ERROR0("discover all service response parsing failure");
+ break;
+ }
+ }
+ }
+ /* 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;
+ }
+ 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);
+ return;
+ }
+ else
+ {
+ GATT_TRACE_ERROR1("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 *)GKI_getbuf(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 or read characteristic value */
+ {
+ 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;
+ }
+ gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p);
+
+ /* 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;
+
+ 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 *)GKI_getbuf(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_DEBUG3("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_ERROR2("attr offset = %d p_attr_buf = %d ", 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;
+ 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_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data)
+{
+ UINT16 handle;
+ UINT8 * p= p_data;
+
+ STREAM_TO_UINT16(handle, p);
+ len -= 2;
+
+ if (op_code == GATT_RSP_WRITE)
+ 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;
+
+ STREAM_TO_UINT16(mtu, p_data);
+
+ if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE)
+ p_tcb->payload_size = mtu;
+
+ gatt_end_operation(p_clcb, 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;
+
+ while (!sent &&
+ p_tcb->pending_cl_req != p_tcb->next_slot_inq &&
+ p_cmd->to_send && p_cmd->p_cmd != NULL)
+ {
+ sent = attp_send_msg_to_L2CAP(p_tcb, p_cmd->p_cmd);
+
+ if (sent)
+ {
+ p_cmd->to_send = FALSE;
+ p_cmd->p_cmd = NULL;
+
+ gatt_start_rsp_timer (p_tcb);
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("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)
+ {
+ 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_WARNING2 ("ATT - Ignore wrong response. Receives (%02x) \
+ Request(%02x) Ignored", op_code, rsp_code);
+
+ return;
+ }
+ else
+ btu_stop_timer (&p_tcb->rsp_timer_ent);
+ }
+ /* 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_ERROR2("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)
+ 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:
+ 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_tcb, p_clcb, op_code, len, p_data);
+ 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:
+ gatt_process_notification(p_tcb, op_code, len, p_data);
+ break;
+
+ default:
+ GATT_TRACE_ERROR1("Unknown opcode = %d", op_code);
+ break;
+ }
+ }
+
+ if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF)
+ {
+ gatt_cl_send_next_cmd_inq(p_tcb);
+ }
+
+ return;
+}
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_db.c b/stack/gatt/gatt_db.c
new file mode 100644
index 0000000..68982bd
--- /dev/null
+++ b/stack/gatt/gatt_db.c
@@ -0,0 +1,1117 @@
+/*****************************************************************************
+**
+** Name: gatts_db.c
+**
+** Description: this file contains GATT database building and query
+** functions
+**
+** Copyright (c) 2009-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+
+#include "bt_trace.h"
+
+#include <stdio.h>
+#include <string.h>
+#include "gatt_int.h"
+#include "l2c_api.h"
+
+/********************************************************************************
+** 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, UINT16 uuid16, UINT8 *p_uuid128, 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 void gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID 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);
+
+/*******************************************************************************
+**
+** 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 service, BOOLEAN is_pri,
+ UINT16 s_hdl, UINT16 num_handle)
+{
+ if (!allocate_svc_db_buf(p_db))
+ {
+ GATT_TRACE_ERROR0("gatts_init_service_db failed, no resources");
+ return FALSE;
+ }
+
+ GATT_TRACE_DEBUG0("gatts_init_service_db");
+ GATT_TRACE_DEBUG2("s_hdl = %d num_handle = %d", s_hdl, num_handle );
+
+ /* update service database information */
+ p_db->next_handle = s_hdl;
+ p_db->end_handle = s_hdl + num_handle;
+
+ gatts_db_add_service_declaration(p_db, service, is_pri);
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** 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_ERROR0("service DB empty");
+
+ 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;
+
+ 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_ERROR0( "GATT_READ_NOT_PERMIT");
+ return GATT_READ_NOT_PERMIT;
+ }
+
+ if ((perm & GATT_READ_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED))
+ {
+ GATT_TRACE_ERROR0( "GATT_INSUF_AUTHENTICATION");
+ return GATT_INSUF_AUTHENTICATION;
+ }
+
+ if ((perm & GATT_READ_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED))
+ {
+ GATT_TRACE_ERROR0( "GATT_INSUF_AUTHENTICATION: MITM Required");
+ return GATT_INSUF_AUTHENTICATION;
+ }
+
+ if ((perm & GATT_READ_ENCRYPTED_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED))
+ {
+ GATT_TRACE_ERROR0( "GATT_INSUF_ENCRYPTION");
+ return GATT_INSUF_ENCRYPTION;
+ }
+
+ if ( (perm & GATT_READ_ENCRYPTED_REQUIRED) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED) && (key_size < min_key_size))
+ {
+ GATT_TRACE_ERROR0( "GATT_INSUF_KEY_SIZE");
+ return GATT_INSUF_KEY_SIZE;
+ }
+
+
+ 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_ERROR0("GATT_NOT_LONG");
+ 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;
+ UINT16 read_long_uuid=0;
+ tGATT_ATTR16 *p_attr16 = (tGATT_ATTR16 *)p_attr;
+
+ GATT_TRACE_DEBUG5("read_attr_value uuid=0x%04x perm=0x%0x sec_flag=0x%x offset=%d read_long=%d",
+ 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 (p_attr16->uuid_type == GATT_ATTR_UUID_TYPE_16)
+ uuid16 = p_attr16->uuid;
+
+ if (status != GATT_SUCCESS)
+ return status;
+
+ status = GATT_NO_RESOURCES;
+
+ if (read_long &&
+ (uuid16 == GATT_UUID_CHAR_DESCRIPTION || uuid16 == GATT_UUID_CHAR_AGG_FORMAT))
+ {
+ read_long_uuid = p_attr16->uuid;
+ }
+
+ 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);
+ }
+ 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)
+ {
+ len = (p_attr16->p_value->incl_handle.service_type.len == 2) ? 6 : 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 == 2)
+ {
+ UINT16_TO_STREAM(p, p_attr16->p_value->incl_handle.service_type.uu.uuid16);
+ }
+ status = GATT_SUCCESS;
+ }
+ }
+ else /* characteristic description or characteristic value */
+ {
+ 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 (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
+ {
+ 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)
+ {
+ status = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, 0, trans_id);
+
+ /* one callback at a time */
+ break;
+ }
+ else if (status == GATT_SUCCESS)
+ {
+ 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_ERROR0("format mismatch");
+ status = GATT_NO_RESOURCES;
+ break;
+ }
+ }
+ else
+ {
+ *p_cur_handle = p_attr->handle;
+ break;
+ }
+ }
+ p_attr = (tGATT_ATTR16 *)p_attr->p_next;
+ }
+ }
+
+ 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;
+
+ GATT_TRACE_DEBUG3("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_ERROR0("gatts_add_included_service Illegal Params.");
+ return 0;
+ }
+
+ if ((p_attr = (tGATT_ATTR16 *) allocate_attr_in_db(p_db, GATT_UUID_INCLUDE_SERVICE, NULL, 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_ATTR16 *p_char_decl, *p_char_val;
+ UINT16 uuid16 = (p_char_uuid->len == LEN_UUID_16) ? p_char_uuid->uu.uuid16 : 0;
+
+ GATT_TRACE_DEBUG2("gatts_add_characteristic perm=0x%0x property=0x%0x", perm, property);
+
+ if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, GATT_UUID_CHAR_DECLARE, NULL, 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, uuid16, p_char_uuid->uu.uuid128, 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;
+
+ p_char_val->p_value = NULL;
+
+ 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_ATTR16 *p_char_dscptr;
+ UINT16 uuid16 = (p_descr_uuid->len == LEN_UUID_16)? p_descr_uuid->uu.uuid16 : 0;
+
+ GATT_TRACE_DEBUG1("gatts_add_char_descr uuid=0x%04x", p_descr_uuid->uu.uuid16);
+
+ /* Add characteristic descriptors */
+ if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db,
+ uuid16,
+ p_descr_uuid->uu.uuid128,
+ perm))
+ == NULL)
+ {
+ GATT_TRACE_DEBUG0("gatts_add_char_descr Fail for adding char descriptors.");
+ return 0;
+ }
+ else
+ {
+ return p_char_dscptr->handle;
+ }
+}
+
+/*******************************************************************************/
+/* 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 = gatts_send_app_read_request(p_tcb, op_code, p_attr->handle, offset, trans_id);
+ }
+ break;
+ }
+ 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_DEBUG6( "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_DEBUG2( "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) && (perm & GATT_WRITE_SIGNED_PERM) )
+ {
+ /* use the rules for the mixed security see section 10.2.3*/
+ if (perm & GATT_PERM_WRITE_SIGNED)
+ {
+ perm = GATT_PERM_WRITE_ENCRYPTED;
+ }
+ else
+ {
+ 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_DEBUG0( "gatts_write_attr_perm_check - sign cmd write not allowed");
+ }
+ if ((op_code == GATT_SIGN_CMD_WRITE) && (sec_flag & GATT_SEC_FLAG_ENCRYPTED))
+ {
+ status = GATT_INVALID_PDU;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - Error!! sign cmd write sent on a encypted link");
+ }
+ else if (!(perm & GATT_WRITE_ALLOWED))
+ {
+ status = GATT_WRITE_NOT_PERMIT;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_WRITE_NOT_PERMIT");
+ }
+ else if ((perm & GATT_WRITE_AUTH_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_UNAUTHED))
+ {
+ status = GATT_INSUF_AUTHENTICATION;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION");
+ }
+ else if ((perm & GATT_WRITE_MITM_REQUIRED ) && !(sec_flag & GATT_SEC_FLAG_LKEY_AUTHED))
+ {
+ status = GATT_INSUF_AUTHENTICATION;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_AUTHENTICATION: MITM required");
+ }
+ else if ((perm & GATT_WRITE_ENCRYPTED_PERM ) && !(sec_flag & GATT_SEC_FLAG_ENCRYPTED))
+ {
+ status = GATT_INSUF_ENCRYPTION;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_ENCRYPTION");
+ }
+ 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_ERROR0( "gatts_write_attr_perm_check - GATT_INSUF_KEY_SIZE");
+ }
+ 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:
+ 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)
+ {
+ 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) )
+// btla-specific --
+ {
+ if (op_code == GATT_REQ_PREPARE_WRITE && offset != 0) /* does not allow write blob */
+ {
+ status = GATT_NOT_LONG;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_NOT_LONG");
+ }
+ else if (len != max_size) /* data does not match the required format */
+ {
+ status = GATT_INVALID_PDU;
+ GATT_TRACE_ERROR0( "gatts_write_attr_perm_check - GATT_INVALID_PDU");
+ }
+ 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.
+** 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, UINT16 uuid16, UINT8 *uuid128, tGATT_PERM perm)
+{
+ tGATT_ATTR16 *p_attr16 = NULL, *p_last;
+ tGATT_ATTR128 *p_attr128 = NULL;
+ UINT16 len = (uuid16 == 0) ? sizeof(tGATT_ATTR128): sizeof(tGATT_ATTR16);
+
+ GATT_TRACE_DEBUG1("allocate attr %d bytes ",len);
+
+ if (uuid16 == GATT_ILLEGAL_UUID && uuid128 == NULL)
+ {
+ GATT_TRACE_ERROR0("illegal UUID");
+ return NULL;
+ }
+
+ if (p_db->end_handle <= p_db->next_handle)
+ {
+ GATT_TRACE_DEBUG2("handle space full. handle_max = %d next_handle = %d",
+ 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_ERROR0("allocate_attr_in_db failed, no resources");
+ return NULL;
+ }
+ }
+
+ p_attr16 = (tGATT_ATTR16 *) p_db->p_free_mem;
+ p_attr128 = (tGATT_ATTR128 *) p_db->p_free_mem;
+
+ memset(p_attr16, 0, len);
+
+ if (uuid16 != GATT_ILLEGAL_UUID)
+ {
+ p_attr16->uuid_type = GATT_ATTR_UUID_TYPE_16;
+ p_attr16->uuid = uuid16;
+ }
+ else
+ {
+ p_attr128->uuid_type = GATT_ATTR_UUID_TYPE_128;
+ memcpy(p_attr128->uuid, 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_DEBUG3("=====> handle = [0x%04x] uuid = [0x%04x] perm=0x%02x ",
+ p_attr16->handle, p_attr16->uuid, p_attr16->permission);
+ }
+ else
+ {
+ GATT_TRACE_DEBUG4("=====> handle = [0x%04x] uuid128 = [0x%02x:0x%02x] perm=0x%02x ",
+ 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_ERROR0("copy_extra_byte_in_db failed, no resources");
+ 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_DEBUG0("allocate_svc_db_buf allocating extra buffer");
+
+ if ((p_buf = (BT_HDR *)GKI_getpoolbuf(GATT_DB_POOL_ID)) == NULL)
+ {
+ GATT_TRACE_ERROR0("allocate_svc_db_buf failed, no resources");
+ return FALSE;
+ }
+
+ memset(p_buf, 0, GKI_get_buf_size(p_buf));
+ p_db->p_free_mem = (UINT8 *) p_buf;
+ p_db->mem_free = GKI_get_buf_size(p_buf);
+
+ GKI_enqueue(&p_db->svc_buffer, p_buf);
+
+ 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)
+{
+ 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);
+ 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;
+
+ gatt_sr_send_req_callback(conn_id,
+ trans_id, GATTS_REQ_TYPE_READ, &sr_data);
+ return(tGATT_STATUS) GATT_PENDING;
+ }
+ 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 void gatts_db_add_service_declaration(tGATT_SVC_DB *p_db, tBT_UUID service, BOOLEAN is_pri)
+{
+ tGATT_ATTR16 *p_attr;
+ UINT16 service_type = is_pri ? GATT_UUID_PRI_SERVICE: GATT_UUID_SEC_SERVICE;
+
+ GATT_TRACE_DEBUG0( "add_service_declaration");
+
+ /* add service declration record */
+ if ((p_attr = (tGATT_ATTR16 *)(allocate_attr_in_db(p_db, service_type, NULL, GATT_PERM_READ))) != NULL)
+ {
+ if (copy_extra_byte_in_db (p_db, (void **)&p_attr->p_value, sizeof(tBT_UUID)))
+ {
+ memcpy (&p_attr->p_value->uuid, &service, sizeof(tBT_UUID));
+ }
+ }
+}
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_int.h b/stack/gatt/gatt_int.h
new file mode 100644
index 0000000..7c7fb89
--- /dev/null
+++ b/stack/gatt/gatt_int.h
@@ -0,0 +1,664 @@
+/****************************************************************************
+**
+** Name: gatt_int.h
+**
+** Function this file contains internally used GATT profile
+**
+** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+**
+******************************************************************************/
+
+#ifndef GATT_INT_H
+#define GATT_INT_H
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+
+
+#include "bt_trace.h"
+#include "gatt_api.h"
+#include "btm_ble_api.h"
+#include "btu.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 */
+
+/* security action for GATT write and read request */
+#define GATT_SEC_NONE 0
+#define GATT_SEC_OK 1
+#define GATT_SEC_ENCRYPT 2 /* encrypt the link with current key */
+#define GATT_SEC_ENCRYPT_NO_MITM 3 /* unauthenticated encryption or better */
+#define GATT_SEC_ENCRYPT_MITM 4 /* authenticated encryption */
+#define GATT_SEC_SIGN_DATA 5 /* compute the signature for the write cmd */
+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
+
+/* 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
+typedef UINT8 tGATT_SEC_FLAG;
+
+/* Find Information Response Type
+*/
+#define GATT_INFO_TYPE_PAIR_16 0x01
+#define GATT_INFO_TYPE_PAIR_128 0x02
+
+/* 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;
+
+/* 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_VALUE;
+
+/* Attribute UUID type
+*/
+#define GATT_ATTR_UUID_TYPE_16 0
+#define GATT_ATTR_UUID_TYPE_128 1
+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;
+ UINT16 handle;
+ UINT16 uuid;
+} tGATT_ATTR16;
+
+/* 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;
+ 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 */
+ BUFFER_Q 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 */
+ //tGATT_SR_CBACK sr_cb; /* server callback functions */
+ 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;
+
+
+/* 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;
+} 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;
+ BUFFER_Q 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
+#define GATT_CH_W4_SEC_COMP 5
+#define GATT_CH_W4_DATA_SIGN_COMP 6
+
+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;
+
+typedef struct
+{
+ void *p_clcb; /* which clcb is doing encryption */
+ tGATT_SEC_ACTION sec_act;
+ BD_ADDR peer_bda;
+ 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 */
+ tGATT_SR_CMD sr_cmd;
+ UINT16 indicate_handle;
+ BUFFER_Q 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 rsp_timer_ent; /* peer response timer */
+ 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 */
+
+ BOOLEAN in_use;
+ UINT8 tcb_idx;
+} 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;
+} tGATT_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];
+ BD_ADDR remote_bda;
+ BOOLEAN in_use;
+}tGATT_BG_CONN_DEV;
+
+
+typedef struct
+{
+ UINT16 conn_id;
+ BOOLEAN in_use;
+ BOOLEAN connected;
+ BD_ADDR bda;
+}tGATT_PROFILE_CLCB;
+
+typedef struct
+{
+ tGATT_TCB tcb[GATT_MAX_PHY_CHANNEL];
+ BUFFER_Q 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;
+ 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];
+
+ BUFFER_Q srv_chg_clt_q; /* service change clients queue */
+ BUFFER_Q pending_new_srv_start_q; /* pending new service start queue */
+ tGATT_REG cl_rcb[GATT_MAX_APPS];
+ tGATT_CLCB clcb[GATT_CL_MAX_LCB]; /* 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;
+#endif
+
+ tGATT_PROFILE_CLCB profile_clcb[GATT_MAX_APPS];
+ UINT16 handle_of_h_r; /* Handle of the handles reused characteristic value */
+
+ tGATT_APPL_INFO cb_info;
+
+
+
+ tGATT_HDL_CFG hdl_cfg;
+ tGATT_BG_CONN_DEV bgconn_dev[GATT_MAX_BG_CONN_DEV];
+
+} tGATT_CB;
+
+
+#define GATT_SIZE_OF_SRV_CHG_HNDL_RANGE 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Global GATT data */
+#if GATT_DYNAMIC_MEMORY == FALSE
+GATT_API extern tGATT_CB gatt_cb;
+#else
+GATT_API extern tGATT_CB *gatt_cb_ptr;
+#define gatt_cb (*gatt_cb_ptr)
+#endif
+
+#if GATT_CONFORMANCE_TESTING == TRUE
+GATT_API 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);
+
+/* from gatt_main.c */
+extern BOOLEAN gatt_disconnect (BD_ADDR rem_bda);
+extern BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr);
+extern BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb);
+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 void 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);
+extern tGATT_PROFILE_CLCB *gatt_profile_find_clcb_by_bd_addr(BD_ADDR bda);
+extern BOOLEAN gatt_profile_clcb_dealloc (UINT16 conn_id);
+extern tGATT_PROFILE_CLCB *gatt_profile_clcb_alloc (UINT16 conn_id, 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 BOOLEAN attp_send_msg_to_L2CAP(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP);
+
+/* utility functions */
+extern UINT8 * gatt_dbg_op_name(UINT8 op_code);
+extern UINT32 gatt_add_sdp_record (tBT_UUID *p_uuid, UINT16 start_hdl, UINT16 end_hdl);
+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_sr_get_sec_info(BD_ADDR rem_bda, BOOLEAN le_conn, UINT8 *p_sec_flag, UINT8 *p_key_size);
+extern void gatt_start_rsp_timer(tGATT_TCB *p_tcb);
+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 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);
+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);
+
+/* 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_alloc_hdl_buffer(void);
+extern void gatt_free_hdl_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);
+extern BOOLEAN gatt_add_bg_dev_list(tGATT_IF gatt_if, BD_ADDR bd_addr);
+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 BOOLEAN gatt_remove_bg_dev_from_list(tGATT_IF gatt_if, BD_ADDR bd_addr);
+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 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 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);
+extern tGATT_TCB * gatt_get_tcb_by_idx(UINT8 tcb_idx);
+extern tGATT_TCB * gatt_find_tcb_by_addr(BD_ADDR bda);
+
+
+/* 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);
+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);
+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 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);
+extern UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID *p_dscp_uuid);
+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_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);
+
+#endif
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_main.c b/stack/gatt/gatt_main.c
new file mode 100644
index 0000000..396367a
--- /dev/null
+++ b/stack/gatt/gatt_main.c
@@ -0,0 +1,1101 @@
+/*****************************************************************************
+**
+** Name: gatt_main.c
+**
+** Description: this file contains the main ATT functions
+**
+** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+
+#include "gki.h"
+#include "gatt_int.h"
+#include "l2c_api.h"
+#include "btm_int.h"
+#include "btm_ble_int.h"
+
+/* Configuration flags. */
+#define GATT_L2C_CFG_IND_DONE (1<<0)
+#define GATT_L2C_CFG_CFM_DONE (1<<1)
+
+/********************************************************************************/
+/* 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 (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason);
+static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf);
+
+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);
+static void gatt_send_conn_cback (BOOLEAN is_bg_conn, tGATT_TCB *p_tcb);
+
+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,
+ NULL
+} ;
+
+#if GATT_DYNAMIC_MEMORY == FALSE
+tGATT_CB gatt_cb;
+#endif
+
+/*******************************************************************************
+**
+** 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;
+
+ GATT_TRACE_DEBUG0("gatt_init()");
+
+ memset (&gatt_cb, 0, sizeof(tGATT_CB));
+
+#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;
+ GKI_init_q (&gatt_cb.sign_op_queue);
+ /* 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.default_idle_tout = 0xffff; /* 0xffff default idle timeout */
+
+ L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg);
+
+ /* Now, register with L2CAP for ATT PSM over BR/EDR */
+ if (!L2CA_Register (BT_PSM_ATT, (tL2CAP_APPL_INFO *) &dyn_info))
+ {
+ GATT_TRACE_ERROR0 ("ATT Dynamic Registration failed");
+ }
+
+ 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;
+ gatt_profile_db_init();
+
+}
+
+
+
+/*******************************************************************************
+**
+** Function gatt_connect
+**
+** Description This function is called to initiate a connection to a peer device.
+**
+** Parameter rem_bda: remote device address to connect to.
+**
+** Returns TRUE if connection is started, otherwise return FALSE.
+**
+*******************************************************************************/
+BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb)
+{
+ BOOLEAN gatt_ret = TRUE;
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type;
+
+ BTM_ReadDevInfo(rem_bda, &dev_type, &addr_type);
+
+ gatt_set_ch_state(p_tcb, GATT_CH_CONN);
+
+ if (dev_type == BT_DEVICE_TYPE_BLE)
+ {
+ p_tcb->att_lcid = L2CAP_ATT_CID;
+ gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda);
+ }
+ else
+ {
+ if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) == 0)
+ gatt_ret = FALSE;
+ }
+
+ return gatt_ret;
+}
+
+/*******************************************************************************
+**
+** Function gatt_disconnect
+**
+** Description This function is called to disconnect to an ATT device.
+**
+** Parameter rem_bda: remote device address to disconnect from.
+**
+** Returns TRUE: if connection found and to be disconnected; otherwise
+** return FALSE.
+**
+*******************************************************************************/
+BOOLEAN gatt_disconnect (BD_ADDR rem_bda)
+{
+ tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(rem_bda);
+ BOOLEAN ret = FALSE;
+ tGATT_CH_STATE ch_state;
+ GATT_TRACE_DEBUG0 ("gatt_disconnect ");
+
+ if (p_tcb != NULL)
+ {
+ ret = TRUE;
+ if ( (ch_state = gatt_get_ch_state(p_tcb)) != GATT_CH_CLOSING )
+ {
+ if (p_tcb->att_lcid == L2CAP_ATT_CID)
+ {
+ if (ch_state == GATT_CH_OPEN)
+ /* only LCB exist between remote device and local */
+ ret = L2CA_RemoveFixedChnl (L2CAP_ATT_CID, rem_bda);
+ else
+ {
+ gatt_set_ch_state(p_tcb, GATT_CH_CLOSING);
+ ret = L2CA_CancelBleConnectReq (rem_bda);
+ }
+ }
+ else
+ {
+ ret = L2CA_DisconnectReq(p_tcb->att_lcid);
+ }
+ }
+ else
+ {
+ GATT_TRACE_DEBUG0 ("gatt_disconnect already in closing state");
+ }
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function gatt_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_ERROR0("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_DEBUG4("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_DEBUG2("gatt_update_app_use_link_flag is_add=%d chk_link=%d",
+ is_add, check_acl_link);
+
+ gatt_update_app_hold_link_status(gatt_if, p_tcb, is_add);
+
+ if (check_acl_link &&
+ p_tcb &&
+ (BTM_GetHCIConnHandle(p_tcb->peer_bda) != GATT_INVALID_ACL_HANDLE))
+ {
+ if (is_add)
+ {
+ GATT_TRACE_DEBUG0("GATT disables link idle timer");
+ /* acl link is connected disable the idle timeout */
+ GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT);
+ }
+ else
+ {
+ if (!gatt_num_apps_hold_link(p_tcb))
+ {
+ /* acl link is connected but no application needs to use the link
+ so set the timeout value to GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP seconds */
+ GATT_TRACE_DEBUG1("GATT starts link idle timer =%d sec", GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP);
+ GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP);
+ }
+
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_act_connect
+**
+** Description GATT connection initiation.
+**
+** Returns void.
+**
+*******************************************************************************/
+BOOLEAN gatt_act_connect (tGATT_REG *p_reg, BD_ADDR bd_addr)
+{
+ BOOLEAN ret = FALSE;
+ tGATT_TCB *p_tcb;
+
+ GATT_TRACE_DEBUG0("gatt_act_connect");
+
+ if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) != NULL)
+ {
+ ret = TRUE;
+ if(gatt_get_ch_state(p_tcb) == GATT_CH_CLOSING )
+ {
+ /* need to complete the closing first */
+ ret = FALSE;
+ }
+ }
+ else
+ {
+ if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL)
+ {
+ if (!gatt_connect(bd_addr, p_tcb))
+ {
+ GATT_TRACE_ERROR0("gatt_connect failed");
+ memset(p_tcb, 0, sizeof(tGATT_TCB));
+ }
+ else
+ ret = TRUE;
+ }
+ else
+ {
+ ret = 0;
+ GATT_TRACE_ERROR1("Max TCB for gatt_if [%d] reached.", p_reg->gatt_if);
+ }
+ }
+
+ if (ret)
+ {
+ gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, TRUE, FALSE);
+ }
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function gatt_le_connect_cback
+**
+** Description This callback function is called by L2CAP to indicate that
+** the ATT fixed channel for LE is
+** connected (conn = TRUE)/disconnected (conn = FALSE).
+**
+*******************************************************************************/
+static void gatt_le_connect_cback (BD_ADDR bd_addr, BOOLEAN connected, UINT16 reason)
+{
+
+ tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr);
+
+ BOOLEAN check_srv_chg = FALSE;
+ tGATTS_SRV_CHG *p_srv_chg_clt=NULL;
+ BOOLEAN is_bg_conn = FALSE;
+
+
+ GATT_TRACE_DEBUG3 ("GATT ATT protocol channel with BDA: %08x%04x is %s",
+ (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3],
+ (bd_addr[4]<<8)+bd_addr[5], (connected) ? "connected" : "disconnected");
+
+
+ if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL)
+ {
+ check_srv_chg = TRUE;
+ }
+ else
+ {
+ if (btm_sec_is_a_bonded_dev(bd_addr))
+ gatt_add_a_bonded_dev_for_srv_chg(bd_addr);
+ }
+
+ if (connected)
+ {
+ GATT_TRACE_DEBUG1("connected is TRUE reason=%d",reason );
+ /* BR/EDR lik, ignore this callback */
+ if (reason == 0)
+ return;
+
+ /* do we have a channel initiating a connection? */
+ if (p_tcb)
+ {
+ if (check_srv_chg)
+ gatt_chk_srv_chg (p_srv_chg_clt);
+ /* we are initiating connection */
+ if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN)
+ {
+ /* send callback */
+ gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
+ p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE;
+
+ gatt_send_conn_cback(FALSE, p_tcb);
+ }
+ else /* there was an exisiting link, ignore the callback */
+ {
+ GATT_TRACE_ERROR0("connection already up, ignore it");
+ return;
+ }
+ }
+ /* this is incoming connection or background connection callback */
+ else
+ {
+ if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) != NULL)
+ {
+ p_tcb->att_lcid = L2CAP_ATT_CID;
+
+ gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
+
+ p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE;
+ if (L2CA_GetBleConnRole(p_tcb->peer_bda)== HCI_ROLE_MASTER)
+ {
+ is_bg_conn = TRUE;
+ }
+ gatt_send_conn_cback (is_bg_conn, p_tcb);
+ if (check_srv_chg)
+ {
+ gatt_chk_srv_chg (p_srv_chg_clt);
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("CCB max out, no rsources");
+ }
+ }
+ }
+ else
+ {
+ gatt_cleanup_upon_disc(bd_addr, reason);
+ GATT_TRACE_DEBUG0 ("ATT disconnected");
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_le_data_ind
+**
+** Description This function is called when data is received from L2CAP.
+** if we are the originator of the connection, we are the ATT
+** client, and the received message is queued up for the client.
+**
+** If we are the destination of the connection, we are the ATT
+** server, so the message is passed to the server processing
+** function.
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_le_data_ind (BD_ADDR bd_addr, BT_HDR *p_buf)
+{
+ tGATT_TCB *p_tcb;
+
+ /* Find CCB based on bd addr */
+ if ((p_tcb = gatt_find_tcb_by_addr (bd_addr)) != NULL &&
+ gatt_get_ch_state(p_tcb) >= GATT_CH_OPEN)
+ {
+ gatt_data_process(p_tcb, p_buf);
+ }
+ else
+ {
+ GKI_freebuf (p_buf);
+
+ if (p_tcb != NULL)
+ {
+ GATT_TRACE_WARNING1 ("ATT - Ignored L2CAP data while in state: %d",
+ 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
+**
+*******************************************************************************/
+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);
+
+ GATT_TRACE_ERROR1("Connection indication cid = %d", lcid);
+ /* new connection ? */
+ if (p_tcb == NULL)
+ {
+ /* allocate tcb */
+ if ((p_tcb = gatt_allocate_tcb_by_bdaddr(bd_addr)) == NULL)
+ {
+ /* no tcb available, reject L2CAP connection */
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+ else
+ p_tcb->att_lcid = lcid;
+
+ }
+ else /* existing connection , reject it */
+ {
+ result = L2CAP_CONN_NO_RESOURCES;
+ }
+
+ /* Send L2CAP connect rsp */
+ L2CA_ConnectRsp(bd_addr, id, lcid, result, 0);
+
+ /* if result ok, proceed with connection */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* transition to configuration state */
+ gatt_set_ch_state(p_tcb, GATT_CH_CFG);
+
+ /* Send L2CAP config req */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ cfg.mtu_present = TRUE;
+ cfg.mtu = GATT_MAX_MTU_SIZE;
+
+ L2CA_ConfigReq(lcid, &cfg);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_l2c_connect_cfm_cback
+**
+** Description This is the L2CAP connect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+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_DEBUG3("gatt_l2c_connect_cfm_cback result: %d ch_state: %d, lcid:0x%x", result, gatt_get_ch_state(p_tcb), p_tcb->att_lcid);
+
+ /* if in correct state */
+ if (gatt_get_ch_state(p_tcb) == GATT_CH_CONN)
+ {
+ /* if result successful */
+ if (result == L2CAP_CONN_OK)
+ {
+ /* set channel state */
+ gatt_set_ch_state(p_tcb, GATT_CH_CFG);
+
+ /* Send L2CAP config req */
+ memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ cfg.mtu_present = TRUE;
+ cfg.mtu = GATT_MAX_MTU_SIZE;
+ L2CA_ConfigReq(lcid, &cfg);
+ }
+ /* else initiating connection failure */
+ else
+ {
+ gatt_cleanup_upon_disc(p_tcb->peer_bda, result);
+ }
+ }
+ 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)
+ {
+ gatt_chk_srv_chg(p_srv_chg_clt);
+ }
+ else
+ {
+ if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda))
+ gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda);
+ }
+
+ /* send callback */
+ gatt_send_conn_cback(FALSE, 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 < L2CAP_DEFAULT_MTU) )
+ p_tcb->payload_size = p_cfg->mtu;
+ else
+ p_tcb->payload_size = L2CAP_DEFAULT_MTU;
+
+ /* send L2CAP configure response */
+ memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ p_cfg->result = L2CAP_CFG_OK;
+ L2CA_ConfigRsp(lcid, p_cfg);
+
+ /* if first config ind */
+ if ((p_tcb->ch_flags & GATT_L2C_CFG_IND_DONE) == 0)
+ {
+ /* update flags */
+ p_tcb->ch_flags |= GATT_L2C_CFG_IND_DONE;
+
+ /* if configuration complete */
+ if (p_tcb->ch_flags & GATT_L2C_CFG_CFM_DONE)
+ {
+ gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
+ if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(p_tcb->peer_bda)) != NULL)
+ {
+ gatt_chk_srv_chg(p_srv_chg_clt);
+ }
+ else
+ {
+ if (btm_sec_is_a_bonded_dev(p_tcb->peer_bda))
+ gatt_add_a_bonded_dev_for_srv_chg(p_tcb->peer_bda);
+ }
+
+ /* send callback */
+ gatt_send_conn_cback(FALSE, 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 (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)) == 0)
+ reason = GATT_CONN_TERMINATE_PEER_USER;
+
+ /* send disconnect callback */
+ gatt_cleanup_upon_disc(p_tcb->peer_bda, reason);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_l2cif_disconnect_cfm_cback
+**
+** Description This is the L2CAP disconnect confirm callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void gatt_l2cif_disconnect_cfm_cback(UINT16 lcid, UINT16 result)
+{
+ tGATT_TCB *p_tcb;
+ UINT16 reason;
+
+ /* look up clcb for this channel */
+ if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != 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)) == 0)
+ reason = GATT_CONN_TERMINATE_LOCAL_HOST;
+
+ gatt_cleanup_upon_disc(p_tcb->peer_bda, reason);
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_l2cif_data_ind_cback
+**
+** Description This is the L2CAP data indication callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void gatt_l2cif_data_ind_cback(UINT16 lcid, BT_HDR *p_buf)
+{
+ tGATT_TCB *p_tcb;
+
+ /* look up clcb for this channel */
+ if ((p_tcb = gatt_find_tcb_by_cid(lcid)) != NULL &&
+ gatt_get_ch_state(p_tcb) == GATT_CH_OPEN)
+ {
+ /* process the data */
+ gatt_data_process(p_tcb, p_buf);
+ }
+ else /* prevent buffer leak */
+ GKI_freebuf(p_buf);
+}
+
+/*******************************************************************************
+**
+** Function gatt_send_conn_cback
+**
+** Description Callback used to notify layer above about a connection.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void gatt_send_conn_cback(BOOLEAN is_bg_conn, tGATT_TCB *p_tcb)
+{
+ UINT8 i;
+ tGATT_REG *p_reg;
+ tGATT_BG_CONN_DEV *p_bg_dev=NULL;
+ UINT16 conn_id;
+
+ if (is_bg_conn)
+ 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);
+ }
+ }
+ }
+
+
+ if (gatt_num_apps_hold_link(p_tcb))
+ {
+ /* 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);
+ }
+}
+
+/*******************************************************************************
+**
+** 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;
+ UINT16 msg_len;
+
+
+ if (p_buf->len > 0)
+ {
+ msg_len = p_buf->len - 1;
+ 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)
+ {
+ gatt_verify_signature(p_tcb, p_buf);
+ return;
+ }
+ else
+ {
+ /* message from client */
+ if ((op_code % 2) == 0)
+ gatt_server_handle_client_req (p_tcb, op_code, msg_len, p);
+ else
+ gatt_client_handle_server_rsp (p_tcb, op_code, msg_len, p);
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR1 ("ATT - Rcvd L2CAP data, unknown cmd: 0x%x", op_code);
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR0 ("invalid data length, ignore");
+ }
+
+ GKI_freebuf (p_buf);
+}
+
+/*******************************************************************************
+**
+** Function gatt_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 *p_buf;
+ 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 ((p_buf = 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 chnaged indication to
+** the specified bd address
+**
+** Returns void
+**
+*******************************************************************************/
+void 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;
+
+ GATT_TRACE_DEBUG0("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);
+ GATTS_HandleValueIndication (conn_id,
+ gatt_cb.handle_of_h_r,
+ GATT_SIZE_OF_SRV_CHG_HNDL_RANGE,
+ handle_range);
+ }
+ else
+ {
+ GATT_TRACE_ERROR2("Unable to find conn_id for %08x%04x ",
+ (peer_bda[0]<<24)+(peer_bda[1]<<16)+(peer_bda[2]<<8)+peer_bda[3],
+ (peer_bda[4]<<8)+peer_bda[5] );
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_chk_srv_chg
+**
+** Description Check sending service chnaged Indication is required or not
+** if required then send the Indication
+**
+** Returns void
+**
+*******************************************************************************/
+void gatt_chk_srv_chg(tGATTS_SRV_CHG *p_srv_chg_clt)
+{
+ GATT_TRACE_DEBUG1("gatt_chk_srv_chg srv_changed=%d", p_srv_chg_clt->srv_changed );
+
+ if (p_srv_chg_clt->srv_changed)
+ {
+ gatt_send_srv_chg_ind(p_srv_chg_clt->bda);
+ }
+ else
+ {
+ GATT_TRACE_DEBUG0("No need to send srv chg ");
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function gatt_init_srv_chg
+**
+** Description This function is used to initialize the service changed
+** attribute value
+**
+** Returns void
+**
+*******************************************************************************/
+void gatt_init_srv_chg (void)
+{
+ tGATTS_SRV_CHG_REQ req;
+ tGATTS_SRV_CHG_RSP rsp;
+ BOOLEAN status;
+ UINT8 num_clients,i;
+ tGATTS_SRV_CHG srv_chg_clt;
+
+ GATT_TRACE_DEBUG0("gatt_init_srv_chg");
+ if (gatt_cb.cb_info.p_srv_chg_callback)
+ {
+ status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_NUM_CLENTS, NULL, &rsp);
+
+ if (status && rsp.num_clients)
+ {
+ GATT_TRACE_DEBUG1("gatt_init_srv_chg num_srv_chg_clt_clients=%d", rsp.num_clients);
+ num_clients = rsp.num_clients;
+ i = 1; /* use one based index */
+ while ((i <= num_clients) && status)
+ {
+ req.client_read_index = i;
+ if ((status = (*gatt_cb.cb_info.p_srv_chg_callback)(GATTS_SRV_CHG_CMD_READ_CLENT, &req, &rsp)))
+ {
+ memcpy(&srv_chg_clt, &rsp.srv_chg ,sizeof(tGATTS_SRV_CHG));
+ if (gatt_add_srv_chg_clt(&srv_chg_clt) == NULL)
+ {
+ GATT_TRACE_ERROR0("Unable to add a service change client");
+ status = FALSE;
+ }
+ }
+ i++;
+ }
+ }
+ }
+ else
+ {
+ GATT_TRACE_DEBUG0("gatt_init_srv_chg callback not registered yet");
+ }
+}
+
+/*******************************************************************************
+**
+** Function gatt_proc_srv_chg
+**
+** Description This function is process the service changed request
+**
+** Returns void
+**
+*******************************************************************************/
+void gatt_proc_srv_chg (void)
+{
+ UINT8 start_idx, found_idx;
+ BD_ADDR bda;
+ BOOLEAN srv_chg_ind_pending=FALSE;
+ tGATT_TCB *p_tcb;
+
+ GATT_TRACE_DEBUG0 ("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))
+ {
+ p_tcb = &gatt_cb.tcb[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_DEBUG0 ("discard srv chg - already has one in the queue");
+ }
+ start_idx = ++found_idx;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** 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_DEBUG2 ("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_DEBUG1 ("gatt_get_ch_state: ch_state=%d", p_tcb->ch_state);
+ ch_state = p_tcb->ch_state;
+ }
+ return ch_state;
+}
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_sr.c b/stack/gatt/gatt_sr.c
new file mode 100644
index 0000000..9da6c34
--- /dev/null
+++ b/stack/gatt/gatt_sr.c
@@ -0,0 +1,1472 @@
+/*****************************************************************************
+**
+** Name: gatt_sr.c
+**
+** Description: this file contains the GATT server functions
+**
+** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+#include <string.h>
+#include "gatt_int.h"
+#include "l2c_api.h"
+
+
+/*******************************************************************************
+**
+** 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_DEBUG0("gatt_dequeue_sr_cmd" );
+ if (p_tcb->sr_cmd.p_rsp_msg)
+ {
+ GATT_TRACE_ERROR1("free p_tcb->sr_cmd.p_rsp_msg = %d", p_tcb->sr_cmd.p_rsp_msg);
+
+ GKI_freebuf (p_tcb->sr_cmd.p_rsp_msg);
+ }
+
+ while (p_tcb->sr_cmd.multi_rsp_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_tcb->sr_cmd.multi_rsp_q));
+ 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)
+{
+ tGATTS_RSP *p_rsp;
+ UINT16 ii, total_len, len;
+ BT_HDR *p_buf = (BT_HDR *)GKI_getbuf((UINT16)sizeof(tGATTS_RSP));
+ UINT8 *p;
+ BOOLEAN is_overflow = FALSE;
+
+ GATT_TRACE_DEBUG2 ("process_read_multi_rsp status=%d mtu=%d", status, mtu);
+
+ if (p_buf == NULL)
+ {
+ p_cmd->status = GATT_INSUF_RESOURCE;
+ return FALSE;
+ }
+
+ /* Enqueue the response */
+ memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP));
+ GKI_enqueue (&p_cmd->multi_rsp_q, p_buf);
+
+ p_cmd->status = status;
+ if (status == GATT_SUCCESS)
+ {
+ GATT_TRACE_DEBUG2 ("Multi read count=%d num_hdls=%d",
+ p_cmd->multi_rsp_q.count, p_cmd->multi_req.num_handles);
+ /* Wait till we get all the responses */
+ if (p_cmd->multi_rsp_q.count == p_cmd->multi_req.num_handles)
+ {
+ len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu;
+ if ((p_buf = (BT_HDR *)GKI_getbuf(len)) == NULL)
+ {
+ p_cmd->status = GATT_INSUF_RESOURCE;
+ return(TRUE);
+ }
+
+ memset(p_buf, 0, len);
+ 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 */
+ for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++)
+ {
+ if (ii==0)
+ {
+ p_rsp = (tGATTS_RSP *)GKI_getfirst (&p_cmd->multi_rsp_q);
+ }
+ else
+ {
+ p_rsp = (tGATTS_RSP *)GKI_getnext (p_rsp);
+ }
+
+ 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_DEBUG2 ("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_ERROR0("process_read_multi_rsp - nothing found!!");
+ p_cmd->status = GATT_NOT_FOUND;
+ GKI_freebuf (p_buf);
+ GATT_TRACE_DEBUG0(" GKI_freebuf (p_buf)");
+ }
+ else if (p_cmd->p_rsp_msg != NULL)
+ {
+ GKI_freebuf (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;
+
+ GATT_TRACE_DEBUG1("gatt_sr_process_app_rsp gatt_if=%d", 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_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_ERROR0("Exception!!! already has respond message");
+ }
+ }
+ }
+ 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
+ {
+ 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_DEBUG1("gatt_sr_process_app_rsp ret_code=%d", 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;
+ BT_HDR *p_buf;
+ tGATT_IF gatt_if;
+ UINT16 conn_id;
+
+ STREAM_TO_UINT8(flag, p);
+
+ /* mask the flag */
+ flag &= GATT_PREP_WRITE_EXEC;
+
+
+ /* no prep write is queued */
+ if (!gatt_sr_is_prep_cnt_zero(p_tcb))
+ {
+ 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;
+ }
+ }
+ }
+ else /* nothing needs to be executed , send response now */
+ {
+ GATT_TRACE_ERROR0("gatt_process_exec_write_req: no prepare write pending");
+
+ if ((p_buf = attp_build_sr_msg(p_tcb, GATT_RSP_EXEC_WRITE, NULL)) != NULL)
+ {
+ attp_send_sr_msg (p_tcb, p_buf);
+ }
+
+ }
+}
+
+/*******************************************************************************
+**
+** 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, ll = len;
+ UINT8 *p = p_data, i_rcb;
+ tGATT_STATUS err = GATT_SUCCESS;
+ UINT8 sec_flag, key_size;
+ tGATTS_RSP *p_msg;
+
+ GATT_TRACE_DEBUG0("gatt_process_read_multi_req" );
+ p_tcb->sr_cmd.multi_req.num_handles = 0;
+
+ gatt_sr_get_sec_info(p_tcb->peer_bda,
+ (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID),
+ &sec_flag,
+ &key_size);
+
+#if GATT_CONFORMANCE_TESTING == TRUE
+ if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code)
+ {
+ GATT_TRACE_DEBUG1("Conformance tst: forced err rspvofr ReadMultiple: error status=%d", 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_DEBUG1("read permission denied : 0x%02x", err);
+ break;
+ }
+ }
+ else
+ {
+ /* invalid handle */
+ err = GATT_INVALID_HANDLE;
+ break;
+ }
+ ll -= 2;
+ }
+
+ if (ll != 0)
+ {
+ GATT_TRACE_ERROR0("max attribute handle reached in ReadMultiple Request.");
+ }
+
+ 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 *)GKI_getbuf(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)
+ {
+ 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 */
+ GKI_freebuf(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_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;
+
+ 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)
+ {
+ p_uuid = gatts_get_service_uuid (p_rcb->p_db);
+
+ 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_DEBUG0("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 sucessfully.
+** 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_128) ? GATT_INFO_TYPE_PAIR_128 : GATT_INFO_TYPE_PAIR_16;
+
+ 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
+ {
+ GATT_TRACE_ERROR0("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_DEBUG0("UUID filter does not exsit");
+ reason = GATT_INVALID_PDU;
+ }
+ else
+ len -= p_uuid_filter->len;
+ }
+ 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);
+
+ 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 *)GKI_getbuf(msg_len)) == NULL)
+ {
+ GATT_TRACE_ERROR0("gatts_process_primary_service_req failed. no resources.");
+ reason = GATT_NO_RESOURCES;
+ }
+ else
+ {
+ memset(p_msg, 0, msg_len);
+ 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_DEBUG1("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_DEBUG1("unexpected ReadByTypeValue type: 0x%04x", uuid.uu.uuid16);
+ }
+ }
+ }
+
+ if (reason != GATT_SUCCESS)
+ {
+ if (p_msg) GKI_freebuf(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 *)GKI_getbuf(buf_len)) == NULL)
+ {
+ reason = GATT_NO_RESOURCES;
+ }
+ else
+ {
+ reason = GATT_NOT_FOUND;
+
+ memset(p_msg, 0, buf_len);
+ 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) GKI_freebuf(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;
+
+ STREAM_TO_UINT16 (mtu, p);
+
+ /* 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
+ {
+ /* mtu must be greater than default MTU which is 23/48 */
+ if (mtu <= GATT_MAX_MTU_SIZE)
+ p_tcb->payload_size = mtu;
+ else
+ p_tcb->payload_size = GATT_MAX_MTU_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 applicaiton 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);
+
+#if GATT_CONFORMANCE_TESTING == TRUE
+ if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code)
+ {
+ GATT_TRACE_DEBUG1("Conformance tst: forced err rsp for ReadByType: error status=%d", 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 *)GKI_getbuf(msg_len)) == NULL)
+ {
+ GATT_TRACE_ERROR0("gatts_process_find_info failed. no resources.");
+
+ reason = GATT_NO_RESOURCES;
+ }
+ else
+ {
+ memset(p_msg, 0, msg_len);
+ 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,
+ (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID),
+ &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)
+ {
+ if (p_msg) GKI_freebuf(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);
+
+}
+
+/*******************************************************************************
+**
+** 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;
+
+ memset(&sr_data, 0, sizeof(tGATTS_DATA));
+
+ switch (op_code)
+ {
+ case GATT_REQ_PREPARE_WRITE:
+ sr_data.write_req.is_prep = TRUE;
+ STREAM_TO_UINT16(sr_data.write_req.offset, p);
+ len -= 2;
+ /* fall through */
+ case GATT_SIGN_CMD_WRITE:
+ if (op_code == GATT_SIGN_CMD_WRITE)
+ {
+ GATT_TRACE_DEBUG0("Write CMD with data sigining" );
+ len -= GATT_AUTH_SIGN_LEN;
+ }
+ /* fall through */
+ case GATT_CMD_WRITE:
+ case GATT_REQ_WRITE:
+ if (op_code == GATT_REQ_WRITE || op_code == GATT_REQ_PREPARE_WRITE)
+ sr_data.write_req.need_rsp = TRUE;
+ sr_data.write_req.handle = handle;
+ sr_data.write_req.len = len;
+ memcpy (sr_data.write_req.value, p, len);
+ break;
+ }
+
+ gatt_sr_get_sec_info(p_tcb->peer_bda,
+ (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID),
+ &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);
+ gatt_sr_send_req_callback(conn_id,
+ trans_id,
+ GATTS_REQ_TYPE_WRITE,
+ &sr_data);
+
+ status = GATT_PENDING;
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("max pending command, send error");
+ status = GATT_BUSY; /* max pending command, application error */
+ }
+ }
+
+ /* in theroy BUSY is not possible(should already been checked), protected check */
+ if (status != GATT_PENDING && status != GATT_BUSY &&
+ (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE))
+ {
+ gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE);
+ }
+ return;
+}
+
+/*******************************************************************************
+**
+** 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;
+
+ if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL)
+ {
+ GATT_TRACE_ERROR0("gatts_process_find_info failed. no resources.");
+
+ reason = GATT_NO_RESOURCES;
+ }
+ else
+ {
+ if (op_code == GATT_REQ_READ_BLOB)
+ STREAM_TO_UINT16(offset, p_data);
+
+ memset(p_msg, 0, buf_len);
+ 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,
+ (BOOLEAN)(p_tcb->att_lcid == L2CAP_ATT_CID),
+ &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)
+ {
+ if (p_msg) GKI_freebuf(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, handle, FALSE);
+ }
+ else
+ attp_send_sr_msg(p_tcb, 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;
+ UINT8 *p = p_data, i;
+ tGATT_SR_REG *p_rcb = gatt_cb.sr_reg;
+ tGATT_STATUS status = GATT_INVALID_HANDLE;
+ tGATT_ATTR16 *p_attr;
+
+ STREAM_TO_UINT16(handle, p);
+ len -= 2;
+
+#if GATT_CONFORMANCE_TESTING == TRUE
+ if (gatt_cb.enable_err_rsp && gatt_cb.req_op_code == op_code)
+ {
+ GATT_TRACE_DEBUG1("Conformance tst: forced err rsp: error status=%d", 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:
+ case GATT_REQ_PREPARE_WRITE:
+ gatts_process_write_req(p_tcb, i, handle, op_code, len, p);
+ break;
+ 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_DEBUG0("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_DEBUG0("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 )
+{
+ tGATT_VALUE *p_buf = (tGATT_VALUE *)GKI_getfirst(&p_tcb->pending_ind_q);
+ GATT_TRACE_DEBUG0("gatts_chk_pending_ind");
+
+ if (p_buf )
+ {
+ GATTS_HandleValueIndication (p_buf->conn_id,
+ p_buf->handle,
+ p_buf->len,
+ p_buf->value);
+ GKI_freebuf(GKI_remove_from_queue (&p_tcb->pending_ind_q, p_buf));
+ }
+}
+
+/*******************************************************************************
+**
+** 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_DEBUG1 ("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;
+ }
+
+ 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);
+ gatt_sr_send_req_callback(conn_id,
+ trans_id, GATTS_REQ_TYPE_CONF, (tGATTS_DATA *)&handle);
+ }
+ }
+ }
+ }
+ else
+ {
+ GATT_TRACE_ERROR0("unexpected handle value confirmation");
+ }
+}
+
+/*******************************************************************************
+**
+** 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_ERROR2("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
+ {
+ 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:
+ gatt_process_read_multi_req (p_tcb, op_code, len, p_data);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+#endif /* BLE_INCLUDED */
diff --git a/stack/gatt/gatt_utils.c b/stack/gatt/gatt_utils.c
new file mode 100644
index 0000000..ca72f9d
--- /dev/null
+++ b/stack/gatt/gatt_utils.c
@@ -0,0 +1,2587 @@
+/*****************************************************************************
+**
+** Name: gatt_utils.c
+**
+** Description: this file contains GATT utility functions
+**
+**
+** Copyright (c) 2009-2011, Broadcom Corp, All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+#include "bt_target.h"
+
+#if BLE_INCLUDED == TRUE
+ #include <string.h>
+ #include "stdio.h"
+ #include "gki.h"
+
+ #include "l2cdefs.h"
+ #include "gatt_int.h"
+ #include "gatt_api.h"
+ #include "gattdefs.h"
+ #include "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};
+
+
+/*******************************************************************************
+**
+** Function gatt_free_pending_ind
+**
+** Description Free all pending indications
+**
+** Returns None
+**
+*******************************************************************************/
+void gatt_free_pending_ind(tGATT_TCB *p_tcb)
+{
+ GATT_TRACE_DEBUG0("gatt_free_pending_ind");
+ /* release all queued indications */
+ while (p_tcb->pending_ind_q.p_first)
+ GKI_freebuf (GKI_dequeue (&p_tcb->pending_ind_q));
+}
+
+/*******************************************************************************
+**
+** 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_DEBUG0 ("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);
+ }
+ GKI_freebuf (GKI_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)
+{
+ tGATTS_SRV_CHG *p_buf = (tGATTS_SRV_CHG *)GKI_getfirst(&gatt_cb.srv_chg_clt_q);
+ tGATTS_SRV_CHG_REQ req;
+
+ GATT_TRACE_DEBUG0 ("gatt_set_srv_chg");
+ while (p_buf)
+ {
+ GATT_TRACE_DEBUG0 ("found a srv_chg clt");
+ if (!p_buf->srv_changed)
+ {
+ GATT_TRACE_DEBUG0 ("set srv_changed to TRUE");
+ p_buf->srv_changed= TRUE;
+ 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);
+ }
+ p_buf = (tGATTS_SRV_CHG *)GKI_getnext(p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** 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_HNDL_RANGE *p;
+ tGATTS_PENDING_NEW_SRV_START *p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getfirst(&gatt_cb.pending_new_srv_start_q);
+
+ while (p_buf != NULL)
+ {
+ 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_DEBUG0 ("gatt_sr_is_new_srv_chg: Yes");
+ break;
+ }
+ p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getnext(p_buf);
+ }
+
+ 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_DEBUG0 ("gatt_add_pending_ind");
+ if ((p_buf = (tGATT_VALUE *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL)
+ {
+ GATT_TRACE_DEBUG0 ("enqueue a pending indication");
+ memcpy(p_buf, p_ind, sizeof(tGATT_VALUE));
+ GKI_enqueue (&p_tcb->pending_ind_q, p_buf);
+ }
+ 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_DEBUG0 ("gatt_add_pending_new_srv_start");
+ if ((p_buf = (tGATTS_PENDING_NEW_SRV_START *)GKI_getbuf((UINT16)sizeof(tGATTS_PENDING_NEW_SRV_START))) != NULL)
+ {
+ GATT_TRACE_DEBUG0 ("enqueue a new pending new srv start");
+ p_buf->p_new_srv_start = p_new_srv_start;
+ GKI_enqueue (&gatt_cb.pending_new_srv_start_q, p_buf);
+ }
+ 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_DEBUG0 ("gatt_add_srv_chg_clt");
+ if ((p_buf = (tGATTS_SRV_CHG *)GKI_getbuf((UINT16)sizeof(tGATTS_SRV_CHG))) != NULL)
+ {
+ GATT_TRACE_DEBUG0 ("enqueue a srv chg client");
+ memcpy(p_buf, p_srv_chg, sizeof(tGATTS_SRV_CHG));
+ GKI_enqueue (&gatt_cb.srv_chg_clt_q, p_buf);
+ }
+
+ return p_buf;
+}
+
+
+/*******************************************************************************
+**
+** Function gatt_alloc_hdl_buffer
+**
+** Description Allocate a handle buufer
+**
+** Returns Pointer to the allocated buffer, NULL no buffer available
+**
+*******************************************************************************/
+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;
+ 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_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_DEBUG0 ("Already allocated handles for this service before!!");
+ return(p_list);
+ }
+ p_list = p_list->p_next;
+ }
+ return NULL;
+}
+/*******************************************************************************
+**
+** 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 (p->svc_db.svc_buffer.p_first)
+ GKI_freebuf (GKI_dequeue (&p->svc_db.svc_buffer));
+ 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
+**
+*******************************************************************************/
+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)
+ {
+ while (p_elem->svc_db.svc_buffer.p_first)
+ GKI_freebuf (GKI_dequeue (&p_elem->svc_db.svc_buffer));
+
+ 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)
+{
+ 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;
+}
+/*******************************************************************************
+**
+** 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_DEBUG0("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_DEBUG0("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_DEBUG0("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_DEBUG0("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)
+{
+ UINT8 i;
+ BOOLEAN found = FALSE;
+ GATT_TRACE_DEBUG1("gatt_find_the_connected_bda start_idx=%d",start_idx);
+
+ for (i = start_idx ; i < GATT_MAX_PHY_CHANNEL; i ++)
+ {
+ if (gatt_cb.tcb[i].in_use)
+ {
+ memcpy( bda, gatt_cb.tcb[i].peer_bda, BD_ADDR_LEN);
+ *p_found_idx = i;
+ found = TRUE;
+ GATT_TRACE_DEBUG6("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_DEBUG2("gatt_find_the_connected_bda found=%d found_idx=%d", found, i);
+ 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)
+{
+ tGATT_VALUE *p_buf = (tGATT_VALUE *)GKI_getfirst(&p_tcb->pending_ind_q);
+ BOOLEAN srv_chg_ind_pending = FALSE;
+
+ GATT_TRACE_DEBUG1("gatt_is_srv_chg_ind_pending is_queue_empty=%d", GKI_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
+ {
+ while (p_buf)
+ {
+ if (p_buf->handle == gatt_cb.handle_of_h_r)
+ {
+ srv_chg_ind_pending = TRUE;
+ break;
+ }
+ p_buf = (tGATT_VALUE *)GKI_getnext(p_buf);
+ }
+ }
+
+ GATT_TRACE_DEBUG1("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 = (tGATTS_SRV_CHG *)GKI_getfirst(&gatt_cb.srv_chg_clt_q);
+
+ GATT_TRACE_DEBUG6("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]);
+
+ while (p_buf != NULL)
+ {
+ if (!memcmp( bda, p_buf->bda, BD_ADDR_LEN))
+ {
+ GATT_TRACE_DEBUG0("bda is in the srv chg clt list");
+ break;
+ }
+ p_buf = (tGATTS_SRV_CHG *)GKI_getnext(p_buf);
+ }
+
+ 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)
+{
+ UINT8 i = 0;
+ BOOLEAN connected=FALSE;
+
+ for ( i=0; i < GATT_MAX_PHY_CHANNEL; i ++)
+ {
+ if (gatt_cb.tcb[i].in_use &&
+ !memcmp(gatt_cb.tcb[i].peer_bda, bda, BD_ADDR_LEN))
+ {
+ connected = TRUE;
+ break;
+ }
+ }
+ 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)
+{
+ UINT8 i = 0, j = GATT_INDEX_INVALID;
+
+ for ( ; i < GATT_MAX_PHY_CHANNEL; i ++)
+ {
+ if (!memcmp(gatt_cb.tcb[i].peer_bda, bda, BD_ADDR_LEN))
+ {
+ j = i;
+ break;
+ }
+ }
+ return j;
+}
+
+
+/*******************************************************************************
+**
+** 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;
+
+ if ( (tcb_idx < GATT_MAX_PHY_CHANNEL) && gatt_cb.tcb[tcb_idx].in_use)
+ p_tcb = &gatt_cb.tcb[tcb_idx];
+
+ 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)
+{
+ tGATT_TCB *p_tcb = NULL;
+ UINT8 i = 0;
+
+ if ((i = gatt_find_i_tcb_by_addr(bda)) != GATT_INDEX_INVALID)
+ p_tcb = &gatt_cb.tcb[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 (!gatt_cb.tcb[i].in_use)
+ {
+ j = i;
+ break;
+ }
+ }
+ return j;
+}
+/*******************************************************************************
+**
+** 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)
+{
+ 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);
+ /* find free tcb */
+ if (i == GATT_INDEX_INVALID)
+ {
+ i = gatt_find_i_tcb_free();
+ allocated = TRUE;
+ }
+ if (i != GATT_INDEX_INVALID)
+ {
+ p_tcb = &gatt_cb.tcb[i];
+
+ if (allocated)
+ {
+ memset(p_tcb, 0, sizeof(tGATT_TCB));
+ p_tcb->in_use = TRUE;
+ p_tcb->tcb_idx = i;
+ }
+ memcpy(p_tcb->peer_bda, bda, BD_ADDR_LEN);
+ }
+ 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_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 == 2 && tar.len == 2)
+ {
+ return src.uu.uuid16 == tar.uu.uuid16;
+ }
+
+ /* 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
+ 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
+ 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_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
+ is_base_uuid = FALSE;
+ }
+ 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;
+
+ case 0:
+ default:
+ if (uuid_size != 0) ret = FALSE;
+ GATT_TRACE_WARNING0("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(tGATT_TCB *p_tcb)
+{
+ p_tcb->rsp_timer_ent.param = (TIMER_PARAM_TYPE)p_tcb;
+ btu_start_timer (&p_tcb->rsp_timer_ent, BTU_TTYPE_ATT_WAIT_FOR_RSP,
+ GATT_WAIT_FOR_RSP_TOUT);
+}
+/*******************************************************************************
+**
+** 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_RSP_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)
+{
+ GATT_TRACE_WARNING0("gatt_rsp_timeout disconnecting...");
+ gatt_disconnect (((tGATT_TCB *)p_tle->param)->peer_bda);
+}
+
+/*******************************************************************************
+**
+** 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_WARNING0("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 0 if not found. Otherwise index of th eservice.
+**
+*******************************************************************************/
+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_ERROR0 ("Active Service Found ");
+ gatt_dbg_display_uuid(*p_svc_uuid);
+
+ 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 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->sr_cb = *p_cback;
+ p_sreg->p_db = &p_list->svc_db;
+
+ GATT_TRACE_DEBUG1 ("total GKI buffer in db [%d]",p_sreg->p_db->svc_buffer.count);
+ 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, BOOLEAN le_conn, UINT8 *p_sec_flag, UINT8 *p_key_size)
+{
+ UINT8 sec_flag = 0;
+
+ BTM_GetSecurityFlags(rem_bda, &sec_flag);
+
+ sec_flag &= (GATT_SEC_FLAG_LKEY_UNAUTHED | GATT_SEC_FLAG_LKEY_AUTHED | GATT_SEC_FLAG_ENCRYPTED);
+
+ *p_key_size = btm_ble_read_sec_key_size(rem_bda);
+ *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_ERROR0 ("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_WARNING1("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 (deq)
+ gatt_dequeue_sr_cmd(p_tcb);
+
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** 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_DEBUG2("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_128:
+ UINT8_TO_BE_STREAM (p, (UUID_DESC_TYPE << 3) | SIZE_SIXTEEN_BYTES);
+ ARRAY_TO_BE_STREAM (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_ERROR1("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);
+}
+
+
+ #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_DEBUG3("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)
+ {
+ ii--; /* convert from one based to zero based */
+ p_reg = &gatt_cb.cl_rcb[ii];
+ if ( (ii < GATT_MAX_APPS) && (p_reg->in_use) )
+ return(p_reg);
+ }
+
+ return NULL;
+}
+
+
+/*******************************************************************************
+**
+** 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)
+{
+ UINT8 i = 0;
+ BOOLEAN is_allocated= FALSE;
+
+ for (i = 0; i < GATT_CL_MAX_LCB; i++)
+ {
+ if (gatt_cb.clcb[i].in_use && (gatt_cb.clcb[i].conn_id == conn_id))
+ {
+ is_allocated = TRUE;
+ break;
+ }
+ }
+
+ return is_allocated;
+}
+
+/*******************************************************************************
+**
+** 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)
+{
+ UINT8 i = 0;
+ 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);
+
+ for (i = 0; i < GATT_CL_MAX_LCB; i++)
+ {
+ if (!gatt_cb.clcb[i].in_use)
+ {
+ p_clcb = &gatt_cb.clcb[i];
+
+ p_clcb->in_use = TRUE;
+ p_clcb->conn_id = conn_id;
+ p_clcb->clcb_idx = i;
+ p_clcb->p_reg = p_reg;
+ p_clcb->p_tcb = p_tcb;
+ break;
+ }
+ }
+ 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)
+ {
+ memset(p_clcb, 0, sizeof(tGATT_CLCB));
+ }
+}
+
+
+
+/*******************************************************************************
+**
+** 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)
+{
+ UINT16 xx = 0;
+ tGATT_TCB *p_tcb = NULL;
+
+ for (xx = 0; xx < GATT_MAX_PHY_CHANNEL; xx++)
+ {
+ if (gatt_cb.tcb[xx].in_use && gatt_cb.tcb[xx].att_lcid == lcid)
+ {
+ p_tcb = &gatt_cb.tcb[xx];
+ 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_DEBUG1("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 i, num = 0;
+
+ for (i = 0; i < GATT_CL_MAX_LCB; i ++)
+ {
+ if (gatt_cb.clcb[i].in_use && memcmp(gatt_cb.clcb[i].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 )
+{
+ 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;
+ }
+ }
+ }
+
+}
+
+/*******************************************************************************
+**
+** 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;
+ 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;
+ }
+ 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 )
+{
+ UINT8 i;
+
+ if (p_tcb)
+ {
+ for (i = 0; i < GATT_MAX_APPS; i ++)
+ {
+ p_tcb->sr_cmd.cback_cnt[i]=0;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** 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 applicaiton 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)
+{
+
+ 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]--;
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** 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_DEBUG4("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);
+ if (p_tcb)
+ {
+ if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN)
+ {
+ GATT_TRACE_ERROR0("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->peer_bda);
+ }
+ }
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** Function gatt_find_app_hold_link
+**
+** Description find the applicaiton 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 = gatt_cb.clcb[i].p_reg->gatt_if;
+ *p_found_idx = i;
+ 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_cb.clcb[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;
+ UINT8 operation;
+
+ GATT_TRACE_DEBUG3 ("gatt_end_operation status=%d op=%d subtype=%d",
+ status, p_clcb->operation, p_clcb->op_subtype);
+
+ if (p_cmpl_cb != NULL && p_clcb->operation != 0)
+ {
+ if (p_clcb->operation == GATTC_OPTYPE_READ)
+ {
+ memset(&cb_data.att_value, 0, sizeof(tGATT_VALUE));
+ cb_data.att_value.handle = p_clcb->s_handle;
+ cb_data.att_value.len = p_clcb->counter;
+ if (p_data)
+ 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_DEBUG0("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)
+ {
+ GKI_freebuf(p_clcb->p_attr_buf);
+ }
+
+ operation = p_clcb->operation;
+ conn_id = p_clcb->conn_id;
+
+ 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_WARNING3 ("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)
+{
+ tGATT_TCB *p_tcb = NULL;
+ tGATT_CLCB *p_clcb;
+ UINT8 i;
+ UINT16 conn_id;
+ tGATT_REG *p_reg=NULL;
+
+
+ GATT_TRACE_DEBUG0 ("gatt_cleanup_upon_disc ");
+
+ if ((p_tcb = gatt_find_tcb_by_addr(bda)) != NULL)
+ {
+ GATT_TRACE_DEBUG0 ("found p_tcb ");
+ for (i = 0; i < GATT_CL_MAX_LCB; i ++)
+ {
+ p_clcb = &gatt_cb.clcb[i];
+ if (p_clcb->in_use && p_clcb->p_tcb == p_tcb)
+ {
+ GATT_TRACE_DEBUG2 ("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);
+
+ gatt_clcb_dealloc(p_clcb);
+
+ }
+ }
+
+ btu_stop_timer (&p_tcb->rsp_timer_ent);
+ btu_stop_timer (&p_tcb->ind_ack_timer_ent);
+ btu_stop_timer (&p_tcb->conf_timer_ent);
+ gatt_free_pending_ind(p_tcb);
+
+ 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_DEBUG3 ("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);
+ }
+ }
+ memset(p_tcb, 0, sizeof(tGATT_TCB));
+
+ }
+ GATT_TRACE_DEBUG0 ("exit gatt_cleanup_upon_disc ");
+}
+/*******************************************************************************
+**
+** 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_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, sizeof(str_buf), "Unknown UUID 0", 15);
+
+ GATT_TRACE_DEBUG1 ("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 back ground connection device list
+**
+** Returns pointer to the device record
+**
+*******************************************************************************/
+BOOLEAN gatt_add_bg_dev_list(tGATT_IF gatt_if, BD_ADDR bd_addr)
+{
+ tGATT_BG_CONN_DEV *p_dev = NULL;
+ UINT8 i;
+
+ 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 (p_dev->gatt_if[i] == gatt_if)
+ {
+ GATT_TRACE_ERROR0("device already in list");
+ return FALSE;
+ }
+ else if (p_dev->gatt_if[i] == 0)
+ {
+ GATT_TRACE_DEBUG0("add device into list");
+ p_dev->gatt_if[i] = gatt_if;
+ return TRUE;
+ }
+ }
+ }
+
+ GATT_TRACE_ERROR0("no device record available");
+
+ return FALSE;
+}
+
+
+/*******************************************************************************
+**
+** 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);
+ 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);
+ 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 back ground connection device list
+**
+** Returns pointer to the device record
+**
+*******************************************************************************/
+BOOLEAN gatt_remove_bg_dev_from_list(tGATT_IF gatt_if, BD_ADDR bd_addr)
+{
+ 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; i ++)
+ {
+ if (p_dev->gatt_if[i] == gatt_if)
+ {
+ p_dev->gatt_if[i] = 0;
+
+ 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);
+ memset(p_dev, 0, sizeof(tGATT_BG_CONN_DEV));
+ }
+ else
+ ret = TRUE;
+
+ break;
+ }
+ }
+
+ return ret;
+}
+/*******************************************************************************
+**
+** Function gatt_deregister_bgdev_list
+**
+** Description deregister all related back ground connetion 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;
+
+ 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)
+ break;
+ else 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);
+ memset(p_dev_list, 0, sizeof(tGATT_BG_CONN_DEV));
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** 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 ret = FALSE, exist_dev = FALSE;
+ tGATT_REG *p_reg;
+ tGATT_TCB *p_tcb = gatt_find_tcb_by_addr(bd_addr);
+
+ GATT_TRACE_API0 ("gatt_update_auto_connect_dev ");
+ /* Make sure app is registered */
+ if ((p_reg = gatt_get_regcb(gatt_if)) == NULL)
+ {
+ GATT_TRACE_ERROR1("gatt_update_auto_connect_dev - gatt_if is not registered", gatt_if);
+ return(FALSE);
+ }
+
+ if (add)
+ {
+ /* new device */
+ if (gatt_find_bg_dev(bd_addr))
+ exist_dev = TRUE;
+
+ if (gatt_add_bg_dev_list(gatt_if, bd_addr))
+ {
+ if (!exist_dev)
+ {
+ ret = BTM_BleUpdateBgConnDev(TRUE, bd_addr);
+ }
+ else
+ ret = TRUE;
+
+ /* if a connected device, update the link holding number */
+ if (p_tcb != NULL)
+ gatt_update_app_use_link_flag(gatt_if, p_tcb, TRUE, TRUE);
+ }
+ }
+ else
+ {
+ ret = gatt_remove_bg_dev_from_list(gatt_if, bd_addr);
+ }
+ return ret;
+}
+
+
+
+/*******************************************************************************
+**
+** Function gatt_get_conn_id
+**
+** Description This function returns a connecttion handle to a ATT server
+** if the server is already connected
+**
+** Parameters gatt_if: client interface.
+** bd_addr: peer device address.
+**
+** Returns Connection handle or invalid handle value
+**
+*******************************************************************************/
+UINT16 gatt_get_conn_id (tGATT_IF gatt_if, BD_ADDR bd_addr)
+{
+ tGATT_REG *p_reg;
+ tGATT_CLCB *p_clcb;
+ tGATT_TCB *p_tcb;
+ UINT8 i;
+
+ GATT_TRACE_API1 ("GATTC_GetConnIfConnected gatt_if=%d", gatt_if);
+ /* Do we have a transport to the peer ? If not, we are not connected */
+ if ((p_tcb = gatt_find_tcb_by_addr(bd_addr)) == NULL)
+ {
+ GATT_TRACE_EVENT0 ("GATTC_GetConnIfConnected - no TCB found");
+ return(GATT_INVALID_CONN_ID);
+ }
+
+ /* Make sure app is registered */
+ if ((p_reg = gatt_get_regcb(gatt_if)) == NULL)
+ {
+ GATT_TRACE_ERROR1("GATTC_GetConnIfConnected - gatt_if is not registered", gatt_if);
+ return(GATT_INVALID_CONN_ID);
+ }
+
+ /* Now see if the app already has a client control block to that peer */
+ for (i = 0, p_clcb = gatt_cb.clcb; i < GATT_CL_MAX_LCB; i++, p_clcb++)
+ {
+ if ( p_clcb->in_use && (p_clcb->p_reg == p_reg) && (p_clcb->p_tcb == p_tcb) )
+ {
+ return(p_clcb->conn_id);
+ }
+ }
+
+ /* If here, failed to allocate a client control block */
+ GATT_TRACE_ERROR1 ("gatt_get_conn_id: not connected- gatt_if: %u", gatt_if);
+ return(GATT_INVALID_CONN_ID);
+}
+
+
+
+
+#endif
+
+