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