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