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