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/init.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/sys/byteorder.h>
14 #include <zephyr/kernel.h>
15
16 #include <zephyr/bluetooth/bluetooth.h>
17 #include <zephyr/bluetooth/l2cap.h>
18 #include <zephyr/bluetooth/conn.h>
19 #include <zephyr/bluetooth/uuid.h>
20 #include <zephyr/bluetooth/gatt.h>
21
22 #include <zephyr/sys/check.h>
23
24 #include <zephyr/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 <zephyr/logging/log.h>
30
31 LOG_MODULE_REGISTER(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
32
33 #if defined(CONFIG_BT_OTS_OACP_CREATE_SUPPORT)
34 #define OACP_FEAT_BIT_CREATE BIT(BT_OTS_OACP_FEAT_CREATE)
35 #else
36 #define OACP_FEAT_BIT_CREATE 0
37 #endif
38
39 #if defined(CONFIG_BT_OTS_OACP_DELETE_SUPPORT)
40 #define OACP_FEAT_BIT_DELETE BIT(BT_OTS_OACP_FEAT_DELETE)
41 #else
42 #define OACP_FEAT_BIT_DELETE 0
43 #endif
44
45 #if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
46 #define OACP_FEAT_BIT_CRC BIT(BT_OTS_OACP_FEAT_CHECKSUM)
47 #else
48 #define OACP_FEAT_BIT_CRC 0
49 #endif
50
51 #if defined(CONFIG_BT_OTS_OACP_READ_SUPPORT)
52 #define OACP_FEAT_BIT_READ BIT(BT_OTS_OACP_FEAT_READ)
53 #else
54 #define OACP_FEAT_BIT_READ 0
55 #endif
56
57 #if defined(CONFIG_BT_OTS_OACP_WRITE_SUPPORT)
58 #define OACP_FEAT_BIT_WRITE BIT(BT_OTS_OACP_FEAT_WRITE)
59 #else
60 #define OACP_FEAT_BIT_WRITE 0
61 #endif
62
63 #if defined(CONFIG_BT_OTS_OACP_PATCH_SUPPORT)
64 #define OACP_FEAT_BIT_PATCH BIT(BT_OTS_OACP_FEAT_PATCH)
65 #else
66 #define OACP_FEAT_BIT_PATCH 0
67 #endif
68
69 /* OACP features supported by Kconfig */
70 #define OACP_FEAT ( \
71 OACP_FEAT_BIT_CREATE | \
72 OACP_FEAT_BIT_DELETE | \
73 OACP_FEAT_BIT_CRC | \
74 OACP_FEAT_BIT_READ | \
75 OACP_FEAT_BIT_WRITE | \
76 OACP_FEAT_BIT_PATCH)
77
78 #if defined(CONFIG_BT_OTS_OLCP_GO_TO_SUPPORT)
79 #define OLCP_FEAT_BIT_GOTO BIT(BT_OTS_OLCP_FEAT_GO_TO)
80 #else
81 #define OLCP_FEAT_BIT_GOTO 0
82 #endif
83
84 /* OLCP features supported by Kconfig */
85 #define OLCP_FEAT OLCP_FEAT_BIT_GOTO
86
ots_obj_validate_prop_against_oacp(uint32_t prop,uint32_t oacp)87 static bool ots_obj_validate_prop_against_oacp(uint32_t prop, uint32_t oacp)
88 {
89 if (BT_OTS_OBJ_GET_PROP_DELETE(prop) > 0 && BT_OTS_OACP_GET_FEAT_DELETE(oacp) == 0) {
90 return false;
91 }
92
93 if (BT_OTS_OBJ_GET_PROP_EXECUTE(prop) > 0 && BT_OTS_OACP_GET_FEAT_EXECUTE(oacp) == 0) {
94 return false;
95 }
96
97 if (BT_OTS_OBJ_GET_PROP_READ(prop) > 0 && BT_OTS_OACP_GET_FEAT_READ(oacp) == 0) {
98 return false;
99 }
100
101 if (BT_OTS_OBJ_GET_PROP_WRITE(prop) > 0 && BT_OTS_OACP_GET_FEAT_WRITE(oacp) == 0) {
102 return false;
103 }
104
105 if (BT_OTS_OBJ_GET_PROP_APPEND(prop) > 0 && BT_OTS_OACP_GET_FEAT_APPEND(oacp) == 0) {
106 return false;
107 }
108
109 if (BT_OTS_OBJ_GET_PROP_TRUNCATE(prop) > 0 && BT_OTS_OACP_GET_FEAT_TRUNCATE(oacp) == 0) {
110 return false;
111 }
112
113 if (BT_OTS_OBJ_GET_PROP_PATCH(prop) > 0 && BT_OTS_OACP_GET_FEAT_PATCH(oacp) == 0) {
114 return false;
115 }
116
117 return true;
118 }
119
ots_feature_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)120 static ssize_t ots_feature_read(struct bt_conn *conn,
121 const struct bt_gatt_attr *attr, void *buf,
122 uint16_t len, uint16_t offset)
123 {
124 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
125
126 LOG_DBG("OTS Feature GATT Read Operation");
127
128 return bt_gatt_attr_read(conn, attr, buf, len, offset, &ots->features,
129 sizeof(ots->features));
130 }
131
ots_obj_name_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)132 static ssize_t ots_obj_name_read(struct bt_conn *conn,
133 const struct bt_gatt_attr *attr, void *buf,
134 uint16_t len, uint16_t offset)
135 {
136 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
137
138 LOG_DBG("OTS Object Name GATT Read Operation");
139
140 if (!ots->cur_obj) {
141 LOG_DBG("No Current Object selected in OTS!");
142 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
143 }
144
145 return bt_gatt_attr_read(conn, attr, buf, len, offset,
146 ots->cur_obj->metadata.name,
147 strlen(ots->cur_obj->metadata.name));
148 }
149
150 #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)151 ssize_t ots_obj_name_write(struct bt_conn *conn,
152 const struct bt_gatt_attr *attr,
153 const void *buf, uint16_t len,
154 uint16_t offset, uint8_t flags)
155 {
156 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
157 struct bt_gatt_ots_object *obj = NULL;
158 int rc = 0;
159 char name[CONFIG_BT_OTS_OBJ_MAX_NAME_LEN + 1];
160
161 LOG_DBG("OTS Object Name GATT Write Operation");
162
163 if (!ots->cur_obj) {
164 LOG_DBG("No Current Object selected in OTS!");
165 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
166 }
167
168 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
169 ots->cur_obj->id == OTS_OBJ_ID_DIR_LIST) {
170 LOG_DBG("Rejecting name write for the directory list object.");
171 return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
172 }
173
174 if (offset > 0) {
175 LOG_DBG("Rejecting a long write, offset must be 0!");
176 return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
177 }
178
179 if (len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
180 LOG_DBG("Object name is too long!");
181 return BT_GATT_ERR(BT_GATT_OTS_WRITE_REQUEST_REJECTED);
182 }
183
184 /* Construct a temporary name for duplication detection */
185 memcpy(name, buf, len);
186 name[len] = '\0';
187
188 rc = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager, &obj);
189 while (rc == 0) {
190 if (obj != ots->cur_obj && strcmp(name, obj->metadata.name) == 0) {
191 LOG_DBG("Object name is duplicated!");
192 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NAME_ALREADY_EXISTS);
193 }
194 rc = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager, obj, &obj);
195 }
196
197 /* No duplicate detected, notify application and update real object name */
198 if (ots->cb->obj_name_written) {
199 ots->cb->obj_name_written(ots, conn, ots->cur_obj->id,
200 ots->cur_obj->metadata.name, name);
201 }
202
203 strcpy(ots->cur_obj->metadata.name, name);
204
205 return len;
206 }
207 #endif
208
ots_obj_type_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)209 static ssize_t ots_obj_type_read(struct bt_conn *conn,
210 const struct bt_gatt_attr *attr, void *buf,
211 uint16_t len, uint16_t offset)
212 {
213 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
214 struct bt_ots_obj_metadata *obj_meta;
215
216 LOG_DBG("OTS Object Type GATT Read Operation");
217
218 if (!ots->cur_obj) {
219 LOG_DBG("No Current Object selected in OTS!");
220 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
221 }
222
223 obj_meta = &ots->cur_obj->metadata;
224 switch (obj_meta->type.uuid.type) {
225 case BT_UUID_TYPE_16:
226 return bt_gatt_attr_read(conn, attr, buf, len, offset,
227 &obj_meta->type.uuid_16.val,
228 sizeof(obj_meta->type.uuid_16.val));
229 case BT_UUID_TYPE_128:
230 return bt_gatt_attr_read(conn, attr, buf, len, offset,
231 obj_meta->type.uuid_128.val,
232 sizeof(obj_meta->type.uuid_128.val));
233 default:
234 return -EINVAL;
235 }
236 }
237
ots_obj_size_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)238 static ssize_t ots_obj_size_read(struct bt_conn *conn,
239 const struct bt_gatt_attr *attr, void *buf,
240 uint16_t len, uint16_t offset)
241 {
242 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
243
244 LOG_DBG("OTS Object Size GATT Read Operation");
245
246 if (!ots->cur_obj) {
247 LOG_DBG("No Current Object selected in OTS!");
248 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
249 }
250
251 return bt_gatt_attr_read(conn, attr, buf, len, offset,
252 &ots->cur_obj->metadata.size,
253 sizeof(ots->cur_obj->metadata.size));
254 }
255
ots_obj_id_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_id_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 uint8_t id[BT_OTS_OBJ_ID_SIZE];
262 char id_str[BT_OTS_OBJ_ID_STR_LEN];
263
264 LOG_DBG("OTS Object ID GATT Read Operation");
265
266 if (!ots->cur_obj) {
267 LOG_DBG("No Current Object selected in OTS!");
268 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
269 }
270
271 sys_put_le48(ots->cur_obj->id, id);
272
273 bt_ots_obj_id_to_str(ots->cur_obj->id, id_str,
274 sizeof(id_str));
275 LOG_DBG("Current Object ID: %s", id_str);
276
277 return bt_gatt_attr_read(conn, attr, buf, len, offset, id, sizeof(id));
278 }
279
ots_obj_prop_read(struct bt_conn * conn,const struct bt_gatt_attr * attr,void * buf,uint16_t len,uint16_t offset)280 static ssize_t ots_obj_prop_read(struct bt_conn *conn,
281 const struct bt_gatt_attr *attr, void *buf,
282 uint16_t len, uint16_t offset)
283 {
284 struct bt_ots *ots = (struct bt_ots *) attr->user_data;
285
286 LOG_DBG("OTS Object Properties GATT Read Operation");
287
288 if (!ots->cur_obj) {
289 LOG_DBG("No Current Object selected in OTS!");
290 return BT_GATT_ERR(BT_GATT_OTS_OBJECT_NOT_SELECTED);
291 }
292
293 return bt_gatt_attr_read(conn, attr, buf, len, offset,
294 &ots->cur_obj->metadata.props,
295 sizeof(ots->cur_obj->metadata.props));
296 }
297
bt_ots_obj_add_internal(struct bt_ots * ots,struct bt_conn * conn,const struct bt_ots_obj_add_param * param,struct bt_gatt_ots_object ** obj)298 int bt_ots_obj_add_internal(struct bt_ots *ots, struct bt_conn *conn,
299 const struct bt_ots_obj_add_param *param,
300 struct bt_gatt_ots_object **obj)
301 {
302 int err;
303 struct bt_gatt_ots_object *new_obj;
304 struct bt_ots_obj_created_desc created_desc;
305
306 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
307 !bt_ots_dir_list_is_idle(ots->dir_list)) {
308 LOG_DBG("Directory Listing Object is being read");
309 return -EBUSY;
310 }
311
312 err = bt_gatt_ots_obj_manager_obj_add(ots->obj_manager, &new_obj);
313 if (err) {
314 LOG_ERR("No space available in the object manager");
315 return err;
316 }
317
318 (void)memset(&created_desc, 0, sizeof(created_desc));
319
320 if (ots->cb->obj_created) {
321 err = ots->cb->obj_created(ots, conn, new_obj->id, param, &created_desc);
322
323 if (err) {
324 (void)bt_gatt_ots_obj_manager_obj_delete(new_obj);
325
326 return err;
327 }
328
329 if (!ots_obj_validate_prop_against_oacp(created_desc.props, ots->features.oacp)) {
330 LOG_ERR("Object properties (0x%04X) are not a subset of OACP (0x%04X)",
331 created_desc.props, ots->features.oacp);
332
333 (void)bt_ots_obj_delete(ots, new_obj->id);
334 return -ECANCELED;
335 }
336
337 if (created_desc.name == NULL) {
338 LOG_ERR("Object name must be set by application after object creation.");
339
340 (void)bt_ots_obj_delete(ots, new_obj->id);
341 return -ECANCELED;
342 }
343
344 if (created_desc.size.alloc < param->size) {
345 LOG_ERR("Object allocated size must >= requested size.");
346
347 (void)bt_ots_obj_delete(ots, new_obj->id);
348 return -ECANCELED;
349 }
350 }
351
352 new_obj->metadata.type = param->type;
353 new_obj->metadata.name = created_desc.name;
354 new_obj->metadata.size = created_desc.size;
355 new_obj->metadata.props = created_desc.props;
356
357 if (obj) {
358 *obj = new_obj;
359 }
360
361 return 0;
362 }
363
bt_ots_obj_add(struct bt_ots * ots,const struct bt_ots_obj_add_param * param)364 int bt_ots_obj_add(struct bt_ots *ots, const struct bt_ots_obj_add_param *param)
365 {
366 int err;
367 size_t name_len;
368 struct bt_gatt_ots_object *obj;
369
370 err = bt_ots_obj_add_internal(ots, NULL, param, &obj);
371 if (err) {
372 return err;
373 }
374
375 name_len = strlen(obj->metadata.name);
376 if (name_len == 0 || name_len > CONFIG_BT_OTS_OBJ_MAX_NAME_LEN) {
377 LOG_ERR("Invalid name length %zu", name_len);
378
379 (void)bt_ots_obj_delete(ots, obj->id);
380 return -ECANCELED;
381 }
382
383 if (obj->metadata.size.cur > param->size) {
384 LOG_ERR("Object current size must be less than or equal to requested size.");
385
386 (void)bt_ots_obj_delete(ots, obj->id);
387 return -ECANCELED;
388 }
389
390 return obj->id;
391 }
392
bt_ots_obj_delete(struct bt_ots * ots,uint64_t id)393 int bt_ots_obj_delete(struct bt_ots *ots, uint64_t id)
394 {
395 int err;
396 struct bt_gatt_ots_object *obj;
397
398 CHECKIF(!BT_OTS_VALID_OBJ_ID(id)) {
399 LOG_DBG("Invalid object ID 0x%016llx", id);
400
401 return -EINVAL;
402 }
403
404 err = bt_gatt_ots_obj_manager_obj_get(ots->obj_manager, id, &obj);
405 if (err) {
406 return err;
407 }
408
409 if (obj->state.type != BT_GATT_OTS_OBJECT_IDLE_STATE) {
410 return -EBUSY;
411 }
412
413 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) && ots->dir_list &&
414 !bt_ots_dir_list_is_idle(ots->dir_list)) {
415 LOG_DBG("Directory Listing Object is being read");
416 return -EBUSY;
417 }
418
419 if (ots->cb->obj_deleted) {
420 err = ots->cb->obj_deleted(ots, NULL, obj->id);
421 if (err) {
422 return err;
423 }
424 }
425
426 err = bt_gatt_ots_obj_manager_obj_delete(obj);
427 if (err) {
428 return err;
429 }
430
431 if (ots->cur_obj == obj) {
432 ots->cur_obj = NULL;
433 }
434
435 return 0;
436 }
437
438 #if defined(CONFIG_BT_OTS_SECONDARY_SVC)
bt_ots_svc_decl_get(struct bt_ots * ots)439 void *bt_ots_svc_decl_get(struct bt_ots *ots)
440 {
441 return ots->service->attrs;
442 }
443 #endif
444
oacp_indicate_work_handler(struct k_work * work)445 static void oacp_indicate_work_handler(struct k_work *work)
446 {
447 struct bt_gatt_ots_indicate *ind = CONTAINER_OF(work, struct bt_gatt_ots_indicate, work);
448 struct bt_ots *ots = CONTAINER_OF(ind, struct bt_ots, oacp_ind);
449
450 bt_gatt_indicate(NULL, &ots->oacp_ind.params);
451 }
452
olcp_indicate_work_handler(struct k_work * work)453 static void olcp_indicate_work_handler(struct k_work *work)
454 {
455 struct bt_gatt_ots_indicate *ind = CONTAINER_OF(work, struct bt_gatt_ots_indicate, work);
456 struct bt_ots *ots = CONTAINER_OF(ind, struct bt_ots, olcp_ind);
457
458 bt_gatt_indicate(NULL, &ots->olcp_ind.params);
459 }
460
bt_ots_init(struct bt_ots * ots,struct bt_ots_init_param * ots_init)461 int bt_ots_init(struct bt_ots *ots,
462 struct bt_ots_init_param *ots_init)
463 {
464 int err;
465
466 if (!ots || !ots_init || !ots_init->cb) {
467 return -EINVAL;
468 }
469
470 __ASSERT(ots_init->cb->obj_created || !BT_OTS_OACP_GET_FEAT_CREATE(ots_init->features.oacp),
471 "Callback for object creation is not set and object creation is enabled");
472 __ASSERT(ots_init->cb->obj_deleted || !BT_OTS_OACP_GET_FEAT_DELETE(ots_init->features.oacp),
473 "Callback for object deletion is not set and object deletion is enabled");
474 #if defined(CONFIG_BT_OTS_OACP_CHECKSUM_SUPPORT)
475 __ASSERT(ots_init->cb->obj_cal_checksum ||
476 !BT_OTS_OACP_GET_FEAT_CHECKSUM(ots_init->features.oacp),
477 "Callback for object calculate checksum is not set and checksum is enabled");
478 #endif
479 __ASSERT(ots_init->cb->obj_read || !BT_OTS_OACP_GET_FEAT_READ(ots_init->features.oacp),
480 "Callback for object reading is not set and object read is enabled");
481 __ASSERT(ots_init->cb->obj_write || !BT_OTS_OACP_GET_FEAT_WRITE(ots_init->features.oacp),
482 "Callback for object write is not set and object write is enabled");
483
484 /* Set callback structure. */
485 ots->cb = ots_init->cb;
486
487 /* Check OACP supported features against Kconfig. */
488 if (ots_init->features.oacp & (~((uint32_t) OACP_FEAT))) {
489 return -ENOTSUP;
490 }
491
492 __ASSERT(!BT_OTS_OACP_GET_FEAT_CREATE(ots_init->features.oacp) ||
493 BT_OTS_OACP_GET_FEAT_WRITE(ots_init->features.oacp),
494 "Object creation requires object write to be supported");
495
496 ots->features.oacp = ots_init->features.oacp;
497 LOG_DBG("OACP features: 0x%04X", ots->features.oacp);
498
499 /* Check OLCP supported features against Kconfig. */
500 if (ots_init->features.olcp & (~((uint32_t) OLCP_FEAT))) {
501 return -ENOTSUP;
502 }
503 ots->features.olcp = ots_init->features.olcp;
504 LOG_DBG("OLCP features: 0x%04X", ots->features.olcp);
505
506 /* Register L2CAP context. */
507 err = bt_gatt_ots_l2cap_register(&ots->l2cap);
508 if (err) {
509 return err;
510 }
511
512 err = bt_gatt_service_register(ots->service);
513 if (err) {
514 bt_gatt_ots_l2cap_unregister(&ots->l2cap);
515
516 return err;
517 }
518
519 if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
520 bt_ots_dir_list_init(&ots->dir_list, ots->obj_manager);
521 }
522
523 k_work_init(&ots->oacp_ind.work, oacp_indicate_work_handler);
524 k_work_init(&ots->olcp_ind.work, olcp_indicate_work_handler);
525
526 LOG_DBG("Initialized OTS");
527
528 return 0;
529 }
530
531 #if defined(CONFIG_BT_OTS_SECONDARY_SVC)
532 #define BT_GATT_OTS_SERVICE BT_GATT_SECONDARY_SERVICE
533 #else
534 #define BT_GATT_OTS_SERVICE BT_GATT_PRIMARY_SERVICE
535 #endif
536
537 #if defined(CONFIG_BT_OTS_OBJ_NAME_WRITE_SUPPORT)
538 #define BT_OTS_OBJ_NAME_GATT_CHRC (BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE)
539 #define BT_OTS_OBJ_NAME_GATT_PERM (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE)
540 #define BT_OTS_OBJ_NAME_GATT_WRITE (ots_obj_name_write)
541 #else
542 #define BT_OTS_OBJ_NAME_GATT_CHRC (BT_GATT_CHRC_READ)
543 #define BT_OTS_OBJ_NAME_GATT_PERM (BT_GATT_PERM_READ)
544 #define BT_OTS_OBJ_NAME_GATT_WRITE (NULL)
545 #endif
546
547 #define BT_GATT_OTS_ATTRS(_ots) { \
548 BT_GATT_OTS_SERVICE(BT_UUID_OTS), \
549 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_FEATURE, \
550 BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
551 ots_feature_read, NULL, &_ots), \
552 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_NAME, \
553 BT_OTS_OBJ_NAME_GATT_CHRC, BT_OTS_OBJ_NAME_GATT_PERM, \
554 ots_obj_name_read, BT_OTS_OBJ_NAME_GATT_WRITE, &_ots), \
555 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_TYPE, \
556 BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
557 ots_obj_type_read, NULL, &_ots), \
558 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_SIZE, \
559 BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
560 ots_obj_size_read, NULL, &_ots), \
561 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_ID, \
562 BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
563 ots_obj_id_read, NULL, &_ots), \
564 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_PROPERTIES, \
565 BT_GATT_CHRC_READ, BT_GATT_PERM_READ, \
566 ots_obj_prop_read, NULL, &_ots), \
567 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_ACTION_CP, \
568 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE, \
569 BT_GATT_PERM_WRITE, NULL, \
570 bt_gatt_ots_oacp_write, &_ots), \
571 BT_GATT_CCC_MANAGED(&_ots.oacp_ind.ccc, \
572 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), \
573 BT_GATT_CHARACTERISTIC(BT_UUID_OTS_LIST_CP, \
574 BT_GATT_CHRC_WRITE | BT_GATT_CHRC_INDICATE, \
575 BT_GATT_PERM_WRITE, NULL, \
576 bt_gatt_ots_olcp_write, &_ots), \
577 BT_GATT_CCC_MANAGED(&_ots.olcp_ind.ccc, \
578 BT_GATT_PERM_READ | BT_GATT_PERM_WRITE) \
579 }
580
581 #define BT_GATT_OTS_INSTANCE_LIST_SIZE (ARRAY_SIZE(ots_instances))
582 #define BT_GATT_OTS_INSTANCE_LIST_START ots_instances
583 #define BT_GATT_OTS_INSTANCE_LIST_END \
584 (&ots_instances[BT_GATT_OTS_INSTANCE_LIST_SIZE])
585
586 #define BT_GATT_OTS_SERVICE_LIST_START ots_service_list
587
588 static struct bt_ots ots_instances[CONFIG_BT_OTS_MAX_INST_CNT];
589 static uint32_t instance_cnt;
590 BT_GATT_SERVICE_INSTANCE_DEFINE(ots_service_list, ots_instances,
591 CONFIG_BT_OTS_MAX_INST_CNT,
592 BT_GATT_OTS_ATTRS);
593
ots_delete_empty_name_objects(struct bt_ots * ots,struct bt_conn * conn)594 static void ots_delete_empty_name_objects(struct bt_ots *ots, struct bt_conn *conn)
595 {
596 char id_str[BT_OTS_OBJ_ID_STR_LEN];
597 struct bt_gatt_ots_object *obj;
598 struct bt_gatt_ots_object *next_obj;
599 int err;
600
601 err = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager, &next_obj);
602 while (!err) {
603 obj = next_obj;
604
605 /* Get the next object before we potentially delete the current object and
606 * no longer can get the next object
607 */
608 err = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager, obj, &next_obj);
609
610 if (strlen(obj->metadata.name) == 0) {
611 bt_ots_obj_id_to_str(obj->id, id_str, sizeof(id_str));
612 LOG_DBG("Deleting object with %s ID due to empty name", id_str);
613
614 if (ots->cb && ots->cb->obj_deleted) {
615 ots->cb->obj_deleted(ots, conn, obj->id);
616 }
617
618 if (bt_gatt_ots_obj_manager_obj_delete(obj)) {
619 LOG_ERR("Failed to remove object with %s ID from object manager",
620 id_str);
621 }
622 }
623 }
624 }
625
ots_conn_disconnected(struct bt_conn * conn,uint8_t reason)626 static void ots_conn_disconnected(struct bt_conn *conn, uint8_t reason)
627 {
628 uint32_t index;
629 struct bt_ots *instance;
630
631 for (instance = BT_GATT_OTS_INSTANCE_LIST_START, index = 0;
632 index < instance_cnt;
633 instance++, index++) {
634
635 LOG_DBG("Processing disconnect for OTS instance %u", index);
636
637 if (instance->cur_obj != NULL) {
638 __ASSERT(instance->cur_obj->state.type == BT_GATT_OTS_OBJECT_IDLE_STATE,
639 "The current object is expected to be in idle state as part "
640 "of cleanup of the L2CAP channel connection close.");
641 instance->cur_obj = NULL;
642 }
643
644 ots_delete_empty_name_objects(instance, conn);
645 }
646 }
647
648 BT_CONN_CB_DEFINE(conn_callbacks) = {
649 .disconnected = ots_conn_disconnected,
650 };
651
bt_ots_free_instance_get(void)652 struct bt_ots *bt_ots_free_instance_get(void)
653 {
654 if (instance_cnt >= BT_GATT_OTS_INSTANCE_LIST_SIZE) {
655 return NULL;
656 }
657
658 return &BT_GATT_OTS_INSTANCE_LIST_START[instance_cnt++];
659 }
660
bt_gatt_ots_instances_prepare(void)661 static int bt_gatt_ots_instances_prepare(void)
662 {
663 uint32_t index;
664 struct bt_ots *instance;
665
666 for (instance = BT_GATT_OTS_INSTANCE_LIST_START, index = 0;
667 instance != BT_GATT_OTS_INSTANCE_LIST_END;
668 instance++, index++) {
669 /* Assign an object pool to the OTS instance. */
670 instance->obj_manager = bt_gatt_ots_obj_manager_assign();
671
672 if (!instance->obj_manager) {
673 LOG_ERR("OTS Object manager instance not available");
674 return -ENOMEM;
675 }
676
677 /* Assign pointer to the service descriptor. */
678 instance->service = &BT_GATT_OTS_SERVICE_LIST_START[index];
679
680 /* Initialize CCC descriptors for characteristics with
681 * indication properties.
682 */
683 instance->oacp_ind.ccc.cfg_changed =
684 bt_gatt_ots_oacp_cfg_changed;
685 instance->olcp_ind.ccc.cfg_changed =
686 bt_gatt_ots_olcp_cfg_changed;
687 }
688
689 return 0;
690 }
691
692 SYS_INIT(bt_gatt_ots_instances_prepare, APPLICATION,
693 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
694