1 /* Copyright (c) 2023 Nordic Semiconductor ASA
2  * SPDX-License-Identifier: Apache-2.0
3  */
4 
5 #include <stdint.h>
6 #include <testlib/att_read.h>
7 #include <zephyr/autoconf.h>
8 #include <zephyr/bluetooth/att.h>
9 #include <zephyr/bluetooth/bluetooth.h>
10 #include <zephyr/bluetooth/conn.h>
11 #include <zephyr/bluetooth/gatt.h>
12 #include <zephyr/bluetooth/l2cap.h>
13 #include <zephyr/bluetooth/uuid.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/logging/log_core.h>
16 #include <zephyr/logging/log.h>
17 #include <zephyr/net_buf.h>
18 #include <zephyr/sys/__assert.h>
19 #include <zephyr/sys/util_macro.h>
20 #include <zephyr/sys/util.h>
21 
22 LOG_MODULE_REGISTER(bt_testlib_att_read, LOG_LEVEL_DBG);
23 
24 struct bt_testlib_att_read_closure {
25 	uint8_t att_err;
26 	struct bt_conn *conn;
27 	struct bt_gatt_read_params params;
28 	uint16_t *result_size;
29 	uint16_t *result_handle;
30 	struct net_buf_simple *result_data;
31 	struct k_mutex lock;
32 	struct k_condvar done;
33 	uint16_t *att_mtu;
34 	bool long_read;
35 };
36 
bt_gatt_read_params_is_by_uuid(const struct bt_gatt_read_params * params)37 static bool bt_gatt_read_params_is_by_uuid(const struct bt_gatt_read_params *params)
38 {
39 	return params->handle_count == 0;
40 }
41 
att_read_cb(struct bt_conn * conn,uint8_t att_err,struct bt_gatt_read_params * params,const void * read_data,uint16_t read_len)42 static uint8_t att_read_cb(struct bt_conn *conn, uint8_t att_err,
43 			   struct bt_gatt_read_params *params, const void *read_data,
44 			   uint16_t read_len)
45 {
46 	struct bt_testlib_att_read_closure *ctx =
47 		CONTAINER_OF(params, struct bt_testlib_att_read_closure, params);
48 
49 	k_mutex_lock(&ctx->lock, K_FOREVER);
50 
51 	ctx->att_err = att_err;
52 
53 	if (!att_err && ctx->result_handle) {
54 		__ASSERT_NO_MSG(bt_gatt_read_params_is_by_uuid(params));
55 		*ctx->result_handle = params->by_uuid.start_handle;
56 	}
57 
58 	if (!att_err && ctx->result_size) {
59 		LOG_DBG("Adding %u bytes to result", read_len);
60 		*ctx->result_size += read_len;
61 		if (*ctx->result_size > BT_ATT_MAX_ATTRIBUTE_LEN) {
62 			LOG_ERR("result_size > 512");
63 		}
64 	}
65 
66 	if (read_data && ctx->result_data) {
67 		uint16_t result_data_size =
68 			MIN(read_len, net_buf_simple_tailroom(ctx->result_data));
69 
70 		net_buf_simple_add_mem(ctx->result_data, read_data, result_data_size);
71 	}
72 
73 	if (!att_err && ctx->att_mtu) {
74 		*ctx->att_mtu = params->_att_mtu;
75 	}
76 
77 	if (ctx->long_read && read_data) {
78 		/* Don't signal `&ctx->done` */
79 		k_mutex_unlock(&ctx->lock);
80 		return BT_GATT_ITER_CONTINUE;
81 	}
82 
83 	k_condvar_signal(&ctx->done);
84 	k_mutex_unlock(&ctx->lock);
85 	return BT_GATT_ITER_STOP;
86 }
87 
bt_testlib_sync_bt_gatt_read(struct bt_testlib_att_read_closure * ctx)88 static int bt_testlib_sync_bt_gatt_read(struct bt_testlib_att_read_closure *ctx)
89 {
90 	int api_err;
91 
92 	/* `result_size` is initialized here so that it can be plussed on in
93 	 * the callback. The result of a long read comes in multiple
94 	 * callbacks and must be added up.
95 	 */
96 	if (ctx->result_size) {
97 		*ctx->result_size = 0;
98 	}
99 
100 	/* `att_read_cb` is smart and does the right thing based on `ctx`. */
101 	ctx->params.func = att_read_cb;
102 
103 	/* Setup synchronization between the cb and the current function. */
104 	k_mutex_init(&ctx->lock);
105 	k_condvar_init(&ctx->done);
106 
107 	k_mutex_lock(&ctx->lock, K_FOREVER);
108 
109 	api_err = bt_gatt_read(ctx->conn, &ctx->params);
110 
111 	if (!api_err) {
112 		k_condvar_wait(&ctx->done, &ctx->lock, K_FOREVER);
113 	}
114 
115 	k_mutex_unlock(&ctx->lock);
116 
117 	if (api_err) {
118 		__ASSERT_NO_MSG(api_err < 0);
119 		return api_err;
120 	}
121 
122 	__ASSERT_NO_MSG(ctx->att_err >= 0);
123 	return ctx->att_err;
124 }
125 
bt_testlib_att_read_by_type_sync(struct net_buf_simple * result_data,uint16_t * result_size,uint16_t * result_handle,uint16_t * result_att_mtu,struct bt_conn * conn,enum bt_att_chan_opt bearer,const struct bt_uuid * type,uint16_t start_handle,uint16_t end_handle)126 int bt_testlib_att_read_by_type_sync(struct net_buf_simple *result_data, uint16_t *result_size,
127 				     uint16_t *result_handle, uint16_t *result_att_mtu,
128 				     struct bt_conn *conn, enum bt_att_chan_opt bearer,
129 				     const struct bt_uuid *type, uint16_t start_handle,
130 				     uint16_t end_handle)
131 {
132 	struct bt_testlib_att_read_closure ctx = {.result_handle = result_handle,
133 						  .result_size = result_size,
134 						  .conn = conn,
135 						  .result_data = result_data,
136 						  .att_mtu = result_att_mtu,
137 						  .params = {
138 							  .by_uuid = {.uuid = type,
139 								      .start_handle = start_handle,
140 								      .end_handle = end_handle},
141 						  }};
142 
143 	IF_ENABLED(CONFIG_BT_EATT, ({ ctx.params.chan_opt = bearer; }))
144 
145 	if (bearer == BT_ATT_CHAN_OPT_ENHANCED_ONLY) {
146 		__ASSERT(IS_ENABLED(CONFIG_BT_EATT), "EATT not complied in");
147 	}
148 
149 	return bt_testlib_sync_bt_gatt_read(&ctx);
150 }
151 
bt_testlib_att_read_by_handle_sync(struct net_buf_simple * result_data,uint16_t * result_size,uint16_t * result_att_mtu,struct bt_conn * conn,enum bt_att_chan_opt bearer,uint16_t handle,uint16_t offset)152 int bt_testlib_att_read_by_handle_sync(struct net_buf_simple *result_data, uint16_t *result_size,
153 				       uint16_t *result_att_mtu, struct bt_conn *conn,
154 				       enum bt_att_chan_opt bearer, uint16_t handle,
155 				       uint16_t offset)
156 {
157 	struct bt_testlib_att_read_closure ctx = {};
158 
159 	ctx.att_mtu = result_att_mtu;
160 	ctx.conn = conn;
161 	ctx.params.handle_count = 1;
162 	ctx.params.single.handle = handle;
163 	ctx.params.single.offset = offset;
164 	ctx.result_data = result_data;
165 	ctx.result_size = result_size;
166 	IF_ENABLED(CONFIG_BT_EATT, (ctx.params.chan_opt = bearer));
167 
168 	if (bearer == BT_ATT_CHAN_OPT_ENHANCED_ONLY) {
169 		__ASSERT(IS_ENABLED(CONFIG_BT_EATT), "EATT not complied in");
170 	}
171 
172 	return bt_testlib_sync_bt_gatt_read(&ctx);
173 }
174 
bt_testlib_gatt_long_read(struct net_buf_simple * result_data,uint16_t * result_size,uint16_t * result_att_mtu,struct bt_conn * conn,enum bt_att_chan_opt bearer,uint16_t handle,uint16_t offset)175 int bt_testlib_gatt_long_read(struct net_buf_simple *result_data, uint16_t *result_size,
176 			      uint16_t *result_att_mtu, struct bt_conn *conn,
177 			      enum bt_att_chan_opt bearer, uint16_t handle, uint16_t offset)
178 {
179 	int err;
180 	uint16_t _result_data_size = 0;
181 	struct bt_testlib_att_read_closure ctx = {};
182 
183 	ctx.att_mtu = result_att_mtu;
184 	ctx.conn = conn;
185 	ctx.long_read = true, ctx.params.handle_count = 1;
186 	ctx.params.single.handle = handle;
187 	ctx.params.single.offset = offset;
188 	ctx.result_data = result_data;
189 	ctx.result_size = &_result_data_size;
190 	IF_ENABLED(CONFIG_BT_EATT, (ctx.params.chan_opt = bearer));
191 
192 	if (bearer == BT_ATT_CHAN_OPT_ENHANCED_ONLY) {
193 		__ASSERT(IS_ENABLED(CONFIG_BT_EATT), "EATT not complied in");
194 	}
195 
196 	err = bt_testlib_sync_bt_gatt_read(&ctx);
197 
198 	if (result_size) {
199 		*result_size = _result_data_size;
200 	}
201 
202 	return err;
203 }
204 
205 struct bt_testlib_gatt_discover_service_closure {
206 	struct bt_gatt_discover_params params;
207 	uint8_t att_err;
208 	uint16_t *const result_handle;
209 	uint16_t *const result_end_handle;
210 	struct k_mutex lock;
211 	struct k_condvar done;
212 };
213 
gatt_discover_service_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)214 static uint8_t gatt_discover_service_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
215 					struct bt_gatt_discover_params *params)
216 {
217 	struct bt_testlib_gatt_discover_service_closure *ctx =
218 		CONTAINER_OF(params, struct bt_testlib_gatt_discover_service_closure, params);
219 
220 	k_mutex_lock(&ctx->lock, K_FOREVER);
221 
222 	ctx->att_err = attr ? BT_ATT_ERR_SUCCESS : BT_ATT_ERR_ATTRIBUTE_NOT_FOUND;
223 
224 	if (!ctx->att_err) {
225 		if (ctx->result_handle) {
226 			*ctx->result_handle = attr->handle;
227 		}
228 
229 		if (ctx->result_end_handle) {
230 			*ctx->result_end_handle = 0;
231 			/* Output 'group end handle'. */
232 			if (params->type == BT_GATT_DISCOVER_PRIMARY ||
233 			    params->type == BT_GATT_DISCOVER_SECONDARY) {
234 				*ctx->result_end_handle =
235 					((struct bt_gatt_service_val *)attr->user_data)->end_handle;
236 			}
237 		}
238 	}
239 
240 	k_condvar_signal(&ctx->done);
241 	k_mutex_unlock(&ctx->lock);
242 	return BT_GATT_ITER_STOP;
243 }
244 
245 /** AKA Service discovery by UUID.
246  */
bt_testlib_gatt_discover_primary(uint16_t * result_handle,uint16_t * result_end_handle,struct bt_conn * conn,const struct bt_uuid * uuid,uint16_t start_handle,uint16_t end_handle)247 int bt_testlib_gatt_discover_primary(uint16_t *result_handle, uint16_t *result_end_handle,
248 				     struct bt_conn *conn, const struct bt_uuid *uuid,
249 				     uint16_t start_handle, uint16_t end_handle)
250 {
251 	int api_err;
252 
253 	struct bt_testlib_gatt_discover_service_closure ctx_val = {
254 		.result_handle = result_handle,
255 		.result_end_handle = result_end_handle,
256 		.params = {
257 			.type = BT_GATT_DISCOVER_PRIMARY,
258 			.start_handle = start_handle,
259 			.end_handle = end_handle,
260 			.func = gatt_discover_service_cb,
261 			.uuid = uuid,
262 		}};
263 	struct bt_testlib_gatt_discover_service_closure *const ctx = &ctx_val;
264 
265 	k_mutex_init(&ctx->lock);
266 	k_condvar_init(&ctx->done);
267 
268 	__ASSERT_NO_MSG(conn);
269 	__ASSERT_NO_MSG(IN_RANGE(start_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
270 				 BT_ATT_LAST_ATTRIBUTE_HANDLE));
271 	__ASSERT_NO_MSG(
272 		IN_RANGE(end_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE));
273 
274 	k_mutex_lock(&ctx->lock, K_FOREVER);
275 
276 	api_err = bt_gatt_discover(conn, &ctx->params);
277 
278 	if (!api_err) {
279 		k_condvar_wait(&ctx->done, &ctx->lock, K_FOREVER);
280 	}
281 
282 	k_mutex_unlock(&ctx->lock);
283 
284 	if (api_err) {
285 		__ASSERT_NO_MSG(api_err < 0);
286 		return api_err;
287 	}
288 	__ASSERT_NO_MSG(ctx->att_err >= 0);
289 	return ctx->att_err;
290 }
291 
292 struct bt_testlib_gatt_discover_char_closure {
293 	struct bt_gatt_discover_params params;
294 	uint8_t att_err;
295 	uint16_t *const result_def_handle;
296 	uint16_t *const result_value_handle;
297 	uint16_t *const result_end_handle;
298 	uint16_t svc_end_handle;
299 	struct k_mutex lock;
300 	struct k_condvar done;
301 };
302 
gatt_discover_char_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)303 static uint8_t gatt_discover_char_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
304 				     struct bt_gatt_discover_params *params)
305 {
306 	struct bt_testlib_gatt_discover_char_closure *ctx =
307 		CONTAINER_OF(params, struct bt_testlib_gatt_discover_char_closure, params);
308 	bool read_more = false;
309 
310 	k_mutex_lock(&ctx->lock, K_FOREVER);
311 
312 	if (ctx->att_err == BT_ATT_ERR_ATTRIBUTE_NOT_FOUND) {
313 		/* The start of the charachteristic was not found yet.
314 		 * This is the start of the characteristic.
315 		 */
316 		if (attr) {
317 			ctx->att_err = BT_ATT_ERR_SUCCESS;
318 			if (ctx->result_def_handle) {
319 				*ctx->result_def_handle = attr->handle;
320 			}
321 			if (ctx->result_value_handle) {
322 				*ctx->result_value_handle =
323 					((struct bt_gatt_chrc *)attr->user_data)->value_handle;
324 			}
325 			if (ctx->result_end_handle) {
326 				read_more = true;
327 			}
328 		}
329 	} else {
330 		/* This is the end of the characteristic.
331 		 */
332 		if (attr) {
333 			__ASSERT_NO_MSG(ctx->result_end_handle);
334 			*ctx->result_end_handle = (attr->handle - 1);
335 		}
336 	};
337 
338 	if (!read_more) {
339 		k_condvar_signal(&ctx->done);
340 	}
341 	k_mutex_unlock(&ctx->lock);
342 	return read_more ? BT_GATT_ITER_CONTINUE : BT_GATT_ITER_STOP;
343 }
344 
bt_testlib_gatt_discover_characteristic(uint16_t * const result_value_handle,uint16_t * const result_end_handle,uint16_t * const result_def_handle,struct bt_conn * conn,const struct bt_uuid * uuid,uint16_t start_handle,uint16_t svc_end_handle)345 int bt_testlib_gatt_discover_characteristic(uint16_t *const result_value_handle,
346 					    uint16_t *const result_end_handle,
347 					    uint16_t *const result_def_handle, struct bt_conn *conn,
348 					    const struct bt_uuid *uuid, uint16_t start_handle,
349 					    uint16_t svc_end_handle)
350 {
351 	int api_err;
352 
353 	if (result_end_handle) {
354 		/* If there is no second result, the end_handle is the svc_end. */
355 		*result_end_handle = svc_end_handle;
356 	}
357 
358 	struct bt_testlib_gatt_discover_char_closure ctx_val = {
359 		.att_err = BT_ATT_ERR_ATTRIBUTE_NOT_FOUND,
360 		.result_value_handle = result_value_handle,
361 		.result_def_handle = result_def_handle,
362 		.result_end_handle = result_end_handle,
363 		.params = {
364 			.type = BT_GATT_DISCOVER_CHARACTERISTIC,
365 			.start_handle = start_handle,
366 			.end_handle = svc_end_handle,
367 			.func = gatt_discover_char_cb,
368 			.uuid = uuid,
369 		}};
370 	struct bt_testlib_gatt_discover_char_closure *const ctx = &ctx_val;
371 
372 	k_mutex_init(&ctx->lock);
373 	k_condvar_init(&ctx->done);
374 
375 	__ASSERT_NO_MSG(conn);
376 	__ASSERT_NO_MSG(IN_RANGE(start_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
377 				 BT_ATT_LAST_ATTRIBUTE_HANDLE));
378 	__ASSERT_NO_MSG(IN_RANGE(svc_end_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE,
379 				 BT_ATT_LAST_ATTRIBUTE_HANDLE));
380 
381 	k_mutex_lock(&ctx->lock, K_FOREVER);
382 
383 	api_err = bt_gatt_discover(conn, &ctx->params);
384 
385 	if (!api_err) {
386 		k_condvar_wait(&ctx->done, &ctx->lock, K_FOREVER);
387 	}
388 
389 	k_mutex_unlock(&ctx->lock);
390 
391 	if (api_err) {
392 		__ASSERT_NO_MSG(api_err < 0);
393 		return api_err;
394 	}
395 	__ASSERT_NO_MSG(ctx->att_err >= 0);
396 	return ctx->att_err;
397 }
398