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