1 /*
2  * Copyright (c) 2022 Codecoup
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <errno.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <string.h>
10 
11 #include <zephyr/autoconf.h>
12 #include <zephyr/bluetooth/audio/has.h>
13 #include <zephyr/bluetooth/att.h>
14 #include <zephyr/bluetooth/bluetooth.h>
15 #include <zephyr/bluetooth/conn.h>
16 #include <zephyr/bluetooth/gatt.h>
17 #include <zephyr/bluetooth/hci_types.h>
18 #include <zephyr/bluetooth/uuid.h>
19 #include <zephyr/logging/log.h>
20 #include <zephyr/logging/log_core.h>
21 #include <zephyr/sys/util_macro.h>
22 
23 #include "../../subsys/bluetooth/audio/has_internal.h"
24 
25 #include "bstests.h"
26 #include "common.h"
27 
28 LOG_MODULE_REGISTER(has_client_test, LOG_LEVEL_DBG);
29 
30 extern enum bst_result_t bst_result;
31 
32 extern const char *test_preset_name_1;
33 extern const char *test_preset_name_5;
34 extern const uint8_t test_preset_index_1;
35 extern const uint8_t test_preset_index_3;
36 extern const uint8_t test_preset_index_5;
37 extern const enum bt_has_properties test_preset_properties;
38 
39 CREATE_FLAG(g_service_discovered);
40 CREATE_FLAG(g_preset_switched);
41 CREATE_FLAG(g_preset_1_found);
42 CREATE_FLAG(g_preset_3_found);
43 CREATE_FLAG(g_preset_5_found);
44 
45 static struct bt_has *g_has;
46 static uint8_t g_active_index;
47 
discover_cb(struct bt_conn * conn,int err,struct bt_has * has,enum bt_has_hearing_aid_type type,enum bt_has_capabilities caps)48 static void discover_cb(struct bt_conn *conn, int err, struct bt_has *has,
49 			enum bt_has_hearing_aid_type type, enum bt_has_capabilities caps)
50 {
51 	if (err) {
52 		FAIL("Failed to discover HAS (err %d)\n", err);
53 		return;
54 	}
55 
56 	LOG_DBG("HAS discovered type %d caps %d", type, caps);
57 
58 	g_has = has;
59 	SET_FLAG(g_service_discovered);
60 }
61 
preset_switch_cb(struct bt_has * has,int err,uint8_t index)62 static void preset_switch_cb(struct bt_has *has, int err, uint8_t index)
63 {
64 	if (err != 0) {
65 		return;
66 	}
67 
68 	LOG_DBG("Active preset index %d", index);
69 
70 	SET_FLAG(g_preset_switched);
71 	g_active_index = index;
72 }
73 
check_preset_record(const struct bt_has_preset_record * record,enum bt_has_properties expected_properties,const char * expected_name)74 static void check_preset_record(const struct bt_has_preset_record *record,
75 				enum bt_has_properties expected_properties,
76 				const char *expected_name)
77 {
78 	if (record->properties != expected_properties || strcmp(record->name, expected_name)) {
79 		FAIL("mismatch 0x%02x %s vs 0x%02x %s expected\n",
80 		     record->properties, record->name, expected_properties, expected_name);
81 	}
82 }
83 
preset_read_rsp_cb(struct bt_has * has,int err,const struct bt_has_preset_record * record,bool is_last)84 static void preset_read_rsp_cb(struct bt_has *has, int err,
85 			       const struct bt_has_preset_record *record, bool is_last)
86 {
87 	if (err) {
88 		FAIL("%s: err %d\n", __func__, err);
89 		return;
90 	}
91 
92 	if (record->index == test_preset_index_1) {
93 		SET_FLAG(g_preset_1_found);
94 
95 		check_preset_record(record, test_preset_properties, test_preset_name_1);
96 	} else if (record->index == test_preset_index_5) {
97 		SET_FLAG(g_preset_5_found);
98 
99 		check_preset_record(record, test_preset_properties, test_preset_name_5);
100 	} else {
101 		FAIL("unexpected index 0x%02x", record->index);
102 	}
103 }
104 
preset_update_cb(struct bt_has * has,uint8_t index_prev,const struct bt_has_preset_record * record,bool is_last)105 static void preset_update_cb(struct bt_has *has, uint8_t index_prev,
106 			     const struct bt_has_preset_record *record, bool is_last)
107 {
108 	if (record->index == test_preset_index_1) {
109 		SET_FLAG(g_preset_1_found);
110 	} else if (record->index == test_preset_index_3) {
111 		SET_FLAG(g_preset_3_found);
112 	} else if (record->index == test_preset_index_5) {
113 		SET_FLAG(g_preset_5_found);
114 	}
115 }
116 
117 static const struct bt_has_client_cb has_cb = {
118 	.discover = discover_cb,
119 	.preset_switch = preset_switch_cb,
120 	.preset_read_rsp = preset_read_rsp_cb,
121 	.preset_update = preset_update_cb,
122 };
123 
test_preset_switch(uint8_t index)124 static bool test_preset_switch(uint8_t index)
125 {
126 	int err;
127 
128 	UNSET_FLAG(g_preset_switched);
129 
130 	err = bt_has_client_preset_set(g_has, index, false);
131 	if (err < 0) {
132 		LOG_DBG("%s (err %d)", __func__, err);
133 		return false;
134 	}
135 
136 	WAIT_FOR_COND(g_preset_switched);
137 
138 	return g_active_index == index;
139 }
140 
test_preset_next(uint8_t active_index_expected)141 static bool test_preset_next(uint8_t active_index_expected)
142 {
143 	int err;
144 
145 	UNSET_FLAG(g_preset_switched);
146 
147 	err = bt_has_client_preset_next(g_has, false);
148 	if (err < 0) {
149 		LOG_DBG("%s (err %d)", __func__, err);
150 		return false;
151 	}
152 
153 	WAIT_FOR_COND(g_preset_switched);
154 
155 	return g_active_index == active_index_expected;
156 }
157 
test_preset_prev(uint8_t active_index_expected)158 static bool test_preset_prev(uint8_t active_index_expected)
159 {
160 	int err;
161 
162 	UNSET_FLAG(g_preset_switched);
163 
164 	err = bt_has_client_preset_prev(g_has, false);
165 	if (err < 0) {
166 		LOG_DBG("%s (err %d)", __func__, err);
167 		return false;
168 	}
169 
170 	WAIT_FOR_COND(g_preset_switched);
171 
172 	return g_active_index == active_index_expected;
173 }
174 
discover_has(void)175 static void discover_has(void)
176 {
177 	int err;
178 
179 	g_service_discovered = false;
180 
181 	err = bt_has_client_discover(default_conn);
182 	if (err < 0) {
183 		FAIL("Failed to discover HAS (err %d)\n", err);
184 		return;
185 	}
186 
187 	WAIT_FOR_COND(g_service_discovered);
188 }
189 
test_main(void)190 static void test_main(void)
191 {
192 	int err;
193 
194 	err = bt_enable(NULL);
195 	if (err < 0) {
196 		FAIL("Bluetooth discover failed (err %d)\n", err);
197 		return;
198 	}
199 
200 	LOG_DBG("Bluetooth initialized");
201 
202 	err = bt_has_client_cb_register(&has_cb);
203 	if (err < 0) {
204 		FAIL("Failed to register callbacks (err %d)\n", err);
205 		return;
206 	}
207 
208 	bt_le_scan_cb_register(&common_scan_cb);
209 
210 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
211 	if (err < 0) {
212 		FAIL("Scanning failed to start (err %d)\n", err);
213 		return;
214 	}
215 
216 	LOG_DBG("Scanning successfully started");
217 
218 	WAIT_FOR_FLAG(flag_connected);
219 
220 	discover_has();
221 	WAIT_FOR_COND(g_preset_switched);
222 
223 	err = bt_has_client_presets_read(g_has, BT_HAS_PRESET_INDEX_FIRST, 255);
224 	if (err < 0) {
225 		FAIL("Failed to read presets (err %d)\n", err);
226 		return;
227 	}
228 
229 	WAIT_FOR_COND(g_preset_1_found);
230 	WAIT_FOR_COND(g_preset_5_found);
231 
232 	if (!test_preset_switch(test_preset_index_1)) {
233 		FAIL("Failed to switch preset %d\n", test_preset_index_1);
234 		return;
235 	}
236 
237 	if (!test_preset_switch(test_preset_index_5)) {
238 		FAIL("Failed to switch preset %d\n", test_preset_index_5);
239 		return;
240 	}
241 
242 	if (!test_preset_next(test_preset_index_1)) {
243 		FAIL("Failed to set next preset %d\n", test_preset_index_1);
244 		return;
245 	}
246 
247 	if (!test_preset_next(test_preset_index_5)) {
248 		FAIL("Failed to set next preset %d\n", test_preset_index_5);
249 		return;
250 	}
251 
252 	if (!test_preset_next(test_preset_index_1)) {
253 		FAIL("Failed to set next preset %d\n", test_preset_index_1);
254 		return;
255 	}
256 
257 	if (!test_preset_prev(test_preset_index_5)) {
258 		FAIL("Failed to set previous preset %d\n", test_preset_index_5);
259 		return;
260 	}
261 
262 	if (!test_preset_prev(test_preset_index_1)) {
263 		FAIL("Failed to set previous preset %d\n", test_preset_index_1);
264 		return;
265 	}
266 
267 	if (!test_preset_prev(test_preset_index_5)) {
268 		FAIL("Failed to set previous preset %d\n", test_preset_index_5);
269 		return;
270 	}
271 
272 	PASS("HAS main PASS\n");
273 }
274 
275 #define FEATURES_SUB_NTF        BIT(0)
276 #define ACTIVE_INDEX_SUB_NTF    BIT(1)
277 #define PRESET_CHANGED_SUB_NTF  BIT(2)
278 #define SUB_NTF_ALL		(FEATURES_SUB_NTF | ACTIVE_INDEX_SUB_NTF | PRESET_CHANGED_SUB_NTF)
279 
280 CREATE_FLAG(flag_features_discovered);
281 CREATE_FLAG(flag_active_preset_index_discovered);
282 CREATE_FLAG(flag_control_point_discovered);
283 CREATE_FLAG(flag_all_notifications_received);
284 
285 enum preset_state {
286 	STATE_UNKNOWN,
287 	STATE_AVAILABLE,
288 	STATE_UNAVAILABLE,
289 	STATE_DELETED,
290 };
291 
292 static enum preset_state preset_state_1;
293 static enum preset_state preset_state_3;
294 static enum preset_state preset_state_5;
295 
296 static struct bt_uuid_16 uuid = BT_UUID_INIT_16(0);
297 static struct bt_gatt_discover_params discover_params;
298 static struct bt_gatt_subscribe_params features_sub;
299 static struct bt_gatt_subscribe_params active_preset_index_sub;
300 static struct bt_gatt_subscribe_params control_point_sub;
301 static uint8_t notify_received_mask;
302 
preset_availability_changed(uint8_t index,bool available)303 static void preset_availability_changed(uint8_t index, bool available)
304 {
305 	enum preset_state state = available ? STATE_AVAILABLE : STATE_UNAVAILABLE;
306 
307 	if (index == test_preset_index_1) {
308 		preset_state_1 = state;
309 	} else if (index == test_preset_index_3) {
310 		preset_state_3 = state;
311 	} else if (index == test_preset_index_5) {
312 		preset_state_5 = state;
313 	} else {
314 		FAIL("invalid preset index 0x%02x", index);
315 	}
316 }
317 
notify_handler(struct bt_conn * conn,struct bt_gatt_subscribe_params * params,const void * data,uint16_t length)318 static uint8_t notify_handler(struct bt_conn *conn, struct bt_gatt_subscribe_params *params,
319 			      const void *data, uint16_t length)
320 {
321 	LOG_DBG("conn %p params %p data %p length %u", (void *)conn, params, data, length);
322 
323 	if (params == &features_sub) {
324 		if (data == NULL) {
325 			LOG_DBG("features_sub [UNSUBSCRIBED]");
326 			return BT_GATT_ITER_STOP;
327 		}
328 
329 		LOG_DBG("Received features_sub notification");
330 		notify_received_mask |= FEATURES_SUB_NTF;
331 	} else if (params == &active_preset_index_sub) {
332 		if (data == NULL) {
333 			LOG_DBG("active_preset_index_sub_sub [UNSUBSCRIBED]");
334 			return BT_GATT_ITER_STOP;
335 		}
336 
337 		LOG_DBG("Received active_preset_index_sub_sub notification");
338 		notify_received_mask |= ACTIVE_INDEX_SUB_NTF;
339 	} else if (params == &control_point_sub) {
340 		const struct bt_has_cp_hdr *hdr;
341 
342 		if (data == NULL) {
343 			LOG_DBG("control_point_sub [UNSUBSCRIBED]");
344 			return BT_GATT_ITER_STOP;
345 		}
346 
347 		if (length < sizeof(*hdr)) {
348 			FAIL("malformed bt_has_cp_hdr");
349 			return BT_GATT_ITER_STOP;
350 		}
351 
352 		hdr = data;
353 
354 		if (hdr->opcode == BT_HAS_OP_PRESET_CHANGED) {
355 			const struct bt_has_cp_preset_changed *pc;
356 
357 			if (length < (sizeof(*hdr) + sizeof(*pc))) {
358 				FAIL("malformed bt_has_cp_preset_changed");
359 				return BT_GATT_ITER_STOP;
360 			}
361 
362 			pc = (const void *)hdr->data;
363 
364 			switch (pc->change_id) {
365 			case BT_HAS_CHANGE_ID_GENERIC_UPDATE: {
366 				const struct bt_has_cp_generic_update *gu;
367 				bool is_available;
368 
369 				if (length < (sizeof(*hdr) + sizeof(*pc) + sizeof(*gu))) {
370 					FAIL("malformed bt_has_cp_generic_update");
371 					return BT_GATT_ITER_STOP;
372 				}
373 
374 				gu = (const void *)pc->additional_params;
375 
376 				LOG_DBG("Received generic update index 0x%02x props 0x%02x",
377 					gu->index, gu->properties);
378 
379 				is_available = (gu->properties & BT_HAS_PROP_AVAILABLE) != 0;
380 
381 				preset_availability_changed(gu->index, is_available);
382 				break;
383 			}
384 			default:
385 				LOG_DBG("Unexpected Change ID 0x%02x", pc->change_id);
386 				return BT_GATT_ITER_STOP;
387 			}
388 
389 			if (pc->is_last) {
390 				notify_received_mask |= PRESET_CHANGED_SUB_NTF;
391 			}
392 		} else {
393 			LOG_DBG("Unexpected opcode 0x%02x", hdr->opcode);
394 			return BT_GATT_ITER_STOP;
395 		}
396 	}
397 
398 	LOG_DBG("pacs_instance.notify_received_mask is %d", notify_received_mask);
399 
400 	if (notify_received_mask == SUB_NTF_ALL) {
401 		SET_FLAG(flag_all_notifications_received);
402 		notify_received_mask = 0;
403 	}
404 
405 	return BT_GATT_ITER_CONTINUE;
406 }
407 
subscribe_cb(struct bt_conn * conn,uint8_t err,struct bt_gatt_subscribe_params * params)408 static void subscribe_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_subscribe_params *params)
409 {
410 	if (err != BT_ATT_ERR_SUCCESS) {
411 		return;
412 	}
413 
414 	LOG_DBG("[SUBSCRIBED]");
415 
416 	if (params == &features_sub) {
417 		SET_FLAG(flag_features_discovered);
418 		return;
419 	}
420 
421 	if (params == &control_point_sub) {
422 		SET_FLAG(flag_control_point_discovered);
423 		return;
424 	}
425 
426 	if (params == &active_preset_index_sub) {
427 		SET_FLAG(flag_active_preset_index_discovered);
428 		return;
429 	}
430 }
431 
discover_features_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)432 static uint8_t discover_features_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
433 				    struct bt_gatt_discover_params *params)
434 {
435 	struct bt_gatt_subscribe_params *subscribe_params;
436 	int err;
437 
438 	if (!attr) {
439 		LOG_DBG("Discover complete");
440 		(void)memset(params, 0, sizeof(*params));
441 		return BT_GATT_ITER_STOP;
442 	}
443 
444 	if (!bt_uuid_cmp(params->uuid, BT_UUID_HAS_HEARING_AID_FEATURES)) {
445 		LOG_DBG("HAS Hearing Aid Features handle at %d", attr->handle);
446 		memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
447 		discover_params.uuid = &uuid.uuid;
448 		discover_params.start_handle = attr->handle + 2;
449 		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
450 		subscribe_params = &features_sub;
451 		subscribe_params->value_handle = bt_gatt_attr_value_handle(attr);
452 
453 		err = bt_gatt_discover(conn, &discover_params);
454 		if (err) {
455 			LOG_DBG("Discover failed (err %d)", err);
456 		}
457 	} else if (!bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC)) {
458 		LOG_DBG("CCC handle at %d", attr->handle);
459 		subscribe_params = &features_sub;
460 		subscribe_params->notify = notify_handler;
461 		subscribe_params->value = BT_GATT_CCC_NOTIFY;
462 		subscribe_params->ccc_handle = attr->handle;
463 		subscribe_params->subscribe = subscribe_cb;
464 
465 		err = bt_gatt_subscribe(conn, subscribe_params);
466 		if (err && err != -EALREADY) {
467 			LOG_DBG("Subscribe failed (err %d)", err);
468 		}
469 	} else {
470 		LOG_DBG("Unknown handle at %d", attr->handle);
471 		return BT_GATT_ITER_CONTINUE;
472 	}
473 
474 	return BT_GATT_ITER_STOP;
475 }
476 
discover_and_subscribe_features(void)477 static void discover_and_subscribe_features(void)
478 {
479 	int err = 0;
480 
481 	LOG_DBG("%s", __func__);
482 
483 	memcpy(&uuid, BT_UUID_HAS_HEARING_AID_FEATURES, sizeof(uuid));
484 	discover_params.uuid = &uuid.uuid;
485 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
486 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
487 	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
488 	discover_params.func = discover_features_cb;
489 
490 	err = bt_gatt_discover(default_conn, &discover_params);
491 	if (err != 0) {
492 		FAIL("Service Discovery failed (err %d)\n", err);
493 		return;
494 	}
495 }
496 
discover_active_preset_index_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)497 static uint8_t discover_active_preset_index_cb(struct bt_conn *conn,
498 					       const struct bt_gatt_attr *attr,
499 					       struct bt_gatt_discover_params *params)
500 {
501 	struct bt_gatt_subscribe_params *subscribe_params;
502 	int err;
503 
504 	if (!attr) {
505 		LOG_DBG("Discover complete");
506 		(void)memset(params, 0, sizeof(*params));
507 		return BT_GATT_ITER_STOP;
508 	}
509 
510 	if (!bt_uuid_cmp(params->uuid, BT_UUID_HAS_ACTIVE_PRESET_INDEX)) {
511 		LOG_DBG("HAS Hearing Aid Features handle at %d", attr->handle);
512 		memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
513 		discover_params.uuid = &uuid.uuid;
514 		discover_params.start_handle = attr->handle + 2;
515 		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
516 		subscribe_params = &active_preset_index_sub;
517 		subscribe_params->value_handle = bt_gatt_attr_value_handle(attr);
518 
519 		err = bt_gatt_discover(conn, &discover_params);
520 		if (err) {
521 			LOG_DBG("Discover failed (err %d)", err);
522 		}
523 	} else if (!bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC)) {
524 		LOG_DBG("CCC handle at %d", attr->handle);
525 		subscribe_params = &active_preset_index_sub;
526 		subscribe_params->notify = notify_handler;
527 		subscribe_params->value = BT_GATT_CCC_NOTIFY;
528 		subscribe_params->ccc_handle = attr->handle;
529 		subscribe_params->subscribe = subscribe_cb;
530 
531 		err = bt_gatt_subscribe(conn, subscribe_params);
532 		if (err && err != -EALREADY) {
533 			LOG_DBG("Subscribe failed (err %d)", err);
534 		}
535 	} else {
536 		LOG_DBG("Unknown handle at %d", attr->handle);
537 		return BT_GATT_ITER_CONTINUE;
538 	}
539 
540 	return BT_GATT_ITER_STOP;
541 }
542 
discover_and_subscribe_active_preset_index(void)543 static void discover_and_subscribe_active_preset_index(void)
544 {
545 	int err = 0;
546 
547 	LOG_DBG("%s", __func__);
548 
549 	memcpy(&uuid, BT_UUID_HAS_ACTIVE_PRESET_INDEX, sizeof(uuid));
550 	discover_params.uuid = &uuid.uuid;
551 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
552 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
553 	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
554 	discover_params.func = discover_active_preset_index_cb;
555 
556 	err = bt_gatt_discover(default_conn, &discover_params);
557 	if (err != 0) {
558 		FAIL("Service Discovery failed (err %d)\n", err);
559 		return;
560 	}
561 }
562 
discover_control_point_cb(struct bt_conn * conn,const struct bt_gatt_attr * attr,struct bt_gatt_discover_params * params)563 static uint8_t discover_control_point_cb(struct bt_conn *conn, const struct bt_gatt_attr *attr,
564 					 struct bt_gatt_discover_params *params)
565 {
566 	struct bt_gatt_subscribe_params *subscribe_params;
567 	int err;
568 
569 	if (!attr) {
570 		LOG_DBG("Discover complete");
571 		(void)memset(params, 0, sizeof(*params));
572 		return BT_GATT_ITER_STOP;
573 	}
574 
575 	if (!bt_uuid_cmp(params->uuid, BT_UUID_HAS_PRESET_CONTROL_POINT)) {
576 		LOG_DBG("HAS Control Point handle at %d", attr->handle);
577 		memcpy(&uuid, BT_UUID_GATT_CCC, sizeof(uuid));
578 		discover_params.uuid = &uuid.uuid;
579 		discover_params.start_handle = attr->handle + 2;
580 		discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR;
581 		subscribe_params = &control_point_sub;
582 		subscribe_params->value_handle = bt_gatt_attr_value_handle(attr);
583 
584 		err = bt_gatt_discover(conn, &discover_params);
585 		if (err) {
586 			LOG_DBG("Discover failed (err %d)", err);
587 		}
588 	} else if (!bt_uuid_cmp(params->uuid, BT_UUID_GATT_CCC)) {
589 		LOG_DBG("CCC handle at %d", attr->handle);
590 		subscribe_params = &control_point_sub;
591 		subscribe_params->notify = notify_handler;
592 		subscribe_params->value = BT_GATT_CCC_INDICATE;
593 		subscribe_params->ccc_handle = attr->handle;
594 		subscribe_params->subscribe = subscribe_cb;
595 
596 		err = bt_gatt_subscribe(conn, subscribe_params);
597 		if (err && err != -EALREADY) {
598 			LOG_DBG("Subscribe failed (err %d)", err);
599 		}
600 	} else {
601 		LOG_DBG("Unknown handle at %d", attr->handle);
602 		return BT_GATT_ITER_CONTINUE;
603 	}
604 
605 	return BT_GATT_ITER_STOP;
606 }
607 
discover_and_subscribe_control_point(void)608 static void discover_and_subscribe_control_point(void)
609 {
610 	int err = 0;
611 
612 	LOG_DBG("%s", __func__);
613 
614 	memcpy(&uuid, BT_UUID_HAS_PRESET_CONTROL_POINT, sizeof(uuid));
615 	discover_params.uuid = &uuid.uuid;
616 	discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
617 	discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
618 	discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC;
619 	discover_params.func = discover_control_point_cb;
620 
621 	err = bt_gatt_discover(default_conn, &discover_params);
622 	if (err != 0) {
623 		FAIL("Control Point failed (err %d)\n", err);
624 		return;
625 	}
626 }
627 
test_gatt_client(void)628 static void test_gatt_client(void)
629 {
630 	int err;
631 
632 	err = bt_enable(NULL);
633 	if (err < 0) {
634 		FAIL("Bluetooth discover failed (err %d)\n", err);
635 		return;
636 	}
637 
638 	LOG_DBG("Bluetooth initialized");
639 
640 	bt_le_scan_cb_register(&common_scan_cb);
641 
642 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
643 	if (err < 0) {
644 		FAIL("Scanning failed to start (err %d)\n", err);
645 		return;
646 	}
647 
648 	LOG_DBG("Scanning successfully started");
649 
650 	WAIT_FOR_FLAG(flag_connected);
651 
652 	err = bt_conn_set_security(default_conn, BT_SECURITY_L2);
653 	if (err) {
654 		FAIL("Failed to set security level %d (err %d)", BT_SECURITY_L2, err);
655 		return;
656 	}
657 
658 	WAIT_FOR_COND(security_level == BT_SECURITY_L2);
659 
660 	discover_and_subscribe_features();
661 	WAIT_FOR_FLAG(flag_features_discovered);
662 
663 	discover_and_subscribe_active_preset_index();
664 	WAIT_FOR_FLAG(flag_active_preset_index_discovered);
665 
666 	discover_and_subscribe_control_point();
667 	WAIT_FOR_FLAG(flag_control_point_discovered);
668 
669 	bt_conn_disconnect(default_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
670 	WAIT_FOR_UNSET_FLAG(flag_connected);
671 
672 	notify_received_mask = 0;
673 	UNSET_FLAG(flag_all_notifications_received);
674 
675 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
676 	if (err < 0) {
677 		FAIL("Scanning failed to start (err %d)\n", err);
678 		return;
679 	}
680 
681 	LOG_DBG("Scanning successfully started");
682 
683 	WAIT_FOR_FLAG(flag_connected);
684 
685 	err = bt_conn_set_security(default_conn, BT_SECURITY_L2);
686 	if (err) {
687 		FAIL("Failed to set security level %d (err %d)\n", BT_SECURITY_L2, err);
688 		return;
689 	}
690 
691 	WAIT_FOR_FLAG(flag_all_notifications_received);
692 
693 	PASS("HAS main PASS\n");
694 }
695 
696 static const struct bst_test_instance test_has[] = {
697 	{
698 		.test_id = "has_client",
699 		.test_pre_init_f = test_init,
700 		.test_tick_f = test_tick,
701 		.test_main_f = test_main,
702 	},
703 	{
704 		.test_id = "has_client_offline_behavior",
705 		.test_descr = "Test receiving notifications after reconnection",
706 		.test_pre_init_f = test_init,
707 		.test_tick_f = test_tick,
708 		.test_main_f = test_gatt_client,
709 	},
710 	BSTEST_END_MARKER
711 };
712 
test_has_client_install(struct bst_test_list * tests)713 struct bst_test_list *test_has_client_install(struct bst_test_list *tests)
714 {
715 	if (IS_ENABLED(CONFIG_BT_HAS_CLIENT)) {
716 		return bst_add_tests(tests, test_has);
717 	} else {
718 		return tests;
719 	}
720 }
721