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/printk.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/kernel.h>
14 
15 #include <zephyr/bluetooth/gatt.h>
16 #include <zephyr/bluetooth/services/ots.h>
17 #include "ots_internal.h"
18 #include "ots_obj_manager_internal.h"
19 #include "ots_dir_list_internal.h"
20 
21 #include <zephyr/logging/log.h>
22 
23 LOG_MODULE_DECLARE(bt_ots, CONFIG_BT_OTS_LOG_LEVEL);
24 
25 #define OLCP_PROC_TYPE_SIZE	1
26 
obj_manager_to_olcp_err_map(int err)27 static enum bt_gatt_ots_olcp_res_code obj_manager_to_olcp_err_map(int err)
28 {
29 	switch (-err) {
30 	case EINVAL:
31 		return BT_GATT_OTS_OLCP_RES_OBJECT_ID_NOT_FOUND;
32 	case ENFILE:
33 		return BT_GATT_OTS_OLCP_RES_OUT_OF_BONDS;
34 	case ENOENT:
35 	default:
36 		return BT_GATT_OTS_OLCP_RES_NO_OBJECT;
37 	}
38 }
39 
olcp_first_proc_execute(struct bt_ots * ots)40 static enum bt_gatt_ots_olcp_res_code olcp_first_proc_execute(
41 	struct bt_ots *ots)
42 {
43 	int err;
44 	struct bt_gatt_ots_object *first_obj;
45 
46 	err = bt_gatt_ots_obj_manager_first_obj_get(ots->obj_manager,
47 						    &first_obj);
48 	if (err) {
49 		return obj_manager_to_olcp_err_map(err);
50 	}
51 
52 	ots->cur_obj = first_obj;
53 
54 	return BT_GATT_OTS_OLCP_RES_SUCCESS;
55 }
56 
olcp_last_proc_execute(struct bt_ots * ots)57 static enum bt_gatt_ots_olcp_res_code olcp_last_proc_execute(
58 	struct bt_ots *ots)
59 {
60 	int err;
61 	struct bt_gatt_ots_object *last_obj;
62 
63 	err = bt_gatt_ots_obj_manager_last_obj_get(ots->obj_manager,
64 						   &last_obj);
65 	if (err) {
66 		return obj_manager_to_olcp_err_map(err);
67 	}
68 
69 	ots->cur_obj = last_obj;
70 
71 	return BT_GATT_OTS_OLCP_RES_SUCCESS;
72 }
73 
olcp_prev_proc_execute(struct bt_ots * ots)74 static enum bt_gatt_ots_olcp_res_code olcp_prev_proc_execute(
75 	struct bt_ots *ots)
76 {
77 	int err;
78 	struct bt_gatt_ots_object *prev_obj;
79 
80 	if (!ots->cur_obj) {
81 		return BT_GATT_OTS_OLCP_RES_OPERATION_FAILED;
82 	}
83 	err = bt_gatt_ots_obj_manager_prev_obj_get(ots->obj_manager,
84 						   ots->cur_obj,
85 						   &prev_obj);
86 	if (err) {
87 		return obj_manager_to_olcp_err_map(err);
88 	}
89 
90 	ots->cur_obj = prev_obj;
91 
92 	return BT_GATT_OTS_OLCP_RES_SUCCESS;
93 }
94 
olcp_next_proc_execute(struct bt_ots * ots)95 static enum bt_gatt_ots_olcp_res_code olcp_next_proc_execute(
96 	struct bt_ots *ots)
97 {
98 	int err;
99 	struct bt_gatt_ots_object *next_obj;
100 
101 	if (!ots->cur_obj) {
102 		return BT_GATT_OTS_OLCP_RES_OPERATION_FAILED;
103 	}
104 	err = bt_gatt_ots_obj_manager_next_obj_get(ots->obj_manager,
105 						   ots->cur_obj,
106 						   &next_obj);
107 	if (err) {
108 		return obj_manager_to_olcp_err_map(err);
109 	}
110 
111 	ots->cur_obj = next_obj;
112 
113 	return BT_GATT_OTS_OLCP_RES_SUCCESS;
114 }
115 
olcp_goto_proc_execute(struct bt_ots * ots,uint64_t id)116 static enum bt_gatt_ots_olcp_res_code olcp_goto_proc_execute(
117 	struct bt_ots *ots, uint64_t id)
118 {
119 	int err;
120 	struct bt_gatt_ots_object *id_obj;
121 
122 	if (!BT_OTS_VALID_OBJ_ID(id)) {
123 		LOG_DBG("Invalid object ID 0x%016llx", id);
124 
125 		return BT_GATT_OTS_OLCP_RES_INVALID_PARAMETER;
126 	}
127 
128 	err = bt_gatt_ots_obj_manager_obj_get(ots->obj_manager,
129 					      id,
130 					      &id_obj);
131 	if (err) {
132 		return obj_manager_to_olcp_err_map(err);
133 	}
134 
135 	ots->cur_obj = id_obj;
136 
137 	return BT_GATT_OTS_OLCP_RES_SUCCESS;
138 }
139 
olcp_proc_execute(struct bt_ots * ots,struct bt_gatt_ots_olcp_proc * proc)140 static enum bt_gatt_ots_olcp_res_code olcp_proc_execute(
141 	struct bt_ots *ots, struct bt_gatt_ots_olcp_proc *proc)
142 {
143 	LOG_DBG("Executing OLCP procedure with 0x%02X Op Code", proc->type);
144 
145 	switch (proc->type) {
146 	case BT_GATT_OTS_OLCP_PROC_FIRST:
147 		return olcp_first_proc_execute(ots);
148 	case BT_GATT_OTS_OLCP_PROC_LAST:
149 		return olcp_last_proc_execute(ots);
150 	case BT_GATT_OTS_OLCP_PROC_PREV:
151 		return olcp_prev_proc_execute(ots);
152 	case BT_GATT_OTS_OLCP_PROC_NEXT:
153 		return olcp_next_proc_execute(ots);
154 	case BT_GATT_OTS_OLCP_PROC_GOTO:
155 		return olcp_goto_proc_execute(ots, proc->goto_params.id);
156 	case BT_GATT_OTS_OLCP_PROC_ORDER:
157 	case BT_GATT_OTS_OLCP_PROC_REQ_NUM_OBJS:
158 	case BT_GATT_OTS_OLCP_PROC_CLEAR_MARKING:
159 	default:
160 		return BT_GATT_OTS_OLCP_RES_PROC_NOT_SUP;
161 	}
162 };
163 
olcp_command_decode(const uint8_t * buf,uint16_t len,struct bt_gatt_ots_olcp_proc * proc)164 static int olcp_command_decode(const uint8_t *buf, uint16_t len,
165 			       struct bt_gatt_ots_olcp_proc *proc)
166 {
167 	if (len < OLCP_PROC_TYPE_SIZE) {
168 		return -ENODATA;
169 	}
170 
171 	memset(proc, 0, sizeof(*proc));
172 
173 	proc->type = *buf++;
174 	len -= OLCP_PROC_TYPE_SIZE;
175 
176 	switch (proc->type) {
177 	case BT_GATT_OTS_OLCP_PROC_FIRST:
178 	case BT_GATT_OTS_OLCP_PROC_LAST:
179 	case BT_GATT_OTS_OLCP_PROC_PREV:
180 	case BT_GATT_OTS_OLCP_PROC_NEXT:
181 		if (len != 0) {
182 			return -EBADMSG;
183 		}
184 
185 		return 0;
186 	case BT_GATT_OTS_OLCP_PROC_GOTO:
187 		if (len != BT_GATT_OTS_OLCP_GOTO_PARAMS_SIZE) {
188 			return -EBADMSG;
189 		}
190 		proc->goto_params.id = sys_get_le48(buf);
191 
192 		return 0;
193 	default:
194 		break;
195 	}
196 
197 	return -ENOTSUP;
198 }
199 
olcp_ind_cb(struct bt_conn * conn,struct bt_gatt_indicate_params * params,uint8_t err)200 static void olcp_ind_cb(struct bt_conn *conn,
201 		struct bt_gatt_indicate_params *params,
202 		uint8_t err)
203 {
204 	LOG_DBG("Received OLCP Indication ACK with status: 0x%04X", err);
205 }
206 
olcp_ind_send(const struct bt_gatt_attr * olcp_attr,enum bt_gatt_ots_olcp_proc_type req_op_code,enum bt_gatt_ots_olcp_res_code olcp_status)207 static void olcp_ind_send(const struct bt_gatt_attr *olcp_attr,
208 			 enum bt_gatt_ots_olcp_proc_type req_op_code,
209 			 enum bt_gatt_ots_olcp_res_code olcp_status)
210 {
211 	struct bt_ots *ots = (struct bt_ots *) olcp_attr->user_data;
212 	uint8_t *olcp_res = ots->olcp_ind.res;
213 	uint16_t olcp_res_len = 0;
214 
215 	/* Encode OLCP Response */
216 	olcp_res[olcp_res_len++] = BT_GATT_OTS_OLCP_PROC_RESP;
217 	olcp_res[olcp_res_len++] = req_op_code;
218 	olcp_res[olcp_res_len++] = olcp_status;
219 
220 	/* Prepare indication parameters */
221 	memset(&ots->olcp_ind.params, 0, sizeof(ots->olcp_ind.params));
222 	memcpy(&ots->olcp_ind.attr, olcp_attr, sizeof(ots->olcp_ind.attr));
223 	ots->olcp_ind.params.attr = olcp_attr;
224 	ots->olcp_ind.params.func = olcp_ind_cb;
225 	ots->olcp_ind.params.data = olcp_res;
226 	ots->olcp_ind.params.len  = olcp_res_len;
227 #if defined(CONFIG_BT_EATT)
228 	ots->olcp_ind.params.chan_opt = BT_ATT_CHAN_OPT_NONE;
229 #endif /* CONFIG_BT_EATT */
230 
231 	LOG_DBG("Sending OLCP indication");
232 
233 	k_work_submit(&ots->olcp_ind.work);
234 }
235 
bt_gatt_ots_olcp_write(struct bt_conn * conn,const struct bt_gatt_attr * attr,const void * buf,uint16_t len,uint16_t offset,uint8_t flags)236 ssize_t bt_gatt_ots_olcp_write(struct bt_conn *conn,
237 				   const struct bt_gatt_attr *attr,
238 				   const void *buf, uint16_t len,
239 				   uint16_t offset, uint8_t flags)
240 {
241 	struct bt_gatt_ots_object *old_obj;
242 	enum bt_gatt_ots_olcp_res_code olcp_status;
243 	int decode_status;
244 	struct bt_gatt_ots_olcp_proc olcp_proc;
245 	struct bt_ots *ots = (struct bt_ots *) attr->user_data;
246 
247 	LOG_DBG("Object List Control Point GATT Write Operation");
248 
249 	if (!ots->olcp_ind.is_enabled) {
250 		LOG_WRN("OLCP indications not enabled");
251 		return BT_GATT_ERR(BT_ATT_ERR_CCC_IMPROPER_CONF);
252 	}
253 
254 	if (offset != 0) {
255 		LOG_ERR("Invalid offset of OLCP Write Request");
256 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
257 	}
258 
259 	if (k_work_is_pending(&ots->olcp_ind.work)) {
260 		LOG_ERR("OLCP Write received before indication sent");
261 		return BT_GATT_ERR(BT_ATT_ERR_PROCEDURE_IN_PROGRESS);
262 	}
263 
264 	old_obj = ots->cur_obj;
265 
266 	decode_status = olcp_command_decode(buf, len, &olcp_proc);
267 	switch (decode_status) {
268 	case 0:
269 		olcp_status = olcp_proc_execute(ots, &olcp_proc);
270 		if (olcp_status != BT_GATT_OTS_OLCP_RES_SUCCESS) {
271 			LOG_WRN("OLCP Write error status: 0x%02X", olcp_status);
272 		} else if (old_obj != ots->cur_obj) {
273 			char id[BT_OTS_OBJ_ID_STR_LEN];
274 
275 			bt_ots_obj_id_to_str(ots->cur_obj->id, id,
276 						sizeof(id));
277 			LOG_DBG("Selecting a new Current Object with id: %s",
278 				id);
279 
280 			if (IS_ENABLED(CONFIG_BT_OTS_DIR_LIST_OBJ)) {
281 				bt_ots_dir_list_selected(ots->dir_list, ots->obj_manager,
282 							 ots->cur_obj);
283 			}
284 
285 			if (ots->cb->obj_selected) {
286 				ots->cb->obj_selected(ots, conn, ots->cur_obj->id);
287 			}
288 		}
289 		break;
290 	case -ENOTSUP:
291 		olcp_status = BT_GATT_OTS_OLCP_RES_PROC_NOT_SUP;
292 		LOG_WRN("OLCP unsupported procedure type: 0x%02X", olcp_proc.type);
293 		break;
294 	case -EBADMSG:
295 		LOG_ERR("Invalid length of OLCP Write Request for 0x%02X "
296 			"Op Code", olcp_proc.type);
297 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
298 	case -ENODATA:
299 		LOG_ERR("Invalid size of OLCP Write Request");
300 		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
301 	default:
302 		LOG_ERR("Invalid return code from olcp_command_decode: %d", decode_status);
303 		return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
304 	}
305 
306 	olcp_ind_send(attr, olcp_proc.type, olcp_status);
307 	return len;
308 }
309 
bt_gatt_ots_olcp_cfg_changed(const struct bt_gatt_attr * attr,uint16_t value)310 void bt_gatt_ots_olcp_cfg_changed(const struct bt_gatt_attr *attr,
311 				      uint16_t value)
312 {
313 	struct bt_gatt_ots_indicate *olcp_ind =
314 	    CONTAINER_OF((struct _bt_gatt_ccc *) attr->user_data,
315 			 struct bt_gatt_ots_indicate, ccc);
316 
317 	LOG_DBG("Object List Control Point CCCD value: 0x%04X", value);
318 
319 	olcp_ind->is_enabled = false;
320 	if (value == BT_GATT_CCC_INDICATE) {
321 		olcp_ind->is_enabled = true;
322 	}
323 }
324