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