1 /****************************************************************************** 2 * 3 * Copyright (C) 1999-2012 Broadcom Corporation 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19 /****************************************************************************** 20 * 21 * this file contains the main GATT client functions 22 * 23 ******************************************************************************/ 24 25 #include "bt_target.h" 26 27 #if BLE_INCLUDED == TRUE 28 29 #include <string.h> 30 #include "bt_utils.h" 31 #include "gki.h" 32 #include "gatt_int.h" 33 #include "l2c_int.h" 34 35 #define GATT_WRITE_LONG_HDR_SIZE 5 /* 1 opcode + 2 handle + 2 offset */ 36 #define GATT_READ_CHAR_VALUE_HDL (GATT_READ_CHAR_VALUE | 0x80) 37 #define GATT_READ_INC_SRV_UUID128 (GATT_DISC_INC_SRVC | 0x90) 38 39 #define GATT_PREP_WRITE_RSP_MIN_LEN 4 40 #define GATT_NOTIFICATION_MIN_LEN 2 41 #define GATT_WRITE_RSP_MIN_LEN 2 42 #define GATT_INFO_RSP_MIN_LEN 1 43 #define GATT_MTU_RSP_MIN_LEN 2 44 #define GATT_READ_BY_TYPE_RSP_MIN_LEN 1 45 46 /******************************************************************************** 47 ** G L O B A L G A T T D A T A * 48 *********************************************************************************/ 49 void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb); 50 51 UINT8 disc_type_to_att_opcode[GATT_DISC_MAX] = 52 { 53 0, 54 GATT_REQ_READ_BY_GRP_TYPE, /* GATT_DISC_SRVC_ALL = 1, */ 55 GATT_REQ_FIND_TYPE_VALUE, /* GATT_DISC_SRVC_BY_UUID, */ 56 GATT_REQ_READ_BY_TYPE, /* GATT_DISC_INC_SRVC, */ 57 GATT_REQ_READ_BY_TYPE, /* GATT_DISC_CHAR, */ 58 GATT_REQ_FIND_INFO /* GATT_DISC_CHAR_DSCPT, */ 59 }; 60 61 UINT16 disc_type_to_uuid[GATT_DISC_MAX] = 62 { 63 0, /* reserved */ 64 GATT_UUID_PRI_SERVICE, /* <service> DISC_SRVC_ALL */ 65 GATT_UUID_PRI_SERVICE, /* <service> for DISC_SERVC_BY_UUID */ 66 GATT_UUID_INCLUDE_SERVICE, /* <include_service> for DISC_INC_SRVC */ 67 GATT_UUID_CHAR_DECLARE, /* <characteristic> for DISC_CHAR */ 68 0 /* no type filtering for DISC_CHAR_DSCPT */ 69 }; 70 71 72 /******************************************************************************* 73 ** 74 ** Function gatt_act_discovery 75 ** 76 ** Description GATT discovery operation. 77 ** 78 ** Returns void. 79 ** 80 *******************************************************************************/ 81 void gatt_act_discovery(tGATT_CLCB *p_clcb) 82 { 83 UINT8 op_code = disc_type_to_att_opcode[p_clcb->op_subtype]; 84 tGATT_CL_MSG cl_req; 85 tGATT_STATUS st; 86 87 if (p_clcb->s_handle <= p_clcb->e_handle && p_clcb->s_handle != 0) 88 { 89 memset(&cl_req, 0, sizeof(tGATT_CL_MSG)); 90 91 cl_req.browse.s_handle = p_clcb->s_handle; 92 cl_req.browse.e_handle = p_clcb->e_handle; 93 94 if (disc_type_to_uuid[p_clcb->op_subtype] != 0) 95 { 96 cl_req.browse.uuid.len = 2; 97 cl_req.browse.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; 98 } 99 100 if (p_clcb->op_subtype == GATT_DISC_SRVC_BY_UUID) /* fill in the FindByTypeValue request info*/ 101 { 102 cl_req.find_type_value.uuid.len = 2; 103 cl_req.find_type_value.uuid.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; 104 cl_req.find_type_value.s_handle = p_clcb->s_handle; 105 cl_req.find_type_value.e_handle = p_clcb->e_handle; 106 cl_req.find_type_value.value_len = p_clcb->uuid.len; 107 /* if service type is 32 bits UUID, convert it now */ 108 if (p_clcb->uuid.len == LEN_UUID_32) 109 { 110 cl_req.find_type_value.value_len = LEN_UUID_128; 111 gatt_convert_uuid32_to_uuid128(cl_req.find_type_value.value, p_clcb->uuid.uu.uuid32); 112 } 113 else 114 memcpy (cl_req.find_type_value.value, &p_clcb->uuid.uu, p_clcb->uuid.len); 115 } 116 117 st = attp_send_cl_msg(p_clcb->p_tcb, p_clcb->clcb_idx, op_code, &cl_req); 118 119 if (st != GATT_SUCCESS && st != GATT_CMD_STARTED) 120 { 121 gatt_end_operation(p_clcb, GATT_ERROR, NULL); 122 } 123 } 124 else /* end of handle range */ 125 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); 126 } 127 128 /******************************************************************************* 129 ** 130 ** Function gatt_act_read 131 ** 132 ** Description GATT read operation. 133 ** 134 ** Returns void. 135 ** 136 *******************************************************************************/ 137 void gatt_act_read (tGATT_CLCB *p_clcb, UINT16 offset) 138 { 139 tGATT_TCB *p_tcb = p_clcb->p_tcb; 140 UINT8 rt = GATT_INTERNAL_ERROR; 141 tGATT_CL_MSG msg; 142 UINT8 op_code = 0; 143 144 memset (&msg, 0, sizeof(tGATT_CL_MSG)); 145 146 switch (p_clcb->op_subtype) 147 { 148 case GATT_READ_CHAR_VALUE: 149 case GATT_READ_BY_TYPE: 150 op_code = GATT_REQ_READ_BY_TYPE; 151 msg.browse.s_handle = p_clcb->s_handle; 152 msg.browse.e_handle = p_clcb->e_handle; 153 if (p_clcb->op_subtype == GATT_READ_BY_TYPE) 154 memcpy(&msg.browse.uuid, &p_clcb->uuid, sizeof(tBT_UUID)); 155 else 156 { 157 msg.browse.uuid.len = LEN_UUID_16; 158 msg.browse.uuid.uu.uuid16 = GATT_UUID_CHAR_DECLARE; 159 } 160 break; 161 162 case GATT_READ_CHAR_VALUE_HDL: 163 case GATT_READ_BY_HANDLE: 164 if (!p_clcb->counter) 165 { 166 op_code = GATT_REQ_READ; 167 msg.handle = p_clcb->s_handle; 168 } 169 else 170 { 171 if (!p_clcb->first_read_blob_after_read) 172 p_clcb->first_read_blob_after_read = TRUE; 173 else 174 p_clcb->first_read_blob_after_read = FALSE; 175 176 GATT_TRACE_DEBUG("gatt_act_read first_read_blob_after_read=%d", 177 p_clcb->first_read_blob_after_read); 178 op_code = GATT_REQ_READ_BLOB; 179 msg.read_blob.offset = offset; 180 msg.read_blob.handle = p_clcb->s_handle; 181 } 182 p_clcb->op_subtype &= ~ 0x80; 183 break; 184 185 case GATT_READ_PARTIAL: 186 op_code = GATT_REQ_READ_BLOB; 187 msg.read_blob.handle = p_clcb->s_handle; 188 msg.read_blob.offset = offset; 189 break; 190 191 case GATT_READ_MULTIPLE: 192 op_code = GATT_REQ_READ_MULTI; 193 memcpy (&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI)); 194 break; 195 196 case GATT_READ_INC_SRV_UUID128: 197 op_code = GATT_REQ_READ; 198 msg.handle = p_clcb->s_handle; 199 p_clcb->op_subtype &= ~ 0x90; 200 break; 201 202 default: 203 GATT_TRACE_ERROR("Unknown read type: %d", p_clcb->op_subtype); 204 break; 205 } 206 207 if (op_code != 0) 208 rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, op_code, &msg); 209 210 if ( op_code == 0 || (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED)) 211 { 212 gatt_end_operation(p_clcb, rt, NULL); 213 } 214 } 215 216 /******************************************************************************* 217 ** 218 ** Function gatt_act_write 219 ** 220 ** Description GATT write operation. 221 ** 222 ** Returns void. 223 ** 224 *******************************************************************************/ 225 void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act) 226 { 227 tGATT_TCB *p_tcb = p_clcb->p_tcb; 228 UINT8 rt = GATT_SUCCESS, op_code = 0; 229 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; 230 231 if (p_attr) 232 { 233 switch (p_clcb->op_subtype) 234 { 235 case GATT_WRITE_NO_RSP: 236 p_clcb->s_handle = p_attr->handle; 237 op_code = (sec_act == GATT_SEC_SIGN_DATA) ? GATT_SIGN_CMD_WRITE : GATT_CMD_WRITE; 238 rt = gatt_send_write_msg(p_tcb, 239 p_clcb->clcb_idx, 240 op_code, 241 p_attr->handle, 242 p_attr->len, 243 0, 244 p_attr->value); 245 break; 246 247 case GATT_WRITE: 248 if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE)) 249 { 250 p_clcb->s_handle = p_attr->handle; 251 252 rt = gatt_send_write_msg(p_tcb, 253 p_clcb->clcb_idx, 254 GATT_REQ_WRITE, 255 p_attr->handle, 256 p_attr->len, 257 0, 258 p_attr->value); 259 } 260 else /* prepare write for long attribute */ 261 { 262 gatt_send_prepare_write(p_tcb, p_clcb); 263 } 264 break; 265 266 case GATT_WRITE_PREPARE: 267 gatt_send_prepare_write(p_tcb, p_clcb); 268 break; 269 270 default: 271 rt = GATT_INTERNAL_ERROR; 272 GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype); 273 break; 274 } 275 } 276 else 277 rt = GATT_INTERNAL_ERROR; 278 279 if ((rt != GATT_SUCCESS && rt != GATT_CMD_STARTED && rt != GATT_CONGESTED) 280 || (rt != GATT_CMD_STARTED && p_clcb->op_subtype == GATT_WRITE_NO_RSP)) 281 { 282 if (rt != GATT_SUCCESS) 283 { 284 GATT_TRACE_ERROR("gatt_act_write() failed op_code=0x%x rt=%d", op_code, rt); 285 } 286 gatt_end_operation(p_clcb, rt, NULL); 287 } 288 } 289 /******************************************************************************* 290 ** 291 ** Function gatt_send_queue_write_cancel 292 ** 293 ** Description send queue write cancel 294 ** 295 ** Returns void. 296 ** 297 *******************************************************************************/ 298 void gatt_send_queue_write_cancel (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_EXEC_FLAG flag) 299 { 300 UINT8 rt ; 301 302 GATT_TRACE_DEBUG("gatt_send_queue_write_cancel "); 303 304 rt = attp_send_cl_msg(p_tcb, p_clcb->clcb_idx, GATT_REQ_EXEC_WRITE, (tGATT_CL_MSG *)&flag); 305 306 if (rt != GATT_SUCCESS) 307 { 308 gatt_end_operation(p_clcb, rt, NULL); 309 } 310 } 311 /******************************************************************************* 312 ** 313 ** Function gatt_check_write_long_terminate 314 ** 315 ** Description To terminate write long or not. 316 ** 317 ** Returns TRUE: write long is terminated; FALSE keep sending. 318 ** 319 *******************************************************************************/ 320 BOOLEAN gatt_check_write_long_terminate(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, tGATT_VALUE *p_rsp_value) 321 { 322 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; 323 BOOLEAN exec = FALSE; 324 tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC; 325 326 GATT_TRACE_DEBUG("gatt_check_write_long_terminate "); 327 /* check the first write response status */ 328 if (p_rsp_value != NULL) 329 { 330 if (p_rsp_value->handle != p_attr->handle || 331 p_rsp_value->len != p_clcb->counter || 332 memcmp(p_rsp_value->value, p_attr->value + p_attr->offset, p_rsp_value->len)) 333 { 334 /* data does not match */ 335 p_clcb->status = GATT_ERROR; 336 flag = GATT_PREP_WRITE_CANCEL; 337 exec = TRUE; 338 } 339 else /* response checking is good */ 340 { 341 p_clcb->status = GATT_SUCCESS; 342 /* update write offset and check if end of attribute value */ 343 if ((p_attr->offset += p_rsp_value->len) >= p_attr->len) 344 exec = TRUE; 345 } 346 } 347 if (exec) 348 { 349 gatt_send_queue_write_cancel (p_tcb, p_clcb, flag); 350 return TRUE; 351 } 352 return FALSE; 353 } 354 /******************************************************************************* 355 ** 356 ** Function gatt_send_prepare_write 357 ** 358 ** Description Send prepare write. 359 ** 360 ** Returns void. 361 ** 362 *******************************************************************************/ 363 void gatt_send_prepare_write(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb) 364 { 365 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; 366 UINT16 to_send, offset; 367 UINT8 rt = GATT_SUCCESS; 368 UINT8 type = p_clcb->op_subtype; 369 370 GATT_TRACE_DEBUG("gatt_send_prepare_write type=0x%x", type ); 371 to_send = p_attr->len - p_attr->offset; 372 373 if (to_send > (p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE)) /* 2 = UINT16 offset bytes */ 374 to_send = p_tcb->payload_size - GATT_WRITE_LONG_HDR_SIZE; 375 376 p_clcb->s_handle = p_attr->handle; 377 378 offset = p_attr->offset; 379 if (type == GATT_WRITE_PREPARE) 380 { 381 offset += p_clcb->start_offset; 382 } 383 384 GATT_TRACE_DEBUG("offset =0x%x len=%d", offset, to_send ); 385 386 rt = gatt_send_write_msg(p_tcb, 387 p_clcb->clcb_idx, 388 GATT_REQ_PREPARE_WRITE, 389 p_attr->handle, 390 to_send, /* length */ 391 offset, /* used as offset */ 392 p_attr->value + p_attr->offset); /* data */ 393 394 /* remember the write long attribute length */ 395 p_clcb->counter = to_send; 396 397 if (rt != GATT_SUCCESS && rt != GATT_CMD_STARTED) 398 { 399 gatt_end_operation(p_clcb, rt, NULL); 400 } 401 } 402 403 404 /******************************************************************************* 405 ** 406 ** Function gatt_process_find_type_value_rsp 407 ** 408 ** Description This function is called to handle find by type value response. 409 ** 410 ** 411 ** Returns void 412 ** 413 *******************************************************************************/ 414 void gatt_process_find_type_value_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) 415 { 416 tGATT_DISC_RES result; 417 UINT8 *p = p_data; 418 419 UNUSED(p_tcb); 420 421 GATT_TRACE_DEBUG("gatt_process_find_type_value_rsp "); 422 /* unexpected response */ 423 if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_SRVC_BY_UUID) 424 return; 425 426 memset (&result, 0, sizeof(tGATT_DISC_RES)); 427 result.type.len = 2; 428 result.type.uu.uuid16 = GATT_UUID_PRI_SERVICE; 429 430 /* returns a series of handle ranges */ 431 while (len >= 4) 432 { 433 STREAM_TO_UINT16 (result.handle, p); 434 STREAM_TO_UINT16 (result.value.group_value.e_handle, p); 435 memcpy (&result.value.group_value.service_type, &p_clcb->uuid, sizeof(tBT_UUID)); 436 437 len -= 4; 438 439 if (p_clcb->p_reg->app_cb.p_disc_res_cb) 440 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); 441 } 442 443 /* last handle + 1 */ 444 p_clcb->s_handle = (result.value.group_value.e_handle == 0) ? 0 : (result.value.group_value.e_handle + 1); 445 /* initiate another request */ 446 gatt_act_discovery(p_clcb) ; 447 } 448 /******************************************************************************* 449 ** 450 ** Function gatt_process_read_info_rsp 451 ** 452 ** Description This function is called to handle the read information 453 ** response. 454 ** 455 ** 456 ** Returns void 457 ** 458 *******************************************************************************/ 459 void gatt_process_read_info_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, 460 UINT16 len, UINT8 *p_data) 461 { 462 tGATT_DISC_RES result; 463 UINT8 *p = p_data, uuid_len = 0, type; 464 465 UNUSED(p_tcb); 466 UNUSED(op_code); 467 468 if (len < GATT_INFO_RSP_MIN_LEN) 469 { 470 GATT_TRACE_ERROR("invalid Info Response PDU received, discard."); 471 gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); 472 return; 473 } 474 /* unexpected response */ 475 if (p_clcb->operation != GATTC_OPTYPE_DISCOVERY || p_clcb->op_subtype != GATT_DISC_CHAR_DSCPT) 476 return; 477 478 STREAM_TO_UINT8(type, p); 479 len -= 1; 480 481 if (type == GATT_INFO_TYPE_PAIR_16) 482 uuid_len = LEN_UUID_16; 483 else if (type == GATT_INFO_TYPE_PAIR_128) 484 uuid_len = LEN_UUID_128; 485 486 while (len >= uuid_len + 2) 487 { 488 STREAM_TO_UINT16 (result.handle, p); 489 490 if (uuid_len > 0) 491 { 492 if (!gatt_parse_uuid_from_cmd(&result.type, uuid_len, &p)) 493 break; 494 } 495 else 496 memcpy (&result.type, &p_clcb->uuid, sizeof(tBT_UUID)); 497 498 len -= (uuid_len + 2); 499 500 if (p_clcb->p_reg->app_cb.p_disc_res_cb) 501 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); 502 } 503 504 p_clcb->s_handle = (result.handle == 0) ? 0 :(result.handle + 1); 505 /* initiate another request */ 506 gatt_act_discovery(p_clcb) ; 507 } 508 /******************************************************************************* 509 ** 510 ** Function gatt_proc_disc_error_rsp 511 ** 512 ** Description This function process the read by type response and send another 513 ** request if needed. 514 ** 515 ** Returns void. 516 ** 517 *******************************************************************************/ 518 void gatt_proc_disc_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 opcode, 519 UINT16 handle, UINT8 reason) 520 { 521 tGATT_STATUS status = (tGATT_STATUS) reason; 522 523 UNUSED(p_tcb); 524 UNUSED(handle); 525 526 GATT_TRACE_DEBUG("gatt_proc_disc_error_rsp reason: %02x cmd_code %04x", reason, opcode); 527 528 switch (opcode) 529 { 530 case GATT_REQ_READ_BY_GRP_TYPE: 531 case GATT_REQ_FIND_TYPE_VALUE: 532 case GATT_REQ_READ_BY_TYPE: 533 case GATT_REQ_FIND_INFO: 534 if (reason == GATT_NOT_FOUND) 535 { 536 status = GATT_SUCCESS; 537 GATT_TRACE_DEBUG("Discovery completed"); 538 } 539 break; 540 default: 541 GATT_TRACE_ERROR("Incorrect discovery opcode %04x", opcode); 542 break; 543 } 544 545 gatt_end_operation(p_clcb, status, NULL); 546 } 547 548 /******************************************************************************* 549 ** 550 ** Function gatt_process_error_rsp 551 ** 552 ** Description This function is called to handle the error response 553 ** 554 ** 555 ** Returns void 556 ** 557 *******************************************************************************/ 558 void gatt_process_error_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, 559 UINT16 len, UINT8 *p_data) 560 { 561 UINT8 opcode, reason, * p= p_data; 562 UINT16 handle; 563 tGATT_VALUE *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf; 564 565 UNUSED(op_code); 566 UNUSED(len); 567 568 GATT_TRACE_DEBUG("gatt_process_error_rsp "); 569 STREAM_TO_UINT8(opcode, p); 570 STREAM_TO_UINT16(handle, p); 571 STREAM_TO_UINT8(reason, p); 572 573 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) 574 { 575 gatt_proc_disc_error_rsp(p_tcb, p_clcb, opcode, handle, reason); 576 } 577 else 578 { 579 if ( (p_clcb->operation == GATTC_OPTYPE_WRITE) && 580 (p_clcb->op_subtype == GATT_WRITE) && 581 (opcode == GATT_REQ_PREPARE_WRITE) && 582 (p_attr) && 583 (handle == p_attr->handle) ) 584 { 585 p_clcb->status = reason; 586 gatt_send_queue_write_cancel(p_tcb, p_clcb, GATT_PREP_WRITE_CANCEL); 587 } 588 else if ((p_clcb->operation == GATTC_OPTYPE_READ) && 589 ((p_clcb->op_subtype == GATT_READ_CHAR_VALUE_HDL) || 590 (p_clcb->op_subtype == GATT_READ_BY_HANDLE)) && 591 (opcode == GATT_REQ_READ_BLOB) && 592 p_clcb->first_read_blob_after_read && 593 (reason == GATT_NOT_LONG)) 594 { 595 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); 596 } 597 else 598 gatt_end_operation(p_clcb, reason, NULL); 599 } 600 } 601 /******************************************************************************* 602 ** 603 ** Function gatt_process_prep_write_rsp 604 ** 605 ** Description This function is called to handle the read response 606 ** 607 ** 608 ** Returns void 609 ** 610 *******************************************************************************/ 611 void gatt_process_prep_write_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, 612 UINT16 len, UINT8 *p_data) 613 { 614 tGATT_VALUE value = {0}; 615 UINT8 *p= p_data; 616 617 GATT_TRACE_ERROR("value resp op_code = %s len = %d", gatt_dbg_op_name(op_code), len); 618 619 if (len < GATT_PREP_WRITE_RSP_MIN_LEN) 620 { 621 GATT_TRACE_ERROR("illegal prepare write response length, discard"); 622 gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value); 623 return; 624 } 625 626 STREAM_TO_UINT16 (value.handle, p); 627 STREAM_TO_UINT16 (value.offset, p); 628 629 value.len = len - 4; 630 631 memcpy (value.value, p, value.len); 632 633 if (p_clcb->op_subtype == GATT_WRITE_PREPARE) 634 { 635 p_clcb->status = GATT_SUCCESS; 636 /* application should verify handle offset 637 and value are matched or not */ 638 639 gatt_end_operation(p_clcb, p_clcb->status, &value); 640 } 641 else if (p_clcb->op_subtype == GATT_WRITE ) 642 { 643 if (!gatt_check_write_long_terminate(p_tcb, p_clcb, &value)) 644 gatt_send_prepare_write(p_tcb, p_clcb); 645 } 646 647 } 648 /******************************************************************************* 649 ** 650 ** Function gatt_process_notification 651 ** 652 ** Description This function is called to handle the handle value indication 653 ** or handle value notification. 654 ** 655 ** 656 ** Returns void 657 ** 658 *******************************************************************************/ 659 void gatt_process_notification(tGATT_TCB *p_tcb, UINT8 op_code, 660 UINT16 len, UINT8 *p_data) 661 { 662 tGATT_VALUE value = {0}; 663 tGATT_REG *p_reg; 664 UINT16 conn_id; 665 tGATT_STATUS encrypt_status; 666 UINT8 *p= p_data, i, 667 event = (op_code == GATT_HANDLE_VALUE_NOTIF) ? GATTC_OPTYPE_NOTIFICATION : GATTC_OPTYPE_INDICATION; 668 669 GATT_TRACE_DEBUG("gatt_process_notification "); 670 671 if (len < GATT_NOTIFICATION_MIN_LEN) 672 { 673 GATT_TRACE_ERROR("illegal notification PDU length, discard"); 674 return; 675 } 676 677 STREAM_TO_UINT16 (value.handle, p); 678 value.len = len - 2; 679 memcpy (value.value, p, value.len); 680 681 if (!GATT_HANDLE_IS_VALID(value.handle)) 682 { 683 /* illegal handle, send ack now */ 684 if (op_code == GATT_HANDLE_VALUE_IND) 685 attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); 686 return; 687 } 688 689 if (event == GATTC_OPTYPE_INDICATION) 690 { 691 if (p_tcb->ind_count) 692 { 693 /* this is an error case that receiving an indication but we 694 still has an indication not being acked yet. 695 For now, just log the error reset the counter. 696 Later we need to disconnect the link unconditionally. 697 */ 698 GATT_TRACE_ERROR("gatt_process_notification rcv Ind. but ind_count=%d (will reset ind_count)", p_tcb->ind_count); 699 } 700 p_tcb->ind_count = 0; 701 } 702 703 /* should notify all registered client with the handle value notificaion/indication 704 Note: need to do the indication count and start timer first then do callback 705 */ 706 707 for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) 708 { 709 if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb && (event == GATTC_OPTYPE_INDICATION)) 710 p_tcb->ind_count++; 711 } 712 713 if (event == GATTC_OPTYPE_INDICATION) 714 { 715 /* start a timer for app confirmation */ 716 if (p_tcb->ind_count > 0) 717 gatt_start_ind_ack_timer(p_tcb); 718 else /* no app to indicate, or invalid handle */ 719 attp_send_cl_msg(p_tcb, 0, GATT_HANDLE_VALUE_CONF, NULL); 720 } 721 722 encrypt_status = gatt_get_link_encrypt_status(p_tcb); 723 for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) 724 { 725 if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) 726 { 727 conn_id = GATT_CREATE_CONN_ID(p_tcb->tcb_idx, p_reg->gatt_if); 728 (*p_reg->app_cb.p_cmpl_cb) (conn_id, event, encrypt_status, (tGATT_CL_COMPLETE *)&value); 729 } 730 } 731 732 } 733 734 /******************************************************************************* 735 ** 736 ** Function gatt_process_read_by_type_rsp 737 ** 738 ** Description This function is called to handle the read by type response. 739 ** read by type can be used for discovery, or read by type or 740 ** read characteristic value. 741 ** 742 ** Returns void 743 ** 744 *******************************************************************************/ 745 void gatt_process_read_by_type_rsp (tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, 746 UINT16 len, UINT8 *p_data) 747 { 748 tGATT_DISC_RES result; 749 tGATT_DISC_VALUE record_value; 750 UINT8 *p = p_data, value_len, handle_len = 2; 751 UINT16 handle = 0; 752 753 /* discovery procedure and no callback function registered */ 754 if (((!p_clcb->p_reg) || (!p_clcb->p_reg->app_cb.p_disc_res_cb)) && (p_clcb->operation == GATTC_OPTYPE_DISCOVERY)) 755 return; 756 757 if (len < GATT_READ_BY_TYPE_RSP_MIN_LEN) 758 { 759 GATT_TRACE_ERROR("Illegal ReadByType/ReadByGroupType Response length, discard"); 760 gatt_end_operation(p_clcb, GATT_INVALID_PDU, NULL); 761 return; 762 } 763 764 STREAM_TO_UINT8(value_len, p); 765 766 if ((value_len > (p_tcb->payload_size - 2)) || (value_len > (len-1)) ) 767 { 768 /* this is an error case that server's response containing a value length which is larger than MTU-2 769 or value_len > message total length -1 */ 770 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)", 771 op_code, value_len, (p_tcb->payload_size - 2), (len-1)); 772 gatt_end_operation(p_clcb, GATT_ERROR, NULL); 773 return; 774 } 775 776 if (op_code == GATT_RSP_READ_BY_GRP_TYPE) 777 handle_len = 4; 778 779 value_len -= handle_len; /* substract the handle pairs bytes */ 780 len -= 1; 781 782 while (len >= (handle_len + value_len)) 783 { 784 STREAM_TO_UINT16(handle, p); 785 786 if (!GATT_HANDLE_IS_VALID(handle)) 787 { 788 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); 789 return; 790 } 791 792 memset(&result, 0, sizeof(tGATT_DISC_RES)); 793 memset(&record_value, 0, sizeof(tGATT_DISC_VALUE)); 794 795 result.handle = handle; 796 result.type.len = 2; 797 result.type.uu.uuid16 = disc_type_to_uuid[p_clcb->op_subtype]; 798 799 /* discover all services */ 800 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && 801 p_clcb->op_subtype == GATT_DISC_SRVC_ALL && 802 op_code == GATT_RSP_READ_BY_GRP_TYPE) 803 { 804 STREAM_TO_UINT16(handle, p); 805 806 if (!GATT_HANDLE_IS_VALID(handle)) 807 { 808 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); 809 return; 810 } 811 else 812 { 813 record_value.group_value.e_handle = handle; 814 if (!gatt_parse_uuid_from_cmd(&record_value.group_value.service_type, value_len, &p)) 815 { 816 GATT_TRACE_ERROR("discover all service response parsing failure"); 817 break; 818 } 819 } 820 } 821 /* discover included service */ 822 else if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->op_subtype == GATT_DISC_INC_SRVC) 823 { 824 STREAM_TO_UINT16(record_value.incl_service.s_handle, p); 825 STREAM_TO_UINT16(record_value.incl_service.e_handle, p); 826 827 if (!GATT_HANDLE_IS_VALID(record_value.incl_service.s_handle) || 828 !GATT_HANDLE_IS_VALID(record_value.incl_service.e_handle)) 829 { 830 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); 831 return; 832 } 833 834 if(value_len == 6) 835 { 836 STREAM_TO_UINT16(record_value.incl_service.service_type.uu.uuid16, p); 837 record_value.incl_service.service_type.len = LEN_UUID_16; 838 } 839 else if (value_len == 4) 840 { 841 p_clcb->s_handle = record_value.incl_service.s_handle; 842 p_clcb->read_uuid128.wait_for_read_rsp = TRUE; 843 p_clcb->read_uuid128.next_disc_start_hdl = handle + 1; 844 memcpy(&p_clcb->read_uuid128.result, &result, sizeof(result)); 845 memcpy(&p_clcb->read_uuid128.result.value, &record_value, sizeof (result.value)); 846 p_clcb->op_subtype |= 0x90; 847 gatt_act_read(p_clcb, 0); 848 return; 849 } 850 else 851 { 852 GATT_TRACE_ERROR("gatt_process_read_by_type_rsp INCL_SRVC failed with invalid data value_len=%d", value_len); 853 gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); 854 return; 855 } 856 } 857 /* read by type */ 858 else if (p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE) 859 { 860 p_clcb->counter = len - 2; 861 p_clcb->s_handle = handle; 862 if ( p_clcb->counter == (p_clcb->p_tcb->payload_size -4)) 863 { 864 p_clcb->op_subtype = GATT_READ_BY_HANDLE; 865 if (!p_clcb->p_attr_buf) 866 p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN); 867 if (p_clcb->p_attr_buf && p_clcb->counter <= GATT_MAX_ATTR_LEN) 868 { 869 memcpy(p_clcb->p_attr_buf, p, p_clcb->counter); 870 gatt_act_read(p_clcb, p_clcb->counter); 871 } 872 else 873 gatt_end_operation(p_clcb, GATT_INTERNAL_ERROR, (void *)p); 874 } 875 else 876 { 877 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); 878 } 879 return; 880 } 881 else /* discover characterisitic */ 882 { 883 STREAM_TO_UINT8 (record_value.dclr_value.char_prop, p); 884 STREAM_TO_UINT16(record_value.dclr_value.val_handle, p); 885 if (!GATT_HANDLE_IS_VALID(record_value.dclr_value.val_handle)) 886 { 887 gatt_end_operation(p_clcb, GATT_INVALID_HANDLE, NULL); 888 return; 889 } 890 if (!gatt_parse_uuid_from_cmd(&record_value.dclr_value.char_uuid, (UINT16)(value_len - 3), &p)) 891 { 892 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); 893 /* invalid format, and skip the result */ 894 return; 895 } 896 897 /* UUID not matching */ 898 if (!gatt_uuid_compare(record_value.dclr_value.char_uuid, p_clcb->uuid)) 899 { 900 len -= (value_len + 2); 901 continue; /* skip the result, and look for next one */ 902 } 903 else if (p_clcb->operation == GATTC_OPTYPE_READ) 904 /* UUID match for read characteristic value */ 905 { 906 /* only read the first matching UUID characteristic value, and 907 discard the rest results */ 908 p_clcb->s_handle = record_value.dclr_value.val_handle; 909 p_clcb->op_subtype |= 0x80; 910 gatt_act_read(p_clcb, 0); 911 return; 912 } 913 } 914 len -= (value_len + handle_len); 915 916 /* result is (handle, 16bits UUID) pairs */ 917 memcpy (&result.value, &record_value, sizeof (result.value)); 918 919 /* send callback if is discover procedure */ 920 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && p_clcb->p_reg->app_cb.p_disc_res_cb) 921 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &result); 922 } 923 924 p_clcb->s_handle = (handle == 0) ? 0 : (handle + 1); 925 926 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY) 927 { 928 /* initiate another request */ 929 gatt_act_discovery(p_clcb) ; 930 } 931 else /* read characteristic value */ 932 { 933 gatt_act_read(p_clcb, 0); 934 } 935 } 936 937 /******************************************************************************* 938 ** 939 ** Function gatt_process_read_rsp 940 ** 941 ** Description This function is called to handle the read BLOB response 942 ** 943 ** 944 ** Returns void 945 ** 946 *******************************************************************************/ 947 void gatt_process_read_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT8 op_code, 948 UINT16 len, UINT8 *p_data) 949 { 950 UINT16 offset = p_clcb->counter; 951 UINT8 * p= p_data; 952 953 UNUSED(op_code); 954 955 if (p_clcb->operation == GATTC_OPTYPE_READ) 956 { 957 if (p_clcb->op_subtype != GATT_READ_BY_HANDLE) 958 { 959 p_clcb->counter = len; 960 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p); 961 } 962 else 963 { 964 965 /* allocate GKI buffer holding up long attribute value */ 966 if (!p_clcb->p_attr_buf) 967 p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf(GATT_MAX_ATTR_LEN); 968 969 /* copy attrobute value into cb buffer */ 970 if (p_clcb->p_attr_buf && offset < GATT_MAX_ATTR_LEN) 971 { 972 if ((len + offset) > GATT_MAX_ATTR_LEN) 973 len = GATT_MAX_ATTR_LEN - offset; 974 975 p_clcb->counter += len; 976 977 memcpy(p_clcb->p_attr_buf + offset, p, len); 978 979 /* send next request if needed */ 980 981 if (len == (p_tcb->payload_size - 1) && /* full packet for read or read blob rsp */ 982 len + offset < GATT_MAX_ATTR_LEN) 983 { 984 GATT_TRACE_DEBUG("full pkt issue read blob for remianing bytes old offset=%d len=%d new offset=%d", 985 offset, len, p_clcb->counter); 986 gatt_act_read(p_clcb, p_clcb->counter); 987 } 988 else /* end of request, send callback */ 989 { 990 gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p_clcb->p_attr_buf); 991 } 992 } 993 else /* exception, should not happen */ 994 { 995 GATT_TRACE_ERROR("attr offset = %d p_attr_buf = %d ", offset, p_clcb->p_attr_buf); 996 gatt_end_operation(p_clcb, GATT_NO_RESOURCES, (void *)p_clcb->p_attr_buf); 997 } 998 } 999 } 1000 else 1001 { 1002 if (p_clcb->operation == GATTC_OPTYPE_DISCOVERY && 1003 p_clcb->op_subtype == GATT_DISC_INC_SRVC && 1004 p_clcb->read_uuid128.wait_for_read_rsp ) 1005 { 1006 p_clcb->s_handle = p_clcb->read_uuid128.next_disc_start_hdl; 1007 p_clcb->read_uuid128.wait_for_read_rsp = FALSE; 1008 if (len == LEN_UUID_128) 1009 { 1010 1011 memcpy(p_clcb->read_uuid128.result.value.incl_service.service_type.uu.uuid128, p, len); 1012 p_clcb->read_uuid128.result.value.incl_service.service_type.len = LEN_UUID_128; 1013 if ( p_clcb->p_reg->app_cb.p_disc_res_cb) 1014 (*p_clcb->p_reg->app_cb.p_disc_res_cb)(p_clcb->conn_id, p_clcb->op_subtype, &p_clcb->read_uuid128.result); 1015 gatt_act_discovery(p_clcb) ; 1016 } 1017 else 1018 { 1019 gatt_end_operation(p_clcb, GATT_INVALID_PDU, (void *)p); 1020 } 1021 } 1022 } 1023 1024 } 1025 1026 1027 /******************************************************************************* 1028 ** 1029 ** Function gatt_process_handle_rsp 1030 ** 1031 ** Description This function is called to handle the write response 1032 ** 1033 ** 1034 ** Returns void 1035 ** 1036 *******************************************************************************/ 1037 void gatt_process_handle_rsp(tGATT_CLCB *p_clcb) 1038 { 1039 gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); 1040 } 1041 /******************************************************************************* 1042 ** 1043 ** Function gatt_process_mtu_rsp 1044 ** 1045 ** Description This function is called to process the configure MTU response. 1046 ** 1047 ** 1048 ** Returns void 1049 ** 1050 *******************************************************************************/ 1051 void gatt_process_mtu_rsp(tGATT_TCB *p_tcb, tGATT_CLCB *p_clcb, UINT16 len, UINT8 *p_data) 1052 { 1053 UINT16 mtu; 1054 tGATT_STATUS status = GATT_SUCCESS; 1055 1056 if (len < GATT_MTU_RSP_MIN_LEN) 1057 { 1058 GATT_TRACE_ERROR("invalid MTU response PDU received, discard."); 1059 status = GATT_INVALID_PDU; 1060 } 1061 else 1062 { 1063 STREAM_TO_UINT16(mtu, p_data); 1064 1065 if (mtu < p_tcb->payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE) 1066 p_tcb->payload_size = mtu; 1067 } 1068 1069 l2cble_set_fixed_channel_tx_data_length(p_tcb->peer_bda, L2CAP_ATT_CID, p_tcb->payload_size); 1070 gatt_end_operation(p_clcb, status, NULL); 1071 } 1072 /******************************************************************************* 1073 ** 1074 ** Function gatt_cmd_to_rsp_code 1075 ** 1076 ** Description The function convert a ATT command op code into the corresponding 1077 ** response code assume no error occurs. 1078 ** 1079 ** Returns response code. 1080 ** 1081 *******************************************************************************/ 1082 UINT8 gatt_cmd_to_rsp_code (UINT8 cmd_code) 1083 { 1084 UINT8 rsp_code = 0; 1085 1086 if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) 1087 { 1088 rsp_code = cmd_code + 1; 1089 } 1090 return rsp_code; 1091 } 1092 /******************************************************************************* 1093 ** 1094 ** Function gatt_cl_send_next_cmd_inq 1095 ** 1096 ** Description Find next command in queue and sent to server 1097 ** 1098 ** Returns TRUE if command sent, otherwise FALSE. 1099 ** 1100 *******************************************************************************/ 1101 BOOLEAN gatt_cl_send_next_cmd_inq(tGATT_TCB *p_tcb) 1102 { 1103 tGATT_CMD_Q *p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; 1104 BOOLEAN sent = FALSE; 1105 UINT8 rsp_code; 1106 tGATT_CLCB *p_clcb = NULL; 1107 tGATT_STATUS att_ret = GATT_SUCCESS; 1108 1109 while (!sent && 1110 p_tcb->pending_cl_req != p_tcb->next_slot_inq && 1111 p_cmd->to_send && p_cmd->p_cmd != NULL) 1112 { 1113 att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd->p_cmd); 1114 1115 if (att_ret == GATT_SUCCESS || att_ret == GATT_CONGESTED) 1116 { 1117 sent = TRUE; 1118 p_cmd->to_send = FALSE; 1119 p_cmd->p_cmd = NULL; 1120 1121 /* dequeue the request if is write command or sign write */ 1122 if (p_cmd->op_code != GATT_CMD_WRITE && p_cmd->op_code != GATT_SIGN_CMD_WRITE) 1123 { 1124 gatt_start_rsp_timer (p_cmd->clcb_idx); 1125 } 1126 else 1127 { 1128 p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); 1129 1130 /* if no ack needed, keep sending */ 1131 if (att_ret == GATT_SUCCESS) 1132 sent = FALSE; 1133 1134 p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; 1135 /* send command complete callback here */ 1136 gatt_end_operation(p_clcb, att_ret, NULL); 1137 } 1138 } 1139 else 1140 { 1141 GATT_TRACE_ERROR("gatt_cl_send_next_cmd_inq: L2CAP sent error"); 1142 1143 memset(p_cmd, 0, sizeof(tGATT_CMD_Q)); 1144 p_tcb->pending_cl_req ++; 1145 p_cmd = &p_tcb->cl_cmd_q[p_tcb->pending_cl_req]; 1146 } 1147 1148 } 1149 return sent; 1150 } 1151 1152 /******************************************************************************* 1153 ** 1154 ** Function gatt_client_handle_server_rsp 1155 ** 1156 ** Description This function is called to handle the server response to 1157 ** client. 1158 ** 1159 ** 1160 ** Returns void 1161 ** 1162 *******************************************************************************/ 1163 void gatt_client_handle_server_rsp (tGATT_TCB *p_tcb, UINT8 op_code, 1164 UINT16 len, UINT8 *p_data) 1165 { 1166 tGATT_CLCB *p_clcb = NULL; 1167 UINT8 rsp_code; 1168 1169 if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF) 1170 { 1171 p_clcb = gatt_cmd_dequeue(p_tcb, &rsp_code); 1172 1173 rsp_code = gatt_cmd_to_rsp_code(rsp_code); 1174 1175 if (p_clcb == NULL || (rsp_code != op_code && op_code != GATT_RSP_ERROR)) 1176 { 1177 GATT_TRACE_WARNING ("ATT - Ignore wrong response. Receives (%02x) \ 1178 Request(%02x) Ignored", op_code, rsp_code); 1179 1180 return; 1181 } 1182 else 1183 { 1184 btu_stop_timer (&p_clcb->rsp_timer_ent); 1185 p_clcb->retry_count = 0; 1186 } 1187 } 1188 /* the size of the message may not be bigger than the local max PDU size*/ 1189 /* The message has to be smaller than the agreed MTU, len does not count op_code */ 1190 if (len >= p_tcb->payload_size) 1191 { 1192 GATT_TRACE_ERROR("invalid response/indicate pkt size: %d, PDU size: %d", len + 1, p_tcb->payload_size); 1193 if (op_code != GATT_HANDLE_VALUE_NOTIF && 1194 op_code != GATT_HANDLE_VALUE_IND) 1195 gatt_end_operation(p_clcb, GATT_ERROR, NULL); 1196 } 1197 else 1198 { 1199 switch (op_code) 1200 { 1201 case GATT_RSP_ERROR: 1202 gatt_process_error_rsp(p_tcb, p_clcb, op_code, len, p_data); 1203 break; 1204 1205 case GATT_RSP_MTU: /* 2 bytes mtu */ 1206 gatt_process_mtu_rsp(p_tcb, p_clcb, len ,p_data); 1207 break; 1208 1209 case GATT_RSP_FIND_INFO: 1210 gatt_process_read_info_rsp(p_tcb, p_clcb, op_code, len, p_data); 1211 break; 1212 1213 case GATT_RSP_READ_BY_TYPE: 1214 case GATT_RSP_READ_BY_GRP_TYPE: 1215 gatt_process_read_by_type_rsp(p_tcb, p_clcb, op_code, len, p_data); 1216 break; 1217 1218 case GATT_RSP_READ: 1219 case GATT_RSP_READ_BLOB: 1220 case GATT_RSP_READ_MULTI: 1221 gatt_process_read_rsp(p_tcb, p_clcb, op_code, len, p_data); 1222 break; 1223 1224 case GATT_RSP_FIND_TYPE_VALUE: /* disc service with UUID */ 1225 gatt_process_find_type_value_rsp(p_tcb, p_clcb, len, p_data); 1226 break; 1227 1228 case GATT_RSP_WRITE: 1229 gatt_process_handle_rsp(p_clcb); 1230 break; 1231 1232 case GATT_RSP_PREPARE_WRITE: 1233 gatt_process_prep_write_rsp(p_tcb, p_clcb, op_code, len, p_data); 1234 break; 1235 1236 case GATT_RSP_EXEC_WRITE: 1237 gatt_end_operation(p_clcb, p_clcb->status, NULL); 1238 break; 1239 1240 case GATT_HANDLE_VALUE_NOTIF: 1241 case GATT_HANDLE_VALUE_IND: 1242 gatt_process_notification(p_tcb, op_code, len, p_data); 1243 break; 1244 1245 default: 1246 GATT_TRACE_ERROR("Unknown opcode = %d", op_code); 1247 break; 1248 } 1249 } 1250 1251 if (op_code != GATT_HANDLE_VALUE_IND && op_code != GATT_HANDLE_VALUE_NOTIF) 1252 { 1253 gatt_cl_send_next_cmd_inq(p_tcb); 1254 } 1255 1256 return; 1257 } 1258 1259 #endif /* BLE_INCLUDED */ 1260