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