1 /* btp_ots.c - Bluetooth OTS Tester */
2 
3 /*
4  * Copyright (c) 2024 Codecoup
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 #include <errno.h>
9 #include <inttypes.h>
10 #include <stdint.h>
11 #include <string.h>
12 
13 #include <zephyr/autoconf.h>
14 #include <zephyr/bluetooth/conn.h>
15 #include <zephyr/bluetooth/services/ots.h>
16 #include <zephyr/bluetooth/uuid.h>
17 #include <zephyr/logging/log.h>
18 
19 #include <zephyr/sys/byteorder.h>
20 #include <zephyr/sys/util.h>
21 #include <sys/types.h>
22 
23 #include "btp/btp.h"
24 
25 #define LOG_MODULE_NAME bttester_ots
26 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
27 
28 #define OBJ_POOL_SIZE CONFIG_BT_OTS_MAX_OBJ_CNT
29 #define OBJ_MAX_SIZE  100
30 
31 static struct object {
32 	uint8_t data[OBJ_MAX_SIZE];
33 	char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
34 	bool in_use;
35 } objects[OBJ_POOL_SIZE];
36 
37 struct object_creation_data {
38 	struct object *object;
39 	struct bt_ots_obj_size size;
40 	uint32_t props;
41 };
42 
43 #define OTS_OBJ_ID_TO_OBJ_IDX(id) (((id) - BT_OTS_OBJ_ID_MIN) % ARRAY_SIZE(objects))
44 
45 static struct object_creation_data *object_being_created;
46 
47 static struct bt_ots *ots;
48 
ots_supported_commands(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)49 static uint8_t ots_supported_commands(const void *cmd, uint16_t cmd_len,
50 				      void *rsp, uint16_t *rsp_len)
51 {
52 	struct btp_ots_read_supported_commands_rp *rp = rsp;
53 
54 	*rsp_len = tester_supported_commands(BTP_SERVICE_ID_OTS, rp->data);
55 	*rsp_len += sizeof(*rp);
56 
57 	return BTP_STATUS_SUCCESS;
58 }
59 
get_object(void)60 static struct object *get_object(void)
61 {
62 	for (size_t i = 0; i < ARRAY_SIZE(objects); i++) {
63 		if (!objects[i].in_use) {
64 			objects[i].in_use = true;
65 			return &objects[i];
66 		}
67 	}
68 
69 	return NULL;
70 }
71 
register_object(const void * cmd,uint16_t cmd_len,void * rsp,uint16_t * rsp_len)72 static uint8_t register_object(const void *cmd, uint16_t cmd_len,
73 			       void *rsp, uint16_t *rsp_len)
74 {
75 	const struct btp_ots_register_object_cmd *cp = cmd;
76 	struct btp_ots_register_object_rp *rp = rsp;
77 	struct object_creation_data obj_data;
78 	struct bt_ots_obj_add_param param;
79 	uint32_t supported_props = 0;
80 	struct object *obj;
81 	uint32_t props;
82 	int err;
83 
84 	if ((cmd_len < sizeof(*cp)) || (cmd_len != sizeof(*cp) + cp->name_len)) {
85 		return BTP_STATUS_FAILED;
86 	}
87 
88 	if (cp->name_len == 0 || cp->name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
89 		return BTP_STATUS_FAILED;
90 	}
91 
92 	/* all supported props (execute, append, truncate not supported) */
93 	BT_OTS_OBJ_SET_PROP_DELETE(supported_props);
94 	BT_OTS_OBJ_SET_PROP_READ(supported_props);
95 	BT_OTS_OBJ_SET_PROP_WRITE(supported_props);
96 	BT_OTS_OBJ_SET_PROP_PATCH(supported_props);
97 
98 	props = sys_le32_to_cpu(cp->ots_props);
99 	if (cp->flags & BTP_OTS_REGISTER_OBJECT_FLAGS_SKIP_UNSUPPORTED_PROPS) {
100 		props &= supported_props;
101 	}
102 
103 	obj = get_object();
104 	if (!obj) {
105 		return BTP_STATUS_FAILED;
106 	}
107 
108 	(void)memset(&obj_data, 0, sizeof(obj_data));
109 
110 	memcpy(obj->name, cp->name, cp->name_len);
111 	obj_data.object = obj;
112 	obj_data.size.cur = sys_le32_to_cpu(cp->current_size);
113 	obj_data.size.alloc = sys_le32_to_cpu(cp->alloc_size);
114 	obj_data.props = props;
115 
116 	/* bt_ots_obj_add() lacks user_data so we need to use global for
117 	 * passing this
118 	 */
119 	object_being_created = &obj_data;
120 
121 	param.size = obj_data.size.alloc;
122 	param.type.uuid.type = BT_UUID_TYPE_16;
123 	param.type.uuid_16.val = BT_UUID_OTS_TYPE_UNSPECIFIED_VAL;
124 
125 	err = bt_ots_obj_add(ots, &param);
126 	object_being_created = NULL;
127 
128 	if (err < 0) {
129 		memset(obj, 0, sizeof(*obj));
130 		return BTP_STATUS_FAILED;
131 	}
132 
133 	rp->object_id = sys_cpu_to_le64(err);
134 	*rsp_len = sizeof(*rp);
135 
136 	return BTP_STATUS_SUCCESS;
137 }
138 
139 static const struct btp_handler ots_handlers[] = {
140 	{
141 		.opcode = BTP_OTS_READ_SUPPORTED_COMMANDS,
142 		.index = BTP_INDEX_NONE,
143 		.expect_len = 0,
144 		.func = ots_supported_commands
145 	},
146 	{
147 		.opcode = BTP_OTS_REGISTER_OBJECT,
148 		.index = 0,
149 		.expect_len = BTP_HANDLER_LENGTH_VARIABLE,
150 		.func = register_object
151 	},
152 };
153 
ots_obj_created(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const struct bt_ots_obj_add_param * add_param,struct bt_ots_obj_created_desc * created_desc)154 static int ots_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
155 			   const struct bt_ots_obj_add_param *add_param,
156 			   struct bt_ots_obj_created_desc *created_desc)
157 {
158 	struct object *obj;
159 
160 	LOG_DBG("id=%"PRIu64" size=%u", id, add_param->size);
161 
162 	/* TS suggests to use OTS service UUID for testing this */
163 	if (conn && bt_uuid_cmp(&add_param->type.uuid, BT_UUID_OTS) == 0) {
164 		return -ENOTSUP;
165 	}
166 
167 	if (add_param->size > OBJ_MAX_SIZE) {
168 		return -ENOMEM;
169 	}
170 
171 	if (conn || !object_being_created) {
172 		uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
173 
174 		if (obj_index >= OBJ_POOL_SIZE) {
175 			return -ENOMEM;
176 		}
177 
178 		obj = &objects[obj_index];
179 		if (obj->in_use) {
180 			return -ENOMEM;
181 		}
182 
183 		obj->in_use = false;
184 		created_desc->name = obj->name;
185 		created_desc->size.alloc = OBJ_MAX_SIZE;
186 		BT_OTS_OBJ_SET_PROP_READ(created_desc->props);
187 		BT_OTS_OBJ_SET_PROP_WRITE(created_desc->props);
188 		BT_OTS_OBJ_SET_PROP_PATCH(created_desc->props);
189 		BT_OTS_OBJ_SET_PROP_DELETE(created_desc->props);
190 	} else {
191 		obj = object_being_created->object;
192 		created_desc->name = obj->name;
193 		created_desc->size = object_being_created->size;
194 		created_desc->props = object_being_created->props;
195 	}
196 
197 	return 0;
198 }
199 
ots_obj_deleted(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)200 static int ots_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
201 			    uint64_t id)
202 {
203 	uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
204 	struct object *obj;
205 
206 	LOG_DBG("id=%"PRIu64, id);
207 
208 	if (obj_index >= OBJ_POOL_SIZE) {
209 		return -ENOENT;
210 	}
211 
212 	obj = &objects[obj_index];
213 	memset(obj, 0, sizeof(*obj));
214 
215 	return 0;
216 }
217 
ots_obj_selected(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)218 static void ots_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
219 			     uint64_t id)
220 {
221 	LOG_DBG("id=%"PRIu64, id);
222 }
223 
ots_obj_read(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,void ** data,size_t len,off_t offset)224 static ssize_t ots_obj_read(struct bt_ots *ots, struct bt_conn *conn,
225 			    uint64_t id, void **data, size_t len,
226 			    off_t offset)
227 {
228 	uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
229 
230 	LOG_DBG("id=%"PRIu64" data=%p offset=%ld len=%zu", id, data, (long)offset, len);
231 
232 	if (!data) {
233 		return 0;
234 	}
235 
236 	if (obj_index >= OBJ_POOL_SIZE) {
237 		return -ENOENT;
238 	}
239 
240 	*data = &objects[obj_index].data[offset];
241 
242 	return len;
243 }
244 
ots_obj_write(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const void * data,size_t len,off_t offset,size_t rem)245 static ssize_t ots_obj_write(struct bt_ots *ots, struct bt_conn *conn,
246 			     uint64_t id, const void *data, size_t len,
247 			     off_t offset, size_t rem)
248 {
249 	uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
250 
251 	LOG_DBG("id=%"PRIu64" data=%p offset=%ld len=%zu", id, data, (long)offset, len);
252 
253 	if (obj_index >= OBJ_POOL_SIZE) {
254 		return -ENOENT;
255 	}
256 
257 	(void)memcpy(&objects[obj_index].data[offset], data, len);
258 
259 	return len;
260 }
261 
ots_obj_name_written(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const char * cur_name,const char * new_name)262 static void ots_obj_name_written(struct bt_ots *ots, struct bt_conn *conn,
263 				 uint64_t id, const char *cur_name, const char *new_name)
264 {
265 	LOG_DBG("id=%"PRIu64"cur_name=%s new_name=%s", id, cur_name, new_name);
266 }
267 
ots_obj_cal_checksum(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,off_t offset,size_t len,void ** data)268 static int ots_obj_cal_checksum(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
269 				off_t offset, size_t len, void **data)
270 {
271 	uint32_t obj_index = OTS_OBJ_ID_TO_OBJ_IDX(id);
272 
273 	if (obj_index >= OBJ_POOL_SIZE) {
274 		return -ENOENT;
275 	}
276 
277 	*data = &objects[obj_index].data[offset];
278 	return 0;
279 }
280 
281 static struct bt_ots_cb ots_callbacks = {
282 	.obj_created = ots_obj_created,
283 	.obj_deleted = ots_obj_deleted,
284 	.obj_selected = ots_obj_selected,
285 	.obj_read = ots_obj_read,
286 	.obj_write = ots_obj_write,
287 	.obj_name_written = ots_obj_name_written,
288 	.obj_cal_checksum = ots_obj_cal_checksum,
289 };
290 
ots_init(void)291 static int ots_init(void)
292 {
293 	int err;
294 	struct bt_ots_init_param ots_init;
295 
296 	/* Configure OTS initialization. */
297 	(void)memset(&ots_init, 0, sizeof(ots_init));
298 	BT_OTS_OACP_SET_FEAT_READ(ots_init.features.oacp);
299 	BT_OTS_OACP_SET_FEAT_WRITE(ots_init.features.oacp);
300 	BT_OTS_OACP_SET_FEAT_CREATE(ots_init.features.oacp);
301 	BT_OTS_OACP_SET_FEAT_DELETE(ots_init.features.oacp);
302 	BT_OTS_OACP_SET_FEAT_CHECKSUM(ots_init.features.oacp);
303 	BT_OTS_OACP_SET_FEAT_PATCH(ots_init.features.oacp);
304 	BT_OTS_OLCP_SET_FEAT_GO_TO(ots_init.features.olcp);
305 	ots_init.cb = &ots_callbacks;
306 
307 	/* Initialize OTS instance. */
308 	err = bt_ots_init(ots, &ots_init);
309 	if (err) {
310 		LOG_ERR("Failed to init OTS (err:%d)\n", err);
311 		return err;
312 	}
313 
314 	return 0;
315 }
316 
tester_init_ots(void)317 uint8_t tester_init_ots(void)
318 {
319 	int err;
320 
321 	/* TODO there is no API to return OTS instance to pool */
322 	if (!ots) {
323 		ots = bt_ots_free_instance_get();
324 	}
325 
326 	if (!ots) {
327 		return BTP_STATUS_FAILED;
328 	}
329 
330 	err = ots_init();
331 	if (err) {
332 		return BTP_STATUS_VAL(err);
333 	}
334 
335 	tester_register_command_handlers(BTP_SERVICE_ID_OTS, ots_handlers,
336 					 ARRAY_SIZE(ots_handlers));
337 
338 	return BTP_STATUS_SUCCESS;
339 }
340 
tester_unregister_ots(void)341 uint8_t tester_unregister_ots(void)
342 {
343 	memset(objects, 0, sizeof(objects));
344 
345 	return BTP_STATUS_SUCCESS;
346 }
347