1 /*
2  * Copyright 2023 NXP
3  * Copyright (c) 2025 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdbool.h>
9 #include <stdint.h>
10 #include <stddef.h>
11 
12 #include <zephyr/autoconf.h>
13 #include <zephyr/bluetooth/audio/bap.h>
14 #include <zephyr/bluetooth/audio/tmap.h>
15 #include <zephyr/bluetooth/addr.h>
16 #include <zephyr/bluetooth/bluetooth.h>
17 #include <zephyr/bluetooth/conn.h>
18 #include <zephyr/bluetooth/gap.h>
19 #include <zephyr/bluetooth/uuid.h>
20 #include <zephyr/bluetooth/gatt.h>
21 #include <zephyr/net_buf.h>
22 #include <zephyr/sys/printk.h>
23 #include <zephyr/types.h>
24 #include <zephyr/sys/byteorder.h>
25 
26 #include "bstests.h"
27 #include "common.h"
28 
29 #ifdef CONFIG_BT_TMAP
30 extern enum bst_result_t bst_result;
31 
32 CREATE_FLAG(flag_tmap_discovered);
33 
tmap_discovery_complete_cb(enum bt_tmap_role role,struct bt_conn * conn,int err)34 void tmap_discovery_complete_cb(enum bt_tmap_role role, struct bt_conn *conn, int err)
35 {
36 	printk("TMAS discovery done\n");
37 	SET_FLAG(flag_tmap_discovered);
38 }
39 
40 static struct bt_tmap_cb tmap_callbacks = {
41 	.discovery_complete = tmap_discovery_complete_cb
42 };
43 
check_audio_support_and_connect(struct bt_data * data,void * user_data)44 static bool check_audio_support_and_connect(struct bt_data *data, void *user_data)
45 {
46 	bt_addr_le_t *addr = user_data;
47 	struct net_buf_simple tmas_svc_data;
48 	const struct bt_uuid *uuid;
49 	uint16_t uuid_val;
50 	uint16_t peer_tmap_role = 0;
51 	int err;
52 
53 	printk("[AD]: %u data_len %u\n", data->type, data->data_len);
54 
55 	if (data->type != BT_DATA_SVC_DATA16) {
56 		return true; /* Continue parsing to next AD data type */
57 	}
58 
59 	if (data->data_len < sizeof(uuid_val)) {
60 		printk("AD invalid size %u\n", data->data_len);
61 		return true; /* Continue parsing to next AD data type */
62 	}
63 
64 	net_buf_simple_init_with_data(&tmas_svc_data, (void *)data->data, data->data_len);
65 	uuid_val = net_buf_simple_pull_le16(&tmas_svc_data);
66 	uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(uuid_val));
67 	if (bt_uuid_cmp(uuid, BT_UUID_TMAS) != 0) {
68 		/* We are looking for the TMAS service data */
69 		return true; /* Continue parsing to next AD data type */
70 	}
71 
72 	printk("Found TMAS in peer adv data!\n");
73 	if (tmas_svc_data.len < sizeof(peer_tmap_role)) {
74 		printk("AD invalid size %u\n", data->data_len);
75 		return false; /* Stop parsing */
76 	}
77 
78 	peer_tmap_role = net_buf_simple_pull_le16(&tmas_svc_data);
79 	if (!(peer_tmap_role & BT_TMAP_ROLE_UMR)) {
80 		printk("No TMAS UMR support!\n");
81 		return false; /* Stop parsing */
82 	}
83 
84 	printk("Attempt to connect!\n");
85 	err = bt_le_scan_stop();
86 	if (err != 0) {
87 		printk("Failed to stop scan: %d\n", err);
88 		return false;
89 	}
90 
91 	err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_BAP_CONN_PARAM_RELAXED,
92 				&default_conn);
93 	if (err != 0) {
94 		printk("Create conn to failed (%u)\n", err);
95 		bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
96 	}
97 
98 	return false; /* Stop parsing */
99 }
100 
scan_recv(const struct bt_le_scan_recv_info * info,struct net_buf_simple * buf)101 static void scan_recv(const struct bt_le_scan_recv_info *info,
102 		      struct net_buf_simple *buf)
103 {
104 	char le_addr[BT_ADDR_LE_STR_LEN];
105 
106 	printk("SCAN RCV CB\n");
107 
108 	/* Check for connectable, extended advertising */
109 	if (((info->adv_props & BT_GAP_ADV_PROP_EXT_ADV) != 0) ||
110 		((info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE)) != 0) {
111 		bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
112 		printk("[DEVICE]: %s, ", le_addr);
113 		/* Check for TMAS support in advertising data */
114 		bt_data_parse(buf, check_audio_support_and_connect, (void *)info->addr);
115 	}
116 }
117 
118 static struct bt_le_scan_cb scan_callbacks = {
119 	.recv = scan_recv,
120 };
121 
discover_tmas(void)122 static void discover_tmas(void)
123 {
124 	int err;
125 
126 	UNSET_FLAG(flag_tmap_discovered);
127 
128 	/* Discover TMAS service on peer */
129 	err = bt_tmap_discover(default_conn, &tmap_callbacks);
130 	if (err != 0) {
131 		FAIL("Failed to initiate TMAS discovery: %d\n", err);
132 		return;
133 	}
134 
135 	printk("TMAP Central Starting Service Discovery...\n");
136 	WAIT_FOR_FLAG(flag_tmap_discovered);
137 }
138 
test_main(void)139 static void test_main(void)
140 {
141 	int err;
142 
143 	err = bt_enable(NULL);
144 	if (err != 0) {
145 		FAIL("Bluetooth init failed (err %d)\n", err);
146 		return;
147 	}
148 
149 	printk("Bluetooth initialized\n");
150 	/* Initialize TMAP */
151 	err = bt_tmap_register(BT_TMAP_ROLE_CG | BT_TMAP_ROLE_UMS);
152 	if (err != 0) {
153 		FAIL("Failed to register TMAP (err %d)\n", err);
154 		return;
155 	}
156 
157 	printk("TMAP initialized. Start scanning...\n");
158 	/* Scan for peer */
159 	bt_le_scan_cb_register(&scan_callbacks);
160 	err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, NULL);
161 	if (err != 0) {
162 		FAIL("Scanning failed to start (err %d)\n", err);
163 		return;
164 	}
165 
166 	printk("Scanning successfully started\n");
167 	WAIT_FOR_FLAG(flag_connected);
168 
169 	discover_tmas();
170 	discover_tmas(); /* test that we can discover twice */
171 
172 	PASS("TMAP Client test passed\n");
173 }
174 
175 static const struct bst_test_instance test_tmap_client[] = {
176 	{
177 		.test_id = "tmap_client",
178 		.test_pre_init_f = test_init,
179 		.test_tick_f = test_tick,
180 		.test_main_f = test_main,
181 	},
182 	BSTEST_END_MARKER
183 };
184 
test_tmap_client_install(struct bst_test_list * tests)185 struct bst_test_list *test_tmap_client_install(struct bst_test_list *tests)
186 {
187 	return bst_add_tests(tests, test_tmap_client);
188 }
189 #else
test_tmap_client_install(struct bst_test_list * tests)190 struct bst_test_list *test_tmap_client_install(struct bst_test_list *tests)
191 {
192 	return tests;
193 }
194 #endif /* CONFIG_BT_TMAP */
195