1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "utils.h"
8 #include "main.h"
9 #include "argparse.h"
10 #include "bs_pc_backchannel.h"
11 #include "bstests.h"
12 
13 #include <zephyr/sys/__assert.h>
14 
15 void server_procedure(void);
16 void client_procedure(void);
17 
18 #define BS_SECONDS(dur_sec)    ((bs_time_t)dur_sec * USEC_PER_SEC)
19 #define TEST_TIMEOUT_SIMULATED BS_SECONDS(30)
20 
21 static int test_round;
22 static int final_round;
23 static char *settings_file;
24 
get_test_round(void)25 int get_test_round(void)
26 {
27 	return test_round;
28 }
29 
is_final_round(void)30 bool is_final_round(void)
31 {
32 	return test_round == final_round;
33 }
34 
get_settings_file(void)35 char *get_settings_file(void)
36 {
37 	return settings_file;
38 }
39 
test_args(int argc,char ** argv)40 static void test_args(int argc, char **argv)
41 {
42 	__ASSERT(argc == 3, "Please specify only 3 test arguments\n");
43 
44 	test_round = atol(argv[0]);
45 	final_round = atol(argv[1]);
46 	settings_file = argv[2];
47 
48 	bs_trace_raw(0, "Test round %u\n", test_round);
49 	bs_trace_raw(0, "Final round %u\n", final_round);
50 }
51 
test_tick(bs_time_t HW_device_time)52 void test_tick(bs_time_t HW_device_time)
53 {
54 	bs_trace_debug_time(0, "Simulation ends now.\n");
55 	if (bst_result != Passed) {
56 		bst_result = Failed;
57 		bs_trace_error("Test did not pass before simulation ended.\n");
58 	}
59 }
60 
test_init(void)61 void test_init(void)
62 {
63 	bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED);
64 	bst_result = In_progress;
65 }
66 
67 static const struct bst_test_instance test_to_add[] = {
68 	{
69 		.test_id = "server",
70 		.test_pre_init_f = test_init,
71 		.test_tick_f = test_tick,
72 		.test_main_f = server_procedure,
73 		.test_args_f = test_args,
74 	},
75 	{
76 		.test_id = "client",
77 		.test_pre_init_f = test_init,
78 		.test_tick_f = test_tick,
79 		.test_main_f = client_procedure,
80 		.test_args_f = test_args,
81 	},
82 	BSTEST_END_MARKER,
83 };
84 
install(struct bst_test_list * tests)85 static struct bst_test_list *install(struct bst_test_list *tests)
86 {
87 	return bst_add_tests(tests, test_to_add);
88 };
89 
90 bst_test_install_t test_installers[] = { install, NULL };
91 
main(void)92 int main(void)
93 {
94 	bst_main();
95 	return 0;
96 }
97 
98 
backchannel_init(void)99 void backchannel_init(void)
100 {
101 	uint device_number = get_device_nbr();
102 	uint channel_numbers[2] = { 0, 0, };
103 	uint device_numbers[2];
104 	uint num_ch;
105 	uint *ch;
106 
107 	/* No backchannels to next/prev device if only device */
108 	if (get_test_round() == 0 && is_final_round()) {
109 		return;
110 	}
111 
112 	/* Each `server` round/instance gets a connection to the previous and to
113 	 * the next instance in the chain. It waits until it is signalled by the
114 	 * previous instance, then runs its test procedure and finally signals
115 	 * the next instance in the chain.
116 	 *
117 	 * The two ends of the chain get only one channel, hence the difference
118 	 * in handling.
119 	 */
120 
121 	if (get_test_round() == 0) {
122 		/* send only */
123 		device_numbers[0] = get_device_nbr() + 1;
124 		num_ch = 1;
125 
126 	} else if (is_final_round()) {
127 		/* receive only */
128 		device_numbers[0] = get_device_nbr() - 1;
129 		num_ch = 1;
130 
131 	} else {
132 		/* send signal */
133 		device_numbers[0] = get_device_nbr() + 1;
134 		/* receive signal */
135 		device_numbers[1] = get_device_nbr() - 1;
136 		num_ch = 2;
137 	}
138 
139 	printk("Opening backchannels\n");
140 	ch = bs_open_back_channel(device_number, device_numbers,
141 				  channel_numbers, num_ch);
142 	if (!ch) {
143 		FAIL("Unable to open backchannel\n");
144 	}
145 }
146 
147 #define MSG_SIZE 1
148 
backchannel_sync_send(uint channel)149 void backchannel_sync_send(uint channel)
150 {
151 	uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
152 
153 	printk("Sending sync\n");
154 	bs_bc_send_msg(channel, sync_msg, ARRAY_SIZE(sync_msg));
155 }
156 
backchannel_sync_wait(uint channel)157 void backchannel_sync_wait(uint channel)
158 {
159 	uint8_t sync_msg[MSG_SIZE];
160 
161 	while (true) {
162 		if (bs_bc_is_msg_received(channel) > 0) {
163 			bs_bc_receive_msg(channel, sync_msg,
164 					  ARRAY_SIZE(sync_msg));
165 			if (sync_msg[0] != get_device_nbr()) {
166 				/* Received a message from another device, exit */
167 				break;
168 			}
169 		}
170 
171 		k_sleep(K_MSEC(1));
172 	}
173 
174 	printk("Sync received\n");
175 }
176 
177 /* We can't really kill the device/process without borking the bsim
178  * backchannels, so the next best thing is stopping all threads from processing,
179  * thus stopping the Bluetooth host from processing the disconnect event (or any
180  * event, really) coming from the link-layer.
181  */
stop_all_threads(void)182 static void stop_all_threads(void)
183 {
184 	/* promote to highest priority */
185 	k_thread_priority_set(k_current_get(), K_HIGHEST_THREAD_PRIO);
186 	/* busy-wait loop */
187 	for (;;) {
188 		k_busy_wait(1000);
189 		k_yield();
190 	}
191 }
192 
signal_next_test_round(void)193 void signal_next_test_round(void)
194 {
195 	if (!is_final_round()) {
196 		backchannel_sync_send(0);
197 	}
198 
199 	PASS("round %d over\n", get_test_round());
200 	stop_all_threads();
201 }
202 
wait_for_round_start(void)203 void wait_for_round_start(void)
204 {
205 	backchannel_init();
206 
207 	if (is_final_round()) {
208 		backchannel_sync_wait(0);
209 	} else if (get_test_round() != 0) {
210 		backchannel_sync_wait(1);
211 	}
212 }
213