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