1 /*
2  * Copyright (c) 2023 Demant A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/bluetooth/audio/pacs.h>
8 
9 #include "common.h"
10 
11 extern enum bst_result_t bst_result;
12 
13 static struct bt_audio_codec_cap lc3_codec_1 =
14 	BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_LC3_FREQ_16KHZ | BT_AUDIO_CODEC_LC3_FREQ_24KHZ,
15 			   BT_AUDIO_CODEC_LC3_DURATION_10,
16 			   BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
17 			   BT_AUDIO_CONTEXT_TYPE_ANY);
18 static struct bt_audio_codec_cap lc3_codec_2 =
19 	BT_AUDIO_CODEC_CAP_LC3(BT_AUDIO_CODEC_LC3_FREQ_16KHZ,
20 			   BT_AUDIO_CODEC_LC3_DURATION_10,
21 			   BT_AUDIO_CODEC_LC3_CHAN_COUNT_SUPPORT(1), 40u, 60u, 1u,
22 			   BT_AUDIO_CONTEXT_TYPE_ANY);
23 static struct bt_pacs_cap                    caps_1 = {
24 	.codec_cap = &lc3_codec_1,
25 };
26 static struct bt_pacs_cap                    caps_2 = {
27 	.codec_cap = &lc3_codec_2,
28 };
29 
is_peer_subscribed(struct bt_conn * conn)30 static bool is_peer_subscribed(struct bt_conn *conn)
31 {
32 	struct bt_gatt_attr *attr;
33 	uint8_t nbr_subscribed = 0;
34 
35 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK);
36 	if (!attr) {
37 		printk("No BT_UUID_PACS_SNK attribute found\n");
38 	}
39 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
40 		nbr_subscribed++;
41 	}
42 
43 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SNK_LOC);
44 	if (!attr) {
45 		printk("No BT_UUID_PACS_SNK_LOC attribute found\n");
46 	}
47 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
48 		nbr_subscribed++;
49 	}
50 
51 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC);
52 	if (!attr) {
53 		printk("No BT_UUID_PACS_SRC attribute found\n");
54 	}
55 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
56 		nbr_subscribed++;
57 	}
58 
59 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SRC_LOC);
60 	if (!attr) {
61 		printk("No BT_UUID_PACS_SRC_LOC attribute found\n");
62 	}
63 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
64 		nbr_subscribed++;
65 	}
66 
67 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_AVAILABLE_CONTEXT);
68 	if (!attr) {
69 		printk("No BT_UUID_PACS_AVAILABLE_CONTEXT attribute found\n");
70 	}
71 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
72 		nbr_subscribed++;
73 	}
74 
75 	attr = bt_gatt_find_by_uuid(NULL, 0, BT_UUID_PACS_SUPPORTED_CONTEXT);
76 	if (!attr) {
77 		printk("No BT_UUID_PACS_SUPPORTED_CONTEXT attribute found\n");
78 	}
79 	if (bt_gatt_is_subscribed(conn, attr, BT_GATT_CCC_NOTIFY)) {
80 		nbr_subscribed++;
81 	}
82 
83 	if (nbr_subscribed != 6) {
84 		return false;
85 	}
86 
87 	return true;
88 }
89 
trigger_notifications(void)90 static void trigger_notifications(void)
91 {
92 	static enum bt_audio_context available = BT_AUDIO_CONTEXT_TYPE_ANY;
93 	static enum bt_audio_context supported = BT_AUDIO_CONTEXT_TYPE_ANY;
94 	static int i;
95 	int err;
96 	struct bt_pacs_cap *caps;
97 	enum bt_audio_location snk_loc;
98 	enum bt_audio_location src_loc;
99 
100 	printk("Triggering Notifications\n");
101 
102 	if (i) {
103 		caps = &caps_1;
104 		snk_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
105 		src_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
106 		i = 0;
107 	} else {
108 		caps = &caps_2;
109 		snk_loc = BT_AUDIO_LOCATION_FRONT_RIGHT;
110 		src_loc = BT_AUDIO_LOCATION_FRONT_LEFT;
111 		i++;
112 	}
113 
114 	printk("Changing Sink PACs\n");
115 	bt_pacs_cap_register(BT_AUDIO_DIR_SINK, caps);
116 	bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, caps);
117 
118 	printk("Changing Sink Location\n");
119 	err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, snk_loc);
120 	if (err != 0) {
121 		printk("Failed to set device sink location\n");
122 	}
123 
124 	printk("Changing Source Location\n");
125 	err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, src_loc);
126 	if (err != 0) {
127 		printk("Failed to set device source location\n");
128 	}
129 
130 	printk("Changing Supported Contexts Location\n");
131 	supported = supported ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
132 	bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, supported);
133 
134 	printk("Changing Available Contexts\n");
135 	available = available ^ BT_AUDIO_CONTEXT_TYPE_MEDIA;
136 	bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, available);
137 }
138 
test_main(void)139 static void test_main(void)
140 {
141 	int err;
142 	const struct bt_data ad[] = {
143 		BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
144 	};
145 
146 	printk("Enabling Bluetooth\n");
147 	err = bt_enable(NULL);
148 	if (err != 0) {
149 		FAIL("Bluetooth enable failed (err %d)\n", err);
150 		return;
151 	}
152 
153 	bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
154 	bt_pacs_set_supported_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
155 	bt_pacs_set_available_contexts(BT_AUDIO_DIR_SINK, BT_AUDIO_CONTEXT_TYPE_ANY);
156 	bt_pacs_set_available_contexts(BT_AUDIO_DIR_SOURCE, BT_AUDIO_CONTEXT_TYPE_ANY);
157 
158 	printk("Registereding PACS\n");
159 	bt_pacs_cap_register(BT_AUDIO_DIR_SINK, &caps_1);
160 	bt_pacs_cap_register(BT_AUDIO_DIR_SOURCE, &caps_1);
161 
162 	err = bt_pacs_set_location(BT_AUDIO_DIR_SINK, BT_AUDIO_LOCATION_FRONT_LEFT);
163 	if (err != 0) {
164 		printk("Failed to set device sink location\n");
165 		return;
166 	}
167 
168 	err = bt_pacs_set_location(BT_AUDIO_DIR_SOURCE, BT_AUDIO_LOCATION_FRONT_RIGHT);
169 	if (err != 0) {
170 		printk("Failed to set device source location\n");
171 		return;
172 	}
173 
174 	printk("Start Advertising\n");
175 	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
176 	if (err != 0) {
177 		FAIL("Advertising failed to start (err %d)\n", err);
178 		return;
179 	}
180 
181 	printk("Waiting to be connected\n");
182 	WAIT_FOR_FLAG(flag_connected);
183 	printk("Connected\n");
184 	printk("Waiting to be subscribed\n");
185 
186 	while (!is_peer_subscribed(default_conn)) {
187 		(void)k_sleep(K_MSEC(10));
188 	}
189 	printk("Subscribed\n");
190 
191 	trigger_notifications();
192 
193 	/* Now wait for client to disconnect, then stop adv so it does not reconnect */
194 	printk("Wait for client disconnect\n");
195 	WAIT_FOR_UNSET_FLAG(flag_connected);
196 	printk("Client disconnected\n");
197 
198 	err = bt_le_adv_stop();
199 	if (err != 0) {
200 		FAIL("Advertising failed to stop (err %d)\n", err);
201 		return;
202 	}
203 
204 	/* Trigger changes while device is disconnected */
205 	trigger_notifications();
206 
207 	printk("Start Advertising\n");
208 	err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
209 	if (err != 0) {
210 		FAIL("Advertising failed to start (err %d)\n", err);
211 		return;
212 	}
213 
214 	WAIT_FOR_FLAG(flag_connected);
215 	WAIT_FOR_UNSET_FLAG(flag_connected);
216 
217 	PASS("PACS Notify Server passed\n");
218 }
219 
220 static const struct bst_test_instance test_pacs_notify_server[] = {
221 	{
222 		.test_id = "pacs_notify_server",
223 		.test_post_init_f = test_init,
224 		.test_tick_f = test_tick,
225 		.test_main_f = test_main,
226 	},
227 	BSTEST_END_MARKER,
228 };
229 
test_pacs_notify_server_install(struct bst_test_list * tests)230 struct bst_test_list *test_pacs_notify_server_install(struct bst_test_list *tests)
231 {
232 	return bst_add_tests(tests, test_pacs_notify_server);
233 }
234