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