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