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