/******************************************************************************* ** ** Function gatt_process_find_type_value_rsp ** ** Description This function is called to handle find by type value response. ** ** ** Returns void ** *******************************************************************************/ void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) { tGATT_DISC_RES result; UINT8 *p = p_data; UNUSED(p_tcb); GATT_TRACE_DEBUG("gatt_process_find_type_value_rsp "); /* unexpected response */ if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) { return; } memset (&result, 0, sizeof(tGATT_DISC_RES)); result.type.len = 2; result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE; /* returns a series of handle ranges */ while (len >= 4) { STREAM_TO_UINT16 (result.handle, p); STREAM_TO_UINT16 (result.value.group_value.e_handle, p); memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID)); len -= 4; if (p_clcb->p_reg->app_cb.p_disc_res_cb) { (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); } } /* last handle + 1 */ p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1); /* initiate another request */ gatt_act_discovery(p_clcb) ; }
/******************************************************************************* ** ** Function gatt_process_read_info_rsp ** ** Description This function is called to handle the read information ** response. ** ** ** Returns void ** *******************************************************************************/ void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data) { tGATT_DISC_RES result; UINT8 *p = p_data, uuid_len = 0, type; UNUSED(p_tcb); UNUSED(op_code); if (len < GATT_INFO_RSP_MIN_LEN) { GATT_TRACE_ERROR("invalid Info Response PDU received, discard."); gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); return; } /* unexpected response */ if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT) { return; } STREAM_TO_UINT8(type, p); len -= 1; if (type == GATT_INFO_TYPE_PAIR_16) { uuid_len = LEN_UUID_16; } else if (type == GATT_INFO_TYPE_PAIR_128) { uuid_len = LEN_UUID_128; } while (len >= uuid_len + 2) { STREAM_TO_UINT16 (result.handle, p); if (uuid_len > 0) { if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) { break; } } else { memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID)); } len -= (uuid_len + 2); 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_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_process_read_rsp ** ** Description This function is called to handle the read BLOB response ** ** ** Returns void ** *******************************************************************************/ void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data) { UINT16 offset = p_clcb->counter; UINT8 *p = p_data; UNUSED(op_code); if (p_clcb->operation == GATTC_OPTYPE_READ) { if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) { p_clcb->counter = len; gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); } else { /* allocate GKI buffer holding up long attribute value */ if (!p_clcb->p_attr_buf) { p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); } /* copy attrobute value into cb buffer */ if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN) { if ((len + offset) > GATT_MAX_ATTR_LEN) { len = GATT_MAX_ATTR_LEN - offset; } p_clcb->counter += len; memcpy(p_clcb->p_attr_buf + offset, p, len); /* send next request if needed */ if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ len + offset < GATT_MAX_ATTR_LEN) { GATT_TRACE_DEBUG("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", offset, len, p_clcb->counter); gatt_act_read(p_clcb, p_clcb->counter); } else { /* end of request, send callback */ gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); } } else { /* exception, should not happen */ GATT_TRACE_ERROR("attr offset = %d p_attr_buf = %p ", offset, p_clcb->p_attr_buf); gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf); } } } else { if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC && p_clcb->read_uuid128.wait_for_read_rsp ) { p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl; p_clcb->read_uuid128.wait_for_read_rsp = FALSE; if (len == LEN_UUID_128) { memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len); p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128; 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_read_by_type_rsp ** ** Description This function is called to handle the read by type response. ** read by type can be used for discovery, or read by type or ** read characteristic value. ** ** Returns void ** *******************************************************************************/ void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, UINT16 len, UINT8 *p_data) { tGATT_DISC_RES result; tGATT_DISC_VALUE record_value; UINT8 *p = p_data, value_len, handle_len = 2; UINT16 handle = 0; /* discovery procedure and no callback function registered */ if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) { return; } if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) { GATT_TRACE_ERROR("Illegal ReadByType/ReadByGroupType Response length, discard"); gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); return; } STREAM_TO_UINT8(value_len, p); if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len - 1)) ) { /* this is an error case that server's response containing a value length which is larger than MTU-2 or value_len > message total length -1 */ GATT_TRACE_ERROR("gatt_process_read_by_type_rsp: Discard response op_code=%d vale_len=%d > (MTU-2=%d or msg_len-1=%d)", op_code, value_len, (p_tcb->payload_size - 2), (len - 1)); gatt_end_operation(p_clcb, GATT_ERROR, NULL); return; } if (op_code == GATT_RSP_READ_BY_GRP_TYPE) { handle_len = 4; } value_len -= handle_len; /* substract the handle pairs bytes */ len -= 1; while (len >= (handle_len + value_len)) { STREAM_TO_UINT16(handle, p); if (!GATT_HANDLE_IS_VALID(handle)) { gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); return; } memset(&result, 0, sizeof(tGATT_DISC_RES)); memset(&record_value, 0, sizeof(tGATT_DISC_VALUE)); result.handle = handle; result.type.len = 2; result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; /* discover all services */ if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_SRVC_ALL && op_code == GATT_RSP_READ_BY_GRP_TYPE) { STREAM_TO_UINT16(handle, p); if (!GATT_HANDLE_IS_VALID(handle)) { gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); return; } else { record_value.group_value.e_handle = handle; if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) { GATT_TRACE_ERROR("discover all service response parsing failure"); break; } } } /* 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_ERROR("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len); gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); return; } } /* read by type */ else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE) { p_clcb->counter = len - 2; p_clcb->s_handle = handle; if ( p_clcb->counter == (p_clcb->p_tcb->payload_size - 4)) { p_clcb->op_subtype = GATT_READ_BY_HANDLE; if (!p_clcb->p_attr_buf) { p_clcb->p_attr_buf = (UINT8 *)osi_malloc(GATT_MAX_ATTR_LEN); } if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN) { memcpy(p_clcb->p_attr_buf, p, p_clcb->counter); gatt_act_read(p_clcb, p_clcb->counter); } else { gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p); } } else { gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); } return; } else { /* discover characterisitic */ STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) { gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); return; } if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p)) { gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); /* invalid format, and skip the result */ return; } /* UUID not matching */ if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid)) { len -= (value_len + 2); continue; /* skip the result, and look for next one */ } else if (p_clcb->operation == GATTC_OPTYPE_READ) /* UUID match for read characteristic value */ { /* only read the first matching UUID characteristic value, and discard the rest results */ p_clcb->s_handle = record_value.dclr_value.val_handle; p_clcb->op_subtype |= 0x80; gatt_act_read(p_clcb, 0); return; } } len -= (value_len + handle_len); /* result is (handle, 16bits UUID) pairs */ memcpy (&result.value, &record_value, sizeof (result.value)); /* send callback if is discover procedure */ if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb) { (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); } } p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1); if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) { /* initiate another request */ gatt_act_discovery(p_clcb) ; } else { /* read characteristic value */ gatt_act_read(p_clcb, 0); } }