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