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;
+ if (p_attr16->p_value == NULL || p_attr16->p_value->attr_val.attr_val == NULL) {
+ status = GATT_ESP_ERROR;
+ }
+ /*if offset equal to max_len, should respond with zero byte value
+ //if offset is greater than max_len, should respond with an error*/
+ else if (offset > p_attr16->p_value->attr_val.attr_len){
+ 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;
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))) {
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;
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_ERROR("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;
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 should not be larger than max length\n",\
+ __func__, __LINE__);
+ 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;
- }
+ }
+ else {
+ 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);
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_ERROR("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);
}
}
}
**
*******************************************************************************/
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;
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
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;
#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
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
/* 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]) {
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;
}
/*******************************************************************************
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));
- if ((p_msg = (BT_HDR *)GKI_getbuf(buf_len)) == NULL) {
- GATT_TRACE_ERROR("gatts_process_write_req failed. no resources.\n");
- }
+ memset(&sr_data, 0, sizeof(tGATTS_DATA));
+ sr_data.write_req.need_rsp = FALSE;
- 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;
}
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);
+ }
+ else {
+ GATT_TRACE_ERROR("Error in %s, line=%d, max pending command, send error\n", __func__, __LINE__);
+ status = GATT_BUSY; /* max pending command, application error */
+ }
+ }
+ //test isl
+ GATT_TRACE_ERROR("!!!write opcode=0x%x, need_rsp=0x%x, status=0x%x\n",op_code, sr_data.write_req.need_rsp, status);
+
+ /* 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);
+ }
+ }
- if (status == GATT_SUCCESS) {
- attp_send_sr_msg(p_tcb, p_msg);
- gatt_dequeue_sr_cmd(p_tcb);
- } else {
- GKI_freebuf(p_msg);
+ 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__);
+ }
+ }
- } else {
- GATT_TRACE_ERROR("max pending command, send error\n");
- status = GATT_BUSY; /* max pending command, application error */
+ 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__);
}
}
- /* 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);
+ 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);
}
- return;
+ 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;
+ }
+
}
/*******************************************************************************
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;
}