1 /** @file
2 * @brief Bluetooth Object Transfer Client
3 *
4 * Copyright (c) 2020-2022 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/types.h>
11
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/sys/check.h>
16
17 #include <zephyr/bluetooth/bluetooth.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/bluetooth/gatt.h>
20 #include <zephyr/bluetooth/l2cap.h>
21
22 #include <zephyr/bluetooth/services/ots.h>
23 #include "ots_internal.h"
24 #include "ots_client_internal.h"
25 #include "ots_l2cap_internal.h"
26 #include "ots_dir_list_internal.h"
27 #include "ots_oacp_internal.h"
28 #include "ots_olcp_internal.h"
29
30 #define LOG_LEVEL CONFIG_BT_OTS_CLIENT_LOG_LEVEL
31 #include <zephyr/logging/log.h>
32 LOG_MODULE_REGISTER(bt_otc);
33
34 /* TODO: KConfig options */
35 #define OTS_CLIENT_INST_COUNT 1
36
37 #define OTS_CLIENT_MAX_WRITE_SIZE 23
38 /* 64-bit value, outside of 48-bit Object ID range */
39 #define OTS_CLIENT_UNKNOWN_ID 0x0001000000000000
40
41 struct dirlisting_record_t {
42 uint16_t len;
43 uint8_t flags;
44 uint8_t name_len;
45 struct bt_ots_obj_metadata metadata;
46 };
47
48 /**@brief String literals for the OACP result codes. Used for logging output.*/
49 static const char * const lit_request[] = {
50 "RFU",
51 "Create",
52 "Delete",
53 "Calculate Checksum",
54 "Execute",
55 "Read",
56 "Write",
57 "Abort",
58 };
59
60 /**@brief String literals for the OACP result codes. Used for logging output.*/
61 static const char * const lit_result[] = {
62 "RFU",
63 "Success",
64 "Op Code Not Supported",
65 "Invalid Parameter",
66 "Insufficient Resources",
67 "Invalid Object",
68 "Channel Unavailable",
69 "Unsupported Type",
70 "Procedure Not Permitted",
71 "Object Locked",
72 "Operation Failed"
73 };
74
75 /**@brief String literals for the OLCP request codes. Used for logging output.*/
76 static const char * const lit_olcp_request[] = {
77 "RFU",
78 "FIRST",
79 "LAST",
80 "PREV",
81 "NEXT",
82 "GOTO",
83 "ORDER",
84 "REQ_NUM_OBJS",
85 "CLEAR_MARKING",
86 };
87
88 /**@brief String literals for the OLCP result codes. Used for logging output.*/
89 static const char * const lit_olcp_result[] = {
90 "RFU",
91 "Success",
92 "Op Code Not Supported",
93 "Invalid Parameter",
94 "Operation Failed",
95 "Out of Bonds",
96 "Too Many Objects",
97 "No Object",
98 "Object ID not found",
99 };
100
101 struct bt_otc_internal_instance_t {
102 struct bt_ots_client *otc_inst;
103 struct bt_gatt_ots_l2cap l2cap_ctx;
104 bool busy;
105 /** Bitfield that is used to determine how much metadata to read */
106 uint8_t metadata_to_read;
107 /** Bitfield of how much metadata has been attempted to read */
108 uint8_t metadata_read_attempted;
109 /** Bitfield of how much metadata has been read */
110 uint8_t metadata_read;
111 int metadata_err;
112 uint32_t rcvd_size;
113 uint32_t sent_size;
114 };
115
116 /* The profile clients that uses the OTS are responsible for discovery and
117 * will simply register any OTS instances as pointers, which is stored here.
118 */
119 static struct bt_otc_internal_instance_t otc_insts[OTS_CLIENT_INST_COUNT];
120 NET_BUF_SIMPLE_DEFINE_STATIC(otc_tx_buf, OTS_CLIENT_MAX_WRITE_SIZE);
121 static struct bt_otc_internal_instance_t *cur_inst;
122
123 static int oacp_read(struct bt_conn *conn,
124 struct bt_otc_internal_instance_t *inst);
125 static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
126 const void *buf, uint32_t len, uint32_t offset,
127 enum bt_ots_oacp_write_op_mode mode);
128 static int oacp_checksum(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
129 uint32_t offset, uint32_t len);
130 static void read_next_metadata(struct bt_conn *conn,
131 struct bt_otc_internal_instance_t *inst);
132 static int read_attr(struct bt_conn *conn,
133 struct bt_otc_internal_instance_t *inst,
134 uint16_t handle, bt_gatt_read_func_t cb);
135
136 /* L2CAP callbacks */
tx_done(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn)137 static void tx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
138 struct bt_conn *conn)
139 {
140 /* Not doing any writes yet */
141 LOG_ERR("Unexpected call, context: %p, conn: %p", l2cap_ctx, (void *)conn);
142 }
143
write_obj_tx_done(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn)144 static void write_obj_tx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
145 struct bt_conn *conn)
146 {
147 int err;
148 size_t written;
149
150 if (cur_inst == NULL) {
151 LOG_ERR("OTS instance invalid\n");
152 return;
153 }
154
155 written = cur_inst->sent_size;
156 LOG_DBG("ctx: %p, conn: %p, written: %d", l2cap_ctx, (void *)conn, written);
157
158 err = bt_gatt_ots_l2cap_disconnect(l2cap_ctx);
159 if (err < 0) {
160 LOG_WRN("Disconnecting L2CAP returned error %d", err);
161 }
162
163 if ((cur_inst->otc_inst != NULL) && (cur_inst->otc_inst->cb != NULL)) {
164 if (cur_inst->otc_inst->cb->obj_data_written) {
165 cur_inst->otc_inst->cb->obj_data_written(0, conn, written);
166 }
167 }
168
169 cur_inst = NULL;
170 }
171
rx_done(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn,struct net_buf * buf)172 static ssize_t rx_done(struct bt_gatt_ots_l2cap *l2cap_ctx,
173 struct bt_conn *conn, struct net_buf *buf)
174 {
175 const uint32_t offset = cur_inst->rcvd_size;
176 bool is_complete = false;
177 const struct bt_ots_obj_metadata *cur_object =
178 &cur_inst->otc_inst->cur_object;
179 int cb_ret;
180
181 LOG_DBG("Incoming L2CAP data, context: %p, conn: %p, len: %u, offset: %u", l2cap_ctx,
182 (void *)conn, buf->len, offset);
183
184 cur_inst->rcvd_size += buf->len;
185
186 if (cur_inst->rcvd_size >= cur_object->size.cur) {
187 is_complete = true;
188 }
189
190 if (cur_inst->rcvd_size > cur_object->size.cur) {
191 LOG_WRN("Received %u but expected maximum %u", cur_inst->rcvd_size,
192 cur_object->size.cur);
193 }
194
195 cb_ret = cur_inst->otc_inst->cb->obj_data_read(0, conn, offset,
196 buf->len, buf->data,
197 is_complete);
198
199 if (is_complete) {
200 const uint32_t rcv_size = cur_object->size.cur;
201 int err;
202
203 LOG_DBG("Received the whole object (%u bytes). "
204 "Disconnecting L2CAP CoC", rcv_size);
205 err = bt_gatt_ots_l2cap_disconnect(l2cap_ctx);
206 if (err < 0) {
207 LOG_WRN("Disconnecting L2CAP returned error %d", err);
208 }
209
210 cur_inst = NULL;
211 } else if (cb_ret == BT_OTS_STOP) {
212 const uint32_t rcv_size = cur_object->size.cur;
213 int err;
214
215 LOG_DBG("Stopped receiving after%u bytes. "
216 "Disconnecting L2CAP CoC", rcv_size);
217 err = bt_gatt_ots_l2cap_disconnect(l2cap_ctx);
218
219 if (err < 0) {
220 LOG_WRN("Disconnecting L2CAP returned error %d", err);
221 }
222
223 cur_inst = NULL;
224 }
225
226 return 0;
227 }
228
chan_closed(struct bt_gatt_ots_l2cap * l2cap_ctx,struct bt_conn * conn)229 static void chan_closed(struct bt_gatt_ots_l2cap *l2cap_ctx,
230 struct bt_conn *conn)
231 {
232 LOG_DBG("L2CAP closed, context: %p, conn: %p", l2cap_ctx, (void *)conn);
233 if (cur_inst) {
234 cur_inst = NULL;
235 }
236 }
237 /* End L2CAP callbacks */
238
print_oacp_response(enum bt_gatt_ots_oacp_proc_type req_opcode,enum bt_gatt_ots_oacp_res_code result_code)239 static void print_oacp_response(enum bt_gatt_ots_oacp_proc_type req_opcode,
240 enum bt_gatt_ots_oacp_res_code result_code)
241 {
242 LOG_DBG("Request OP Code: %s", lit_request[req_opcode]);
243 LOG_DBG("Result Code : %s", lit_result[result_code]);
244 }
245
print_olcp_response(enum bt_gatt_ots_olcp_proc_type req_opcode,enum bt_gatt_ots_olcp_res_code result_code)246 static void print_olcp_response(enum bt_gatt_ots_olcp_proc_type req_opcode,
247 enum bt_gatt_ots_olcp_res_code result_code)
248 {
249 LOG_DBG("Request OP Code: %s", lit_olcp_request[req_opcode]);
250 LOG_DBG("Result Code : %s", lit_olcp_result[result_code]);
251 }
252
date_time_decode(struct net_buf_simple * buf,struct bt_ots_date_time * p_date_time)253 static void date_time_decode(struct net_buf_simple *buf,
254 struct bt_ots_date_time *p_date_time)
255 {
256 p_date_time->year = net_buf_simple_pull_le16(buf);
257 p_date_time->month = net_buf_simple_pull_u8(buf);
258 p_date_time->day = net_buf_simple_pull_u8(buf);
259 p_date_time->hours = net_buf_simple_pull_u8(buf);
260 p_date_time->minutes = net_buf_simple_pull_u8(buf);
261 p_date_time->seconds = net_buf_simple_pull_u8(buf);
262 }
263
lookup_inst_by_handle(uint16_t handle)264 static struct bt_otc_internal_instance_t *lookup_inst_by_handle(uint16_t handle)
265 {
266 for (int i = 0; i < ARRAY_SIZE(otc_insts); i++) {
267 if (otc_insts[i].otc_inst &&
268 otc_insts[i].otc_inst->start_handle <= handle &&
269 otc_insts[i].otc_inst->end_handle >= handle) {
270 return &otc_insts[i];
271 }
272 }
273
274 LOG_DBG("Could not find OTS instance with handle 0x%04x", handle);
275
276 return NULL;
277 }
278
on_object_selected(struct bt_conn * conn,enum bt_gatt_ots_olcp_res_code res,struct bt_ots_client * otc_inst)279 static void on_object_selected(struct bt_conn *conn,
280 enum bt_gatt_ots_olcp_res_code res,
281 struct bt_ots_client *otc_inst)
282 {
283 memset(&otc_inst->cur_object, 0, sizeof(otc_inst->cur_object));
284 otc_inst->cur_object.id = OTS_CLIENT_UNKNOWN_ID;
285
286 if (otc_inst->cb->obj_selected) {
287 otc_inst->cb->obj_selected(otc_inst, conn, res);
288 }
289
290 LOG_DBG("Object selected");
291 }
292
olcp_ind_handler(struct bt_conn * conn,struct bt_ots_client * otc_inst,const void * data,uint16_t length)293 static void olcp_ind_handler(struct bt_conn *conn,
294 struct bt_ots_client *otc_inst,
295 const void *data, uint16_t length)
296 {
297 enum bt_gatt_ots_olcp_proc_type op_code;
298 struct net_buf_simple net_buf;
299
300 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
301
302 op_code = net_buf_simple_pull_u8(&net_buf);
303
304 LOG_DBG("OLCP indication");
305
306 if (op_code == BT_GATT_OTS_OLCP_PROC_RESP) {
307 enum bt_gatt_ots_olcp_proc_type req_opcode =
308 net_buf_simple_pull_u8(&net_buf);
309 enum bt_gatt_ots_olcp_res_code result_code =
310 net_buf_simple_pull_u8(&net_buf);
311
312 print_olcp_response(req_opcode, result_code);
313
314 switch (req_opcode) {
315 case BT_GATT_OTS_OLCP_PROC_FIRST:
316 LOG_DBG("First");
317 on_object_selected(conn, result_code, otc_inst);
318 break;
319 case BT_GATT_OTS_OLCP_PROC_LAST:
320 LOG_DBG("Last");
321 on_object_selected(conn, result_code, otc_inst);
322 break;
323 case BT_GATT_OTS_OLCP_PROC_PREV:
324 LOG_DBG("Previous");
325 on_object_selected(conn, result_code, otc_inst);
326 break;
327 case BT_GATT_OTS_OLCP_PROC_NEXT:
328 LOG_DBG("Next");
329 on_object_selected(conn, result_code, otc_inst);
330 break;
331 case BT_GATT_OTS_OLCP_PROC_GOTO:
332 LOG_DBG("Goto");
333 on_object_selected(conn, result_code, otc_inst);
334 break;
335 case BT_GATT_OTS_OLCP_PROC_ORDER:
336 LOG_DBG("Order");
337 on_object_selected(conn, result_code, otc_inst);
338 break;
339 case BT_GATT_OTS_OLCP_PROC_REQ_NUM_OBJS:
340 LOG_DBG("Request number of objects");
341 if (net_buf.len == sizeof(uint32_t)) {
342 uint32_t obj_cnt =
343 net_buf_simple_pull_le32(&net_buf);
344 LOG_DBG("Number of objects %u", obj_cnt);
345 }
346 break;
347 case BT_GATT_OTS_OLCP_PROC_CLEAR_MARKING:
348 LOG_DBG("Clear marking");
349 break;
350 default:
351 LOG_DBG("Invalid indication req opcode %u", req_opcode);
352 break;
353 }
354 } else {
355 LOG_DBG("Invalid indication opcode %u", op_code);
356 }
357 }
358
oacp_ind_handler(struct bt_conn * conn,struct bt_ots_client * otc_inst,const void * data,uint16_t length)359 static void oacp_ind_handler(struct bt_conn *conn,
360 struct bt_ots_client *otc_inst,
361 const void *data, uint16_t length)
362 {
363 enum bt_gatt_ots_oacp_proc_type op_code;
364 enum bt_gatt_ots_oacp_proc_type req_opcode;
365 enum bt_gatt_ots_oacp_res_code result_code;
366 uint32_t checksum;
367 struct net_buf_simple net_buf;
368
369 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
370
371 op_code = net_buf_simple_pull_u8(&net_buf);
372
373 LOG_DBG("OACP indication");
374
375 if (op_code == BT_GATT_OTS_OACP_PROC_RESP) {
376 if (net_buf.len >= (sizeof(req_opcode) + sizeof(result_code))) {
377 req_opcode = net_buf_simple_pull_u8(&net_buf);
378 result_code = net_buf_simple_pull_u8(&net_buf);
379 } else {
380 LOG_ERR("Invalid indication data len %u", net_buf.len);
381 return;
382 }
383
384 if (req_opcode == BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC) {
385 if (net_buf.len == sizeof(checksum)) {
386 checksum = net_buf_simple_pull_le32(&net_buf);
387 LOG_DBG("Object checksum 0x%08x\n", checksum);
388 if (otc_inst->cb->obj_checksum_calculated) {
389 otc_inst->cb->obj_checksum_calculated(
390 otc_inst, conn, result_code, checksum);
391 }
392 } else {
393 LOG_ERR("Invalid indication data len %u after opcode and result "
394 "pulled", net_buf.len);
395 return;
396 }
397 }
398
399 print_oacp_response(req_opcode, result_code);
400 } else {
401 LOG_DBG("Invalid indication opcode %u", op_code);
402 }
403 }
404
bt_ots_client_indicate_handler(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)405 uint8_t bt_ots_client_indicate_handler(struct bt_conn *conn,
406 struct bt_gatt_subscribe_params *params,
407 const void *data, uint16_t length)
408 {
409 uint16_t handle = params->value_handle;
410 struct bt_otc_internal_instance_t *inst;
411
412 if (conn == NULL) {
413 return BT_GATT_ITER_CONTINUE;
414 }
415
416 inst = lookup_inst_by_handle(handle);
417
418 /* TODO: Can we somehow avoid exposing this
419 * callback via the public API?
420 */
421
422 if (!inst) {
423 LOG_ERR("Instance not found");
424 return BT_GATT_ITER_STOP;
425 }
426
427 inst->busy = false;
428
429 if (data) {
430 if (handle == inst->otc_inst->olcp_handle) {
431 olcp_ind_handler(conn, inst->otc_inst, data, length);
432 } else if (handle == inst->otc_inst->oacp_handle) {
433 oacp_ind_handler(conn, inst->otc_inst, data, length);
434 }
435 }
436 return BT_GATT_ITER_CONTINUE;
437 }
438
read_feature_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)439 static uint8_t read_feature_cb(struct bt_conn *conn, uint8_t err,
440 struct bt_gatt_read_params *params,
441 const void *data, uint16_t length)
442 {
443 uint8_t cb_err = err;
444 struct bt_otc_internal_instance_t *inst =
445 lookup_inst_by_handle(params->single.handle);
446 struct net_buf_simple net_buf;
447
448 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
449
450 if (!inst) {
451 LOG_ERR("Instance not found");
452 return BT_GATT_ITER_STOP;
453 }
454
455 inst->busy = false;
456
457 if (err) {
458 LOG_DBG("err: 0x%02X", err);
459 } else if (data) {
460 if (length == OTS_FEATURE_LEN) {
461 inst->otc_inst->features.oacp =
462 net_buf_simple_pull_le32(&net_buf);
463
464 inst->otc_inst->features.olcp =
465 net_buf_simple_pull_le32(&net_buf);
466
467 LOG_DBG("features : oacp 0x%x, olcp 0x%x", inst->otc_inst->features.oacp,
468 inst->otc_inst->features.olcp);
469 } else {
470 LOG_DBG("Invalid length %u (expected %u)", length, OTS_FEATURE_LEN);
471 cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
472 }
473 }
474
475 return BT_GATT_ITER_STOP;
476 }
477
bt_ots_client_register(struct bt_ots_client * otc_inst)478 int bt_ots_client_register(struct bt_ots_client *otc_inst)
479 {
480 for (int i = 0; i < ARRAY_SIZE(otc_insts); i++) {
481 int err;
482
483 if (otc_insts[i].otc_inst) {
484 continue;
485 }
486
487 LOG_DBG("%u", i);
488 err = bt_gatt_ots_l2cap_register(&otc_insts[i].l2cap_ctx);
489 if (err) {
490 LOG_WRN("Could not register L2CAP context %d", err);
491 return err;
492 }
493
494 otc_insts[i].otc_inst = otc_inst;
495 return 0;
496 }
497
498 return -ENOMEM;
499 }
500
bt_ots_client_unregister(uint8_t index)501 int bt_ots_client_unregister(uint8_t index)
502 {
503 if (index < ARRAY_SIZE(otc_insts)) {
504 memset(&otc_insts[index], 0, sizeof(otc_insts[index]));
505 } else {
506 return -EINVAL;
507 }
508
509 return 0;
510 }
511
bt_ots_client_read_feature(struct bt_ots_client * otc_inst,struct bt_conn * conn)512 int bt_ots_client_read_feature(struct bt_ots_client *otc_inst,
513 struct bt_conn *conn)
514 {
515 if (OTS_CLIENT_INST_COUNT > 0) {
516 struct bt_otc_internal_instance_t *inst;
517 int err;
518
519 if (!conn) {
520 LOG_WRN("Invalid Connection");
521 return -ENOTCONN;
522 } else if (!otc_inst) {
523 LOG_ERR("Invalid OTC instance");
524 return -EINVAL;
525 } else if (!otc_inst->feature_handle) {
526 LOG_DBG("Handle not set");
527 return -EINVAL;
528 }
529
530 inst = lookup_inst_by_handle(otc_inst->start_handle);
531
532 if (!inst) {
533 LOG_ERR("Invalid OTC instance");
534 return -EINVAL;
535 } else if (inst->busy) {
536 return -EBUSY;
537 }
538
539 otc_inst->read_proc.func = read_feature_cb;
540 otc_inst->read_proc.handle_count = 1;
541 otc_inst->read_proc.single.handle = otc_inst->feature_handle;
542 otc_inst->read_proc.single.offset = 0U;
543
544 err = bt_gatt_read(conn, &otc_inst->read_proc);
545 if (!err) {
546 inst->busy = true;
547 }
548 return err;
549 }
550
551 LOG_DBG("Not supported");
552 return -EOPNOTSUPP;
553 }
554
write_olcp_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)555 static void write_olcp_cb(struct bt_conn *conn, uint8_t err,
556 struct bt_gatt_write_params *params)
557 {
558 struct bt_otc_internal_instance_t *inst =
559 lookup_inst_by_handle(params->handle);
560
561 LOG_DBG("Write %s (0x%02X)", err ? "failed" : "successful", err);
562
563 if (!inst) {
564 LOG_ERR("Instance not found");
565 return;
566 }
567
568 inst->busy = false;
569 }
570
write_olcp(struct bt_otc_internal_instance_t * inst,struct bt_conn * conn,enum bt_gatt_ots_olcp_proc_type opcode,uint8_t * params,uint8_t param_len)571 static int write_olcp(struct bt_otc_internal_instance_t *inst,
572 struct bt_conn *conn, enum bt_gatt_ots_olcp_proc_type opcode,
573 uint8_t *params, uint8_t param_len)
574 {
575 int err;
576
577 net_buf_simple_reset(&otc_tx_buf);
578
579 net_buf_simple_add_u8(&otc_tx_buf, opcode);
580
581 if (param_len && params) {
582 net_buf_simple_add_mem(&otc_tx_buf, params, param_len);
583 }
584
585 inst->otc_inst->write_params.offset = 0;
586 inst->otc_inst->write_params.data = otc_tx_buf.data;
587 inst->otc_inst->write_params.length = otc_tx_buf.len;
588 inst->otc_inst->write_params.handle = inst->otc_inst->olcp_handle;
589 inst->otc_inst->write_params.func = write_olcp_cb;
590
591 err = bt_gatt_write(conn, &inst->otc_inst->write_params);
592
593 if (!err) {
594 inst->busy = true;
595 }
596 return err;
597 }
598
bt_ots_client_select_id(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint64_t obj_id)599 int bt_ots_client_select_id(struct bt_ots_client *otc_inst,
600 struct bt_conn *conn,
601 uint64_t obj_id)
602 {
603 CHECKIF(!BT_OTS_VALID_OBJ_ID(obj_id)) {
604 LOG_DBG("Invalid object ID 0x%016llx", obj_id);
605
606 return -EINVAL;
607 }
608
609 if (OTS_CLIENT_INST_COUNT > 0) {
610 struct bt_otc_internal_instance_t *inst;
611 uint8_t param[BT_OTS_OBJ_ID_SIZE];
612
613 if (!conn) {
614 LOG_WRN("Invalid Connection");
615 return -ENOTCONN;
616 } else if (!otc_inst) {
617 LOG_ERR("Invalid OTC instance");
618 return -EINVAL;
619 } else if (!otc_inst->olcp_handle) {
620 LOG_DBG("Handle not set");
621 return -EINVAL;
622 }
623
624 inst = lookup_inst_by_handle(otc_inst->start_handle);
625
626 if (!inst) {
627 LOG_ERR("Invalid OTC instance");
628 return -EINVAL;
629 } else if (inst->busy) {
630 return -EBUSY;
631 }
632
633 /* TODO: Should not update this before ID is read */
634 otc_inst->cur_object.id = obj_id;
635 sys_put_le48(obj_id, param);
636
637 return write_olcp(inst, conn, BT_GATT_OTS_OLCP_PROC_GOTO,
638 param, BT_OTS_OBJ_ID_SIZE);
639 }
640
641 LOG_DBG("Not supported");
642 return -EOPNOTSUPP;
643 }
644
bt_ots_client_select_first(struct bt_ots_client * otc_inst,struct bt_conn * conn)645 int bt_ots_client_select_first(struct bt_ots_client *otc_inst,
646 struct bt_conn *conn)
647 {
648 if (OTS_CLIENT_INST_COUNT > 0) {
649 struct bt_otc_internal_instance_t *inst;
650
651 if (!conn) {
652 LOG_WRN("Invalid Connection");
653 return -ENOTCONN;
654 } else if (!otc_inst) {
655 LOG_ERR("Invalid OTC instance");
656 return -EINVAL;
657 } else if (!otc_inst->olcp_handle) {
658 LOG_DBG("Handle not set");
659 return -EINVAL;
660 }
661
662 inst = lookup_inst_by_handle(otc_inst->start_handle);
663
664 if (!inst) {
665 LOG_ERR("Invalid OTC instance");
666 return -EINVAL;
667 } else if (inst->busy) {
668 return -EBUSY;
669 }
670
671 return write_olcp(inst, conn, BT_GATT_OTS_OLCP_PROC_FIRST,
672 NULL, 0);
673 }
674
675 LOG_DBG("Not supported");
676 return -EOPNOTSUPP;
677 }
678
bt_ots_client_select_last(struct bt_ots_client * otc_inst,struct bt_conn * conn)679 int bt_ots_client_select_last(struct bt_ots_client *otc_inst,
680 struct bt_conn *conn)
681 {
682 if (OTS_CLIENT_INST_COUNT > 0) {
683 struct bt_otc_internal_instance_t *inst;
684
685 if (!conn) {
686 LOG_WRN("Invalid Connection");
687 return -ENOTCONN;
688 } else if (!otc_inst) {
689 LOG_ERR("Invalid OTC instance");
690 return -EINVAL;
691 } else if (!otc_inst->olcp_handle) {
692 LOG_DBG("Handle not set");
693 return -EINVAL;
694 }
695
696 inst = lookup_inst_by_handle(otc_inst->start_handle);
697
698 if (!inst) {
699 LOG_ERR("Invalid OTC instance");
700 return -EINVAL;
701 } else if (inst->busy) {
702 return -EBUSY;
703 }
704
705 return write_olcp(inst, conn, BT_GATT_OTS_OLCP_PROC_LAST,
706 NULL, 0);
707
708 }
709
710 LOG_DBG("Not supported");
711 return -EOPNOTSUPP;
712 }
713
bt_ots_client_select_next(struct bt_ots_client * otc_inst,struct bt_conn * conn)714 int bt_ots_client_select_next(struct bt_ots_client *otc_inst,
715 struct bt_conn *conn)
716 {
717 if (OTS_CLIENT_INST_COUNT > 0) {
718 struct bt_otc_internal_instance_t *inst;
719
720 if (!conn) {
721 LOG_WRN("Invalid Connection");
722 return -ENOTCONN;
723 } else if (!otc_inst) {
724 LOG_ERR("Invalid OTC instance");
725 return -EINVAL;
726 } else if (!otc_inst->olcp_handle) {
727 LOG_DBG("Handle not set");
728 return -EINVAL;
729 }
730
731 inst = lookup_inst_by_handle(otc_inst->start_handle);
732
733 if (!inst) {
734 LOG_ERR("Invalid OTC instance");
735 return -EINVAL;
736 } else if (inst->busy) {
737 return -EBUSY;
738 }
739
740 return write_olcp(inst, conn, BT_GATT_OTS_OLCP_PROC_NEXT,
741 NULL, 0);
742 }
743
744 LOG_DBG("Not supported");
745 return -EOPNOTSUPP;
746 }
747
bt_ots_client_select_prev(struct bt_ots_client * otc_inst,struct bt_conn * conn)748 int bt_ots_client_select_prev(struct bt_ots_client *otc_inst,
749 struct bt_conn *conn)
750 {
751 if (OTS_CLIENT_INST_COUNT > 0) {
752 struct bt_otc_internal_instance_t *inst;
753
754 if (!conn) {
755 LOG_WRN("Invalid Connection");
756 return -ENOTCONN;
757 } else if (!otc_inst) {
758 LOG_ERR("Invalid OTC instance");
759 return -EINVAL;
760 } else if (!otc_inst->olcp_handle) {
761 LOG_DBG("Handle not set");
762 return -EINVAL;
763 }
764
765 inst = lookup_inst_by_handle(otc_inst->start_handle);
766
767 if (!inst) {
768 LOG_ERR("Invalid OTC instance");
769 return -EINVAL;
770 } else if (inst->busy) {
771 return -EBUSY;
772 }
773
774 return write_olcp(inst, conn, BT_GATT_OTS_OLCP_PROC_PREV,
775 NULL, 0);
776 }
777
778 LOG_DBG("Not supported");
779 return -EOPNOTSUPP;
780 }
781
read_object_size_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)782 static uint8_t read_object_size_cb(struct bt_conn *conn, uint8_t err,
783 struct bt_gatt_read_params *params,
784 const void *data, uint16_t length)
785 {
786 struct bt_otc_internal_instance_t *inst =
787 lookup_inst_by_handle(params->single.handle);
788 struct net_buf_simple net_buf;
789
790 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
791
792 LOG_DBG("handle %d, length %u", params->single.handle, length);
793
794 if (!inst) {
795 LOG_ERR("Instance not found");
796 return BT_GATT_ITER_STOP;
797 }
798
799 if (err) {
800 LOG_DBG("err: 0x%02X", err);
801 } else if (data) {
802 if (length != OTS_SIZE_LEN) {
803 LOG_DBG("Invalid length %u (expected %u)", length, OTS_SIZE_LEN);
804 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
805 } else {
806 struct bt_ots_obj_metadata *cur_object =
807 &inst->otc_inst->cur_object;
808 cur_object->size.cur =
809 net_buf_simple_pull_le32(&net_buf);
810 cur_object->size.alloc =
811 net_buf_simple_pull_le32(&net_buf);
812
813 LOG_DBG("Object Size : current size %u, "
814 "allocated size %u",
815 cur_object->size.cur,
816 cur_object->size.alloc);
817
818 if (cur_object->size.cur == 0) {
819 LOG_WRN("Obj size read returned a current "
820 "size of 0");
821 } else if (cur_object->size.cur >
822 cur_object->size.alloc &&
823 cur_object->size.alloc != 0) {
824 LOG_WRN("Allocated size %u is smaller than "
825 "current size %u",
826 cur_object->size.alloc,
827 cur_object->size.cur);
828 }
829
830 BT_OTS_SET_METADATA_REQ_SIZE(inst->metadata_read);
831 }
832 }
833
834 if (err) {
835 LOG_WRN("err: 0x%02X", err);
836 if (!inst->metadata_err) {
837 inst->metadata_err = err;
838 }
839 }
840
841 read_next_metadata(conn, inst);
842
843 return BT_GATT_ITER_STOP;
844 }
845
read_obj_id_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)846 static uint8_t read_obj_id_cb(struct bt_conn *conn, uint8_t err,
847 struct bt_gatt_read_params *params,
848 const void *data, uint16_t length)
849 {
850 struct bt_otc_internal_instance_t *inst =
851 lookup_inst_by_handle(params->single.handle);
852 struct net_buf_simple net_buf;
853
854 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
855
856 LOG_DBG("handle %d, length %u", params->single.handle, length);
857
858 if (!inst) {
859 LOG_ERR("Instance not found");
860 return BT_GATT_ITER_STOP;
861 }
862
863 if (err) {
864 LOG_DBG("err: 0x%02X", err);
865 } else if (data) {
866 if (length == BT_OTS_OBJ_ID_SIZE) {
867 uint64_t obj_id = net_buf_simple_pull_le48(&net_buf);
868 char t[BT_OTS_OBJ_ID_STR_LEN];
869 struct bt_ots_obj_metadata *cur_object =
870 &inst->otc_inst->cur_object;
871
872 (void)bt_ots_obj_id_to_str(obj_id, t, sizeof(t));
873 LOG_DBG("Object Id : %s", t);
874
875 if (cur_object->id != OTS_CLIENT_UNKNOWN_ID &&
876 cur_object->id != obj_id) {
877 char str[BT_OTS_OBJ_ID_STR_LEN];
878
879 (void)bt_ots_obj_id_to_str(cur_object->id, str,
880 sizeof(str));
881 LOG_INF("Read Obj Id %s not selected obj Id %s", t, str);
882 } else {
883 LOG_INF("Read Obj Id confirmed correct Obj Id");
884 cur_object->id = obj_id;
885
886 BT_OTS_SET_METADATA_REQ_ID(inst->metadata_read);
887 }
888 } else {
889 LOG_DBG("Invalid length %u (expected %u)", length, BT_OTS_OBJ_ID_SIZE);
890 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
891 }
892 }
893
894 if (err) {
895 LOG_WRN("err: 0x%02X", err);
896 if (!inst->metadata_err) {
897 inst->metadata_err = err;
898 }
899 }
900
901 read_next_metadata(conn, inst);
902
903 return BT_GATT_ITER_STOP;
904 }
905
read_obj_name_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)906 static uint8_t read_obj_name_cb(struct bt_conn *conn, uint8_t err,
907 struct bt_gatt_read_params *params,
908 const void *data, uint16_t length)
909 {
910 struct bt_otc_internal_instance_t *inst =
911 lookup_inst_by_handle(params->single.handle);
912
913 LOG_DBG("handle %d, length %u", params->single.handle, length);
914
915 if (!inst) {
916 LOG_ERR("Instance not found");
917 return BT_GATT_ITER_STOP;
918 }
919
920 if (data) {
921 if (length <= CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
922 memcpy(inst->otc_inst->cur_object.name_c, data, length);
923 inst->otc_inst->cur_object.name_c[length] = '\0';
924 } else {
925 LOG_WRN("Invalid length %u (expected max %u)", length,
926 CONFIG_BT_OTS_OBJ_MAX_NAME_LEN);
927 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
928 }
929 }
930
931 if (err) {
932 LOG_WRN("err: 0x%02X", err);
933 if (!inst->metadata_err) {
934 inst->metadata_err = err;
935 }
936 }
937
938 read_next_metadata(conn, inst);
939
940 return BT_GATT_ITER_STOP;
941 }
942
read_obj_type_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)943 static uint8_t read_obj_type_cb(struct bt_conn *conn, uint8_t err,
944 struct bt_gatt_read_params *params,
945 const void *data, uint16_t length)
946 {
947 struct bt_otc_internal_instance_t *inst =
948 lookup_inst_by_handle(params->single.handle);
949
950 LOG_DBG("handle %d, length %u", params->single.handle, length);
951
952 if (!inst) {
953 LOG_ERR("Instance not found");
954 return BT_GATT_ITER_STOP;
955 }
956
957 if (data) {
958 if (length == BT_UUID_SIZE_128 || length == BT_UUID_SIZE_16) {
959 char uuid_str[BT_UUID_STR_LEN];
960 struct bt_uuid *uuid =
961 &inst->otc_inst->cur_object.type.uuid;
962
963 bt_uuid_create(uuid, data, length);
964
965 bt_uuid_to_str(uuid, uuid_str, sizeof(uuid_str));
966 LOG_DBG("UUID type read: %s", uuid_str);
967
968 BT_OTS_SET_METADATA_REQ_TYPE(inst->metadata_read);
969 } else {
970 LOG_WRN("Invalid length %u (expected max %u)", length, OTS_TYPE_MAX_LEN);
971 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
972 }
973 }
974
975 if (err) {
976 LOG_WRN("err: 0x%02X", err);
977 if (!inst->metadata_err) {
978 inst->metadata_err = err;
979 }
980 }
981
982 read_next_metadata(conn, inst);
983
984 return BT_GATT_ITER_STOP;
985 }
986
987
read_obj_created_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)988 static uint8_t read_obj_created_cb(struct bt_conn *conn, uint8_t err,
989 struct bt_gatt_read_params *params,
990 const void *data, uint16_t length)
991 {
992 struct bt_otc_internal_instance_t *inst =
993 lookup_inst_by_handle(params->single.handle);
994 struct net_buf_simple net_buf;
995
996 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
997
998 LOG_DBG("handle %d, length %u", params->single.handle, length);
999
1000 if (!inst) {
1001 LOG_ERR("Instance not found");
1002 return BT_GATT_ITER_STOP;
1003 }
1004
1005 if (data) {
1006 if (length <= BT_OTS_DATE_TIME_FIELD_SIZE) {
1007 date_time_decode(
1008 &net_buf,
1009 &inst->otc_inst->cur_object.first_created);
1010 } else {
1011 LOG_WRN("Invalid length %u (expected max %u)", length,
1012 BT_OTS_DATE_TIME_FIELD_SIZE);
1013 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
1014 }
1015 }
1016
1017 if (err) {
1018 LOG_WRN("err: 0x%02X", err);
1019 if (!inst->metadata_err) {
1020 inst->metadata_err = err;
1021 }
1022 }
1023
1024 read_next_metadata(conn, inst);
1025
1026 return BT_GATT_ITER_STOP;
1027 }
1028
read_obj_modified_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)1029 static uint8_t read_obj_modified_cb(struct bt_conn *conn, uint8_t err,
1030 struct bt_gatt_read_params *params,
1031 const void *data, uint16_t length)
1032 {
1033 struct bt_otc_internal_instance_t *inst =
1034 lookup_inst_by_handle(params->single.handle);
1035 struct net_buf_simple net_buf;
1036
1037 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
1038
1039 LOG_DBG("handle %d, length %u", params->single.handle, length);
1040
1041 if (!inst) {
1042 LOG_ERR("Instance not found");
1043 return BT_GATT_ITER_STOP;
1044 }
1045
1046 if (data) {
1047 if (length <= BT_OTS_DATE_TIME_FIELD_SIZE) {
1048 date_time_decode(&net_buf,
1049 &inst->otc_inst->cur_object.modified);
1050 } else {
1051 LOG_WRN("Invalid length %u (expected max %u)", length,
1052 BT_OTS_DATE_TIME_FIELD_SIZE);
1053 err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
1054 }
1055 }
1056
1057 if (err) {
1058 LOG_WRN("err: 0x%02X", err);
1059 if (!inst->metadata_err) {
1060 inst->metadata_err = err;
1061 }
1062 }
1063
1064 read_next_metadata(conn, inst);
1065
1066 return BT_GATT_ITER_STOP;
1067 }
1068
read_attr(struct bt_conn * conn,struct bt_otc_internal_instance_t * inst,uint16_t handle,bt_gatt_read_func_t cb)1069 static int read_attr(struct bt_conn *conn,
1070 struct bt_otc_internal_instance_t *inst,
1071 uint16_t handle, bt_gatt_read_func_t cb)
1072 {
1073 if (!handle) {
1074 LOG_DBG("Handle not set");
1075 return -EINVAL;
1076 } else if (cb == NULL) {
1077 LOG_ERR("No callback set");
1078 return -EINVAL;
1079 }
1080
1081 /* TODO: With EATT we can request multiple metadata to be read at once*/
1082 inst->otc_inst->read_proc.func = cb;
1083 inst->otc_inst->read_proc.handle_count = 1;
1084 inst->otc_inst->read_proc.single.handle = handle;
1085 inst->otc_inst->read_proc.single.offset = 0;
1086
1087 return bt_gatt_read(conn, &inst->otc_inst->read_proc);
1088 }
1089
read_obj_properties_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_read_params * params,const void * data,uint16_t length)1090 static uint8_t read_obj_properties_cb(struct bt_conn *conn, uint8_t err,
1091 struct bt_gatt_read_params *params,
1092 const void *data, uint16_t length)
1093 {
1094 uint8_t cb_err = err;
1095 struct bt_otc_internal_instance_t *inst =
1096 lookup_inst_by_handle(params->single.handle);
1097 struct net_buf_simple net_buf;
1098
1099 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
1100
1101 LOG_INF("handle %d, length %u", params->single.handle, length);
1102
1103 if (!inst) {
1104 LOG_ERR("Instance not found");
1105 return BT_GATT_ITER_STOP;
1106 }
1107
1108 if (err) {
1109 LOG_WRN("err: 0x%02X", err);
1110 } else if (data && length <= OTS_PROPERTIES_LEN) {
1111 struct bt_ots_obj_metadata *cur_object =
1112 &inst->otc_inst->cur_object;
1113
1114 cur_object->props = net_buf_simple_pull_le32(&net_buf);
1115
1116 LOG_INF("Object properties (raw) : 0x%x", cur_object->props);
1117
1118 if (!BT_OTS_OBJ_GET_PROP_READ(cur_object->props)) {
1119 LOG_WRN("Obj properties: Obj read not supported");
1120 }
1121
1122 BT_OTS_SET_METADATA_REQ_PROPS(inst->metadata_read);
1123 } else {
1124 LOG_WRN("Invalid length %u (expected %u)", length, OTS_PROPERTIES_LEN);
1125 cb_err = BT_ATT_ERR_INVALID_ATTRIBUTE_LEN;
1126 }
1127
1128 if (err) {
1129 LOG_WRN("err: 0x%02X", err);
1130 if (!inst->metadata_err) {
1131 inst->metadata_err = err;
1132 }
1133 }
1134
1135 read_next_metadata(conn, inst);
1136
1137 return BT_GATT_ITER_STOP;
1138 }
1139
write_oacp_cp_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)1140 static void write_oacp_cp_cb(struct bt_conn *conn, uint8_t err,
1141 struct bt_gatt_write_params *params)
1142 {
1143 struct bt_otc_internal_instance_t *inst =
1144 lookup_inst_by_handle(params->handle);
1145
1146 LOG_DBG("Write %s (0x%02X)", err ? "failed" : "successful", err);
1147
1148 if (!inst) {
1149 LOG_ERR("Instance not found");
1150 return;
1151 }
1152
1153 inst->busy = false;
1154 }
1155
write_oacp_cp_write_req_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_write_params * params)1156 static void write_oacp_cp_write_req_cb(struct bt_conn *conn, uint8_t err,
1157 struct bt_gatt_write_params *params)
1158 {
1159 struct bt_otc_internal_instance_t *inst =
1160 lookup_inst_by_handle(params->handle);
1161 uint32_t len;
1162
1163 LOG_DBG("Write Object request %s (0x%02X)", err ? "failed" : "successful", err);
1164 if (!inst) {
1165 LOG_ERR("Instance not found");
1166 return;
1167 }
1168
1169 len = inst->l2cap_ctx.tx.len;
1170 inst->l2cap_ctx.tx.len = 0;
1171 err = bt_gatt_ots_l2cap_send(&inst->l2cap_ctx, inst->l2cap_ctx.tx.data, len);
1172 if (err) {
1173 LOG_WRN("L2CAP CoC error: %d while trying to execute OACP "
1174 "Read procedure", err);
1175 }
1176
1177 inst->busy = false;
1178 }
1179
oacp_read(struct bt_conn * conn,struct bt_otc_internal_instance_t * inst)1180 static int oacp_read(struct bt_conn *conn,
1181 struct bt_otc_internal_instance_t *inst)
1182 {
1183 int err;
1184 uint32_t offset = 0;
1185 uint32_t length;
1186 struct bt_gatt_ots_l2cap *l2cap;
1187
1188 if (!inst->otc_inst->oacp_handle) {
1189 LOG_DBG("Handle not set");
1190 return -EINVAL;
1191 } else if (inst->busy) {
1192 return -EBUSY;
1193 } else if (cur_inst) {
1194 return -EBUSY;
1195 }
1196
1197 /* TODO: How do we ensure that the L2CAP is connected in time for the
1198 * transfer?
1199 */
1200
1201 err = bt_gatt_ots_l2cap_connect(conn, &l2cap);
1202 if (err) {
1203 LOG_DBG("Could not connect l2cap: %d", err);
1204 return err;
1205 }
1206
1207 l2cap->tx_done = tx_done;
1208 l2cap->rx_done = rx_done;
1209 l2cap->closed = chan_closed;
1210
1211 net_buf_simple_reset(&otc_tx_buf);
1212
1213 /* OP Code */
1214 net_buf_simple_add_u8(&otc_tx_buf, BT_GATT_OTS_OACP_PROC_READ);
1215
1216 /* Offset */
1217 net_buf_simple_add_le32(&otc_tx_buf, offset);
1218
1219 /* Len */
1220 length = inst->otc_inst->cur_object.size.cur - offset;
1221 net_buf_simple_add_le32(&otc_tx_buf, length);
1222
1223 inst->otc_inst->write_params.offset = 0;
1224 inst->otc_inst->write_params.data = otc_tx_buf.data;
1225 inst->otc_inst->write_params.length = otc_tx_buf.len;
1226 inst->otc_inst->write_params.handle = inst->otc_inst->oacp_handle;
1227 inst->otc_inst->write_params.func = write_oacp_cp_cb;
1228
1229 err = bt_gatt_write(conn, &inst->otc_inst->write_params);
1230
1231 if (!err) {
1232 inst->busy = true;
1233 }
1234
1235 cur_inst = inst;
1236 inst->rcvd_size = 0;
1237
1238 return err;
1239 }
1240
oacp_write(struct bt_conn * conn,struct bt_otc_internal_instance_t * inst,const void * buf,uint32_t len,uint32_t offset,enum bt_ots_oacp_write_op_mode mode)1241 static int oacp_write(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
1242 const void *buf, uint32_t len, uint32_t offset,
1243 enum bt_ots_oacp_write_op_mode mode)
1244 {
1245 int err;
1246 struct bt_gatt_ots_l2cap *l2cap;
1247
1248 if (!inst->otc_inst->oacp_handle) {
1249 LOG_DBG("Handle not set");
1250 return -EINVAL;
1251 } else if (inst->busy) {
1252 return -EBUSY;
1253 } else if (cur_inst) {
1254 return -EBUSY;
1255 }
1256
1257 err = bt_gatt_ots_l2cap_connect(conn, &l2cap);
1258 if (err) {
1259 LOG_DBG("Could not connect l2cap: %d", err);
1260 return err;
1261 }
1262
1263 l2cap->tx_done = write_obj_tx_done;
1264 l2cap->rx_done = rx_done;
1265 l2cap->closed = chan_closed;
1266 l2cap->tx.data = (uint8_t *)buf;
1267 l2cap->tx.len = len;
1268 net_buf_simple_reset(&otc_tx_buf);
1269
1270 /* OP Code */
1271 net_buf_simple_add_u8(&otc_tx_buf, BT_GATT_OTS_OACP_PROC_WRITE);
1272
1273 /* Offset */
1274 net_buf_simple_add_le32(&otc_tx_buf, offset);
1275
1276 /* Len */
1277 net_buf_simple_add_le32(&otc_tx_buf, len);
1278
1279 /* Mode, truncate or not */
1280 net_buf_simple_add_u8(&otc_tx_buf, mode);
1281
1282 inst->otc_inst->write_params.offset = 0;
1283 inst->otc_inst->write_params.data = otc_tx_buf.data;
1284 inst->otc_inst->write_params.length = otc_tx_buf.len;
1285 inst->otc_inst->write_params.handle = inst->otc_inst->oacp_handle;
1286 inst->otc_inst->write_params.func = write_oacp_cp_write_req_cb;
1287 inst->sent_size = len;
1288 err = bt_gatt_write(conn, &inst->otc_inst->write_params);
1289
1290 if (!err) {
1291 inst->busy = true;
1292 cur_inst = inst;
1293 }
1294
1295 inst->rcvd_size = 0;
1296
1297 return err;
1298 }
1299
oacp_checksum(struct bt_conn * conn,struct bt_otc_internal_instance_t * inst,uint32_t offset,uint32_t len)1300 static int oacp_checksum(struct bt_conn *conn, struct bt_otc_internal_instance_t *inst,
1301 uint32_t offset, uint32_t len)
1302 {
1303 int err;
1304
1305 if (!inst->otc_inst->oacp_handle) {
1306 LOG_DBG("Handle not set");
1307 return -EINVAL;
1308 } else if (inst->busy) {
1309 LOG_DBG("Client is busy");
1310 return -EBUSY;
1311 } else if (cur_inst) {
1312 LOG_DBG("Previous operation is not finished");
1313 return -EBUSY;
1314 }
1315
1316 net_buf_simple_reset(&otc_tx_buf);
1317
1318 /* OP Code */
1319 net_buf_simple_add_u8(&otc_tx_buf, BT_GATT_OTS_OACP_PROC_CHECKSUM_CALC);
1320
1321 /* Offset */
1322 net_buf_simple_add_le32(&otc_tx_buf, offset);
1323
1324 /* Len */
1325 net_buf_simple_add_le32(&otc_tx_buf, len);
1326
1327 inst->otc_inst->write_params.offset = 0;
1328 inst->otc_inst->write_params.data = otc_tx_buf.data;
1329 inst->otc_inst->write_params.length = otc_tx_buf.len;
1330 inst->otc_inst->write_params.handle = inst->otc_inst->oacp_handle;
1331 inst->otc_inst->write_params.func = write_oacp_cp_cb;
1332
1333 err = bt_gatt_write(conn, &inst->otc_inst->write_params);
1334 if (err != 0) {
1335 inst->busy = true;
1336 cur_inst = inst;
1337 }
1338
1339 return err;
1340 }
1341
bt_ots_client_read_object_data(struct bt_ots_client * otc_inst,struct bt_conn * conn)1342 int bt_ots_client_read_object_data(struct bt_ots_client *otc_inst,
1343 struct bt_conn *conn)
1344 {
1345 struct bt_otc_internal_instance_t *inst;
1346
1347 if (!conn) {
1348 LOG_WRN("Invalid Connection");
1349 return -ENOTCONN;
1350 } else if (!otc_inst) {
1351 LOG_ERR("Invalid OTC instance");
1352 return -EINVAL;
1353 }
1354
1355 inst = lookup_inst_by_handle(otc_inst->start_handle);
1356
1357 if (!inst) {
1358 LOG_ERR("Invalid OTC instance");
1359 return -EINVAL;
1360 }
1361
1362 if (otc_inst->cur_object.size.cur == 0) {
1363 LOG_WRN("Unknown object size");
1364 return -EINVAL;
1365 }
1366
1367 return oacp_read(conn, inst);
1368 }
1369
bt_ots_client_write_object_data(struct bt_ots_client * otc_inst,struct bt_conn * conn,const void * buf,size_t len,off_t offset,enum bt_ots_oacp_write_op_mode mode)1370 int bt_ots_client_write_object_data(struct bt_ots_client *otc_inst,
1371 struct bt_conn *conn, const void *buf, size_t len,
1372 off_t offset, enum bt_ots_oacp_write_op_mode mode)
1373 {
1374 struct bt_otc_internal_instance_t *inst;
1375
1376 CHECKIF(!conn) {
1377 LOG_WRN("Invalid Connection");
1378 return -ENOTCONN;
1379 }
1380
1381 CHECKIF(!otc_inst) {
1382 LOG_ERR("Invalid OTC instance");
1383 return -EINVAL;
1384 }
1385
1386 CHECKIF((mode != BT_OTS_OACP_WRITE_OP_MODE_NONE) &&
1387 (mode != BT_OTS_OACP_WRITE_OP_MODE_TRUNCATE)) {
1388 LOG_ERR("Invalid write object mode parameter %d", mode);
1389 return -EINVAL;
1390 }
1391
1392 /* OTS_v10.pdf Table 3.9: Object Action Control Point Procedure Requirements
1393 * Offset and Length field are UINT32 Length
1394 */
1395 CHECKIF(len > UINT32_MAX) {
1396 LOG_ERR("length %zu exceeds UINT32", len);
1397 return -EINVAL;
1398 }
1399
1400 CHECKIF(len == 0) {
1401 LOG_ERR("length equals zero");
1402 return -EINVAL;
1403 }
1404
1405 CHECKIF((sizeof(offset) > sizeof(uint32_t) && (offset > UINT32_MAX)) || (offset < 0)) {
1406 LOG_ERR("offset %ld exceeds UINT32 and must be >= 0", offset);
1407 return -EINVAL;
1408 }
1409
1410 CHECKIF(offset > otc_inst->cur_object.size.cur) {
1411 LOG_ERR("offset %ld exceeds cur size %zu", offset, otc_inst->cur_object.size.cur);
1412 return -EINVAL;
1413 }
1414
1415 CHECKIF((offset < otc_inst->cur_object.size.cur) &&
1416 !BT_OTS_OBJ_GET_PROP_PATCH(otc_inst->cur_object.props)) {
1417 LOG_ERR("Patch is not supported");
1418 return -EACCES;
1419 }
1420
1421 CHECKIF(((len + offset) > otc_inst->cur_object.size.alloc) &&
1422 !BT_OTS_OBJ_GET_PROP_APPEND(otc_inst->cur_object.props)) {
1423 LOG_ERR("APPEND is not supported. Invalid new end of object %lu alloc %zu."
1424 , (len + offset), otc_inst->cur_object.size.alloc);
1425 return -EINVAL;
1426 }
1427
1428 inst = lookup_inst_by_handle(otc_inst->start_handle);
1429
1430 if (!inst) {
1431 LOG_ERR("Invalid OTC instance");
1432 return -EINVAL;
1433 }
1434
1435 return oacp_write(conn, inst, buf, (uint32_t)len, (uint32_t)offset, mode);
1436 }
1437
bt_ots_client_get_object_checksum(struct bt_ots_client * otc_inst,struct bt_conn * conn,off_t offset,size_t len)1438 int bt_ots_client_get_object_checksum(struct bt_ots_client *otc_inst, struct bt_conn *conn,
1439 off_t offset, size_t len)
1440 {
1441 struct bt_otc_internal_instance_t *inst;
1442
1443 CHECKIF(!conn) {
1444 LOG_DBG("Invalid Connection");
1445 return -ENOTCONN;
1446 }
1447
1448 CHECKIF(!otc_inst) {
1449 LOG_DBG("Invalid OTC instance");
1450 return -EINVAL;
1451 }
1452
1453 /* OTS_v10.pdf Table 3.9: Object Action Control Point Procedure Requirements
1454 * Offset and Length field are UINT32 Length
1455 */
1456 CHECKIF(len > UINT32_MAX) {
1457 LOG_DBG("length %zu exceeds UINT32", len);
1458 return -EINVAL;
1459 }
1460
1461 CHECKIF(len == 0) {
1462 LOG_DBG("length equals zero");
1463 return -EINVAL;
1464 }
1465
1466 CHECKIF((sizeof(offset) > sizeof(uint32_t) && (offset > UINT32_MAX)) || (offset < 0)) {
1467 LOG_DBG("offset exceeds %ld UINT32 and must be >= 0", offset);
1468 return -EINVAL;
1469 }
1470
1471 CHECKIF((len + offset) > otc_inst->cur_object.size.cur) {
1472 LOG_DBG("The sum of offset (%ld) and length (%zu) exceed the Current Size %lu "
1473 "alloc %zu.", offset, len, (len + offset), otc_inst->cur_object.size.cur);
1474 return -EINVAL;
1475 }
1476
1477 inst = lookup_inst_by_handle(otc_inst->start_handle);
1478 if (!inst) {
1479 LOG_DBG("Invalid OTC instance");
1480 return -EINVAL;
1481 }
1482
1483 return oacp_checksum(conn, inst, (uint32_t)offset, (uint32_t)len);
1484 }
1485
read_next_metadata(struct bt_conn * conn,struct bt_otc_internal_instance_t * inst)1486 static void read_next_metadata(struct bt_conn *conn,
1487 struct bt_otc_internal_instance_t *inst)
1488 {
1489 uint8_t metadata_remaining =
1490 inst->metadata_to_read ^ inst->metadata_read_attempted;
1491 int err = 0;
1492
1493 LOG_DBG("Attempting to read metadata 0x%02X", metadata_remaining);
1494
1495 if (BT_OTS_GET_METADATA_REQ_NAME(metadata_remaining)) {
1496 BT_OTS_SET_METADATA_REQ_NAME(inst->metadata_read_attempted);
1497 err = read_attr(conn, inst, inst->otc_inst->obj_name_handle,
1498 read_obj_name_cb);
1499 } else if (BT_OTS_GET_METADATA_REQ_TYPE(metadata_remaining)) {
1500 BT_OTS_SET_METADATA_REQ_TYPE(inst->metadata_read_attempted);
1501 err = read_attr(conn, inst, inst->otc_inst->obj_type_handle,
1502 read_obj_type_cb);
1503 } else if (BT_OTS_GET_METADATA_REQ_SIZE(metadata_remaining)) {
1504 BT_OTS_SET_METADATA_REQ_SIZE(inst->metadata_read_attempted);
1505 err = read_attr(conn, inst, inst->otc_inst->obj_size_handle,
1506 read_object_size_cb);
1507 } else if (BT_OTS_GET_METADATA_REQ_CREATED(metadata_remaining)) {
1508 BT_OTS_SET_METADATA_REQ_CREATED(inst->metadata_read_attempted);
1509 err = read_attr(conn, inst, inst->otc_inst->obj_created_handle,
1510 read_obj_created_cb);
1511 } else if (BT_OTS_GET_METADATA_REQ_MODIFIED(metadata_remaining)) {
1512 BT_OTS_SET_METADATA_REQ_MODIFIED(inst->metadata_read_attempted);
1513 err = read_attr(conn, inst, inst->otc_inst->obj_modified_handle,
1514 read_obj_modified_cb);
1515 } else if (BT_OTS_GET_METADATA_REQ_ID(metadata_remaining)) {
1516 BT_OTS_SET_METADATA_REQ_ID(inst->metadata_read_attempted);
1517 err = read_attr(conn, inst, inst->otc_inst->obj_id_handle,
1518 read_obj_id_cb);
1519 } else if (BT_OTS_GET_METADATA_REQ_PROPS(metadata_remaining)) {
1520 BT_OTS_SET_METADATA_REQ_PROPS(inst->metadata_read_attempted);
1521 err = read_attr(conn, inst,
1522 inst->otc_inst->obj_properties_handle,
1523 read_obj_properties_cb);
1524 } else {
1525 inst->busy = false;
1526 if (inst->otc_inst->cb->obj_metadata_read) {
1527 inst->otc_inst->cb->obj_metadata_read(
1528 inst->otc_inst, conn, inst->metadata_err,
1529 inst->metadata_read);
1530 }
1531 return;
1532 }
1533
1534 if (err) {
1535 LOG_DBG("Metadata read failed (%d), trying next", err);
1536 read_next_metadata(conn, inst);
1537 }
1538 }
1539
bt_ots_client_read_object_metadata(struct bt_ots_client * otc_inst,struct bt_conn * conn,uint8_t metadata)1540 int bt_ots_client_read_object_metadata(struct bt_ots_client *otc_inst,
1541 struct bt_conn *conn,
1542 uint8_t metadata)
1543 {
1544 struct bt_otc_internal_instance_t *inst;
1545
1546 if (!conn) {
1547 LOG_WRN("Invalid Connection");
1548 return -ENOTCONN;
1549 } else if (!otc_inst) {
1550 LOG_ERR("Invalid OTC instance");
1551 return -EINVAL;
1552 } else if (!metadata) {
1553 LOG_WRN("No metadata to read");
1554 return -ENOEXEC;
1555 }
1556
1557 inst = lookup_inst_by_handle(otc_inst->start_handle);
1558
1559 if (!inst) {
1560 LOG_ERR("Invalid OTC instance");
1561 return -EINVAL;
1562 } else if (inst->busy) {
1563 return -EBUSY;
1564 }
1565
1566 inst->metadata_read = 0;
1567 inst->metadata_to_read = metadata & BT_OTS_METADATA_REQ_ALL;
1568 inst->metadata_read_attempted = 0;
1569
1570 inst->busy = true;
1571 read_next_metadata(conn, inst);
1572
1573 return 0;
1574 }
1575
decode_record(struct net_buf_simple * buf,struct dirlisting_record_t * rec)1576 static int decode_record(struct net_buf_simple *buf,
1577 struct dirlisting_record_t *rec)
1578 {
1579 uint16_t start_len = buf->len;
1580
1581 rec->len = net_buf_simple_pull_le16(buf);
1582
1583 if (rec->len > buf->len) {
1584 LOG_WRN("incorrect DirListing record length %u, "
1585 "longer than remaining size %u",
1586 rec->len, buf->len);
1587 return -EINVAL;
1588 }
1589
1590 if ((start_len - buf->len) + BT_OTS_OBJ_ID_SIZE > rec->len) {
1591 LOG_WRN("incorrect DirListing record, reclen %u too short, "
1592 "includes only record length",
1593 rec->len);
1594 return -EINVAL;
1595 }
1596
1597 rec->metadata.id = net_buf_simple_pull_le48(buf);
1598
1599 if (IS_ENABLED(CONFIG_BT_OTS_CLIENT_LOG_LEVEL_DBG)) {
1600 char t[BT_OTS_OBJ_ID_STR_LEN];
1601
1602 (void)bt_ots_obj_id_to_str(rec->metadata.id, t, sizeof(t));
1603 LOG_DBG("Object ID 0x%s", t);
1604 }
1605
1606 if ((start_len - buf->len) + sizeof(uint8_t) > rec->len) {
1607 LOG_WRN("incorrect DirListing record, reclen %u too short, "
1608 "includes only record length + ObjId",
1609 rec->len);
1610 return -EINVAL;
1611 }
1612
1613 rec->name_len = net_buf_simple_pull_u8(buf);
1614
1615 if (rec->name_len > 0) {
1616 uint8_t *name;
1617
1618 if ((start_len - buf->len) + rec->name_len > rec->len) {
1619 LOG_WRN("incorrect DirListing record, remaining length "
1620 "%u shorter than name length %u",
1621 rec->len - (start_len - buf->len),
1622 rec->name_len);
1623 return -EINVAL;
1624 }
1625
1626 if (rec->name_len >= sizeof(rec->metadata.name_c)) {
1627 LOG_WRN("Name length %u too long, invalid record", rec->name_len);
1628 return -EINVAL;
1629 }
1630
1631 name = net_buf_simple_pull_mem(buf, rec->name_len);
1632 memcpy(rec->metadata.name_c, name, rec->name_len);
1633 }
1634
1635 rec->metadata.name_c[rec->name_len] = '\0';
1636 rec->flags = 0;
1637
1638 if ((start_len - buf->len) + sizeof(uint8_t) > rec->len) {
1639 LOG_WRN("incorrect DirListing record, reclen %u too short, "
1640 "does not include flags", rec->len);
1641 return -EINVAL;
1642 }
1643
1644 rec->flags = net_buf_simple_pull_u8(buf);
1645 LOG_DBG("flags 0x%x", rec->flags);
1646
1647 if (BT_OTS_DIR_LIST_GET_FLAG_TYPE_128(rec->flags)) {
1648 uint8_t *uuid;
1649
1650 if ((start_len - buf->len) + BT_UUID_SIZE_128 > rec->len) {
1651 LOG_WRN("incorrect DirListing record, reclen %u "
1652 "flags indicates uuid128, too short",
1653 rec->len);
1654 LOG_INF("flags 0x%x", rec->flags);
1655 return -EINVAL;
1656 }
1657
1658 uuid = net_buf_simple_pull_mem(buf, BT_UUID_SIZE_128);
1659 bt_uuid_create(&rec->metadata.type.uuid,
1660 uuid, BT_UUID_SIZE_128);
1661 } else {
1662 if ((start_len - buf->len) + BT_UUID_SIZE_16 > rec->len) {
1663 LOG_WRN("incorrect DirListing record, reclen %u "
1664 "flags indicates uuid16, too short",
1665 rec->len);
1666 LOG_INF("flags 0x%x", rec->flags);
1667 return -EINVAL;
1668 }
1669
1670 rec->metadata.type.uuid_16.val =
1671 net_buf_simple_pull_le16(buf);
1672 }
1673
1674 if (BT_OTS_DIR_LIST_GET_FLAG_CUR_SIZE(rec->flags)) {
1675 if ((start_len - buf->len) + sizeof(uint32_t) > rec->len) {
1676 LOG_WRN("incorrect DirListing record, reclen %u "
1677 "flags indicates cur_size, too short",
1678 rec->len);
1679 LOG_INF("flags 0x%x", rec->flags);
1680 return -EINVAL;
1681 }
1682
1683 rec->metadata.size.cur = net_buf_simple_pull_le32(buf);
1684 }
1685
1686 if (BT_OTS_DIR_LIST_GET_FLAG_ALLOC_SIZE(rec->flags)) {
1687 if ((start_len - buf->len) + sizeof(uint32_t) > rec->len) {
1688 LOG_WRN("incorrect DirListing record, reclen %u "
1689 "flags indicates allocated size, too short",
1690 rec->len);
1691 LOG_INF("flags 0x%x", rec->flags);
1692 return -EINVAL;
1693 }
1694
1695 rec->metadata.size.alloc = net_buf_simple_pull_le32(buf);
1696 }
1697
1698 if (BT_OTS_DIR_LIST_GET_FLAG_FIRST_CREATED(rec->flags)) {
1699 if ((start_len - buf->len) + BT_OTS_DATE_TIME_FIELD_SIZE > rec->len) {
1700 LOG_WRN("incorrect DirListing record, reclen %u "
1701 "too short flags indicates first_created",
1702 rec->len);
1703 LOG_INF("flags 0x%x", rec->flags);
1704 return -EINVAL;
1705 }
1706
1707 date_time_decode(buf, &rec->metadata.first_created);
1708 }
1709
1710 if (BT_OTS_DIR_LIST_GET_FLAG_LAST_MODIFIED(rec->flags)) {
1711 if ((start_len - buf->len) + BT_OTS_DATE_TIME_FIELD_SIZE > rec->len) {
1712 LOG_WRN("incorrect DirListing record, reclen %u "
1713 "flags indicates las_mod, too short",
1714 rec->len);
1715 LOG_INF("flags 0x%x", rec->flags);
1716 return -EINVAL;
1717 }
1718
1719 date_time_decode(buf, &rec->metadata.modified);
1720 }
1721
1722 if (BT_OTS_DIR_LIST_GET_FLAG_PROPERTIES(rec->flags)) {
1723 if ((start_len - buf->len) + sizeof(uint32_t) > rec->len) {
1724 LOG_WRN("incorrect DirListing record, reclen %u "
1725 "flags indicates properties, too short",
1726 rec->len);
1727 LOG_INF("flags 0x%x", rec->flags);
1728 return -EINVAL;
1729 }
1730
1731 rec->metadata.props = net_buf_simple_pull_le32(buf);
1732 }
1733
1734 return rec->len;
1735 }
1736
bt_ots_client_decode_dirlisting(uint8_t * data,uint16_t length,bt_ots_client_dirlisting_cb cb)1737 int bt_ots_client_decode_dirlisting(uint8_t *data, uint16_t length,
1738 bt_ots_client_dirlisting_cb cb)
1739 {
1740 struct net_buf_simple net_buf;
1741 uint8_t count = 0;
1742 struct dirlisting_record_t record;
1743
1744 net_buf_simple_init_with_data(&net_buf, (void *)data, length);
1745
1746 if (!data || length == 0) {
1747 return -EINVAL;
1748 }
1749
1750 while (net_buf.len) {
1751 int ret;
1752
1753 count++;
1754
1755 if (net_buf.len < sizeof(uint16_t)) {
1756 LOG_WRN("incorrect DirListing record, len %u too short", net_buf.len);
1757 return -EINVAL;
1758 }
1759
1760 LOG_DBG("Decoding record %u", count);
1761 ret = decode_record(&net_buf, &record);
1762
1763 if (ret < 0) {
1764 LOG_WRN("DirListing, record %u invalid", count);
1765 return ret;
1766 }
1767
1768 ret = cb(&record.metadata);
1769
1770 if (ret == BT_OTS_STOP) {
1771 break;
1772 }
1773 }
1774
1775 return count;
1776 }
1777
bt_ots_metadata_display(struct bt_ots_obj_metadata * metadata,uint16_t count)1778 void bt_ots_metadata_display(struct bt_ots_obj_metadata *metadata,
1779 uint16_t count)
1780 {
1781 LOG_INF("--- Displaying %u metadata records ---", count);
1782
1783 for (int i = 0; i < count; i++) {
1784 char t[BT_OTS_OBJ_ID_STR_LEN];
1785
1786 (void)bt_ots_obj_id_to_str(metadata->id, t, sizeof(t));
1787 LOG_INF("Object ID: 0x%s", t);
1788 LOG_INF("Object name: %s", metadata->name_c);
1789 LOG_INF("Object Current Size: %u", metadata->size.cur);
1790 LOG_INF("Object Allocate Size: %u", metadata->size.alloc);
1791
1792 if (!bt_uuid_cmp(&metadata->type.uuid,
1793 BT_UUID_OTS_TYPE_MPL_ICON)) {
1794 LOG_INF("Type: Icon Obj Type");
1795 } else if (!bt_uuid_cmp(&metadata->type.uuid,
1796 BT_UUID_OTS_TYPE_TRACK_SEGMENT)) {
1797 LOG_INF("Type: Track Segment Obj Type");
1798 } else if (!bt_uuid_cmp(&metadata->type.uuid,
1799 BT_UUID_OTS_TYPE_TRACK)) {
1800 LOG_INF("Type: Track Obj Type");
1801 } else if (!bt_uuid_cmp(&metadata->type.uuid,
1802 BT_UUID_OTS_TYPE_GROUP)) {
1803 LOG_INF("Type: Group Obj Type");
1804 } else if (!bt_uuid_cmp(&metadata->type.uuid,
1805 BT_UUID_OTS_DIRECTORY_LISTING)) {
1806 LOG_INF("Type: Directory Listing");
1807 }
1808
1809
1810 LOG_INF("Properties:0x%x", metadata->props);
1811
1812 if (BT_OTS_OBJ_GET_PROP_APPEND(metadata->props)) {
1813 LOG_INF(" - append permitted");
1814 }
1815
1816 if (BT_OTS_OBJ_GET_PROP_DELETE(metadata->props)) {
1817 LOG_INF(" - delete permitted");
1818 }
1819
1820 if (BT_OTS_OBJ_GET_PROP_EXECUTE(metadata->props)) {
1821 LOG_INF(" - execute permitted");
1822 }
1823
1824 if (BT_OTS_OBJ_GET_PROP_MARKED(metadata->props)) {
1825 LOG_INF(" - marked");
1826 }
1827
1828 if (BT_OTS_OBJ_GET_PROP_PATCH(metadata->props)) {
1829 LOG_INF(" - patch permitted");
1830 }
1831
1832 if (BT_OTS_OBJ_GET_PROP_READ(metadata->props)) {
1833 LOG_INF(" - read permitted");
1834 }
1835
1836 if (BT_OTS_OBJ_GET_PROP_TRUNCATE(metadata->props)) {
1837 LOG_INF(" - truncate permitted");
1838 }
1839
1840 if (BT_OTS_OBJ_GET_PROP_WRITE(metadata->props)) {
1841 LOG_INF(" - write permitted");
1842 }
1843 }
1844 }
1845