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