1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/types.h>
8 #include <stddef.h>
9 #include <string.h>
10 #include <errno.h>
11 #include <zephyr/sys/printk.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/check.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/crc.h>
16
17 #include <zephyr/bluetooth/gatt.h>
18 #include <zephyr/bluetooth/services/ots.h>
19 #include "ots_internal.h"
20 #include "ots_dir_list_internal.h"
21 #include "ots_obj_manager_internal.h"
22
23 #include <zephyr/logging/log.h>
24
25 LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
26
27 #define OACP_PROC_TYPE_SIZE 1
28
29 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
30 static ssize_t oacp_write_proc_cb(struct bt_gatt_ots_l2cap *l2cap_ctx,
31 struct bt_conn *conn, struct net_buf *buf);
32 #endif
33
oacp_l2cap_closed(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn)34 static void oacp_l2cap_closed(struct bt_gatt_ots_l2cap *l2cap_ctx,
35 struct bt_conn *conn)
36 {
37 struct bt_ots *ots;
38
39 ots = CONTAINER_OF(l2cap_ctx, struct bt_ots, l2cap);
40
41 if (!ots->cur_obj) {
42 return;
43 }
44
45 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
46 l2cap_ctx->rx_done = NULL;
47 l2cap_ctx->tx_done = NULL;
48 }
49
50 #if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
oacp_create_proc_validate(struct bt_conn * conn,struct bt_ots * ots,struct bt_gatt_ots_oacp_proc * proc)51 static enum bt_gatt_ots_oacp_res_code oacp_create_proc_validate(
52 struct bt_conn *conn,
53 struct bt_ots *ots,
54 struct bt_gatt_ots_oacp_proc *proc)
55 {
56 char str[BT_UUID_STR_LEN];
57 int err;
58 struct bt_gatt_ots_object *obj;
59 const struct bt_ots_obj_add_param param = {
60 .size = proc->create_params.size,
61 .type = proc->create_params.type,
62 };
63
64 bt_uuid_to_str(¶m.type.uuid, str, BT_UUID_STR_LEN);
65 LOG_DBG("Validating Create procedure with size: 0x%08X and "
66 "type: %s", param.size, str);
67
68 if (!BT_OTS_OACP_GET_FEAT_CREATE(ots->features.oacp)) {
69 LOG_DBG("Create Procedure is not supported.");
70 return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
71 }
72
73 err = bt_ots_obj_add_internal(ots, conn, ¶m, &obj);
74 if (err) {
75 goto exit;
76 }
77
78 /* Verify Initialization Metadata */
79 if (strlen(obj->metadata.name) > 0) {
80 LOG_ERR("Object name shall be a zero length string after object creation.");
81 (void)bt_ots_obj_delete(ots, obj->id);
82 err = -ECANCELED;
83 goto exit;
84 }
85
86 if (obj->metadata.size.cur > 0) {
87 LOG_ERR("Object current size must be 0.");
88 (void)bt_ots_obj_delete(ots, obj->id);
89 err = -ECANCELED;
90 goto exit;
91 }
92
93 if (!BT_OTS_OBJ_GET_PROP_WRITE(obj->metadata.props)) {
94 LOG_ERR("Created object must have write property.");
95 (void)bt_ots_obj_delete(ots, obj->id);
96 err = -ECANCELED;
97 goto exit;
98 }
99
100 ots->cur_obj = obj;
101 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
102
103 LOG_DBG("Create procedure is complete");
104
105 exit:
106 switch (err) {
107 case 0:
108 return BT_GATT_OTS_OACP_RES_SUCCESS;
109 case -ENOTSUP:
110 return BT_GATT_OTS_OACP_RES_UNSUP_TYPE;
111 case -ENOMEM:
112 return BT_GATT_OTS_OACP_RES_INSUFF_RES;
113 case -EINVAL:
114 return BT_GATT_OTS_OACP_RES_INV_PARAM;
115 case -ECANCELED:
116 default:
117 return BT_GATT_OTS_OACP_RES_OPER_FAILED;
118 }
119 }
120 #endif
121
122 #if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
oacp_delete_proc_validate(struct bt_conn * conn,struct bt_ots * ots,struct bt_gatt_ots_oacp_proc * proc)123 static enum bt_gatt_ots_oacp_res_code oacp_delete_proc_validate(
124 struct bt_conn *conn,
125 struct bt_ots *ots,
126 struct bt_gatt_ots_oacp_proc *proc)
127 {
128 int err;
129
130 if (!BT_OTS_OACP_GET_FEAT_DELETE(ots->features.oacp)) {
131 LOG_DBG("Delete Procedure is not supported.");
132 return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
133 }
134
135 if (!ots->cur_obj) {
136 LOG_DBG("No object is selected.");
137 return BT_GATT_OTS_OACP_RES_INV_OBJ;
138 }
139
140 if (!BT_OTS_OBJ_GET_PROP_DELETE(ots->cur_obj->metadata.props)) {
141 LOG_DBG("Object properties do not permit deletion.");
142 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
143 }
144
145 err = bt_ots_obj_delete(ots, ots->cur_obj->id);
146 if (err) {
147 LOG_ERR("Deleting object during Delete procedure failed: %d", err);
148 goto exit;
149 }
150
151 LOG_DBG("Delete procedure is complete");
152
153 exit:
154 switch (err) {
155 case 0:
156 return BT_GATT_OTS_OACP_RES_SUCCESS;
157 case -EBUSY:
158 return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
159 default:
160 return BT_GATT_OTS_OACP_RES_OPER_FAILED;
161 }
162 }
163 #endif
164
165 #if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
oacp_checksum_proc_validate(struct bt_conn * conn,struct bt_ots * ots,struct bt_gatt_ots_oacp_proc * proc,struct net_buf_simple * resp_param)166 static enum bt_gatt_ots_oacp_res_code oacp_checksum_proc_validate(
167 struct bt_conn *conn,
168 struct bt_ots *ots,
169 struct bt_gatt_ots_oacp_proc *proc,
170 struct net_buf_simple *resp_param)
171 {
172 struct bt_gatt_ots_oacp_cs_calc_params *params = &proc->cs_calc_params;
173 void *obj_data;
174 int err;
175 uint32_t checksum;
176
177 LOG_DBG("Validating Checksum procedure with offset: 0x%08X and "
178 "length: 0x%08X", params->offset, params->len);
179
180 if (!ots->cur_obj) {
181 return BT_GATT_OTS_OACP_RES_INV_OBJ;
182 }
183
184 if (params->offset > ots->cur_obj->metadata.size.cur) {
185 return BT_GATT_OTS_OACP_RES_INV_PARAM;
186 }
187
188 if ((params->offset + (uint64_t) params->len) > ots->cur_obj->metadata.size.alloc) {
189 return BT_GATT_OTS_OACP_RES_INV_PARAM;
190 }
191
192 if (ots->cur_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
193 return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
194 }
195
196 if (ots->cb->obj_cal_checksum) {
197 err = ots->cb->obj_cal_checksum(ots, conn, ots->cur_obj->id, params->offset,
198 params->len, &obj_data);
199 if (err != 0) {
200 return BT_GATT_OTS_OACP_RES_OPER_FAILED;
201 }
202
203 checksum = bt_ots_client_calc_checksum((const uint8_t *)obj_data, params->len);
204 net_buf_simple_add_le32(resp_param, checksum);
205 LOG_DBG("Calculate from offset %u len %u checksum 0x%08x\n", params->offset,
206 params->len, checksum);
207 return BT_GATT_OTS_OACP_RES_SUCCESS;
208 } else {
209 return BT_GATT_OTS_OACP_RES_OPER_FAILED;
210 }
211 }
212 #endif
213
oacp_read_proc_validate(struct bt_conn * conn,struct bt_ots * ots,const struct bt_gatt_ots_oacp_proc * proc)214 static enum bt_gatt_ots_oacp_res_code oacp_read_proc_validate(
215 struct bt_conn *conn,
216 struct bt_ots *ots,
217 const struct bt_gatt_ots_oacp_proc *proc)
218 {
219 const struct bt_gatt_ots_oacp_read_params *params = &proc->read_params;
220
221 LOG_DBG("Validating Read procedure with offset: 0x%08X and "
222 "length: 0x%08X", params->offset, params->len);
223
224 if (!ots->cur_obj) {
225 return BT_GATT_OTS_OACP_RES_INV_OBJ;
226 }
227
228 if (!BT_OTS_OBJ_GET_PROP_READ(ots->cur_obj->metadata.props)) {
229 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
230 }
231
232 if (!bt_gatt_ots_l2cap_is_open(&ots->l2cap, conn)) {
233 return BT_GATT_OTS_OACP_RES_CHAN_UNAVAIL;
234 }
235
236 if ((params->offset + (uint64_t) params->len) >
237 ots->cur_obj->metadata.size.cur) {
238 return BT_GATT_OTS_OACP_RES_INV_PARAM;
239 }
240
241 if (ots->cur_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
242 return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
243 }
244
245 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_READ_OP_STATE;
246 ots->cur_obj->state.read_op.sent_len = 0;
247 memcpy(&ots->cur_obj->state.read_op.oacp_params, &proc->read_params,
248 sizeof(ots->cur_obj->state.read_op.oacp_params));
249
250 LOG_DBG("Read procedure is accepted");
251
252 return BT_GATT_OTS_OACP_RES_SUCCESS;
253 }
254
255 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
oacp_write_proc_validate(struct bt_conn * conn,struct bt_ots * ots,struct bt_gatt_ots_oacp_proc * proc)256 static enum bt_gatt_ots_oacp_res_code oacp_write_proc_validate(
257 struct bt_conn *conn,
258 struct bt_ots *ots,
259 struct bt_gatt_ots_oacp_proc *proc)
260 {
261 struct bt_gatt_ots_oacp_write_params *params = &proc->write_params;
262
263 LOG_DBG("Validating Write procedure with offset: 0x%08X and "
264 "length: 0x%08X", params->offset, params->len);
265
266 if (!ots->cur_obj) {
267 return BT_GATT_OTS_OACP_RES_INV_OBJ;
268 }
269
270 if (!BT_OTS_OBJ_GET_PROP_WRITE(ots->cur_obj->metadata.props)) {
271 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
272 }
273
274 /* patching is attempted */
275 if (params->offset < ots->cur_obj->metadata.size.cur) {
276 if (!BT_OTS_OACP_GET_FEAT_PATCH(ots->features.oacp)) {
277 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
278 }
279 if (!BT_OTS_OBJ_GET_PROP_PATCH(ots->cur_obj->metadata.props)) {
280 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
281 }
282 }
283
284 /* truncation is not supported */
285 if (BT_GATT_OTS_OACP_PROC_WRITE_MODE_GET_TRUNC(params->mode)) {
286 return BT_GATT_OTS_OACP_RES_NOT_PERMITTED;
287 }
288
289 if (!bt_gatt_ots_l2cap_is_open(&ots->l2cap, conn)) {
290 return BT_GATT_OTS_OACP_RES_CHAN_UNAVAIL;
291 }
292
293 if (BT_GATT_OTS_OACP_PROC_WRITE_MODE_GET_RFU(params->mode)) {
294 return BT_GATT_OTS_OACP_RES_INV_PARAM;
295 }
296
297 if (params->offset > ots->cur_obj->metadata.size.cur) {
298 return BT_GATT_OTS_OACP_RES_INV_PARAM;
299 }
300
301 /* append is not supported */
302 if ((params->offset + (uint64_t) params->len) > ots->cur_obj->metadata.size.alloc) {
303 return BT_GATT_OTS_OACP_RES_INV_PARAM;
304 }
305
306 if (ots->cur_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
307 return BT_GATT_OTS_OACP_RES_OBJ_LOCKED;
308 }
309
310 ots->l2cap.rx_done = oacp_write_proc_cb;
311 ots->l2cap.closed = oacp_l2cap_closed;
312 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_WRITE_OP_STATE;
313 ots->cur_obj->state.write_op.recv_len = 0;
314 memcpy(&ots->cur_obj->state.write_op.oacp_params, params,
315 sizeof(ots->cur_obj->state.write_op.oacp_params));
316
317 LOG_DBG("Write procedure is accepted");
318
319 return BT_GATT_OTS_OACP_RES_SUCCESS;
320 }
321 #endif
322
oacp_proc_validate(struct bt_conn * conn,struct bt_ots * ots,struct bt_gatt_ots_oacp_proc * proc,struct net_buf_simple * resp_param)323 static enum bt_gatt_ots_oacp_res_code oacp_proc_validate(
324 struct bt_conn *conn,
325 struct bt_ots *ots,
326 struct bt_gatt_ots_oacp_proc *proc,
327 struct net_buf_simple *resp_param)
328 {
329 switch (proc->type) {
330 case BT_GATT_OTS_OACP_PROC_READ:
331 return oacp_read_proc_validate(conn, ots, proc);
332 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
333 case BT_GATT_OTS_OACP_PROC_WRITE:
334 return oacp_write_proc_validate(conn, ots, proc);
335 #endif
336 #if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
337 case BT_GATT_OTS_OACP_PROC_CREATE:
338 return oacp_create_proc_validate(conn, ots, proc);
339 #endif
340 #if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
341 case BT_GATT_OTS_OACP_PROC_DELETE:
342 return oacp_delete_proc_validate(conn, ots, proc);
343 #endif
344 #if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
345 case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
346 return oacp_checksum_proc_validate(conn, ots, proc, resp_param);
347 #endif
348 case BT_GATT_OTS_OACP_PROC_EXECUTE:
349 case BT_GATT_OTS_OACP_PROC_ABORT:
350 default:
351 return BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
352 }
353 };
354
oacp_command_decode(const uint8_t * buf,uint16_t len,struct bt_gatt_ots_oacp_proc * proc)355 static int oacp_command_decode(const uint8_t *buf, uint16_t len,
356 struct bt_gatt_ots_oacp_proc *proc)
357 {
358 struct net_buf_simple net_buf;
359
360 if (len < OACP_PROC_TYPE_SIZE) {
361 return -ENODATA;
362 }
363
364 net_buf_simple_init_with_data(&net_buf, (void *) buf, len);
365
366 proc->type = net_buf_simple_pull_u8(&net_buf);
367 switch (proc->type) {
368 #if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
369 case BT_GATT_OTS_OACP_PROC_CREATE:
370 if (net_buf.len < BT_GATT_OTS_OACP_CREATE_GENERIC_PARAMS_SIZE) {
371 return -EBADMSG;
372 }
373 proc->create_params.size = net_buf_simple_pull_le32(&net_buf);
374 if (!bt_uuid_create(&proc->create_params.type.uuid, net_buf.data,
375 net_buf.len)) {
376 return -EBADMSG;
377 }
378 net_buf_simple_pull_mem(&net_buf, net_buf.len);
379
380 /* Only 16-bit and 128-bit UUIDs are supported */
381 switch (proc->create_params.type.uuid.type) {
382 case BT_UUID_TYPE_16:
383 case BT_UUID_TYPE_128:
384 return 0;
385 default:
386 break;
387 }
388
389 return -EBADMSG;
390 #endif
391 #if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
392 case BT_GATT_OTS_OACP_PROC_DELETE:
393 if (net_buf.len != 0) {
394 return -EBADMSG;
395 }
396
397 return 0;
398 #endif
399 case BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC:
400 if (net_buf.len != BT_GATT_OTS_OACP_CS_CALC_PARAMS_SIZE) {
401 return -EBADMSG;
402 }
403 proc->cs_calc_params.offset =
404 net_buf_simple_pull_le32(&net_buf);
405 proc->cs_calc_params.len =
406 net_buf_simple_pull_le32(&net_buf);
407
408 return 0;
409 case BT_GATT_OTS_OACP_PROC_EXECUTE:
410 if (net_buf.len != 0) {
411 return -EBADMSG;
412 }
413
414 return 0;
415 case BT_GATT_OTS_OACP_PROC_READ:
416 if (net_buf.len != BT_GATT_OTS_OACP_READ_PARAMS_SIZE) {
417 return -EBADMSG;
418 }
419 proc->read_params.offset =
420 net_buf_simple_pull_le32(&net_buf);
421 proc->read_params.len =
422 net_buf_simple_pull_le32(&net_buf);
423
424 return 0;
425 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
426 case BT_GATT_OTS_OACP_PROC_WRITE:
427 if (net_buf.len != BT_GATT_OTS_OACP_WRITE_PARAMS_SIZE) {
428 return -EBADMSG;
429 }
430 proc->write_params.offset =
431 net_buf_simple_pull_le32(&net_buf);
432 proc->write_params.len =
433 net_buf_simple_pull_le32(&net_buf);
434 proc->write_params.mode =
435 net_buf_simple_pull_u8(&net_buf);
436
437 return 0;
438 #endif
439 case BT_GATT_OTS_OACP_PROC_ABORT:
440 default:
441 break;
442 }
443
444 return -ENOTSUP;
445 }
446
oacp_read_proc_cb(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn)447 static void oacp_read_proc_cb(struct bt_gatt_ots_l2cap *l2cap_ctx,
448 struct bt_conn *conn)
449 {
450 int err;
451 void *obj_chunk;
452 off_t offset;
453 ssize_t len;
454 struct bt_ots *ots;
455 struct bt_gatt_ots_object_read_op *read_op;
456
457 ots = CONTAINER_OF(l2cap_ctx, struct bt_ots, l2cap);
458
459 if (ots->cb->obj_read == NULL &&
460 !(IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST)) {
461 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
462 LOG_ERR("OTS Read operation failed: there is no OTS Read callback");
463
464 return;
465 }
466
467 read_op = &ots->cur_obj->state.read_op;
468 offset = read_op->oacp_params.offset + read_op->sent_len;
469
470 if (read_op->sent_len >= read_op->oacp_params.len) {
471 LOG_DBG("OACP Read Op over L2CAP is completed");
472
473 if (read_op->sent_len > read_op->oacp_params.len) {
474 LOG_WRN("More bytes sent that the client requested");
475 }
476
477 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
478
479 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
480 ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST) {
481 return;
482 }
483
484 ots->cb->obj_read(ots, conn, ots->cur_obj->id, NULL, 0,
485 offset);
486 return;
487 }
488
489 len = read_op->oacp_params.len - read_op->sent_len;
490 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
491 ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST) {
492 len = bt_ots_dir_list_content_get(ots->dir_list, ots->obj_manager,
493 &obj_chunk, len, offset);
494 } else {
495 len = ots->cb->obj_read(ots, conn, ots->cur_obj->id, &obj_chunk,
496 len, offset);
497 }
498
499 if (len < 0) {
500 LOG_ERR("OCAP Read Op failed with error: %zd", len);
501
502 bt_gatt_ots_l2cap_disconnect(&ots->l2cap);
503 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
504
505 return;
506 }
507
508 ots->l2cap.tx_done = oacp_read_proc_cb;
509 ots->l2cap.closed = oacp_l2cap_closed;
510 err = bt_gatt_ots_l2cap_send(&ots->l2cap, obj_chunk, len);
511 if (err) {
512 LOG_ERR("L2CAP CoC error: %d while trying to execute OACP "
513 "Read procedure", err);
514 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
515 } else {
516 read_op->sent_len += len;
517 }
518 }
519
oacp_read_proc_execute(struct bt_ots * ots,struct bt_conn * conn)520 static void oacp_read_proc_execute(struct bt_ots *ots,
521 struct bt_conn *conn)
522 {
523 struct bt_gatt_ots_oacp_read_params *params =
524 &ots->cur_obj->state.read_op.oacp_params;
525
526 if (!ots->cur_obj) {
527 LOG_ERR("Invalid Current Object on OACP Read procedure");
528 return;
529 }
530
531 LOG_DBG("Executing Read procedure with offset: 0x%08X and "
532 "length: 0x%08X", params->offset, params->len);
533
534 oacp_read_proc_cb(&ots->l2cap, conn);
535 }
536
537 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
oacp_write_proc_cb(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn,struct net_buf * buf)538 static ssize_t oacp_write_proc_cb(struct bt_gatt_ots_l2cap *l2cap_ctx,
539 struct bt_conn *conn, struct net_buf *buf)
540 {
541 struct bt_gatt_ots_object_write_op *write_op;
542 struct bt_ots *ots;
543 off_t offset;
544 size_t rem;
545 size_t len;
546 ssize_t rc;
547
548 ots = CONTAINER_OF(l2cap_ctx, struct bt_ots, l2cap);
549
550 if (!ots->cur_obj) {
551 LOG_ERR("Invalid Current Object on OACP Write procedure");
552 return -ENODEV;
553 }
554
555 if (!ots->cb->obj_write) {
556 LOG_ERR("OTS Write operation failed: "
557 "there is no OTS Write callback");
558 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
559 return -ENODEV;
560 }
561
562 write_op = &ots->cur_obj->state.write_op;
563 offset = write_op->oacp_params.offset + write_op->recv_len;
564 len = buf->len;
565 if (write_op->recv_len + len > write_op->oacp_params.len) {
566 LOG_WRN("More bytes received than the client indicated");
567 len = write_op->oacp_params.len - write_op->recv_len;
568 }
569 rem = write_op->oacp_params.len - (write_op->recv_len + len);
570
571 rc = ots->cb->obj_write(ots, conn, ots->cur_obj->id, buf->data, len,
572 offset, rem);
573
574 if (rc < 0) {
575 len = 0;
576
577 /*
578 * Returning an EINPROGRESS return code results in the write buffer not being
579 * released by the l2cap layer. This is an unsupported use case at the moment.
580 */
581 if (rc == -EINPROGRESS) {
582 LOG_ERR("Unsupported error code %zd returned by object write callback", rc);
583 }
584
585 LOG_ERR("OTS Write operation failed with error: %zd", rc);
586 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
587 } else {
588 /* Return -EIO as an error if all of data was not written */
589 if (rc != len) {
590 len = rc;
591 rc = -EIO;
592 }
593 }
594
595 write_op->recv_len += len;
596 if (write_op->recv_len == write_op->oacp_params.len) {
597 LOG_DBG("OACP Write Op over L2CAP is completed");
598 ots->cur_obj->state.type = BT_GATT_OTS_OBJECT_IDLE_STATE;
599 }
600
601 if (offset + len > ots->cur_obj->metadata.size.cur) {
602 ots->cur_obj->metadata.size.cur = offset + len;
603 }
604
605 return rc;
606 }
607 #endif
608
oacp_ind_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)609 static void oacp_ind_cb(struct bt_conn *conn,
610 struct bt_gatt_indicate_params *params,
611 uint8_t err)
612 {
613 struct bt_ots *ots = (struct bt_ots *) params->attr->user_data;
614
615 LOG_DBG("Received OACP Indication ACK with status: 0x%04X", err);
616
617 if (!ots->cur_obj) {
618 LOG_DBG("There is no object associated with this ACK");
619 return;
620 }
621
622 switch (ots->cur_obj->state.type) {
623 case BT_GATT_OTS_OBJECT_READ_OP_STATE:
624 oacp_read_proc_execute(ots, conn);
625 break;
626 case BT_GATT_OTS_OBJECT_WRITE_OP_STATE:
627 /* procedure execution is driven by L2CAP socket receive */
628 break;
629 case BT_GATT_OTS_OBJECT_IDLE_STATE:
630 /* procedure is not in progress and was already completed */
631 break;
632 default:
633 LOG_ERR("Unsupported OTS state: %d", ots->cur_obj->state.type);
634 break;
635 }
636 }
637
oacp_ind_send(const struct bt_gatt_attr * oacp_attr,struct bt_gatt_ots_oacp_proc oacp_proc,enum bt_gatt_ots_oacp_res_code oacp_status,struct net_buf_simple * resp_param)638 static void oacp_ind_send(const struct bt_gatt_attr *oacp_attr,
639 struct bt_gatt_ots_oacp_proc oacp_proc,
640 enum bt_gatt_ots_oacp_res_code oacp_status,
641 struct net_buf_simple *resp_param)
642 {
643 struct bt_ots *ots = (struct bt_ots *) oacp_attr->user_data;
644 uint8_t *oacp_res = ots->oacp_ind.res;
645 uint16_t oacp_res_len = 0;
646
647 /* Encode OACP Response */
648 oacp_res[oacp_res_len++] = BT_GATT_OTS_OACP_PROC_RESP;
649 oacp_res[oacp_res_len++] = oacp_proc.type;
650 oacp_res[oacp_res_len++] = oacp_status;
651
652 if (oacp_proc.type == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC) {
653 sys_put_le32(net_buf_simple_pull_le32(resp_param), (oacp_res + oacp_res_len));
654 oacp_res_len += sizeof(uint32_t);
655 }
656
657 /* Prepare indication parameters */
658 memset(&ots->oacp_ind.params, 0, sizeof(ots->oacp_ind.params));
659 memcpy(&ots->oacp_ind.attr, oacp_attr, sizeof(ots->oacp_ind.attr));
660 ots->oacp_ind.params.attr = &ots->oacp_ind.attr;
661 ots->oacp_ind.params.func = oacp_ind_cb;
662 ots->oacp_ind.params.data = oacp_res;
663 ots->oacp_ind.params.len = oacp_res_len;
664
665 LOG_DBG("Sending OACP indication");
666
667
668 k_work_submit(&ots->oacp_ind.work);
669 }
670
bt_gatt_ots_oacp_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)671 ssize_t bt_gatt_ots_oacp_write(struct bt_conn *conn,
672 const struct bt_gatt_attr *attr,
673 const void *buf, uint16_t len,
674 uint16_t offset, uint8_t flags)
675 {
676 enum bt_gatt_ots_oacp_res_code oacp_status;
677 int decode_status;
678 struct bt_gatt_ots_oacp_proc oacp_proc = {0};
679 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
680 NET_BUF_SIMPLE_DEFINE(resp_param, sizeof(uint32_t));
681
682 LOG_DBG("Object Action Control Point GATT Write Operation");
683
684 if (!ots->oacp_ind.is_enabled) {
685 LOG_WRN("OACP indications not enabled");
686 return BT_GATT_ERR(BT_ATT_ERR_CCC_IMPROPER_CONF);
687 }
688
689 if (offset != 0) {
690 LOG_ERR("Invalid offset of OACP Write Request");
691 return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
692 }
693
694 if (k_work_is_pending(&ots->oacp_ind.work)) {
695 LOG_ERR("OACP Write received before indication sent");
696 return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
697 }
698
699 decode_status = oacp_command_decode(buf, len, &oacp_proc);
700 switch (decode_status) {
701 case 0:
702 oacp_status = oacp_proc_validate(conn, ots, &oacp_proc, &resp_param);
703 if (oacp_status != BT_GATT_OTS_OACP_RES_SUCCESS) {
704 LOG_WRN("OACP Write error status: 0x%02X", oacp_status);
705 }
706 break;
707 case -ENOTSUP:
708 oacp_status = BT_GATT_OTS_OACP_RES_OPCODE_NOT_SUP;
709 LOG_WRN("OACP unsupported procedure type: 0x%02X", oacp_proc.type);
710 break;
711 case -EBADMSG:
712 LOG_ERR("Invalid length of OACP Write Request for 0x%02X "
713 "Op Code", oacp_proc.type);
714 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
715 case -ENODATA:
716 LOG_ERR("Invalid length of OACP Write Request");
717 return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
718 default:
719 LOG_ERR("Invalid return code from oacp_command_decode: %d", decode_status);
720 return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
721 }
722
723 oacp_ind_send(attr, oacp_proc, oacp_status, &resp_param);
724 return len;
725 }
726
bt_gatt_ots_oacp_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)727 void bt_gatt_ots_oacp_cfg_changed(const struct bt_gatt_attr *attr,
728 uint16_t value)
729 {
730 struct bt_gatt_ots_indicate *oacp_ind =
731 CONTAINER_OF((struct _bt_gatt_ccc *) attr->user_data,
732 struct bt_gatt_ots_indicate, ccc);
733
734 LOG_DBG("Object Action Control Point CCCD value: 0x%04X", value);
735
736 oacp_ind->is_enabled = false;
737 if (value == BT_GATT_CCC_INDICATE) {
738 oacp_ind->is_enabled = true;
739 }
740 }
741