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