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