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