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