diff --git a/components/bt/bluedroid/api/esp_gatts_api.c b/components/bt/bluedroid/api/esp_gatts_api.c index f5ebe59a2..318dd25bf 100644 --- a/components/bt/bluedroid/api/esp_gatts_api.c +++ b/components/bt/bluedroid/api/esp_gatts_api.c @@ -142,6 +142,27 @@ esp_err_t esp_ble_gatts_add_char(uint16_t service_handle, esp_bt_uuid_t *char_ return ESP_ERR_INVALID_STATE; } + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (char_val == NULL){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_val should not be NULL here\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } else if (char_val->attr_max_len == 0){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + } + + if (char_val != NULL){ + if (char_val->attr_len > char_val->attr_max_len){ + LOG_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, char_val->attr_len, char_val->attr_max_len); + return ESP_ERR_INVALID_ARG; + } + } + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; msg.pid = BTC_PID_GATTS; @@ -175,6 +196,29 @@ esp_err_t esp_ble_gatts_add_char_descr (uint16_t service_handle, if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) { return ESP_ERR_INVALID_STATE; } + + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (char_descr_val == NULL){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, char_descr_val should not be NULL here\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + else if (char_descr_val->attr_max_len == 0){ + LOG_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return ESP_ERR_INVALID_ARG; + } + } + + if (char_descr_val != NULL){ + if (char_descr_val->attr_len > char_descr_val->attr_max_len){ + LOG_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, char_descr_val->attr_len, char_descr_val->attr_max_len); + return ESP_ERR_INVALID_ARG; + } + } + memset(&arg, 0, sizeof(btc_ble_gatts_args_t)); msg.sig = BTC_SIG_API_CALL; diff --git a/components/bt/bluedroid/stack/gatt/gatt_db.c b/components/bt/bluedroid/stack/gatt/gatt_db.c index bb9bfc5f7..ae4f6430f 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_db.c +++ b/components/bt/bluedroid/stack/gatt/gatt_db.c @@ -268,14 +268,21 @@ static tGATT_STATUS read_attr_value (void *p_attr, status = GATT_SUCCESS; } } else { /* characteristic description or characteristic value */ - if (p_attr16->control.auto_rsp == GATT_RSP_BY_STACK) { - if (p_attr16->p_value != NULL && p_attr16->p_value->attr_val.attr_val != NULL) { - uint8_t *value = p_attr16->p_value->attr_val.attr_val + offset; - len = (mtu >= p_attr16->p_value->attr_val.attr_len) ? (p_attr16->p_value->attr_val.attr_len) : mtu; - ARRAY_TO_STREAM(p, value, len); + if (p_attr16->p_value == NULL || p_attr16->p_value->attr_val.attr_val == NULL) { + status = GATT_ESP_ERROR; + } + else if (offset > p_attr16->p_value->attr_val.attr_len){ + /*if offset equal to max_len, should respond with zero byte value + //if offset is greater than max_len, should respond with an error*/ + status = GATT_INVALID_OFFSET; + } else { + UINT8 *value = (UINT8 *)(p_attr16->p_value->attr_val.attr_val) + offset; + UINT16 len_left = p_attr16->p_value->attr_val.attr_len - offset; + len = (mtu >= len_left) ? (len_left) : mtu; + ARRAY_TO_STREAM(p, value, len); + status = GATT_STACK_RSP; } - status = GATT_STACK_RSP; } else { status = GATT_PENDING; @@ -463,6 +470,27 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, tBT_UUID uuid = {LEN_UUID_16, {GATT_UUID_CHAR_DECLARE}}; GATT_TRACE_DEBUG("gatts_add_characteristic perm=0x%0x property=0x%0x\n", perm, property); + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return 0; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return 0; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length should not be larger than max length\n",\ + __func__, __LINE__); + return 0; + } + } + if ((p_char_decl = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, &uuid, GATT_PERM_READ)) != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_decl->p_value, sizeof(tGATT_CHAR_DECL))) { @@ -483,10 +511,9 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_val->control.auto_rsp = control->auto_rsp; } else { p_char_val->control.auto_rsp = GATT_RSP_DEFAULT; - } - if (attr_val != NULL) { + if (attr_val != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_val->p_value, sizeof(tGATT_ATTR_VAL))) { deallocate_attr_in_db(p_db, p_char_val); return 0; @@ -496,12 +523,23 @@ UINT16 gatts_add_characteristic (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_val->p_value->attr_val.attr_len = attr_val->attr_len; p_char_val->p_value->attr_val.attr_max_len = attr_val->attr_max_len; p_char_val->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); - if (p_char_val->p_value->attr_val.attr_val != NULL) { - GATT_TRACE_DEBUG("attribute value not NULL"); - memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); + if (p_char_val->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_decl); + deallocate_attr_in_db(p_db, p_char_val); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for attribute value\n", __func__, __LINE__); + return 0; + } + + //initiate characteristic attribute value part + memset(p_char_val->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if (attr_val->attr_val != NULL) { + if (attr_val->attr_max_len < attr_val->attr_len){ + GATT_TRACE_ERROR("Error in %s, Line=%d, attribute actual length (%d) should not larger than max size (%d)\n", + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + } + UINT16 actual_len = (attr_val->attr_max_len < attr_val->attr_len) ? (attr_val->attr_max_len) : (attr_val->attr_len); + memcpy(p_char_val->p_value->attr_val.attr_val, attr_val->attr_val, actual_len); } - } else { - p_char_val->p_value = NULL; } return p_char_val->handle; @@ -582,14 +620,35 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, GATT_TRACE_DEBUG("gatts_add_char_descr uuid=0x%04x\n", p_descr_uuid->uu.uuid16); + /* parameter validation check */ + if ((control != NULL) && (control->auto_rsp == GATT_STACK_RSP)){ + if (attr_val == NULL){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attr_val should not be NULL here\n",\ + __func__, __LINE__); + return 0; + } else if (attr_val->attr_max_len == 0){ + GATT_TRACE_ERROR("Error in %s, line=%d, for stack respond attribute, attribute max length should not be 0\n",\ + __func__, __LINE__); + return 0; + } + } + + if (attr_val != NULL){ + if (attr_val->attr_len > attr_val->attr_max_len){ + GATT_TRACE_ERROR("Error in %s, line=%d,attribute actual length (%d) should not be larger than max length (%d)\n",\ + __func__, __LINE__, attr_val->attr_len, attr_val->attr_max_len); + return 0; + } + } + + /* Add characteristic descriptors */ if ((p_char_dscptr = (tGATT_ATTR16 *)allocate_attr_in_db(p_db, p_descr_uuid, perm)) == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); GATT_TRACE_DEBUG("gatts_add_char_descr Fail for adding char descriptors."); return 0; } else { - if (control != NULL) { - p_char_dscptr->control.auto_rsp = control->auto_rsp; - } + p_char_dscptr->control.auto_rsp = (control == NULL) ? GATT_RSP_DEFAULT : (control->auto_rsp); if (attr_val != NULL) { if (!copy_extra_byte_in_db(p_db, (void **)&p_char_dscptr->p_value, sizeof(tGATT_ATTR_VAL))) { deallocate_attr_in_db(p_db, p_char_dscptr); @@ -599,12 +658,16 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, p_char_dscptr->p_value->attr_val.attr_max_len = attr_val->attr_max_len; if (attr_val->attr_max_len != 0) { p_char_dscptr->p_value->attr_val.attr_val = GKI_getbuf(attr_val->attr_max_len); - if (p_char_dscptr->p_value->attr_val.attr_val != NULL) { - memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); - if(attr_val->attr_val != NULL) { - memcpy(p_char_dscptr->p_value->attr_val.attr_val, - attr_val->attr_val, attr_val->attr_len); - } + if (p_char_dscptr->p_value->attr_val.attr_val == NULL) { + deallocate_attr_in_db(p_db, p_char_dscptr); + GATT_TRACE_WARNING("Warning in %s, line=%d, insufficient resource to allocate for descriptor value\n", __func__, __LINE__); + return 0; + } + + //initiate characteristic attribute value part + memset(p_char_dscptr->p_value->attr_val.attr_val, 0, attr_val->attr_max_len); + if(attr_val->attr_val != NULL) { + memcpy(p_char_dscptr->p_value->attr_val.attr_val, attr_val->attr_val, attr_val->attr_len); } } } @@ -628,7 +691,7 @@ UINT16 gatts_add_char_descr (tGATT_SVC_DB *p_db, tGATT_PERM perm, ** *******************************************************************************/ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, - UINT16 length, UINT8 *value) + UINT16 length, UINT8 *value) { tGATT_ATTR16 *p_cur; @@ -640,51 +703,46 @@ tGATT_STATUS gatts_set_attribute_value(tGATT_SVC_DB *p_db, UINT16 attr_handle, GATT_TRACE_DEBUG("gatts_set_attribute_value Fail:p_db->p_attr_list is NULL.\n"); return GATT_INVALID_PDU; } + if ((length > 0) && (value == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, value should not be NULL here\n",__func__, __LINE__); + return GATT_INVALID_PDU; + } p_cur = (tGATT_ATTR16 *) p_db->p_attr_list; while (p_cur != NULL) { if (p_cur->handle == attr_handle) { - + /* for characteristic should not be set, return GATT_NOT_FOUND */ if (p_cur->uuid_type == GATT_ATTR_UUID_TYPE_16) { switch (p_cur->uuid) { - case GATT_UUID_PRI_SERVICE: - case GATT_UUID_SEC_SERVICE: - case GATT_UUID_CHAR_DECLARE: - case GATT_UUID_INCLUDE_SERVICE: - return GATT_NOT_FOUND; - default: - if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len < length) { - GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); - return GATT_INVALID_ATTR_LEN; - } else if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len > 0) { - memcpy(p_cur->p_value->attr_val.attr_val, value, length); - p_cur->p_value->attr_val.attr_len = length; - } else { - return GATT_INVALID_ATTR_LEN; - } - break; - } - } else { - if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len < length) { - GATT_TRACE_ERROR("gatts_set_attribute_vaule failt:Invalid value length"); - } else if (p_cur->p_value != NULL && p_cur->p_value->attr_val.attr_max_len > 0) { - memcpy(p_cur->p_value->attr_val.attr_val, value, length); - p_cur->p_value->attr_val.attr_len = length; - } else { - return GATT_INVALID_ATTR_LEN; + case GATT_UUID_PRI_SERVICE: + case GATT_UUID_SEC_SERVICE: + case GATT_UUID_CHAR_DECLARE: + return GATT_NOT_FOUND; + break; } } + + /* in other cases, value can be set*/ + if ((p_cur->p_value == NULL) || (p_cur->p_value->attr_val.attr_val == NULL) \ + || (p_cur->p_value->attr_val.attr_max_len == 0)){ + GATT_TRACE_ERROR("Error in %s, line=%d, attribute value should not be NULL here\n", __func__, __LINE__); + return GATT_NOT_FOUND; + } else if (p_cur->p_value->attr_val.attr_max_len < length) { + GATT_TRACE_ERROR("gatts_set_attribute_value failed:Invalid value length"); + return GATT_INVALID_ATTR_LEN; + } else{ + memcpy(p_cur->p_value->attr_val.attr_val, value, length); + p_cur->p_value->attr_val.attr_len = length; + } break; } - p_cur = p_cur->p_next; } return GATT_SUCCESS; } - /******************************************************************************* ** ** Function gatts_get_attribute_value @@ -875,20 +933,25 @@ tGATT_STATUS gatts_write_attr_value_by_handle(tGATT_SVC_DB *p_db, return GATT_APP_RSP; } - if (p_attr->p_value != NULL && (p_attr->p_value->attr_val.attr_max_len >= - offset + len) && p_attr->p_value->attr_val.attr_val != NULL) { + if ((p_attr->p_value != NULL) && + (p_attr->p_value->attr_val.attr_max_len >= offset + len) && + p_attr->p_value->attr_val.attr_val != NULL) { memcpy(p_attr->p_value->attr_val.attr_val + offset, p_value, len); p_attr->p_value->attr_val.attr_len = len + offset; return GATT_SUCCESS; - } else { - return GATT_NOT_LONG; + } else if (p_attr->p_value->attr_val.attr_max_len < offset + len){ + GATT_TRACE_DEBUG("Remote device try to write with a length larger then attribute's max length\n"); + return GATT_INVALID_ATTR_LEN; + } else if ((p_attr->p_value == NULL) || (p_attr->p_value->attr_val.attr_val == NULL)){ + GATT_TRACE_ERROR("Error in %s, line=%d, %s should not be NULL here\n", __func__, __LINE__, \ + (p_attr->p_value == NULL) ? "p_value" : "attr_val.attr_val"); + return GATT_ESP_ERROR; } } p_attr = (tGATT_ATTR16 *)p_attr->p_next; } - } return status; diff --git a/components/bt/bluedroid/stack/gatt/gatt_sr.c b/components/bt/bluedroid/stack/gatt/gatt_sr.c index 2d34a0504..79d518398 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_sr.c +++ b/components/bt/bluedroid/stack/gatt/gatt_sr.c @@ -33,6 +33,42 @@ #define GATT_MTU_REQ_MIN_LEN 2 + +/******************************************************************************* +** +** Function gatt_send_packet +** +** Description This function is called to send gatt packets directly + +** +** Returns status +** +*******************************************************************************/ +tGATT_STATUS gatt_send_packet (tGATT_TCB *p_tcb, UINT8 *p_data, UINT16 len) +{ + BT_HDR *p_msg = NULL; + UINT8 *p_m = NULL; + UINT16 buf_len; + tGATT_STATUS status; + + if (len > p_tcb->payload_size){ + return GATT_ILLEGAL_PARAMETER; + } + + buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); + if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { + return GATT_NO_RESOURCES; + } + + memset(p_msg, 0, buf_len); + p_msg->len = len; + p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; + memcpy(p_m, p_data, len); + + status = attp_send_sr_msg(p_tcb, p_msg); + return status; +} + /******************************************************************************* ** ** Function gatt_sr_enqueue_cmd @@ -300,7 +336,11 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U UINT32 trans_id = 0; tGATT_IF gatt_if; UINT16 conn_id; - + UINT16 queue_num = 0; + BOOLEAN is_prepare_write_valid = FALSE; + BOOLEAN is_need_dequeue_sr_cmd = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; UNUSED(len); #if GATT_CONFORMANCE_TESTING == TRUE @@ -319,11 +359,60 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U /* mask the flag */ flag &= GATT_PREP_WRITE_EXEC; + prepare_record = &(p_tcb->prepare_write_record); + queue_num = prepare_record->queue._count; + - /* no prep write is queued */ + //if received prepare_write packets include stack_rsp and app_rsp, + //stack respond to execute_write only when stack_rsp handle has invalid_offset + //or invalid_length error; + //app need to respond to execute_write if it has received app_rsp handle packets + if (((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num == queue_num)) + || (flag == GATT_PREP_WRITE_CANCEL)){ + tGATT_EXEC_WRITE_RSP gatt_exec_write_rsp; + gatt_exec_write_rsp.op_code = GATT_RSP_EXEC_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_exec_write_rsp), sizeof(gatt_exec_write_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + if (flag != GATT_PREP_WRITE_CANCEL){ + is_prepare_write_valid = TRUE; + } + GATT_TRACE_DEBUG("Send execute_write_rsp\n"); + } else if ((prepare_record->error_code_app == GATT_SUCCESS) && + (prepare_record->total_num > queue_num)){ + //No error for stack_rsp's handles and there exist some app_rsp's handles, + //so exec_write_rsp depends to app's response; but stack_rsp's data is valid + //TODO: there exist problem if stack_rsp's data is valid but app_rsp's data is not valid. + is_prepare_write_valid = TRUE; + } else if(prepare_record->total_num < queue_num) { + GATT_TRACE_ERROR("Error in %s, line=%d, prepare write total number (%d) \ + should not smaller than prepare queue number (%d)\n", \ + __func__, __LINE__,prepare_record->total_num, queue_num); + } else if (prepare_record->error_code_app != GATT_SUCCESS){ + GATT_TRACE_DEBUG("Send error code for execute_write, code=0x%x\n", prepare_record->error_code_app); + is_need_dequeue_sr_cmd = (prepare_record->total_num == queue_num) ? TRUE : FALSE; + gatt_send_error_rsp(p_tcb, prepare_record->error_code_app, GATT_REQ_EXEC_WRITE, 0, is_need_dequeue_sr_cmd); + } + + //dequeue prepare write data + while(GKI_getfirst(&(prepare_record->queue))) { + queue_data = GKI_dequeue(&(prepare_record->queue)); + if (is_prepare_write_valid){ + if((queue_data->p_attr->p_value != NULL) && (queue_data->p_attr->p_value->attr_val.attr_val != NULL)){ + memcpy(queue_data->p_attr->p_value->attr_val.attr_val+queue_data->offset, queue_data->value, queue_data->len); + } + } + GKI_freebuf(queue_data); + } + + /* according to ble spec, even if there is no prep write queued, + * need to respond execute_write_response + * Note: exec_write_rsp callback should be called after all data has been written*/ 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); + if (prepare_record->total_num > queue_num){ + 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]) { @@ -336,10 +425,10 @@ void gatt_process_exec_write_req (tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, U p_tcb->prep_cnt[i] = 0; } } - } else { /* nothing needs to be executed , send response now */ - GATT_TRACE_ERROR("gatt_process_exec_write_req: no prepare write pending"); - gatt_send_error_rsp(p_tcb, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, FALSE); } + + prepare_record->total_num = 0; + prepare_record->error_code_app = GATT_SUCCESS; } /******************************************************************************* @@ -987,54 +1076,29 @@ void gatts_process_read_by_type_req(tGATT_TCB *p_tcb, UINT8 op_code, UINT16 len, void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, UINT8 op_code, UINT16 len, UINT8 *p_data) { - UINT16 buf_len = (UINT16)(sizeof(BT_HDR) + p_tcb->payload_size + L2CAP_MIN_OFFSET); tGATTS_DATA sr_data; UINT32 trans_id; tGATT_STATUS status; - UINT8 sec_flag, key_size, *p = p_data, *p_m; + UINT8 sec_flag, key_size, *p = p_data; tGATT_SR_REG *p_sreg; UINT16 conn_id, offset = 0; - BT_HDR *p_msg = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + sr_data.write_req.need_rsp = FALSE; - if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) { - GATT_TRACE_ERROR("gatts_process_write_req failed. no resources.\n"); - } - - memset(p_msg, 0, buf_len); - p_m = (UINT8 *)(p_msg + 1) + L2CAP_MIN_OFFSET; - *p_m ++ = op_code + 1; - p_msg->len = 1; - buf_len = p_tcb->payload_size - 1; - switch (op_code) { - case GATT_REQ_PREPARE_WRITE: - sr_data.write_req.is_prep = TRUE; - STREAM_TO_UINT16(sr_data.write_req.offset, p); - UINT16_TO_STREAM(p_m, sr_data.write_req.is_prep); - offset = sr_data.write_req.offset; - len -= 2; - /* fall through */ case GATT_SIGN_CMD_WRITE: if (op_code == GATT_SIGN_CMD_WRITE) { - GATT_TRACE_DEBUG("Write CMD with data sigining" ); + GATT_TRACE_DEBUG("Write CMD with data signing" ); 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; - if(op_code == GATT_REQ_PREPARE_WRITE){ - memcpy(p_m, p, len); - p_msg->len += len; - } - } sr_data.write_req.handle = handle; sr_data.write_req.len = len; if (len != 0 && p != NULL) { memcpy (sr_data.write_req.value, p, len); - } break; } @@ -1059,42 +1123,182 @@ void gatts_process_write_req (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); status = gatts_write_attr_value_by_handle(gatt_cb.sr_reg[i_rcb].p_db, handle, offset, p, len); - if((sr_data.write_req.need_rsp == TRUE) && (status == GATT_APP_RSP)){ + if((op_code == GATT_REQ_WRITE) && (status == GATT_APP_RSP)){ sr_data.write_req.need_rsp = TRUE; status = GATT_PENDING; } - else{ - sr_data.write_req.need_rsp = FALSE; - } - gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); - - if (status == GATT_SUCCESS) { - attp_send_sr_msg(p_tcb, p_msg); - gatt_dequeue_sr_cmd(p_tcb); - } else { - GKI_freebuf(p_msg); - } - } else { - GATT_TRACE_ERROR("max pending command, send error\n"); + GATT_TRACE_ERROR("Error in %s, line=%d, max pending command, send error\n", __func__, __LINE__); 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 && status != GATT_SUCCESS && - (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_REQ_WRITE)) { - gatt_send_error_rsp (p_tcb, status, op_code, handle, FALSE); - gatt_dequeue_sr_cmd(p_tcb); + /* response should be sent only for write_request */ + if ((op_code == GATT_REQ_WRITE) && (sr_data.write_req.need_rsp == FALSE)){ + if (status == GATT_SUCCESS){ + tGATT_WRITE_REQ_RSP gatt_write_req_rsp; + gatt_write_req_rsp.op_code = GATT_RSP_WRITE; + gatt_send_packet(p_tcb, (UINT8 *)(&gatt_write_req_rsp), sizeof(gatt_write_req_rsp)); + gatt_dequeue_sr_cmd(p_tcb); + } else if (status != GATT_PENDING){ + /* note: in case of GATT_BUSY, will respond this application error to remote device */ + gatt_send_error_rsp (p_tcb, status, op_code, handle, TRUE); + } } + return; } + +/******************************************************************************* + ** + ** Function gatts_attr_process_preapre_write + ** + ** Description This function is called to process the prepare write request + ** from client. + ** + ** Returns void + ** + *******************************************************************************/ +void gatt_attr_process_prepare_write (tGATT_TCB *p_tcb, UINT8 i_rcb, UINT16 handle, + UINT8 op_code, UINT16 len, UINT8 *p_data) +{ + tGATT_STATUS status; + tGATT_PREPARE_WRITE_QUEUE_DATA * queue_data = NULL; + tGATT_ATTR16 *p_attr; + tGATT_ATTR16 *p_attr_temp; + tGATTS_DATA sr_data; + UINT32 trans_id = 0; + UINT8 sec_flag, key_size, *p = p_data; + tGATT_SR_REG *p_sreg; + UINT16 conn_id, offset = 0; + tGATT_SVC_DB *p_db; + BOOLEAN is_need_prepare_write_rsp = FALSE; + BOOLEAN is_need_queue_data = FALSE; + tGATT_PREPARE_WRITE_RECORD *prepare_record = NULL; + memset(&sr_data, 0, sizeof(tGATTS_DATA)); + + //get offset from p_data + STREAM_TO_UINT16(offset, p); + len -= 2; + p_sreg = &gatt_cb.sr_reg[i_rcb]; + conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_sreg->gatt_if); + //prepare_record = &(prepare_write_record); + prepare_record = &(p_tcb->prepare_write_record); + + gatt_sr_get_sec_info(p_tcb->peer_bda, + p_tcb->transport, + &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_db = gatt_cb.sr_reg[i_rcb].p_db; + 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 ) { + p_attr_temp = p_attr; + if (p_attr->control.auto_rsp == GATT_RSP_BY_APP) { + status = GATT_APP_RSP; + } else if (p_attr->p_value != NULL && + offset > p_attr->p_value->attr_val.attr_max_len) { + status = GATT_INVALID_OFFSET; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value != NULL && + ((offset + len) > p_attr->p_value->attr_val.attr_max_len)){ + status = GATT_INVALID_ATTR_LEN; + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } else if (p_attr->p_value == NULL) { + LOG_ERROR("Error in %s, attribute of handle 0x%x not allocate value buffer\n", + __func__, handle); + status = GATT_ESP_ERROR; + } else { + //valid prepare write request, need to send response and queue the data + //status: GATT_SUCCESS + is_need_prepare_write_rsp = TRUE; + is_need_queue_data = TRUE; + } + } + p_attr = (tGATT_ATTR16 *)p_attr->p_next; + } + } + } else{ + status = GATT_ESP_ERROR; + GATT_TRACE_ERROR("Error in %s, Line %d: GATT BUSY\n", __func__, __LINE__); + } + } + + if (is_need_queue_data){ + queue_data = (tGATT_PREPARE_WRITE_QUEUE_DATA *)GKI_getbuf(len + sizeof(tGATT_PREPARE_WRITE_QUEUE_DATA)); + if (queue_data == NULL){ + status = GATT_PREPARE_Q_FULL; + } else { + queue_data->p_attr = p_attr_temp; + queue_data->len = len; + queue_data->handle = handle; + queue_data->offset = offset; + memcpy(queue_data->value, p, len); + GKI_enqueue(&(prepare_record->queue), queue_data); + } + } + + if (is_need_prepare_write_rsp){ + //send prepare write response + if (queue_data != NULL){ + queue_data->op_code = op_code + 1; + //5: op_code 1 + handle 2 + offset 2 + tGATT_STATUS rsp_send_status = gatt_send_packet(p_tcb, &(queue_data->op_code), queue_data->len + 5); + gatt_sr_update_prep_cnt(p_tcb, p_sreg->gatt_if, TRUE, FALSE); + gatt_dequeue_sr_cmd(p_tcb); + + if (rsp_send_status != GATT_SUCCESS){ + LOG_ERROR("Error in %s, line=%d, fail to send prepare_write_rsp, status=0x%x\n", + __func__, __LINE__, rsp_send_status); + } + } else{ + LOG_ERROR("Error in %s, line=%d, queue_data should not be NULL here, fail to send prepare_write_rsp\n", + __func__, __LINE__); + } + } + + if ((status == GATT_APP_RSP) || (is_need_prepare_write_rsp)){ + prepare_record->total_num++; + memset(&sr_data, 0, sizeof(sr_data)); + sr_data.write_req.is_prep = TRUE; + sr_data.write_req.handle = handle; + sr_data.write_req.offset = offset; + sr_data.write_req.len = len; + sr_data.write_req.need_rsp = (status == GATT_APP_RSP) ? TRUE : FALSE; + memcpy(sr_data.write_req.value, p, len); + gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE, &sr_data); + } else{ + gatt_send_error_rsp(p_tcb, status, GATT_REQ_PREPARE_WRITE, handle, TRUE); + } + + if ((prepare_record->error_code_app == GATT_SUCCESS) + && ((status == GATT_INVALID_OFFSET) || (status == GATT_INVALID_ATTR_LEN))){ + prepare_record->error_code_app = status; + } + +} + /******************************************************************************* ** ** Function gatts_process_read_req @@ -1226,9 +1430,11 @@ void gatts_process_attribute_req (tGATT_TCB *p_tcb, UINT8 op_code, 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; + + case GATT_REQ_PREPARE_WRITE: + gatt_attr_process_prepare_write (p_tcb, i, handle, op_code, len, p); default: break; } diff --git a/components/bt/bluedroid/stack/gatt/gatt_utils.c b/components/bt/bluedroid/stack/gatt/gatt_utils.c index 09f4e8df2..60f99966b 100644 --- a/components/bt/bluedroid/stack/gatt/gatt_utils.c +++ b/components/bt/bluedroid/stack/gatt/gatt_utils.c @@ -116,6 +116,26 @@ void gatt_free_pending_enc_queue(tGATT_TCB *p_tcb) } } +/******************************************************************************* +** +** Function gatt_free_pending_prepare_write_queue +** +** Description Free all buffers in pending prepare write packets queue +** +** Returns None +** +*******************************************************************************/ +void gatt_free_pending_prepare_write_queue(tGATT_TCB *p_tcb) +{ + GATT_TRACE_DEBUG("gatt_free_pending_prepare_write_queue"); + /* release all queued prepare write packets */ + while (!GKI_queue_is_empty(&(p_tcb->prepare_write_record.queue))) { + GKI_freebuf (GKI_dequeue (&(p_tcb->prepare_write_record.queue))); + } + p_tcb->prepare_write_record.total_num = 0; + p_tcb->prepare_write_record.error_code_app = GATT_SUCCESS; +} + /******************************************************************************* ** ** Function gatt_delete_dev_from_srv_chg_clt_list @@ -2108,6 +2128,7 @@ void gatt_cleanup_upon_disc(BD_ADDR bda, UINT16 reason, tBT_TRANSPORT transport) btu_stop_timer (&p_tcb->conf_timer_ent); gatt_free_pending_ind(p_tcb); gatt_free_pending_enc_queue(p_tcb); + gatt_free_pending_prepare_write_queue(p_tcb); for (i = 0; i < GATT_MAX_APPS; i ++) { p_reg = &gatt_cb.cl_rcb[i]; diff --git a/components/bt/bluedroid/stack/gatt/include/gatt_int.h b/components/bt/bluedroid/stack/gatt/include/gatt_int.h index c9622a24c..9aca0c5df 100644 --- a/components/bt/bluedroid/stack/gatt/include/gatt_int.h +++ b/components/bt/bluedroid/stack/gatt/include/gatt_int.h @@ -133,6 +133,16 @@ typedef struct { UINT8 reason; } tGATT_ERROR; +/* Execute write response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_EXEC_WRITE_RSP; + +/* Write request response structure */ +typedef struct { + UINT8 op_code; +}__attribute__((packed)) tGATT_WRITE_REQ_RSP; + /* server response message to ATT protocol */ typedef union { @@ -329,6 +339,32 @@ typedef struct { UINT16 count; } tGATT_SRV_LIST_INFO; +/* prepare write queue data */ +typedef struct{ + //len: length of value + tGATT_ATTR16 *p_attr; + UINT16 len; + UINT8 op_code; + UINT16 handle; + UINT16 offset; + UINT8 value[2]; +}__attribute__((packed)) tGATT_PREPARE_WRITE_QUEUE_DATA; + +/* structure to store prepare write packts information */ +typedef struct{ + //only store prepare write packets which need + //to be responded by stack (not by application) + BUFFER_Q queue; + + //store the total number of prepare write packets + //including that should be responded by stack or by application + UINT16 total_num; + + //store application error code for prepare write, + //invalid offset && invalid length + UINT8 error_code_app; +}tGATT_PREPARE_WRITE_RECORD; + typedef struct { BUFFER_Q pending_enc_clcb; /* pending encryption channel q */ tGATT_SEC_ACTION sec_act; @@ -362,6 +398,7 @@ typedef struct { BOOLEAN in_use; UINT8 tcb_idx; + tGATT_PREPARE_WRITE_RECORD prepare_write_record; /* prepare write packets record */ } tGATT_TCB; diff --git a/components/bt/bluedroid/stack/include/gatt_api.h b/components/bt/bluedroid/stack/include/gatt_api.h index 9360d4fbf..42812a5be 100644 --- a/components/bt/bluedroid/stack/include/gatt_api.h +++ b/components/bt/bluedroid/stack/include/gatt_api.h @@ -65,6 +65,8 @@ #define GATT_CONGESTED 0x8f #define GATT_STACK_RSP 0x90 #define GATT_APP_RSP 0x91 +//Error caused by customer application or stack bug +#define GATT_ESP_ERROR 0X9f /* 0xE0 ~ 0xFC reserved for future use */ #define GATT_CCC_CFG_ERR 0xFD /* Client Characteristic Configuration Descriptor Improperly Configured */