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