1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdlib.h>
8 #include "utils.h"
9 #include "main.h"
10 #include "argparse.h"
11 #include "bs_pc_backchannel.h"
12 #include "bstests.h"
13 
14 #include <zephyr/sys/__assert.h>
15 
16 #include "babblekit/testcase.h"
17 
18 void server_procedure(void);
19 void client_procedure(void);
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 = strtol(argv[0], NULL, 10);
45 	final_round = strtol(argv[1], NULL, 10);
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 
52 static const struct bst_test_instance test_to_add[] = {
53 	{
54 		.test_id = "server",
55 		.test_main_f = server_procedure,
56 		.test_args_f = test_args,
57 	},
58 	{
59 		.test_id = "client",
60 		.test_main_f = client_procedure,
61 		.test_args_f = test_args,
62 	},
63 	BSTEST_END_MARKER,
64 };
65 
install(struct bst_test_list * tests)66 static struct bst_test_list *install(struct bst_test_list *tests)
67 {
68 	return bst_add_tests(tests, test_to_add);
69 };
70 
71 bst_test_install_t test_installers[] = { install, NULL };
72 
main(void)73 int main(void)
74 {
75 	bst_main();
76 	return 0;
77 }
78 
79 
backchannel_init(void)80 void backchannel_init(void)
81 {
82 	uint device_number = get_device_nbr();
83 	uint channel_numbers[2] = { 0, 0, };
84 	uint device_numbers[2];
85 	uint num_ch;
86 	uint *ch;
87 
88 	/* No backchannels to next/prev device if only device */
89 	if (get_test_round() == 0 && is_final_round()) {
90 		return;
91 	}
92 
93 	/* Each `server` round/instance gets a connection to the previous and to
94 	 * the next instance in the chain. It waits until it is signalled by the
95 	 * previous instance, then runs its test procedure and finally signals
96 	 * the next instance in the chain.
97 	 *
98 	 * The two ends of the chain get only one channel, hence the difference
99 	 * in handling.
100 	 */
101 
102 	if (get_test_round() == 0) {
103 		/* send only */
104 		device_numbers[0] = get_device_nbr() + 1;
105 		num_ch = 1;
106 
107 	} else if (is_final_round()) {
108 		/* receive only */
109 		device_numbers[0] = get_device_nbr() - 1;
110 		num_ch = 1;
111 
112 	} else {
113 		/* send signal */
114 		device_numbers[0] = get_device_nbr() + 1;
115 		/* receive signal */
116 		device_numbers[1] = get_device_nbr() - 1;
117 		num_ch = 2;
118 	}
119 
120 	printk("Opening backchannels\n");
121 	ch = bs_open_back_channel(device_number, device_numbers,
122 				  channel_numbers, num_ch);
123 	if (!ch) {
124 		TEST_FAIL("Unable to open backchannel");
125 	}
126 }
127 
128 #define MSG_SIZE 1
129 
backchannel_sync_send(uint channel)130 void backchannel_sync_send(uint channel)
131 {
132 	uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
133 
134 	printk("Sending sync\n");
135 	bs_bc_send_msg(channel, sync_msg, ARRAY_SIZE(sync_msg));
136 }
137 
backchannel_sync_wait(uint channel)138 void backchannel_sync_wait(uint channel)
139 {
140 	uint8_t sync_msg[MSG_SIZE];
141 
142 	while (true) {
143 		if (bs_bc_is_msg_received(channel) > 0) {
144 			bs_bc_receive_msg(channel, sync_msg,
145 					  ARRAY_SIZE(sync_msg));
146 			if (sync_msg[0] != get_device_nbr()) {
147 				/* Received a message from another device, exit */
148 				break;
149 			}
150 		}
151 
152 		k_sleep(K_MSEC(1));
153 	}
154 
155 	printk("Sync received\n");
156 }
157 
158 /* We can't really kill the device/process without borking the bsim
159  * backchannels, so the next best thing is stopping all threads from processing,
160  * thus stopping the Bluetooth host from processing the disconnect event (or any
161  * event, really) coming from the link-layer.
162  */
stop_all_threads(void)163 static void stop_all_threads(void)
164 {
165 	/* promote to highest priority */
166 	k_thread_priority_set(k_current_get(), K_HIGHEST_THREAD_PRIO);
167 	/* busy-wait loop */
168 	for (;;) {
169 		k_busy_wait(1000);
170 		k_yield();
171 	}
172 }
173 
signal_next_test_round(void)174 void signal_next_test_round(void)
175 {
176 	if (!is_final_round()) {
177 		backchannel_sync_send(0);
178 	}
179 
180 	TEST_PASS("round %d over", get_test_round());
181 	stop_all_threads();
182 }
183 
wait_for_round_start(void)184 void wait_for_round_start(void)
185 {
186 	backchannel_init();
187 
188 	if (is_final_round()) {
189 		backchannel_sync_wait(0);
190 	} else if (get_test_round() != 0) {
191 		backchannel_sync_wait(1);
192 	}
193 }
194