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