/* * Copyright (c) 2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include "ots_internal.h" #include "ots_obj_manager_internal.h" #include "ots_dir_list_internal.h" #include LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL); static struct bt_ots_dir_list dir_lists[CONFIG_BT_OTS_MAX_INST_CNT]; static void dir_list_object_encode(struct bt_gatt_ots_object *obj, struct net_buf_simple *net_buf) { uint8_t flags = 0; uint8_t *start; BT_OTS_DIR_LIST_SET_FLAG_PROPERTIES(flags); BT_OTS_DIR_LIST_SET_FLAG_CUR_SIZE(flags); if (obj->metadata.type.uuid.type == BT_UUID_TYPE_128) { BT_OTS_DIR_LIST_SET_FLAG_TYPE_128(flags); } /* skip 16bits at the beginning of the record for the record's length */ start = net_buf_simple_add(net_buf, sizeof(uint16_t)); /* ID */ net_buf_simple_add_le48(net_buf, obj->id); /* Name length */ net_buf_simple_add_u8(net_buf, strlen(obj->metadata.name)); /* Name */ net_buf_simple_add_mem(net_buf, obj->metadata.name, strlen(obj->metadata.name)); /* Flags */ net_buf_simple_add_u8(net_buf, flags); /* encode Object type */ if (obj->metadata.type.uuid.type == BT_UUID_TYPE_16) { net_buf_simple_add_le16(net_buf, obj->metadata.type.uuid_16.val); } else { net_buf_simple_add_mem(net_buf, obj->metadata.type.uuid_128.val, BT_UUID_SIZE_128); } /* encode Object Current size */ net_buf_simple_add_le32(net_buf, obj->metadata.size.cur); /* Object properties */ net_buf_simple_add_le32(net_buf, obj->metadata.props); /* Update the record length at the beginning */ sys_put_le16(net_buf_simple_tail(net_buf) - start, start); } void bt_ots_dir_list_obj_add(struct bt_ots_dir_list *dir_list, void *obj_manager, struct bt_gatt_ots_object *cur_obj, struct bt_gatt_ots_object *obj) { __ASSERT(dir_list->dir_list_obj != obj, "Cannot add Directory Listing Object"); __ASSERT(bt_gatt_ots_obj_manager_obj_contains(obj_manager, obj), "Object not part of OTS instance"); if (dir_list->dir_list_obj != cur_obj) { /* We only need to update the object directory listing if it is currently selected, * as we otherwise only create it when it is selected. */ return; } dir_list_object_encode(obj, &dir_list->net_buf); /*re-encode the Directory Listing Object size with the is new size*/ sys_put_le16(dir_list->net_buf.len, dir_list->net_buf.data); /* Update Directory Listing Object metadata size */ dir_list->dir_list_obj->metadata.size.cur = dir_list->net_buf.len; } void bt_ots_dir_list_obj_remove(struct bt_ots_dir_list *dir_list, void *obj_manager, struct bt_gatt_ots_object *cur_obj, struct bt_gatt_ots_object *obj) { uint16_t offset = 0; __ASSERT(dir_list->dir_list_obj != obj, "Cannot remove Directory Listing Object"); __ASSERT(bt_gatt_ots_obj_manager_obj_contains(obj_manager, obj), "Object not part of OTS instance"); if (dir_list->dir_list_obj != cur_obj) { /* We only need to update the object directory listing if it is currently selected, * as we otherwise only create it when it is selected. */ return; } while (offset < dir_list->net_buf.len) { uint16_t len = sys_get_le16(dir_list->net_buf.data + offset); uint64_t id = sys_get_le64(dir_list->net_buf.data + offset + sizeof(len)); __ASSERT(len, "Invalid object length"); if (id == obj->id) { /* Delete object by moving memory after the object to * the objects current location */ memmove(dir_list->net_buf.data + offset, dir_list->net_buf.data + offset + len, dir_list->net_buf.len - (offset + len)); /* Decrement net_buf len to new length */ dir_list->net_buf.len -= len; break; } offset += len; } __ASSERT(offset <= dir_list->net_buf.len, "Object was not removed"); /*re-encode the Directory Listing Object size with the is new size*/ sys_put_le16(dir_list->net_buf.len, dir_list->net_buf.data); /* Update Directory Listing Object metadata size */ dir_list->dir_list_obj->metadata.size.cur = dir_list->net_buf.len; } static void dir_list_encode(struct bt_ots_dir_list *dir_list, void *obj_manager) { struct bt_gatt_ots_object *obj; int err; err = bt_gatt_ots_obj_manager_first_obj_get(obj_manager, &obj); __ASSERT(err == 0 && first_obj == dir_list->dir_list_obj, "Expecting first object to be the Directory Listing Object"); /* Init with len = 0 to reset data */ net_buf_simple_init_with_data(&dir_list->net_buf, dir_list->_content, sizeof(dir_list->_content)); dir_list->net_buf.len = 0; do { dir_list_object_encode(obj, &dir_list->net_buf); err = bt_gatt_ots_obj_manager_next_obj_get(obj_manager, obj, &obj); } while (!err); /*re-encode the Directory Listing Object size with the is new size*/ sys_put_le16(dir_list->net_buf.len, dir_list->net_buf.data); /* Update Directory Listing Object metadata size */ dir_list->dir_list_obj->metadata.size.cur = dir_list->net_buf.len; } void bt_ots_dir_list_selected(struct bt_ots_dir_list *dir_list, void *obj_manager, struct bt_gatt_ots_object *cur_obj) { if (dir_list->dir_list_obj != cur_obj) { /* We only need to update the object directory listing if it is currently selected, * as we otherwise only create it when it is selected. */ return; } dir_list_encode(dir_list, obj_manager); } void bt_ots_dir_list_init(struct bt_ots_dir_list **dir_list, void *obj_manager) { struct bt_gatt_ots_object *dir_list_obj; int err; static char *dir_list_obj_name = CONFIG_BT_OTS_DIR_LIST_OBJ_NAME; __ASSERT(*dir_list, "Already initialized"); for (int i = 0; i < ARRAY_SIZE(dir_lists); i++) { if (!dir_lists[i].dir_list_obj) { *dir_list = &dir_lists[i]; } } __ASSERT(*dir_list, "Could not assign Directory Listing Object"); __ASSERT(strlen(dir_list_obj_name) < UINT8_MAX, "BT_OTS_DIR_LIST_OBJ_NAME shall be less than 255 octets"); err = bt_gatt_ots_obj_manager_obj_add(obj_manager, &dir_list_obj); __ASSERT(!err, "Could not add Directory Listing Object for object manager %p", obj_man); memset(&dir_list_obj->metadata, 0, sizeof(dir_list_obj->metadata)); dir_list_obj->metadata.name = dir_list_obj_name; dir_list_obj->metadata.size.alloc = sizeof((*dir_list)->_content); dir_list_obj->metadata.type.uuid.type = BT_UUID_TYPE_16; dir_list_obj->metadata.type.uuid_16.val = BT_UUID_OTS_DIRECTORY_LISTING_VAL; BT_OTS_OBJ_SET_PROP_READ(dir_list_obj->metadata.props); (*dir_list)->dir_list_obj = dir_list_obj; /* Set size in dir_list_encode */ dir_list_encode(*dir_list, obj_manager); } int bt_ots_dir_list_content_get(struct bt_ots_dir_list *dir_list, uint8_t **data, uint32_t len, uint32_t offset) { if (offset >= dir_list->net_buf.len) { *data = NULL; return 0; } *data = dir_list->net_buf.data + offset; return MIN(len, dir_list->net_buf.len - offset); }