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