bluedroid: add internal GATT API for PTS

This commit is contained in:
chenjianhua 2022-12-08 19:49:35 +08:00
parent 69b8b587c9
commit 0b76c3615e
13 changed files with 600 additions and 35 deletions

View File

@ -126,6 +126,7 @@ static void bta_gattc_enable(tBTA_GATTC_CB *p_cb)
if (p_cb->state == BTA_GATTC_STATE_DISABLED) {
/* initialize control block */
memset(&bta_gattc_cb, 0, sizeof(tBTA_GATTC_CB));
bta_gattc_cb.auto_disc = true;
p_cb->state = BTA_GATTC_STATE_ENABLED;
} else {
APPL_TRACE_DEBUG("GATTC is already enabled");
@ -692,9 +693,11 @@ void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
} else
#endif
{ /* cache is building */
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
/* cache load failure, start discovery */
bta_gattc_start_discover(p_clcb, NULL);
if (bta_gattc_cb.auto_disc) {
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
/* cache load failure, start discovery */
bta_gattc_start_discover(p_clcb, NULL);
}
}
} else { /* cache is building */
p_clcb->state = BTA_GATTC_DISCOVER_ST;

View File

@ -1103,4 +1103,108 @@ void BTA_GATTC_Broadcast(tBTA_GATTC_IF client_if, BOOLEAN start)
return;
}
/* Add For BLE PTS */
uint8_t BTA_GATTC_AutoDiscoverEnable(uint8_t enable)
{
APPL_TRACE_DEBUG("%s enable %d", __func__, enable);
bta_gattc_cb.auto_disc = ((enable > 0) ? true : false);
GATTC_AutoDiscoverEnable(enable);
return 0;
}
typedef struct {
UINT16 len;
union {
UINT16 uuid16;
UINT32 uuid32;
UINT8 uuid128[LEN_UUID_128];
} uuid;
} __attribute__((packed)) tAPP_UUID;
uint8_t BTA_GATTC_Discover(uint8_t gatt_if, uint16_t conn_id, void *uuid, uint8_t disc_type, uint16_t s_handle, uint16_t e_handle)
{
tGATT_STATUS status;
tGATT_DISC_PARAM param;
tAPP_UUID *app_uuid = (tAPP_UUID *)uuid;
conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if);
memset(&param, 0, sizeof(tGATT_DISC_PARAM));
if (disc_type == GATT_DISC_SRVC_ALL || disc_type == GATT_DISC_SRVC_BY_UUID) {
param.s_handle = 1;
param.e_handle = 0xFFFF;
} else {
param.s_handle = s_handle;
param.e_handle = e_handle;
}
if (app_uuid) {
param.service.len = app_uuid->len;
if (app_uuid->len == LEN_UUID_16) {
param.service.uu.uuid16 = app_uuid->uuid.uuid16;
} else if (app_uuid->len == LEN_UUID_32) {
param.service.uu.uuid32 = app_uuid->uuid.uuid32;
} else if (app_uuid->len == LEN_UUID_128) {
memcpy(param.service.uu.uuid128, app_uuid->uuid.uuid128, LEN_UUID_128);
} else {
APPL_TRACE_ERROR("%s invalid uuid len %u", __func__, app_uuid->len);
}
}
status = GATTC_Discover (conn_id, disc_type, &param);
if (status != GATT_SUCCESS) {
APPL_TRACE_ERROR("%s status %x", __func__, status);
return -1;
}
return 0;
}
uint8_t BTA_GATTC_ReadLongChar(uint8_t gatt_if, uint16_t conn_id, uint16_t handle, uint16_t offset, uint8_t auth_req)
{
tGATT_STATUS status;
tGATT_READ_PARAM read_param;
conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if);
memset (&read_param, 0, sizeof(tGATT_READ_PARAM));
read_param.partial.handle = handle;
read_param.partial.offset = offset;
read_param.partial.auth_req = auth_req;
status = GATTC_Read(conn_id, GATT_READ_PARTIAL, &read_param);
if (status != GATT_SUCCESS) {
APPL_TRACE_ERROR("%s status %x", __func__, status);
return -1;
}
return 0;
}
uint8_t BTA_GATTC_ReadMultiVariableChar(uint8_t gatt_if, uint16_t conn_id, uint16_t num_handles, uint16_t *handles, uint8_t auth_req)
{
tGATT_STATUS status;
tGATT_READ_PARAM read_param;
if (num_handles > GATT_MAX_READ_MULTI_HANDLES) {
APPL_TRACE_ERROR("%s max read multi handlse %x", __func__, num_handles);
return -1;
}
conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if);
memset (&read_param, 0, sizeof(tGATT_READ_PARAM));
read_param.read_multiple.num_handles = num_handles;
memcpy(read_param.read_multiple.handles, handles, num_handles);
read_param.read_multiple.auth_req = auth_req;
status = GATTC_Read(conn_id, GATT_READ_MULTIPLE_VAR, &read_param);
if (status != GATT_SUCCESS) {
APPL_TRACE_ERROR("%s status %x", __func__, status);
return -1;
}
return 0;
}
/* End BLE PTS */
#endif /* defined(GATTC_INCLUDED) && (GATTC_INCLUDED == TRUE) */

View File

@ -969,6 +969,10 @@ void bta_gattc_disc_res_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT_
BOOLEAN pri_srvc;
tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
if (bta_gattc_cb.auto_disc == FALSE) {
return;
}
p_srvc_cb = bta_gattc_find_scb_by_cid(conn_id);
if (p_srvc_cb != NULL && p_clcb != NULL && p_clcb->state == BTA_GATTC_DISCOVER_ST) {
@ -1042,6 +1046,10 @@ void bta_gattc_disc_cmpl_cback (UINT16 conn_id, tGATT_DISC_TYPE disc_type, tGATT
tBTA_GATTC_SERV *p_srvc_cb;
tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
if (bta_gattc_cb.auto_disc == FALSE) {
return;
}
if ( p_clcb && (status != GATT_SUCCESS || p_clcb->status != GATT_SUCCESS) ) {
if (status == GATT_SUCCESS) {
p_clcb->status = status;

View File

@ -640,4 +640,31 @@ void BTA_GATTS_Listen(tBTA_GATTS_IF server_if, BOOLEAN start, BD_ADDR_PTR target
return;
}
uint8_t BTA_GATTS_SetServiceChangeMode(uint8_t mode)
{
tGATT_STATUS status;
APPL_TRACE_DEBUG("%s mode %u", __func__, mode);
status = GATTS_SetServiceChangeMode(mode);
if (status != GATT_SUCCESS) {
APPL_TRACE_ERROR("%s status %x", __func__, status);
return -1;
}
return 0;
}
uint8_t BTA_GATTS_SendMultiNotification(uint8_t gatt_if, uint16_t conn_id, void *tuples, uint16_t num_tuples)
{
tGATT_STATUS status;
conn_id = (UINT16)((((UINT8)conn_id) << 8) | gatt_if);
status = GATTS_HandleMultiValueNotification(conn_id, (tGATT_HLV *)tuples, num_tuples);
if (status != GATT_SUCCESS) {
APPL_TRACE_ERROR("%s status %x", __func__, status);
return -1;
}
return 0;
}
#endif /* BTA_GATT_INCLUDED */

View File

@ -394,6 +394,7 @@ enum {
typedef struct {
UINT8 state;
BOOLEAN auto_disc; /* internal use: true for auto discovering after connected */
tBTA_GATTC_CONN conn_track[BTA_GATTC_CONN_MAX];
tBTA_GATTC_BG_TCK bg_track[BTA_GATTC_KNOWN_SR_MAX];
tBTA_GATTC_RCB cl_rcb[BTA_GATTC_CL_MAX];

View File

@ -197,7 +197,7 @@ BT_HDR *attp_build_read_by_type_value_cmd (UINT16 payload_size, tGATT_FIND_TYPE_
** Returns None.
**
*******************************************************************************/
BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle)
BT_HDR *attp_build_read_multi_cmd(UINT8 op_code, UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle)
{
BT_HDR *p_buf = NULL;
UINT8 *p, i = 0;
@ -208,7 +208,7 @@ BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16
p_buf->offset = L2CAP_MIN_OFFSET;
p_buf->len = 1;
UINT8_TO_STREAM (p, GATT_REQ_READ_MULTI);
UINT8_TO_STREAM (p, op_code);
for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) {
UINT16_TO_STREAM (p, *(p_handle + i));
@ -304,7 +304,7 @@ BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle,
UINT8_TO_STREAM (p, pair_len);
p_buf->len += 1;
}
if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ) {
if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ && op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) {
UINT16_TO_STREAM (p, handle);
p_buf->len += 2;
}
@ -391,6 +391,7 @@ BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
case GATT_RSP_READ:
case GATT_HANDLE_VALUE_NOTIF:
case GATT_HANDLE_VALUE_IND:
case GATT_HANDLE_MULTI_VALUE_NOTIF:
case GATT_RSP_ERROR:
case GATT_RSP_MTU:
/* Need to check the validation of parameter p_msg*/
@ -417,6 +418,7 @@ BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
case GATT_RSP_READ:
case GATT_HANDLE_VALUE_NOTIF:
case GATT_HANDLE_VALUE_IND:
case GATT_HANDLE_MULTI_VALUE_NOTIF:
p_cmd = attp_build_value_cmd(p_tcb->payload_size,
op_code,
p_msg->attr_value.handle,
@ -613,7 +615,8 @@ tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
break;
case GATT_REQ_READ_MULTI:
p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size,
case GATT_REQ_READ_MULTI_VAR:
p_cmd = attp_build_read_multi_cmd(op_code, p_tcb->payload_size,
p_msg->read_multi.num_handles,
p_msg->read_multi.handles);
break;

View File

@ -413,7 +413,7 @@ BOOLEAN GATTS_DeleteService (tGATT_IF gatt_if, tBT_UUID *p_svc_uuid, UINT16 svc_
osi_free(fixed_queue_try_remove_from_queue(gatt_cb.pending_new_srv_start_q, p_buf));
} else {
gatt_update_for_database_change();
if (GATTS_SEND_SERVICE_CHANGE_MODE == GATTS_SEND_SERVICE_CHANGE_AUTO) {
if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) {
gatt_proc_srv_chg();
}
}
@ -526,7 +526,7 @@ tGATT_STATUS GATTS_StartService (tGATT_IF gatt_if, UINT16 service_handle,
&p_list->asgn_range.svc_uuid,
p_list->asgn_range.svc_inst)) != NULL) {
gatt_update_for_database_change();
if (GATTS_SEND_SERVICE_CHANGE_MODE == GATTS_SEND_SERVICE_CHANGE_AUTO) {
if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) {
gatt_proc_srv_chg();
}
/* remove the new service element after the srv changed processing is completed*/
@ -996,6 +996,7 @@ tGATT_STATUS GATTC_Read (UINT16 conn_id, tGATT_READ_TYPE type, tGATT_READ_PARAM
memcpy(&p_clcb->uuid, &p_read->service.uuid, sizeof(tBT_UUID));
break;
case GATT_READ_MULTIPLE:
case GATT_READ_MULTIPLE_VAR:
p_clcb->s_handle = 0;
/* copy multiple handles in CB */
p_read_multi = (tGATT_READ_MULTI *)osi_malloc(sizeof(tGATT_READ_MULTI));
@ -1187,6 +1188,12 @@ tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle)
return ret;
}
tGATT_STATUS GATTC_AutoDiscoverEnable(UINT8 enable)
{
gatt_cb.auto_disc = (enable > 0) ? TRUE : FALSE;
return GATT_SUCCESS;
}
#endif ///GATTC_INCLUDED == TRUE
/*******************************************************************************/
@ -1559,7 +1566,8 @@ tGATT_STATUS GATT_SendServiceChangeIndication (BD_ADDR bd_addr)
tGATT_TCB *p_tcb;
tBT_TRANSPORT transport;
tGATT_STATUS status = GATT_NOT_FOUND;
if (GATTS_SEND_SERVICE_CHANGE_MODE == GATTS_SEND_SERVICE_CHANGE_AUTO) {
if (gatt_cb.srv_chg_mode == GATTS_SEND_SERVICE_CHANGE_AUTO) {
status = GATT_WRONG_STATE;
GATT_TRACE_ERROR ("%s can't send service change indication manually, please configure the option through menuconfig", __func__);
return status;
@ -1691,4 +1699,72 @@ BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr)
return gatt_update_listen_mode();
}
tGATT_STATUS GATTS_SetServiceChangeMode(UINT8 mode)
{
if (mode > GATTS_SEND_SERVICE_CHANGE_MANUAL) {
GATT_TRACE_ERROR("%s invalid service change mode %u", __func__, mode);
return GATT_VALUE_NOT_ALLOWED;
}
gatt_cb.srv_chg_mode = mode;
return GATT_SUCCESS;
}
tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tuples, UINT16 num_tuples)
{
tGATT_STATUS cmd_sent = GATT_ILLEGAL_PARAMETER;
BT_HDR *p_buf;
tGATT_VALUE notif;
tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id);
UINT8 tcb_idx = GATT_GET_TCB_IDX(conn_id);
tGATT_REG *p_reg = gatt_get_regcb(gatt_if);
tGATT_TCB *p_tcb = gatt_get_tcb_by_idx(tcb_idx);
UINT8 *p = notif.value;
tGATT_HLV *p_hlv = tuples;
GATT_TRACE_API ("GATTS_HandleMultiValueNotification");
if ( (p_reg == NULL) || (p_tcb == NULL)) {
GATT_TRACE_ERROR ("GATTS_HandleMultiValueNotification Unknown conn_id: %u \n", conn_id);
return (tGATT_STATUS) GATT_INVALID_CONN_ID;
}
if (!gatt_check_connection_state_by_tcb(p_tcb)) {
GATT_TRACE_ERROR("connection not established\n");
return GATT_WRONG_STATE;
}
if (tuples == NULL) {
return GATT_ILLEGAL_PARAMETER;
}
notif.len = 0;
while (num_tuples) {
if (!GATT_HANDLE_IS_VALID (p_hlv->handle)) {
return GATT_ILLEGAL_PARAMETER;
}
UINT16_TO_STREAM(p, p_hlv->handle); //handle
UINT16_TO_STREAM(p, p_hlv->length); //length
memcpy (p, p_hlv->value, p_hlv->length); //value
GATT_TRACE_DEBUG("%s handle %x, length %u", __func__, p_hlv->handle, p_hlv->length);
p += p_hlv->length;
notif.len += 4 + p_hlv->length;
num_tuples--;
p_hlv++;
}
notif.auth_req = GATT_AUTH_REQ_NONE;
p_buf = attp_build_sr_msg (p_tcb, GATT_HANDLE_MULTI_VALUE_NOTIF, (tGATT_SR_MSG *)&notif);
if (p_buf != NULL) {
cmd_sent = attp_send_sr_msg (p_tcb, p_buf);
} else {
cmd_sent = GATT_NO_RESOURCES;
}
return cmd_sent;
}
#endif

View File

@ -53,6 +53,7 @@ static const UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = {
GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */
GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */
GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */
GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR_BY_UUID, */
GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */
};
@ -65,6 +66,41 @@ static const UINT16 disc_type_to_uuid[GATT_DISC_MAX] = {
0 /* no type filtering for DISC_CHAR_DSCPT */
};
// Use for GATTC discover infomation print
#define GATT_DISC_INFO(fmt, args...) {if (gatt_cb.auto_disc == FALSE) BT_PRINT_I("BT_GATT", fmt, ## args);}
char *gatt_uuid_to_str(const tBT_UUID *uuid)
{
static char dst[48] = {0};
const uint8_t *u8p;
memset(dst, 0, sizeof(dst));
switch (uuid->len) {
case LEN_UUID_16:
sprintf(dst, "0x%04x", uuid->uu.uuid16);
break;
case LEN_UUID_32:
sprintf(dst, "0x%08x", uuid->uu.uuid32);
break;
case LEN_UUID_128:
u8p = uuid->uu.uuid128;
sprintf(dst, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x",
u8p[15], u8p[14], u8p[13], u8p[12],
u8p[11], u8p[10], u8p[9], u8p[8],
u8p[7], u8p[6], u8p[5], u8p[4],
u8p[3], u8p[2], u8p[1], u8p[0]);
break;
default:
dst[0] = '\0';
break;
}
return dst;
}
/*******************************************************************************
**
@ -107,6 +143,10 @@ void gatt_act_discovery(tGATT_CLCB *p_clcb)
}
}
if (p_clcb->op_subtype == GATT_DISC_CHAR_BY_UUID) {
memcpy(&cl_req.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID));
}
st = attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req);
if (st != GATT_SUCCESS && st != GATT_CMD_STARTED) {
@ -181,6 +221,11 @@ void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset)
memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
break;
case GATT_READ_MULTIPLE_VAR:
op_code = GATT_REQ_READ_MULTI_VAR;
memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
break;
case GATT_READ_INC_SRV_UUID128:
op_code = GATT_REQ_READ;
msg.handle = p_clcb->s_handle;
@ -408,6 +453,7 @@ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UIN
while (len >= 4) {
STREAM_TO_UINT16 (result.handle, p);
STREAM_TO_UINT16 (result.value.group_value.e_handle, p);
GATT_DISC_INFO("%s handle %x, end handle %x", __func__, result.handle, result.value.group_value.e_handle);
memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID));
len -= 4;
@ -474,6 +520,8 @@ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_c
len -= (uuid_len + 2);
GATT_DISC_INFO("%s handle %x, uuid %s", __func__, result.handle, gatt_uuid_to_str(&result.type));
if (p_clcb->p_reg->app_cb.p_disc_res_cb) {
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result);
}
@ -510,7 +558,7 @@ void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode
case GATT_REQ_FIND_INFO:
if (reason == GATT_NOT_FOUND) {
status = GATT_SUCCESS;
GATT_TRACE_DEBUG("Discovery completed");
GATT_DISC_INFO("Discovery completed");
}
break;
default:
@ -541,7 +589,7 @@ void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
UNUSED(op_code);
UNUSED(len);
GATT_TRACE_DEBUG("gatt_process_error_rsp ");
GATT_TRACE_DEBUG("%s", __func__);
STREAM_TO_UINT8(opcode, p);
STREAM_TO_UINT16(handle, p);
STREAM_TO_UINT8(reason, p);
@ -634,7 +682,7 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
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;
event = (op_code == GATT_HANDLE_VALUE_IND) ? GATTC_OPTYPE_INDICATION: GATTC_OPTYPE_NOTIFICATION;
GATT_TRACE_DEBUG("gatt_process_notification ");
@ -644,8 +692,6 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
}
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 */
@ -655,6 +701,28 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
return;
}
if (op_code == GATT_HANDLE_MULTI_VALUE_NOTIF) {
if (len < GATT_NOTIFICATION_MIN_LEN + 2) {
GATT_TRACE_ERROR("illegal notification PDU length, discard");
return;
}
STREAM_TO_UINT16(value.len, p);
if (value.len > len - 4) {
return;
}
} else {
value.len = len - 2;
}
if (value.len > GATT_MAX_ATTR_LEN) {
GATT_TRACE_ERROR("value length larger than GATT_MAX_ATTR_LEN, discard");
return;
}
memcpy(value.value, p, value.len);
p += value.len;
if (event == GATTC_OPTYPE_INDICATION) {
if (p_tcb->ind_count) {
/* this is an error case that receiving an indication but we
@ -665,19 +733,16 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
GATT_TRACE_ERROR("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count);
}
p_tcb->ind_count = 0;
}
/* should notify all registered client with the handle value notificaion/indication
Note: need to do the indication count and start timer first then do callback
*/
for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION)) {
p_tcb->ind_count++;
/* 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);
@ -694,6 +759,33 @@ void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code,
}
}
if (op_code != GATT_HANDLE_MULTI_VALUE_NOTIF) {
return;
}
if (len < (4 + value.len)) {
GATT_TRACE_ERROR("no remain data for multi notification");
return;
}
len -= (4 + value.len);
while (len > 4) {
STREAM_TO_UINT16(value.handle, p);
STREAM_TO_UINT16(value.len, p);
len -= 4;
value.len = MIN(len, value.len);
memcpy(value.value, p, value.len);
p += value.len;
len -= value.len;
for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) {
if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) {
conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if);
(*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value);
}
}
}
}
/*******************************************************************************
@ -746,6 +838,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
while (len >= (handle_len + value_len)) {
STREAM_TO_UINT16(handle, p);
GATT_DISC_INFO("%s op %x, handle %x", __func__, op_code, handle);
if (!GATT_HANDLE_IS_VALID(handle)) {
gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL);
@ -775,6 +868,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
break;
}
}
GATT_DISC_INFO("DISC ALL SVC end handle %x, uuid %s", record_value.group_value.e_handle, gatt_uuid_to_str(&record_value.group_value.service_type));
}
/* discover included service */
else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) {
@ -790,6 +884,8 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
if (value_len == 6) {
STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p);
record_value.incl_service.service_type.len = LEN_UUID_16;
GATT_DISC_INFO("DISC INC SVC start handle %x, end handle %x, uuid %s",
record_value.incl_service.s_handle, record_value.incl_service.e_handle, gatt_uuid_to_str(&record_value.incl_service.service_type));
} else if (value_len == 4) {
p_clcb->s_handle = record_value.incl_service.s_handle;
p_clcb->read_uuid128.wait_for_read_rsp = TRUE;
@ -797,7 +893,7 @@ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8
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);
gatt_act_read(p_clcb, 0); // read 128-bit uuid of include service
return;
} else {
GATT_TRACE_ERROR("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len);
@ -937,6 +1033,9 @@ void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code,
memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len);
p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128;
tGATT_INCL_SRVC *inc_srvc = &p_clcb->read_uuid128.result.value.incl_service;
GATT_DISC_INFO("DISC INC SRVC start handle %x, end handle %x, uuid %s",
inc_srvc->s_handle, inc_srvc->e_handle, gatt_uuid_to_str(&inc_srvc->service_type));
if ( p_clcb->p_reg->app_cb.p_disc_res_cb) {
(*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result);
}
@ -1133,6 +1232,7 @@ void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
case GATT_RSP_READ:
case GATT_RSP_READ_BLOB:
case GATT_RSP_READ_MULTI:
case GATT_RSP_READ_MULTI_VAR:
gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data);
break;
@ -1154,6 +1254,7 @@ void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code,
case GATT_HANDLE_VALUE_NOTIF:
case GATT_HANDLE_VALUE_IND:
case GATT_HANDLE_MULTI_VALUE_NOTIF:
gatt_process_notification(p_tcb, op_code, len, p_data);
break;

View File

@ -35,6 +35,9 @@
#include "stack/l2c_api.h"
#include "btm_int.h"
extern tGATT_STATUS gap_proc_read(tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp);
extern tGATT_STATUS gatt_proc_read(UINT16 conn_id, tGATTS_REQ_TYPE type, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp);
/********************************************************************************
** L O C A L F U N C T I O N P R O T O T Y P E S *
*********************************************************************************/
@ -764,6 +767,66 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle,
return GATT_SUCCESS;
}
/*******************************************************************************
**
** Function gatts_get_attr_value_internal
**
** Description This function get the attribute value in gap service and gatt service
**
** Parameter attr_handle: the attribute handle
** length: the attribute value length
** value: the pointer to the data to be get to the attribute value in the database
**
** Returns Status of the operation.
**
*******************************************************************************/
static tGATT_STATUS gatts_get_attr_value_internal(UINT16 attr_handle, UINT16 *length, UINT8 **value)
{
UINT8 i;
tGATT_READ_REQ read_req;
tGATT_STATUS status = GATT_NOT_FOUND;
tGATT_SR_REG *p_rcb = gatt_cb.sr_reg;
UINT8 service_uuid[LEN_UUID_128] = {0};
// find the service by handle
for (i = 0; i < GATT_MAX_SR_PROFILES; i++, p_rcb++) {
if (p_rcb->in_use && p_rcb->s_hdl <= attr_handle && p_rcb->e_hdl >= attr_handle) {
break;
}
}
// service cb not found
if (i == GATT_MAX_SR_PROFILES) {
return status;
}
if (p_rcb->app_uuid.len != LEN_UUID_128) {
return status;
}
memset(&read_req, 0, sizeof(tGATT_READ_REQ));
read_req.handle = attr_handle;
// read gatt service attribute value
memset(service_uuid, 0x81, LEN_UUID_128);
if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) {
status = gatt_proc_read(0, GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp);
}
// read gap service attribute value
memset(service_uuid, 0x82, LEN_UUID_128);
if (!memcmp(p_rcb->app_uuid.uu.uuid128, service_uuid, LEN_UUID_128)) {
status = gap_proc_read(GATTS_REQ_TYPE_READ, &read_req, &gatt_cb.rsp);
}
if (status == GATT_SUCCESS) {
*length = gatt_cb.rsp.attr_value.len;
*value = gatt_cb.rsp.attr_value.value;
}
return status;
}
/*******************************************************************************
**
** Function gatts_get_attribute_value
@ -805,7 +868,11 @@ tGATT_STATUS gatts_get_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle,
return GATT_INVALID_PDU;
}
p_cur = (tGATT_ATTR16 *) p_db->p_attr_list;
if (gatts_get_attr_value_internal(attr_handle, length, value) == GATT_SUCCESS) {
return GATT_SUCCESS;
}
p_cur = (tGATT_ATTR16 *) p_db->p_attr_list;
while (p_cur != NULL) {
if (p_cur->handle == attr_handle) {

View File

@ -103,6 +103,7 @@ void gatt_init (void)
memset (&gatt_cb, 0, sizeof(tGATT_CB));
memset (&fixed_reg, 0, sizeof(tL2CAP_FIXED_CHNL_REG));
gatt_cb.auto_disc = TRUE;
gatt_cb.p_clcb_list = list_new(osi_free_func);
gatt_cb.p_tcb_list = list_new(osi_free_func);
#if defined(GATT_INITIAL_TRACE_LEVEL)
@ -114,6 +115,8 @@ void gatt_init (void)
gatt_cb.sign_op_queue = fixed_queue_new(QUEUE_SIZE_MAX);
gatt_cb.srv_chg_clt_q = fixed_queue_new(QUEUE_SIZE_MAX);
gatt_cb.pending_new_srv_start_q = fixed_queue_new(QUEUE_SIZE_MAX);
gatt_cb.srv_chg_mode = GATTS_SEND_SERVICE_CHANGE_MODE;
/* First, register fixed L2CAP channel for ATT over BLE */
fixed_reg.fixed_chnl_opts.mode = L2CAP_FCR_BASIC_MODE;
fixed_reg.fixed_chnl_opts.max_transmit = 0xFF;

View File

@ -277,6 +277,117 @@ static BOOLEAN process_read_multi_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status,
return (FALSE);
}
static BOOLEAN process_read_multi_var_rsp (tGATT_SR_CMD *p_cmd, tGATT_STATUS status,
tGATTS_RSP *p_msg, UINT16 mtu)
{
UINT16 ii;
UINT16 total_len;
UINT16 len;
UINT8 *p;
GATT_TRACE_DEBUG ("process_read_multi_var rsp status=%d mtu=%d", status, mtu);
if (p_cmd->multi_rsp_q == NULL) {
p_cmd->multi_rsp_q = fixed_queue_new(QUEUE_SIZE_MAX);
}
/* Enqueue the response */
BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(tGATTS_RSP));
if (p_buf == NULL) {
p_cmd->status = GATT_INSUF_RESOURCE;
return FALSE;
}
memcpy((void *)p_buf, (const void *)p_msg, sizeof(tGATTS_RSP));
fixed_queue_enqueue(p_cmd->multi_rsp_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
p_cmd->status = status;
if (status == GATT_SUCCESS) {
GATT_TRACE_DEBUG ("Multi var read count=%d num_hdls=%d",
fixed_queue_length(p_cmd->multi_rsp_q),
p_cmd->multi_req.num_handles);
/* Wait till we get all the responses */
if (fixed_queue_length(p_cmd->multi_rsp_q) == p_cmd->multi_req.num_handles) {
len = sizeof(BT_HDR) + L2CAP_MIN_OFFSET + mtu;
if ((p_buf = (BT_HDR *)osi_calloc(len)) == NULL) {
p_cmd->status = GATT_INSUF_RESOURCE;
return (TRUE);
}
p_buf->offset = L2CAP_MIN_OFFSET;
p = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* First byte in the response is the opcode */
*p++ = GATT_RSP_READ_MULTI_VAR;
p_buf->len = 1;
/* Now walk through the buffers puting the data into the response in order */
list_t *list = NULL;
const list_node_t *node = NULL;
if (! fixed_queue_is_empty(p_cmd->multi_rsp_q)) {
list = fixed_queue_get_list(p_cmd->multi_rsp_q);
}
for (ii = 0; ii < p_cmd->multi_req.num_handles; ii++) {
tGATTS_RSP *p_rsp = NULL;
if (list != NULL) {
if (ii == 0) {
node = list_begin(list);
} else {
node = list_next(node);
}
if (node != list_end(list)) {
p_rsp = (tGATTS_RSP *)list_node(node);
}
}
if (p_rsp != NULL) {
total_len = (p_buf->len + 2); // value length
if (total_len > mtu) {
GATT_TRACE_DEBUG ("multi read variable overflow available len=%d val_len=%d", len, p_rsp->attr_value.len );
break;
}
len = MIN(p_rsp->attr_value.len, (mtu - total_len)); // attribute value length
if (p_rsp->attr_value.handle == p_cmd->multi_req.handles[ii]) {
GATT_TRACE_DEBUG("%s handle %x len %u", __func__, p_rsp->attr_value.handle, p_rsp->attr_value.len);
UINT16_TO_STREAM(p, p_rsp->attr_value.len);
memcpy (p, p_rsp->attr_value.value, len);
p += len;
p_buf->len += (2+len);
} else {
p_cmd->status = GATT_NOT_FOUND;
break;
}
} else {
p_cmd->status = GATT_NOT_FOUND;
break;
}
} /* loop through all handles*/
/* Sanity check on the buffer length */
if (p_buf->len == 0) {
GATT_TRACE_ERROR("%s - nothing found!!", __func__);
p_cmd->status = GATT_NOT_FOUND;
osi_free (p_buf);
} else if (p_cmd->p_rsp_msg != NULL) {
osi_free (p_buf);
} else {
p_cmd->p_rsp_msg = p_buf;
}
return (TRUE);
}
} else { /* any handle read exception occurs, return error */
return (TRUE);
}
/* If here, still waiting */
return (FALSE);
}
/*******************************************************************************
**
** Function gatt_sr_process_app_rsp
@ -303,6 +414,10 @@ tGATT_STATUS gatt_sr_process_app_rsp (tGATT_TCB *p_tcb, tGATT_IF gatt_if,
if (!process_read_multi_rsp (&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) {
return (GATT_SUCCESS);
}
} else if (op_code == GATT_REQ_READ_MULTI_VAR) {
if (!process_read_multi_var_rsp(&p_tcb->sr_cmd, status, p_msg, p_tcb->payload_size)) {
return (GATT_SUCCESS);
}
} else {
if (op_code == GATT_REQ_PREPARE_WRITE && status == GATT_SUCCESS) {
gatt_sr_update_prep_cnt(p_tcb, gatt_if, TRUE, FALSE);
@ -514,7 +629,7 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
sec_flag,
key_size))
!= GATT_SUCCESS) {
GATT_TRACE_DEBUG("read permission denied : 0x%02x", err);
GATT_TRACE_ERROR("read permission denied : 0x%02x", err);
break;
}
} else {
@ -525,13 +640,15 @@ void gatt_process_read_multi_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U
ll -= 2;
}
if (ll != 0) {
GATT_TRACE_ERROR("max attribute handle reached in ReadMultiple Request.");
err = GATT_INVALID_HANDLE;
}
if (err == GATT_SUCCESS) {
if (ll != 0) {
GATT_TRACE_ERROR("max attribute handle reached in ReadMultiple Request.");
err = GATT_INVALID_HANDLE;
}
if (p_tcb->sr_cmd.multi_req.num_handles == 0) {
err = GATT_INVALID_HANDLE;
if (p_tcb->sr_cmd.multi_req.num_handles == 0) {
err = GATT_INVALID_HANDLE;
}
}
if (err == GATT_SUCCESS) {
@ -1765,6 +1882,7 @@ void gatt_server_handle_client_req (tGATT_TCB *p_tcb, UINT8 op_code,
break;
case GATT_REQ_READ_MULTI:
case GATT_REQ_READ_MULTI_VAR:
gatt_process_read_multi_req (p_tcb, op_code, len, p_data);
break;

View File

@ -553,6 +553,9 @@ typedef struct {
tGATT_HDL_CFG hdl_cfg;
tGATT_BG_CONN_DEV bgconn_dev[GATT_MAX_BG_CONN_DEV];
BOOLEAN auto_disc; /* internal use: true for auto discovering after connected */
UINT8 srv_chg_mode; /* internal use: service change mode */
tGATTS_RSP rsp; /* use to read internal service attribute */
} tGATT_CB;
typedef struct{

View File

@ -110,8 +110,11 @@ typedef UINT8 tGATT_STATUS;
#define GATT_HANDLE_VALUE_NOTIF 0x1B
#define GATT_HANDLE_VALUE_IND 0x1D
#define GATT_HANDLE_VALUE_CONF 0x1E
#define GATT_REQ_READ_MULTI_VAR 0x20
#define GATT_RSP_READ_MULTI_VAR 0x21
#define GATT_HANDLE_MULTI_VALUE_NOTIF 0x23
#define GATT_SIGN_CMD_WRITE 0xD2 /* changed in V4.0 1101-0010 (signed write) see write cmd above*/
#define GATT_OP_CODE_MAX GATT_HANDLE_VALUE_CONF + 1 /* 0x1E = 30 + 1 = 31*/
#define GATT_OP_CODE_MAX GATT_HANDLE_MULTI_VALUE_NOTIF + 1 /* 0x1E = 30 + 1 = 31*/
#define GATT_COMMAND_FLAG 0x40 /* Command Flag: set to one means command */
@ -415,6 +418,7 @@ enum {
GATT_DISC_SRVC_BY_UUID, /* discover service of a special type */
GATT_DISC_INC_SRVC, /* discover the included service within a service */
GATT_DISC_CHAR, /* discover characteristics of a service with/without type requirement */
GATT_DISC_CHAR_BY_UUID, /* discover characteristic with type requirement */
GATT_DISC_CHAR_DSCPT, /* discover characteristic descriptors of a character */
GATT_DISC_MAX /* maximnun discover type */
};
@ -434,6 +438,7 @@ enum {
GATT_READ_BY_TYPE = 1,
GATT_READ_BY_HANDLE,
GATT_READ_MULTIPLE,
GATT_READ_MULTIPLE_VAR,
GATT_READ_CHAR_VALUE,
GATT_READ_PARTIAL,
GATT_READ_MAX
@ -657,6 +662,12 @@ typedef struct {
tGATTS_NV_SRV_CHG_CBACK *p_srv_chg_callback;
} tGATT_APPL_INFO;
typedef struct {
UINT16 handle;
UINT16 length;
UINT8 *value;
} tGATT_HLV;
/*
*********************** End Handle Management Definitions **********************/
@ -1034,6 +1045,18 @@ extern tGATT_STATUS GATTC_ExecuteWrite (UINT16 conn_id, BOOLEAN is_execute);
*******************************************************************************/
extern tGATT_STATUS GATTC_SendHandleValueConfirm (UINT16 conn_id, UINT16 handle);
/*******************************************************************************
**
** Function GATTC_AutoDiscoverEnable
**
** Description This function is called to enable/disable auto discover.
**
** Parameters enable: 0 for disable, otherwise enable.
**
** Returns GATT_SUCCESS if command started successfully.
**
*******************************************************************************/
extern tGATT_STATUS GATTC_AutoDiscoverEnable(UINT8 enable);
/*******************************************************************************
**
@ -1228,6 +1251,34 @@ extern BOOLEAN GATT_Listen (tGATT_IF gatt_if, BOOLEAN start, BD_ADDR_PTR bd_addr
extern void GATT_ConfigServiceChangeCCC (BD_ADDR remote_bda, BOOLEAN enable,
tBT_TRANSPORT transport);
/*******************************************************************************
**
** Function GATTS_SetServiceChangeMode
**
** Description Configure service change indication mode
**
** Parameters mode: service change mode
**
** Returns Status.
**
*******************************************************************************/
extern tGATT_STATUS GATTS_SetServiceChangeMode(UINT8 mode);
/*******************************************************************************
**
** Function GATTS_HandleMultiValueNotification
**
** Description This function sends multiple handle value notification to a client.
**
** Parameter conn_id: connection identifier.
** tuples: Pointer to handle-length-value tuple list.
** num_tuples: Number of tuples.
**
** Returns GATT_SUCCESS if successfully sent; otherwise error code.
**
*******************************************************************************/
extern tGATT_STATUS GATTS_HandleMultiValueNotification (UINT16 conn_id, tGATT_HLV *tuples, UINT16 num_tuples);
#ifdef __cplusplus
}