summaryrefslogtreecommitdiffstats
path: root/stack/gatt/gatt_cl.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/gatt/gatt_cl.c')
-rw-r--r--stack/gatt/gatt_cl.c1206
1 files changed, 1206 insertions, 0 deletions
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 */