1 /******************************************************************************
2  *
3  *  Copyright (C) 2008-2014 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 ATT protocol functions
22  *
23  ******************************************************************************/
24 
25 #include "common/bt_target.h"
26 #include "osi/allocator.h"
27 
28 #if BLE_INCLUDED == TRUE
29 
30 #include "gatt_int.h"
31 #include "stack/l2c_api.h"
32 
33 #define GATT_HDR_FIND_TYPE_VALUE_LEN    21
34 #define GATT_OP_CODE_SIZE   1
35 /**********************************************************************
36 **   ATT protocl message building utility                              *
37 ***********************************************************************/
38 /*******************************************************************************
39 **
40 ** Function         attp_build_mtu_exec_cmd
41 **
42 ** Description      Build a exchange MTU request
43 **
44 ** Returns          None.
45 **
46 *******************************************************************************/
attp_build_mtu_cmd(UINT8 op_code,UINT16 rx_mtu)47 BT_HDR *attp_build_mtu_cmd(UINT8 op_code, UINT16 rx_mtu)
48 {
49     BT_HDR      *p_buf = NULL;
50     UINT8       *p;
51 
52     if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + GATT_HDR_SIZE + L2CAP_MIN_OFFSET)) != NULL) {
53         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
54 
55         UINT8_TO_STREAM (p, op_code);
56         UINT16_TO_STREAM (p, rx_mtu);
57 
58         p_buf->offset = L2CAP_MIN_OFFSET;
59         p_buf->len = GATT_HDR_SIZE; /* opcode + 2 bytes mtu */
60     }
61     return p_buf;
62 }
63 /*******************************************************************************
64 **
65 ** Function         attp_build_exec_write_cmd
66 **
67 ** Description      Build a execute write request or response.
68 **
69 ** Returns          None.
70 **
71 *******************************************************************************/
attp_build_exec_write_cmd(UINT8 op_code,UINT8 flag)72 BT_HDR *attp_build_exec_write_cmd (UINT8 op_code, UINT8 flag)
73 {
74     BT_HDR      *p_buf = NULL;
75     UINT8       *p;
76 
77     if ((p_buf = (BT_HDR *)osi_malloc(GATT_DATA_BUF_SIZE)) != NULL) {
78         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
79 
80         p_buf->offset = L2CAP_MIN_OFFSET;
81         p_buf->len = GATT_OP_CODE_SIZE;
82 
83         UINT8_TO_STREAM (p, op_code);
84 
85         if (op_code == GATT_REQ_EXEC_WRITE) {
86             flag &= GATT_PREP_WRITE_EXEC;
87             UINT8_TO_STREAM (p, flag);
88             p_buf->len += 1;
89         }
90 
91     }
92 
93     return p_buf;
94 }
95 
96 /*******************************************************************************
97 **
98 ** Function         attp_build_err_cmd
99 **
100 ** Description      Build a exchange MTU request
101 **
102 ** Returns          None.
103 **
104 *******************************************************************************/
attp_build_err_cmd(UINT8 cmd_code,UINT16 err_handle,UINT8 reason)105 BT_HDR *attp_build_err_cmd(UINT8 cmd_code, UINT16 err_handle, UINT8 reason)
106 {
107     BT_HDR      *p_buf = NULL;
108     UINT8       *p;
109 
110     if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + L2CAP_MIN_OFFSET + 5)) != NULL) {
111         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
112 
113         UINT8_TO_STREAM (p, GATT_RSP_ERROR);
114         UINT8_TO_STREAM (p, cmd_code);
115         UINT16_TO_STREAM(p, err_handle);
116         UINT8_TO_STREAM (p, reason);
117 
118         p_buf->offset = L2CAP_MIN_OFFSET;
119         /* GATT_HDR_SIZE (1B ERR_RSP op code+ 2B handle) + 1B cmd_op_code  + 1B status */
120         p_buf->len = GATT_HDR_SIZE + 1 + 1;
121     }
122     return p_buf;
123 }
124 /*******************************************************************************
125 **
126 ** Function         attp_build_browse_cmd
127 **
128 ** Description      Build a read information request or read by type request
129 **
130 ** Returns          None.
131 **
132 *******************************************************************************/
attp_build_browse_cmd(UINT8 op_code,UINT16 s_hdl,UINT16 e_hdl,tBT_UUID uuid)133 BT_HDR *attp_build_browse_cmd(UINT8 op_code, UINT16 s_hdl, UINT16 e_hdl, tBT_UUID uuid)
134 {
135     BT_HDR      *p_buf = NULL;
136     UINT8       *p;
137 
138     if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 8 + L2CAP_MIN_OFFSET)) != NULL) {
139         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
140         /* Describe the built message location and size */
141         p_buf->offset = L2CAP_MIN_OFFSET;
142         p_buf->len = GATT_OP_CODE_SIZE + 4;
143 
144         UINT8_TO_STREAM (p, op_code);
145         UINT16_TO_STREAM (p, s_hdl);
146         UINT16_TO_STREAM (p, e_hdl);
147         p_buf->len += gatt_build_uuid_to_stream(&p, uuid);
148     }
149 
150     return p_buf;
151 }
152 /*******************************************************************************
153 **
154 ** Function         attp_build_read_handles_cmd
155 **
156 ** Description      Build a read by type and value request.
157 **
158 ** Returns          pointer to the command buffer.
159 **
160 *******************************************************************************/
attp_build_read_by_type_value_cmd(UINT16 payload_size,tGATT_FIND_TYPE_VALUE * p_value_type)161 BT_HDR *attp_build_read_by_type_value_cmd (UINT16 payload_size, tGATT_FIND_TYPE_VALUE *p_value_type)
162 {
163     BT_HDR      *p_buf = NULL;
164     UINT8       *p;
165     UINT16      len = p_value_type->value_len;
166 
167     if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) {
168         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
169 
170         p_buf->offset = L2CAP_MIN_OFFSET;
171         p_buf->len = 5; /* opcode + s_handle + e_handle */
172 
173         UINT8_TO_STREAM  (p, GATT_REQ_FIND_TYPE_VALUE);
174         UINT16_TO_STREAM (p, p_value_type->s_handle);
175         UINT16_TO_STREAM (p, p_value_type->e_handle);
176 
177         p_buf->len += gatt_build_uuid_to_stream(&p, p_value_type->uuid);
178 
179         if (p_value_type->value_len +  p_buf->len > payload_size ) {
180             len = payload_size - p_buf->len;
181         }
182 
183         memcpy (p, p_value_type->value, len);
184         p_buf->len += len;
185     }
186 
187     return p_buf;
188 }
189 /*******************************************************************************
190 **
191 ** Function         attp_build_read_multi_cmd
192 **
193 ** Description      Build a read multiple request
194 **
195 ** Returns          None.
196 **
197 *******************************************************************************/
attp_build_read_multi_cmd(UINT16 payload_size,UINT16 num_handle,UINT16 * p_handle)198 BT_HDR *attp_build_read_multi_cmd(UINT16 payload_size, UINT16 num_handle, UINT16 *p_handle)
199 {
200     BT_HDR      *p_buf = NULL;
201     UINT8       *p, i = 0;
202 
203     if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + num_handle * 2 + 1 + L2CAP_MIN_OFFSET))) != NULL) {
204         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
205 
206         p_buf->offset = L2CAP_MIN_OFFSET;
207         p_buf->len = 1;
208 
209         UINT8_TO_STREAM (p, GATT_REQ_READ_MULTI);
210 
211         for (i = 0; i < num_handle && p_buf->len + 2 <= payload_size; i ++) {
212             UINT16_TO_STREAM (p, *(p_handle + i));
213             p_buf->len += 2;
214         }
215     }
216 
217     return p_buf;
218 }
219 /*******************************************************************************
220 **
221 ** Function         attp_build_handle_cmd
222 **
223 ** Description      Build a read /read blob request
224 **
225 ** Returns          None.
226 **
227 *******************************************************************************/
attp_build_handle_cmd(UINT8 op_code,UINT16 handle,UINT16 offset)228 BT_HDR *attp_build_handle_cmd(UINT8 op_code, UINT16 handle, UINT16 offset)
229 {
230     BT_HDR      *p_buf = NULL;
231     UINT8       *p;
232 
233     if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 5 + L2CAP_MIN_OFFSET)) != NULL) {
234         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
235 
236         p_buf->offset = L2CAP_MIN_OFFSET;
237 
238         UINT8_TO_STREAM (p, op_code);
239         p_buf->len  = 1;
240 
241         UINT16_TO_STREAM (p, handle);
242         p_buf->len += 2;
243 
244         if (op_code == GATT_REQ_READ_BLOB) {
245             UINT16_TO_STREAM (p, offset);
246             p_buf->len += 2;
247         }
248 
249     }
250 
251     return p_buf;
252 }
253 /*******************************************************************************
254 **
255 ** Function         attp_build_opcode_cmd
256 **
257 ** Description      Build a  request/response with opcode only.
258 **
259 ** Returns          None.
260 **
261 *******************************************************************************/
attp_build_opcode_cmd(UINT8 op_code)262 BT_HDR *attp_build_opcode_cmd(UINT8 op_code)
263 {
264     BT_HDR      *p_buf = NULL;
265     UINT8       *p;
266 
267     if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR) + 1 + L2CAP_MIN_OFFSET)) != NULL) {
268         p = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
269         p_buf->offset = L2CAP_MIN_OFFSET;
270 
271         UINT8_TO_STREAM (p, op_code);
272         p_buf->len  = 1;
273     }
274 
275     return p_buf;
276 }
277 /*******************************************************************************
278 **
279 ** Function         attp_build_value_cmd
280 **
281 ** Description      Build a attribute value request
282 **
283 ** Returns          None.
284 **
285 *******************************************************************************/
attp_build_value_cmd(UINT16 payload_size,UINT8 op_code,UINT16 handle,UINT16 offset,UINT16 len,UINT8 * p_data)286 BT_HDR *attp_build_value_cmd (UINT16 payload_size, UINT8 op_code, UINT16 handle,
287                               UINT16 offset, UINT16 len, UINT8 *p_data)
288 {
289     BT_HDR      *p_buf = NULL;
290     UINT8       *p, *pp, pair_len, *p_pair_len;
291 
292     if ((p_buf = (BT_HDR *)osi_malloc((UINT16)(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET))) != NULL) {
293         p = pp = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
294 
295         UINT8_TO_STREAM (p, op_code);
296         p_buf->offset = L2CAP_MIN_OFFSET;
297         p_buf->len = 1;
298 
299         if (op_code == GATT_RSP_READ_BY_TYPE) {
300             p_pair_len = p;
301             pair_len = len + 2;
302             UINT8_TO_STREAM (p, pair_len);
303             p_buf->len += 1;
304         }
305         if (op_code != GATT_RSP_READ_BLOB && op_code != GATT_RSP_READ) {
306             UINT16_TO_STREAM (p, handle);
307             p_buf->len += 2;
308         }
309 
310         if (op_code == GATT_REQ_PREPARE_WRITE || op_code == GATT_RSP_PREPARE_WRITE ) {
311             UINT16_TO_STREAM (p, offset);
312             p_buf->len += 2;
313         }
314 
315         if (len > 0 && p_data != NULL) {
316             /* ensure data not exceed MTU size */
317             if (payload_size - p_buf->len < len) {
318                 len = payload_size - p_buf->len;
319                 /* update handle value pair length */
320                 if (op_code == GATT_RSP_READ_BY_TYPE) {
321                     *p_pair_len = (len + 2);
322                 }
323 
324                 GATT_TRACE_WARNING("attribute value too long, to be truncated to %d", len);
325             }
326 
327             ARRAY_TO_STREAM (p, p_data, len);
328             p_buf->len += len;
329         }
330     }
331     return p_buf;
332 }
333 
334 /*******************************************************************************
335 **
336 ** Function         attp_send_msg_to_l2cap
337 **
338 ** Description      Send message to L2CAP.
339 **
340 *******************************************************************************/
attp_send_msg_to_l2cap(tGATT_TCB * p_tcb,BT_HDR * p_toL2CAP)341 tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB *p_tcb, BT_HDR *p_toL2CAP)
342 {
343     UINT16      l2cap_ret;
344 
345 
346     if (p_tcb->att_lcid == L2CAP_ATT_CID) {
347         l2cap_ret = L2CA_SendFixedChnlData (L2CAP_ATT_CID, p_tcb->peer_bda, p_toL2CAP);
348     } else {
349 #if (CLASSIC_BT_INCLUDED == TRUE)
350         l2cap_ret = (UINT16) L2CA_DataWrite (p_tcb->att_lcid, p_toL2CAP);
351 #else
352         l2cap_ret = L2CAP_DW_FAILED;
353 #endif  ///CLASSIC_BT_INCLUDED == TRUE
354     }
355 
356     if (l2cap_ret == L2CAP_DW_FAILED) {
357         GATT_TRACE_DEBUG("ATT   failed to pass msg:0x%0x to L2CAP",
358                          *((UINT8 *)(p_toL2CAP + 1) + p_toL2CAP->offset));
359         return GATT_INTERNAL_ERROR;
360     } else if (l2cap_ret == L2CAP_DW_CONGESTED) {
361         GATT_TRACE_DEBUG("ATT congested, message accepted");
362         return GATT_CONGESTED;
363     }
364     return GATT_SUCCESS;
365 }
366 
367 /*******************************************************************************
368 **
369 ** Function         attp_build_sr_msg
370 **
371 ** Description      Build ATT Server PDUs.
372 **
373 *******************************************************************************/
attp_build_sr_msg(tGATT_TCB * p_tcb,UINT8 op_code,tGATT_SR_MSG * p_msg)374 BT_HDR *attp_build_sr_msg(tGATT_TCB *p_tcb, UINT8 op_code, tGATT_SR_MSG *p_msg)
375 {
376     BT_HDR          *p_cmd = NULL;
377     UINT16          offset = 0;
378 
379     switch (op_code) {
380     case GATT_RSP_READ_BLOB:
381     case GATT_RSP_PREPARE_WRITE:
382     case GATT_RSP_READ_BY_TYPE:
383     case GATT_RSP_READ:
384     case GATT_HANDLE_VALUE_NOTIF:
385     case GATT_HANDLE_VALUE_IND:
386     case GATT_RSP_ERROR:
387     case GATT_RSP_MTU:
388     /* Need to check the validation of parameter p_msg*/
389         if (p_msg == NULL) {
390             GATT_TRACE_ERROR("Invalid parameters in %s, op_code=0x%x, the p_msg should not be NULL.", __func__, op_code);
391             return NULL;
392         }
393         break;
394 
395     default:
396         break;
397     }
398 
399     switch (op_code) {
400     case GATT_RSP_READ_BLOB:
401     case GATT_RSP_PREPARE_WRITE:
402         GATT_TRACE_EVENT ("ATT_RSP_READ_BLOB/GATT_RSP_PREPARE_WRITE: len = %d offset = %d",
403                           p_msg->attr_value.len, p_msg->attr_value.offset);
404         offset = p_msg->attr_value.offset;
405     /* Coverity: [FALSE-POSITIVE error] intended fall through */
406     /* Missing break statement between cases in switch statement */
407     /* fall through */
408     case GATT_RSP_READ_BY_TYPE:
409     case GATT_RSP_READ:
410     case GATT_HANDLE_VALUE_NOTIF:
411     case GATT_HANDLE_VALUE_IND:
412         p_cmd = attp_build_value_cmd(p_tcb->payload_size,
413                                      op_code,
414                                      p_msg->attr_value.handle,
415                                      offset,
416                                      p_msg->attr_value.len,
417                                      p_msg->attr_value.value);
418         break;
419 
420     case GATT_RSP_WRITE:
421         p_cmd = attp_build_opcode_cmd(op_code);
422         break;
423 
424     case GATT_RSP_ERROR:
425         p_cmd = attp_build_err_cmd(p_msg->error.cmd_code, p_msg->error.handle, p_msg->error.reason);
426         break;
427 
428     case GATT_RSP_EXEC_WRITE:
429         p_cmd = attp_build_exec_write_cmd(op_code, 0);
430         break;
431 
432     case GATT_RSP_MTU:
433         p_cmd = attp_build_mtu_cmd(op_code, p_msg->mtu);
434         break;
435 
436     default:
437         GATT_TRACE_DEBUG("attp_build_sr_msg: unknown op code = %d", op_code);
438         break;
439     }
440 
441     if (!p_cmd) {
442         GATT_TRACE_ERROR("No resources");
443     }
444 
445     return p_cmd;
446 }
447 
448 /*******************************************************************************
449 **
450 ** Function         attp_send_sr_msg
451 **
452 ** Description      This function sends the server response or indication message
453 **                  to client.
454 **
455 ** Parameter        p_tcb: pointer to the connecton control block.
456 **                  p_msg: pointer to message parameters structure.
457 **
458 ** Returns          GATT_SUCCESS if successfully sent; otherwise error code.
459 **
460 **
461 *******************************************************************************/
attp_send_sr_msg(tGATT_TCB * p_tcb,BT_HDR * p_msg)462 tGATT_STATUS attp_send_sr_msg (tGATT_TCB *p_tcb, BT_HDR *p_msg)
463 {
464     tGATT_STATUS     cmd_sent = GATT_NO_RESOURCES;
465 
466     if (p_tcb != NULL) {
467         if (p_msg != NULL) {
468             p_msg->offset = L2CAP_MIN_OFFSET;
469             cmd_sent = attp_send_msg_to_l2cap (p_tcb, p_msg);
470         }
471     }
472     return cmd_sent;
473 }
474 
475 /*******************************************************************************
476 **
477 ** Function         attp_cl_send_cmd
478 **
479 ** Description      Send a ATT command or enqueue it.
480 **
481 ** Returns          GATT_SUCCESS if command sent
482 **                  GATT_CONGESTED if command sent but channel congested
483 **                  GATT_CMD_STARTED if command queue up in GATT
484 **                  GATT_ERROR if command sending failure
485 **
486 *******************************************************************************/
attp_cl_send_cmd(tGATT_TCB * p_tcb,UINT16 clcb_idx,UINT8 cmd_code,BT_HDR * p_cmd)487 tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
488 {
489     tGATT_STATUS att_ret = GATT_SUCCESS;
490 
491     if (p_tcb != NULL) {
492         cmd_code &= ~GATT_AUTH_SIGN_MASK;
493 
494         /* no pending request or value confirmation */
495         if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
496                 cmd_code == GATT_HANDLE_VALUE_CONF) {
497             att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd);
498             if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS) {
499                 /* do not enq cmd if handle value confirmation or set request */
500                 if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE) {
501                     gatt_start_rsp_timer (clcb_idx);
502                     gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
503                 }
504             } else {
505                 att_ret = GATT_INTERNAL_ERROR;
506             }
507         } else {
508             att_ret = GATT_CMD_STARTED;
509             gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
510         }
511     } else {
512         att_ret = GATT_ERROR;
513     }
514 
515     return att_ret;
516 }
517 /*******************************************************************************
518 **
519 ** Function         attp_send_cl_msg
520 **
521 ** Description      This function sends the client request or confirmation message
522 **                  to server.
523 **
524 ** Parameter        p_tcb: pointer to the connection control block.
525 **                  clcb_idx: clcb index
526 **                  op_code: message op code.
527 **                  p_msg: pointer to message parameters structure.
528 **
529 ** Returns          GATT_SUCCESS if successfully sent; otherwise error code.
530 **
531 **
532 *******************************************************************************/
attp_send_cl_msg(tGATT_TCB * p_tcb,UINT16 clcb_idx,UINT8 op_code,tGATT_CL_MSG * p_msg)533 tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg)
534 {
535     tGATT_STATUS     status = GATT_NO_RESOURCES;
536     BT_HDR          *p_cmd = NULL;
537     UINT16          offset = 0, handle;
538 
539     if (p_tcb != NULL) {
540         switch (op_code) {
541         case GATT_REQ_MTU:
542             if (p_msg->mtu <= GATT_MAX_MTU_SIZE) {
543                 p_tcb->payload_size = p_msg->mtu;
544                 p_cmd = attp_build_mtu_cmd(GATT_REQ_MTU, p_msg->mtu);
545             } else {
546                 status = GATT_ILLEGAL_PARAMETER;
547             }
548             break;
549 
550         case GATT_REQ_FIND_INFO:
551         case GATT_REQ_READ_BY_TYPE:
552         case GATT_REQ_READ_BY_GRP_TYPE:
553             if (GATT_HANDLE_IS_VALID (p_msg->browse.s_handle) &&
554                     GATT_HANDLE_IS_VALID (p_msg->browse.e_handle)  &&
555                     p_msg->browse.s_handle <= p_msg->browse.e_handle) {
556                 p_cmd = attp_build_browse_cmd(op_code,
557                                               p_msg->browse.s_handle,
558                                               p_msg->browse.e_handle,
559                                               p_msg->browse.uuid);
560             } else {
561                 status = GATT_ILLEGAL_PARAMETER;
562             }
563             break;
564 
565         case GATT_REQ_READ_BLOB:
566             offset = p_msg->read_blob.offset;
567         /* fall through */
568         case GATT_REQ_READ:
569             handle = (op_code == GATT_REQ_READ) ? p_msg->handle : p_msg->read_blob.handle;
570             /*  handle checking */
571             if (GATT_HANDLE_IS_VALID (handle)) {
572                 p_cmd = attp_build_handle_cmd(op_code, handle, offset);
573             } else {
574                 status = GATT_ILLEGAL_PARAMETER;
575             }
576             break;
577 
578         case GATT_HANDLE_VALUE_CONF:
579             p_cmd = attp_build_opcode_cmd(op_code);
580             break;
581 
582         case GATT_REQ_PREPARE_WRITE:
583             offset = p_msg->attr_value.offset;
584         /* fall through */
585         case GATT_REQ_WRITE:
586         case GATT_CMD_WRITE:
587         case GATT_SIGN_CMD_WRITE:
588             if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle)) {
589                 p_cmd = attp_build_value_cmd (p_tcb->payload_size,
590                                               op_code, p_msg->attr_value.handle,
591                                               offset,
592                                               p_msg->attr_value.len,
593                                               p_msg->attr_value.value);
594             } else {
595                 status = GATT_ILLEGAL_PARAMETER;
596             }
597             break;
598 
599         case GATT_REQ_EXEC_WRITE:
600             p_cmd = attp_build_exec_write_cmd(op_code, p_msg->exec_write);
601             break;
602 
603         case GATT_REQ_FIND_TYPE_VALUE:
604             p_cmd = attp_build_read_by_type_value_cmd(p_tcb->payload_size, &p_msg->find_type_value);
605             break;
606 
607         case GATT_REQ_READ_MULTI:
608             p_cmd = attp_build_read_multi_cmd(p_tcb->payload_size,
609                                               p_msg->read_multi.num_handles,
610                                               p_msg->read_multi.handles);
611             break;
612 
613         default:
614             break;
615         }
616 
617         if (p_cmd != NULL) {
618             status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd);
619         }
620 
621     } else {
622         GATT_TRACE_ERROR("Peer device not connected");
623     }
624 
625     return status;
626 }
627 #endif
628