diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/gatt | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_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.c | 617 | ||||
-rw-r--r-- | stack/gatt/gatt_api.c | 1542 | ||||
-rw-r--r-- | stack/gatt/gatt_attr.c | 264 | ||||
-rw-r--r-- | stack/gatt/gatt_auth.c | 435 | ||||
-rw-r--r-- | stack/gatt/gatt_cl.c | 1206 | ||||
-rw-r--r-- | stack/gatt/gatt_db.c | 1117 | ||||
-rw-r--r-- | stack/gatt/gatt_int.h | 664 | ||||
-rw-r--r-- | stack/gatt/gatt_main.c | 1101 | ||||
-rw-r--r-- | stack/gatt/gatt_sr.c | 1472 | ||||
-rw-r--r-- | stack/gatt/gatt_utils.c | 2587 |
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 *)¬if); + 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 + + |