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 <init.h>
12 #include <sys/printk.h>
13 #include <sys/byteorder.h>
14 #include <zephyr.h>
15 
16 #include <bluetooth/bluetooth.h>
17 #include <bluetooth/l2cap.h>
18 #include <bluetooth/conn.h>
19 #include <bluetooth/uuid.h>
20 #include <bluetooth/gatt.h>
21 
22 #include <sys/check.h>
23 
24 #include <bluetooth/services/ots.h>
25 #include "ots_internal.h"
26 #include "ots_obj_manager_internal.h"
27 #include "ots_dir_list_internal.h"
28 
29 #include <logging/log.h>
30 
31 LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
32 
33 #if defined(CONFIG_BT_OTS_OACP_READ_SUPPORT)
34 #define OACP_FEAT_BIT_READ BIT(BT_OTS_OACP_FEAT_READ)
35 #else
36 #define OACP_FEAT_BIT_READ 0
37 #endif
38 
39 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
40 #define OACP_FEAT_BIT_WRITE BIT(BT_OTS_OACP_FEAT_WRITE)
41 #else
42 #define OACP_FEAT_BIT_WRITE 0
43 #endif
44 
45 #if defined(CONFIG_BT_OTS_OACP_PATCH_SUPPORT)
46 #define OACP_FEAT_BIT_PATCH BIT(BT_OTS_OACP_FEAT_PATCH)
47 #else
48 #define OACP_FEAT_BIT_PATCH 0
49 #endif
50 
51 /* OACP features supported by Kconfig */
52 #define OACP_FEAT (		\
53 	OACP_FEAT_BIT_READ |	\
54 	OACP_FEAT_BIT_WRITE |	\
55 	OACP_FEAT_BIT_PATCH)
56 
57 #if defined(CONFIG_BT_OTS_OLCP_GO_TO_SUPPORT)
58 #define OLCP_FEAT_BIT_GOTO BIT(BT_OTS_OLCP_FEAT_GO_TO)
59 #else
60 #define OLCP_FEAT_BIT_GOTO 0
61 #endif
62 
63 /* OLCP features supported by Kconfig */
64 #define OLCP_FEAT OLCP_FEAT_BIT_GOTO
65 
ots_obj_validate_prop_against_oacp(uint32_t prop,uint32_t oacp)66 static bool ots_obj_validate_prop_against_oacp(uint32_t prop, uint32_t oacp)
67 {
68 	if (BT_OTS_OBJ_GET_PROP_DELETE(prop) > 0 && BT_OTS_OACP_GET_FEAT_DELETE(oacp) == 0) {
69 		return false;
70 	}
71 
72 	if (BT_OTS_OBJ_GET_PROP_EXECUTE(prop) > 0 && BT_OTS_OACP_GET_FEAT_EXECUTE(oacp) == 0) {
73 		return false;
74 	}
75 
76 	if (BT_OTS_OBJ_GET_PROP_READ(prop) > 0 && BT_OTS_OACP_GET_FEAT_READ(oacp) == 0) {
77 		return false;
78 	}
79 
80 	if (BT_OTS_OBJ_GET_PROP_WRITE(prop) > 0 && BT_OTS_OACP_GET_FEAT_WRITE(oacp) == 0) {
81 		return false;
82 	}
83 
84 	if (BT_OTS_OBJ_GET_PROP_APPEND(prop) > 0 && BT_OTS_OACP_GET_FEAT_APPEND(oacp) == 0) {
85 		return false;
86 	}
87 
88 	if (BT_OTS_OBJ_GET_PROP_TRUNCATE(prop) > 0 && BT_OTS_OACP_GET_FEAT_TRUNCATE(oacp) == 0) {
89 		return false;
90 	}
91 
92 	if (BT_OTS_OBJ_GET_PROP_PATCH(prop) > 0 && BT_OTS_OACP_GET_FEAT_PATCH(oacp) == 0) {
93 		return false;
94 	}
95 
96 	return true;
97 }
98 
ots_feature_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)99 static ssize_t ots_feature_read(struct bt_conn *conn,
100 				const struct bt_gatt_attr *attr, void *buf,
101 				uint16_t len, uint16_t offset)
102 {
103 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
104 
105 	LOG_DBG("OTS Feature GATT Read Operation");
106 
107 	return bt_gatt_attr_read(conn, attr, buf, len, offset, &ots->features,
108 		sizeof(ots->features));
109 }
110 
ots_obj_name_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)111 static ssize_t ots_obj_name_read(struct bt_conn *conn,
112 				 const struct bt_gatt_attr *attr, void *buf,
113 				 uint16_t len, uint16_t offset)
114 {
115 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
116 
117 	LOG_DBG("OTS Object Name GATT Read Operation");
118 
119 	if (!ots->cur_obj) {
120 		LOG_DBG("No Current Object selected in OTS!");
121 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
122 	}
123 
124 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
125 				 ots->cur_obj->metadata.name,
126 				 strlen(ots->cur_obj->metadata.name));
127 }
128 
129 #if defined(CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT)
ots_obj_name_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)130 ssize_t ots_obj_name_write(struct bt_conn *conn,
131 			   const struct bt_gatt_attr *attr,
132 			   const void *buf, uint16_t len,
133 			   uint16_t offset, uint8_t flags)
134 {
135 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
136 	struct bt_gatt_ots_object *obj = NULL;
137 	int rc = 0;
138 	char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
139 
140 	LOG_DBG("OTS Object Name GATT Write Operation");
141 
142 	if (!ots->cur_obj) {
143 		LOG_DBG("No Current Object selected in OTS!");
144 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
145 	}
146 
147 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
148 	    ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST) {
149 		LOG_DBG("Rejecting name write for the directory list object.");
150 		return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
151 	}
152 
153 	if (offset > 0) {
154 		LOG_DBG("Rejecting a long write, offset must be 0!");
155 		return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
156 	}
157 
158 	if (len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
159 		LOG_DBG("Object name is too long!");
160 		return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
161 	}
162 
163 	/* Construct a temporary name for duplication detection */
164 	memcpy(name, buf, len);
165 	name[len] = '\0';
166 
167 	rc = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager, &obj);
168 	while (rc == 0) {
169 		if (obj != ots->cur_obj && strcmp(name, obj->metadata.name) == 0) {
170 			LOG_DBG("Object name is duplicated!");
171 			return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NAME_ALREADY_EXISTS);
172 		}
173 		rc = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager, obj, &obj);
174 	}
175 
176 	/* Update real object name after no duplicate detected */
177 	strcpy(ots->cur_obj->metadata.name, name);
178 
179 	if (ots->cb->obj_name_written) {
180 		ots->cb->obj_name_written(ots, conn, ots->cur_obj->id,
181 					  ots->cur_obj->metadata.name);
182 	}
183 
184 	return len;
185 }
186 #endif
187 
ots_obj_type_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)188 static ssize_t ots_obj_type_read(struct bt_conn *conn,
189 				 const struct bt_gatt_attr *attr, void *buf,
190 				 uint16_t len, uint16_t offset)
191 {
192 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
193 	struct bt_ots_obj_metadata *obj_meta;
194 
195 	LOG_DBG("OTS Object Type GATT Read Operation");
196 
197 	if (!ots->cur_obj) {
198 		LOG_DBG("No Current Object selected in OTS!");
199 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
200 	}
201 
202 	obj_meta = &ots->cur_obj->metadata;
203 	if (obj_meta->type.uuid.type == BT_UUID_TYPE_128) {
204 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
205 					 obj_meta->type.uuid_128.val,
206 					 sizeof(obj_meta->type.uuid_128.val));
207 	} else {
208 		return bt_gatt_attr_read(conn, attr, buf, len, offset,
209 					 &obj_meta->type.uuid_16.val,
210 					 sizeof(obj_meta->type.uuid_16.val));
211 	}
212 }
213 
ots_obj_size_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)214 static ssize_t ots_obj_size_read(struct bt_conn *conn,
215 				 const struct bt_gatt_attr *attr, void *buf,
216 				 uint16_t len, uint16_t offset)
217 {
218 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
219 
220 	LOG_DBG("OTS Object Size GATT Read Operation");
221 
222 	if (!ots->cur_obj) {
223 		LOG_DBG("No Current Object selected in OTS!");
224 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
225 	}
226 
227 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
228 				 &ots->cur_obj->metadata.size,
229 				 sizeof(ots->cur_obj->metadata.size));
230 }
231 
ots_obj_id_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)232 static ssize_t ots_obj_id_read(struct bt_conn *conn,
233 			       const struct bt_gatt_attr *attr, void *buf,
234 			       uint16_t len, uint16_t offset)
235 {
236 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
237 	uint8_t id[BT_OTS_OBJ_ID_SIZE];
238 	char id_str[BT_OTS_OBJ_ID_STR_LEN];
239 
240 	LOG_DBG("OTS Object ID GATT Read Operation");
241 
242 	if (!ots->cur_obj) {
243 		LOG_DBG("No Current Object selected in OTS!");
244 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
245 	}
246 
247 	sys_put_le48(ots->cur_obj->id, id);
248 
249 	bt_ots_obj_id_to_str(ots->cur_obj->id, id_str,
250 				      sizeof(id_str));
251 	LOG_DBG("Current Object ID: %s", log_strdup(id_str));
252 
253 	return bt_gatt_attr_read(conn, attr, buf, len, offset, id, sizeof(id));
254 }
255 
ots_obj_prop_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)256 static ssize_t ots_obj_prop_read(struct bt_conn *conn,
257 				 const struct bt_gatt_attr *attr, void *buf,
258 				 uint16_t len, uint16_t offset)
259 {
260 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
261 
262 	LOG_DBG("OTS Object Properties GATT Read Operation");
263 
264 	if (!ots->cur_obj) {
265 		LOG_DBG("No Current Object selected in OTS!");
266 		return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
267 	}
268 
269 	return bt_gatt_attr_read(conn, attr, buf, len, offset,
270 				 &ots->cur_obj->metadata.props,
271 				 sizeof(ots->cur_obj->metadata.props));
272 }
273 
bt_ots_obj_add(struct bt_ots * ots,struct bt_ots_obj_metadata * obj_init)274 int bt_ots_obj_add(struct bt_ots *ots,
275 			    struct bt_ots_obj_metadata *obj_init)
276 {
277 	int err;
278 	struct bt_gatt_ots_object *obj;
279 	size_t name_len;
280 
281 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
282 	    ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
283 		LOG_DBG("Directory Listing Object is being read");
284 		return -EBUSY;
285 	}
286 
287 	name_len = strlen(obj_init->name);
288 
289 	CHECKIF(name_len == 0 || name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
290 		LOG_DBG("Invalid name length %zu", name_len);
291 		return -EINVAL;
292 	}
293 
294 	CHECKIF(!ots_obj_validate_prop_against_oacp(obj_init->props, ots->features.oacp)) {
295 		LOG_DBG("Object properties (0x%04X) are not a subset of OACP (0x%04X)",
296 				obj_init->props, ots->features.oacp);
297 		return -ENOTSUP;
298 	}
299 
300 	err = bt_gatt_ots_obj_manager_obj_add(ots->obj_manager, &obj);
301 	if (err) {
302 		LOG_ERR("No space available in the object manager");
303 		return err;
304 	}
305 
306 	/* Initialize object. */
307 	memcpy(&obj->metadata, obj_init, sizeof(obj->metadata));
308 
309 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
310 		bt_ots_dir_list_obj_add(ots->dir_list, ots->obj_manager, ots->cur_obj, obj);
311 	}
312 
313 	/* Request object data. */
314 	if (ots->cb->obj_created) {
315 		err = ots->cb->obj_created(ots, NULL, obj->id, obj_init);
316 		if (err) {
317 			bt_gatt_ots_obj_manager_obj_delete(obj);
318 
319 			if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
320 				bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager,
321 							   ots->cur_obj, obj);
322 			}
323 
324 			return err;
325 		}
326 	}
327 
328 	return 0;
329 }
330 
bt_ots_obj_delete(struct bt_ots * ots,uint64_t id)331 int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
332 {
333 	int err;
334 	struct bt_gatt_ots_object *obj;
335 
336 	err = bt_gatt_ots_obj_manager_obj_get(ots->obj_manager, id, &obj);
337 	if (err) {
338 		return err;
339 	}
340 
341 	if (ots->cur_obj == obj) {
342 		if (obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
343 			return -EBUSY;
344 		}
345 		ots->cur_obj = NULL;
346 	}
347 
348 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
349 	    ots->dir_list->dir_list_obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
350 		LOG_DBG("Directory Listing Object is being read");
351 		return -EBUSY;
352 	}
353 
354 	err = bt_gatt_ots_obj_manager_obj_delete(obj);
355 	if (err) {
356 		return err;
357 	}
358 
359 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
360 		bt_ots_dir_list_obj_remove(ots->dir_list, ots->obj_manager, ots->cur_obj, obj);
361 	}
362 
363 	if (ots->cb->obj_deleted) {
364 		ots->cb->obj_deleted(ots, NULL, obj->id);
365 	}
366 
367 	return 0;
368 }
369 
370 #if defined(CONFIG_BT_OTS_SECONDARY_SVC)
bt_ots_svc_decl_get(struct bt_ots * ots)371 void *bt_ots_svc_decl_get(struct bt_ots *ots)
372 {
373 	return ots->service->attrs;
374 }
375 #endif
376 
bt_ots_init(struct bt_ots * ots,struct bt_ots_init * ots_init)377 int bt_ots_init(struct bt_ots *ots,
378 		     struct bt_ots_init *ots_init)
379 {
380 	int err;
381 
382 	if (!ots || !ots_init || !ots_init->cb) {
383 		return -EINVAL;
384 	}
385 
386 	__ASSERT(ots_init->cb->obj_created,
387 		 "Callback for object creation is not set");
388 	__ASSERT(ots_init->cb->obj_read ||
389 		 !BT_OTS_OACP_GET_FEAT_READ(ots_init->features.oacp),
390 		 "Callback for object reading is not set");
391 	__ASSERT(ots_init->cb->obj_write ||
392 		 !BT_OTS_OACP_GET_FEAT_WRITE(ots_init->features.oacp),
393 		 "Callback for object write is not set");
394 
395 	/* Set callback structure. */
396 	ots->cb = ots_init->cb;
397 
398 	/* Check OACP supported features against Kconfig. */
399 	if (ots_init->features.oacp & (~((uint32_t) OACP_FEAT))) {
400 		return -ENOTSUP;
401 	}
402 
403 	ots->features.oacp = ots_init->features.oacp;
404 	LOG_DBG("OACP features: 0x%04X", ots->features.oacp);
405 
406 	/* Check OLCP supported features against Kconfig. */
407 	if (ots_init->features.olcp & (~((uint32_t) OLCP_FEAT))) {
408 		return -ENOTSUP;
409 	}
410 	ots->features.olcp = ots_init->features.olcp;
411 	LOG_DBG("OLCP features: 0x%04X", ots->features.olcp);
412 
413 	/* Register L2CAP context. */
414 	err = bt_gatt_ots_l2cap_register(&ots->l2cap);
415 	if (err) {
416 		return err;
417 	}
418 
419 	err = bt_gatt_service_register(ots->service);
420 	if (err) {
421 		bt_gatt_ots_l2cap_unregister(&ots->l2cap);
422 
423 		return err;
424 	}
425 
426 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
427 		bt_ots_dir_list_init(&ots->dir_list, ots->obj_manager);
428 	}
429 
430 	LOG_DBG("Initialized OTS");
431 
432 	return 0;
433 }
434 
435 #if defined(CONFIG_BT_OTS_SECONDARY_SVC)
436 	#define BT_GATT_OTS_SERVICE	BT_GATT_SECONDARY_SERVICE
437 #else
438 	#define BT_GATT_OTS_SERVICE	BT_GATT_PRIMARY_SERVICE
439 #endif
440 
441 #if defined(CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT)
442 	#define BT_OTS_OBJ_NAME_GATT_CHRC  (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE)
443 	#define BT_OTS_OBJ_NAME_GATT_PERM  (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
444 	#define BT_OTS_OBJ_NAME_GATT_WRITE (ots_obj_name_write)
445 #else
446 	#define BT_OTS_OBJ_NAME_GATT_CHRC  (BT_GATT_CHRC_READ)
447 	#define BT_OTS_OBJ_NAME_GATT_PERM  (BT_GATT_PERM_READ)
448 	#define BT_OTS_OBJ_NAME_GATT_WRITE (NULL)
449 #endif
450 
451 #define BT_GATT_OTS_ATTRS(_ots) {					\
452 	BT_GATT_OTS_SERVICE(BT_UUID_OTS),				\
453 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_FEATURE,			\
454 		BT_GATT_CHRC_READ, BT_GATT_PERM_READ,			\
455 		ots_feature_read, NULL, &_ots),				\
456 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_NAME,			\
457 		BT_OTS_OBJ_NAME_GATT_CHRC, BT_OTS_OBJ_NAME_GATT_PERM,	\
458 		ots_obj_name_read, BT_OTS_OBJ_NAME_GATT_WRITE, &_ots),	\
459 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_TYPE,			\
460 		BT_GATT_CHRC_READ, BT_GATT_PERM_READ,			\
461 		ots_obj_type_read, NULL, &_ots),			\
462 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_SIZE,			\
463 		BT_GATT_CHRC_READ, BT_GATT_PERM_READ,			\
464 		ots_obj_size_read, NULL, &_ots),			\
465 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_ID,				\
466 		BT_GATT_CHRC_READ, BT_GATT_PERM_READ,			\
467 		ots_obj_id_read, NULL, &_ots),				\
468 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_PROPERTIES,			\
469 		BT_GATT_CHRC_READ, BT_GATT_PERM_READ,			\
470 		ots_obj_prop_read, NULL, &_ots),			\
471 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_ACTION_CP,			\
472 		BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE,		\
473 		BT_GATT_PERM_WRITE, NULL,				\
474 		bt_gatt_ots_oacp_write, &_ots),				\
475 	BT_GATT_CCC_MANAGED(&_ots.oacp_ind.ccc,				\
476 		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),		\
477 	BT_GATT_CHARACTERISTIC(BT_UUID_OTS_LIST_CP,			\
478 		BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE,		\
479 		BT_GATT_PERM_WRITE, NULL,				\
480 		bt_gatt_ots_olcp_write, &_ots),				\
481 	BT_GATT_CCC_MANAGED(&_ots.olcp_ind.ccc,				\
482 		BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)			\
483 }
484 
485 #define BT_GATT_OTS_INSTANCE_LIST_SIZE	(ARRAY_SIZE(ots_instances))
486 #define BT_GATT_OTS_INSTANCE_LIST_START	ots_instances
487 #define BT_GATT_OTS_INSTANCE_LIST_END	\
488 	(&ots_instances[BT_GATT_OTS_INSTANCE_LIST_SIZE])
489 
490 #define BT_GATT_OTS_SERVICE_LIST_START	ots_service_list
491 
492 static struct bt_ots ots_instances[CONFIG_BT_OTS_MAX_INST_CNT];
493 static uint32_t instance_cnt;
494 BT_GATT_SERVICE_INSTANCE_DEFINE(ots_service_list, ots_instances,
495 				CONFIG_BT_OTS_MAX_INST_CNT,
496 				BT_GATT_OTS_ATTRS);
497 
bt_ots_free_instance_get(void)498 struct bt_ots *bt_ots_free_instance_get(void)
499 {
500 	if (instance_cnt >= BT_GATT_OTS_INSTANCE_LIST_SIZE) {
501 		return NULL;
502 	}
503 
504 	return &BT_GATT_OTS_INSTANCE_LIST_START[instance_cnt++];
505 }
506 
bt_gatt_ots_instances_prepare(const struct device * dev)507 static int bt_gatt_ots_instances_prepare(const struct device *dev)
508 {
509 	uint32_t index;
510 	struct bt_ots *instance;
511 
512 	for (instance = BT_GATT_OTS_INSTANCE_LIST_START, index = 0;
513 	     instance != BT_GATT_OTS_INSTANCE_LIST_END;
514 	     instance++, index++) {
515 		/* Assign an object pool to the OTS instance. */
516 		instance->obj_manager = bt_gatt_ots_obj_manager_assign();
517 
518 		if (!instance->obj_manager) {
519 			LOG_ERR("OTS Object manager instance not available");
520 			return -ENOMEM;
521 		}
522 
523 		/* Assign pointer to the service descriptor. */
524 		instance->service = &BT_GATT_OTS_SERVICE_LIST_START[index];
525 
526 		/* Initialize CCC descriptors for characteristics with
527 		 * indication properties.
528 		 */
529 		instance->oacp_ind.ccc.cfg_changed =
530 			bt_gatt_ots_oacp_cfg_changed;
531 		instance->olcp_ind.ccc.cfg_changed =
532 			bt_gatt_ots_olcp_cfg_changed;
533 	}
534 
535 	return 0;
536 }
537 
538 SYS_INIT(bt_gatt_ots_instances_prepare, APPLICATION,
539 	 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
540