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/sys/dlist.h>
12 #include <zephyr/sys/byteorder.h>
13 
14 #include <zephyr/bluetooth/services/ots.h>
15 #include "ots_internal.h"
16 
17 #include <zephyr/logging/log.h>
18 
19 LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
20 
21 
22 struct bt_gatt_ots_pool_item {
23 	sys_dnode_t dnode;
24 	struct bt_gatt_ots_object val;
25 	bool is_allocated;
26 };
27 
28 struct bt_gatt_ots_obj_manager {
29 	sys_dlist_t list;
30 	struct bt_gatt_ots_pool_item pool[CONFIG_BT_OTS_MAX_OBJ_CNT];
31 	bool is_assigned;
32 };
33 
obj_id_to_index(uint64_t id)34 static uint64_t obj_id_to_index(uint64_t id)
35 {
36 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
37 		if (id == OTS_OBJ_ID_DIR_LIST) {
38 			return id;
39 		} else {
40 			return id - BT_OTS_OBJ_ID_MIN + 1;
41 		}
42 	} else {
43 		return id - BT_OTS_OBJ_ID_MIN;
44 	}
45 }
46 
obj_index_to_id(uint64_t index)47 static uint64_t obj_index_to_id(uint64_t index)
48 {
49 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
50 		if (index == 0) {
51 			return OTS_OBJ_ID_DIR_LIST;
52 		} else {
53 			return BT_OTS_OBJ_ID_MIN + index - 1;
54 		}
55 	} else {
56 		return BT_OTS_OBJ_ID_MIN + index;
57 	}
58 }
59 
bt_gatt_ots_obj_manager_first_obj_get(struct bt_gatt_ots_obj_manager * obj_manager,struct bt_gatt_ots_object ** obj)60 int bt_gatt_ots_obj_manager_first_obj_get(
61 	struct bt_gatt_ots_obj_manager *obj_manager,
62 	struct bt_gatt_ots_object **obj)
63 {
64 	sys_dnode_t *obj_dnode;
65 	struct bt_gatt_ots_pool_item *first_item;
66 
67 	if (sys_dlist_is_empty(&obj_manager->list)) {
68 		return -ENOENT;
69 	}
70 
71 	obj_dnode = sys_dlist_peek_head_not_empty(&obj_manager->list);
72 	first_item = CONTAINER_OF(obj_dnode, struct bt_gatt_ots_pool_item,
73 				  dnode);
74 	*obj = &first_item->val;
75 
76 	return 0;
77 }
78 
bt_gatt_ots_obj_manager_last_obj_get(struct bt_gatt_ots_obj_manager * obj_manager,struct bt_gatt_ots_object ** obj)79 int bt_gatt_ots_obj_manager_last_obj_get(
80 	struct bt_gatt_ots_obj_manager *obj_manager,
81 	struct bt_gatt_ots_object **obj)
82 {
83 	sys_dnode_t *obj_dnode;
84 	struct bt_gatt_ots_pool_item *last_item;
85 
86 	if (sys_dlist_is_empty(&obj_manager->list)) {
87 		return -ENOENT;
88 	}
89 
90 	obj_dnode = sys_dlist_peek_tail(&obj_manager->list);
91 	last_item = CONTAINER_OF(obj_dnode, struct bt_gatt_ots_pool_item,
92 				 dnode);
93 	*obj = &last_item->val;
94 
95 	return 0;
96 }
97 
bt_gatt_ots_obj_manager_prev_obj_get(struct bt_gatt_ots_obj_manager * obj_manager,const struct bt_gatt_ots_object * cur_obj,struct bt_gatt_ots_object ** prev_obj)98 int bt_gatt_ots_obj_manager_prev_obj_get(
99 	struct bt_gatt_ots_obj_manager *obj_manager,
100 	const struct bt_gatt_ots_object *cur_obj,
101 	struct bt_gatt_ots_object **prev_obj)
102 {
103 	sys_dnode_t *prev_obj_dnode;
104 	struct bt_gatt_ots_pool_item *cur_item, *prev_item;
105 
106 	if (sys_dlist_is_empty(&obj_manager->list)) {
107 		return -ENOENT;
108 	}
109 
110 	cur_item = CONTAINER_OF(cur_obj, struct bt_gatt_ots_pool_item, val);
111 	prev_obj_dnode = sys_dlist_peek_prev_no_check(&obj_manager->list,
112 						      &cur_item->dnode);
113 	if (!prev_obj_dnode) {
114 		return -ENFILE;
115 	}
116 
117 	prev_item = CONTAINER_OF(prev_obj_dnode,
118 				 struct bt_gatt_ots_pool_item,
119 				 dnode);
120 	*prev_obj = &prev_item->val;
121 
122 	return 0;
123 }
124 
bt_gatt_ots_obj_manager_next_obj_get(struct bt_gatt_ots_obj_manager * obj_manager,const struct bt_gatt_ots_object * cur_obj,struct bt_gatt_ots_object ** next_obj)125 int bt_gatt_ots_obj_manager_next_obj_get(
126 	struct bt_gatt_ots_obj_manager *obj_manager,
127 	const struct bt_gatt_ots_object *cur_obj,
128 	struct bt_gatt_ots_object **next_obj)
129 {
130 	sys_dnode_t *next_obj_dnode;
131 	struct bt_gatt_ots_pool_item *cur_item, *next_item;
132 
133 	if (sys_dlist_is_empty(&obj_manager->list)) {
134 		return -ENOENT;
135 	}
136 
137 	cur_item = CONTAINER_OF(cur_obj, struct bt_gatt_ots_pool_item, val);
138 	next_obj_dnode = sys_dlist_peek_next_no_check(&obj_manager->list,
139 						      &cur_item->dnode);
140 	if (!next_obj_dnode) {
141 		return -ENFILE;
142 	}
143 
144 	next_item = CONTAINER_OF(next_obj_dnode,
145 				 struct bt_gatt_ots_pool_item,
146 				 dnode);
147 	*next_obj = &next_item->val;
148 
149 	return 0;
150 }
151 
bt_gatt_ots_obj_manager_obj_get(struct bt_gatt_ots_obj_manager * obj_manager,uint64_t id,struct bt_gatt_ots_object ** object)152 int bt_gatt_ots_obj_manager_obj_get(
153 	struct bt_gatt_ots_obj_manager *obj_manager, uint64_t id,
154 	struct bt_gatt_ots_object **object)
155 {
156 	uint64_t index;
157 
158 	if (sys_dlist_is_empty(&obj_manager->list)) {
159 		return -ENOENT;
160 	}
161 
162 	if (id == OTS_OBJ_ID_DIR_LIST && !IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
163 		return -EINVAL;
164 	}
165 
166 	index = obj_id_to_index(id);
167 
168 	if (index >= ARRAY_SIZE(obj_manager->pool)) {
169 		return -EINVAL;
170 	}
171 
172 	if (!obj_manager->pool[index].is_allocated) {
173 		return -EINVAL;
174 	}
175 
176 	*object = &obj_manager->pool[index].val;
177 
178 	return 0;
179 }
180 
bt_gatt_ots_obj_manager_obj_add(struct bt_gatt_ots_obj_manager * obj_manager,struct bt_gatt_ots_object ** object)181 int bt_gatt_ots_obj_manager_obj_add(
182 	struct bt_gatt_ots_obj_manager *obj_manager,
183 	struct bt_gatt_ots_object **object)
184 {
185 	for (uint64_t i = 0; i < ARRAY_SIZE(obj_manager->pool); i++) {
186 		struct bt_gatt_ots_pool_item *cur_obj =
187 			&obj_manager->pool[i];
188 
189 		if (!cur_obj->is_allocated) {
190 			cur_obj->is_allocated = true;
191 			cur_obj->val.id = obj_index_to_id(i);
192 			sys_dlist_append(&obj_manager->list, &cur_obj->dnode);
193 
194 			*object = &cur_obj->val;
195 			return 0;
196 		}
197 	}
198 
199 	return -ENOMEM;
200 }
201 
bt_gatt_ots_obj_manager_obj_delete(struct bt_gatt_ots_object * obj)202 int bt_gatt_ots_obj_manager_obj_delete(struct bt_gatt_ots_object *obj)
203 {
204 	struct bt_gatt_ots_pool_item *item;
205 
206 	item = CONTAINER_OF(obj, struct bt_gatt_ots_pool_item, val);
207 
208 	if (!item->is_allocated) {
209 		return -EINVAL;
210 	}
211 
212 	if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ) &&
213 	    obj->id == OTS_OBJ_ID_DIR_LIST) {
214 		return -EINVAL;
215 	}
216 
217 	item->is_allocated = false;
218 	sys_dlist_remove(&item->dnode);
219 
220 	return 0;
221 }
222 
223 
bt_gatt_ots_obj_manager_obj_contains(struct bt_gatt_ots_obj_manager * obj_manager,struct bt_gatt_ots_object * obj)224 bool bt_gatt_ots_obj_manager_obj_contains(struct bt_gatt_ots_obj_manager *obj_manager,
225 					  struct bt_gatt_ots_object *obj)
226 {
227 	struct bt_gatt_ots_pool_item *item;
228 
229 	item = CONTAINER_OF(obj, struct bt_gatt_ots_pool_item, val);
230 
231 	return PART_OF_ARRAY(obj_manager->pool, item);
232 }
233 
bt_gatt_ots_obj_manager_assign(void)234 void *bt_gatt_ots_obj_manager_assign(void)
235 {
236 	static struct bt_gatt_ots_obj_manager
237 		obj_manager[CONFIG_BT_OTS_MAX_INST_CNT];
238 	struct bt_gatt_ots_obj_manager *cur_manager;
239 
240 
241 	for (cur_manager = obj_manager;
242 	     cur_manager != obj_manager + CONFIG_BT_OTS_MAX_INST_CNT;
243 	     cur_manager++) {
244 		if (!cur_manager->is_assigned) {
245 			break;
246 		}
247 	}
248 
249 	if (cur_manager->is_assigned) {
250 		return NULL;
251 	}
252 
253 	cur_manager->is_assigned = true;
254 	sys_dlist_init(&cur_manager->list);
255 
256 	return cur_manager;
257 }
258