/******************************************************************************* ** ** 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_ERROR("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); if (len < GATT_PREP_WRITE_RSP_MIN_LEN) { GATT_TRACE_ERROR("illegal prepare write response length, discard"); gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value); return; } 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_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; UNUSED(p_tcb); UNUSED(handle); GATT_TRACE_DEBUG("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_DEBUG("Discovery completed"); } break; default: GATT_TRACE_ERROR("Incorrect discovery opcode %04x", opcode); break; } gatt_end_operation(p_clcb, status, NULL); }
/******************************************************************************* ** ** 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; tGATT_STATUS st; 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; /* if service type is 32 bits UUID, convert it now */ if (p_clcb->uuid.len == LEN_UUID_32) { cl_req.find_type_value.value_len = LEN_UUID_128; gatt_convert_uuid32_to_uuid128(cl_req.find_type_value.value, p_clcb->uuid.uu.uuid32); } else memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len); } 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) { gatt_end_operation(p_clcb, GATT_ERROR, NULL); } } else /* end of handle range */ gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); }
/******************************************************************************* ** ** Function gatt_act_write ** ** Description GATT write operation. ** ** Returns void. ** *******************************************************************************/ void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act) { tGATT_TCB *p_tcb = p_clcb->p_tcb; UINT8 rt = GATT_SUCCESS, op_code = 0; 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 = (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_ERROR("Unknown write type: %d", p_clcb->op_subtype); break; } } else { rt = GATT_INTERNAL_ERROR; } if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) { if (rt != GATT_SUCCESS) { GATT_TRACE_ERROR("gatt_act_write() failed op_code=0x%x rt=%d", op_code, rt); } gatt_end_operation(p_clcb, rt, 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; UNUSED(op_code); UNUSED(len); GATT_TRACE_DEBUG("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_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_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_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_DEBUG("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_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_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; UINT8 rsp_code; tGATT_CLCB *p_clcb = NULL; tGATT_STATUS att_ret = GATT_SUCCESS; while (!sent && p_tcb->pending_cl_req != p_tcb->next_slot_inq && p_cmd->to_send && p_cmd->p_cmd != NULL) { att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd->p_cmd); if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) { sent = TRUE; p_cmd->to_send = FALSE; p_cmd->p_cmd = NULL; /* dequeue the request if is write command or sign write */ if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) { gatt_start_rsp_timer (p_cmd->clcb_idx); } else { p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); /* if no ack needed, keep sending */ if (att_ret == GATT_SUCCESS) sent = FALSE; p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; /* send command complete callback here */ gatt_end_operation(p_clcb, att_ret, NULL); } } else { GATT_TRACE_ERROR("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_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_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, UINT8 sec_act) { if (p_clcb && p_clcb->p_tcb && GKI_queue_is_empty(&p_clcb->p_tcb->pending_enc_clcb)) { gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE); } #if (GATTC_INCLUDED == TRUE) 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, sec_act); } else if (p_clcb->operation == GATTC_OPTYPE_READ) { gatt_act_read(p_clcb, p_clcb->counter); } #endif ///GATTC_INCLUDED == TRUE }
/******************************************************************************* ** ** 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; /* do not need to mark channel securoty activity for data signing */ gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_OK); 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, GATT_SEC_SIGN_DATA); } else { gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, NULL); } GKI_freebuf(p_data); } return status; }
/******************************************************************************* ** ** 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; tGATT_STATUS status = GATT_SUCCESS; if (len < GATT_MTU_RSP_MIN_LEN) { GATT_TRACE_ERROR("invalid MTU response PDU received, discard."); status = GATT_INVALID_PDU; } else { STREAM_TO_UINT16(mtu, p_data); if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE) { p_tcb->payload_size = mtu; } } l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); gatt_end_operation(p_clcb, status, NULL); }
/******************************************************************************* ** ** 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_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_DEBUG("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_ERROR("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); } if ( op_code == 0 || (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)) { gatt_end_operation(p_clcb, rt, NULL); } }
/******************************************************************************* ** ** Function gatt_process_handle_rsp ** ** Description This function is called to handle the write response ** ** ** Returns void ** *******************************************************************************/ void gatt_process_handle_rsp(tGATT_CLCB *p_clcb) { gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); }
/******************************************************************************* ** ** 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); } }