1 /*
2  * Copyright (c) 2017 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  *
7  * Common command line arguments and overall command line argument handling
8  * for Zephyr Babblesim boards.
9  *
10  * Note that this is code runs in the native simulator runner context,
11  * and not in any embedded CPU context.
12  * This file should therefore only be built once for all CPUs.
13  */
14 
15 #include <stdint.h>
16 #include <stdbool.h>
17 #include <limits.h>
18 #include <string.h>
19 #include "bs_cmd_line.h"
20 #include "bs_cmd_line_typical.h"
21 #include "bs_dynargs.h"
22 #include "bs_tracing.h"
23 #include "bs_dump_files.h"
24 #include "bs_rand_main.h"
25 #include "nsi_cpu_if.h"
26 #include "nsi_tasks.h"
27 #include "nsi_main.h"
28 #include "nsi_cpu_ctrl.h"
29 #include "NRF_HWLowL.h"
30 #include "NHW_misc.h"
31 
32 static bs_args_struct_t *args_struct;
33 /* Direct use of this global is deprecated, use bsim_args_get_global_device_nbr() instead */
34 uint global_device_nbr;
35 
36 #define MAXPARAMS_TESTCASES 1024
37 
38 static struct bsim_global_args_t {
39 	BS_BASIC_DEVICE_OPTIONS_FIELDS
40 } global_args;
41 
42 static bool nosim;
43 
44 /* Extra "command line options" provided programmatically: */
45 static int extra_argc;
46 static char **extra_argv;
47 
cmd_trace_lvl_found(char * argv,int offset)48 static void cmd_trace_lvl_found(char *argv, int offset)
49 {
50 	bs_trace_set_level(global_args.verb);
51 }
52 
cmd_gdev_nbr_found(char * argv,int offset)53 static void cmd_gdev_nbr_found(char *argv, int offset)
54 {
55 	bs_trace_set_prefix_dev(global_args.global_device_nbr);
56 }
57 
print_no_sim_warning(void)58 static void print_no_sim_warning(void)
59 {
60 	bs_trace_warning("Neither simulation id or the device number "
61 			"have been set. I assume you want to run "
62 			"without a BabbleSim phy (-nosim)\n");
63 	bs_trace_warning("If this is not what you wanted, check with "
64 			"--help how to set them\n");
65 	bs_trace_raw(3, "setting sim_id to 'bogus', device number to 0 "
66 			"and nosim\n");
67 }
68 
print_mcus_info(char * argv,int offset)69 static void print_mcus_info(char *argv, int offset)
70 {
71 	(void) argv;
72 	(void) offset;
73 	bs_trace_raw(0, "CPU  #,       Name  , Autostart\n");
74 	bs_trace_raw(0, "-------------------------------\n");
75 	for (int i = 0; i < NSI_N_CPUS; i++) {
76 		bs_trace_raw(0, "CPU %2i, %12s,    %i\n",
77 			     i, nhw_get_core_name(i), nsi_cpu_get_auto_start(i));
78 	}
79 }
80 
bsim_register_basic_args(void)81 static void bsim_register_basic_args(void)
82 {
83 #define args (&global_args)
84 	/* This define allows reusing the definitions provided by the utils library */
85 	static bs_args_struct_t args_struct_toadd[] = {
86 		ARG_TABLE_S_ID,
87 		ARG_TABLE_P_ID_2G4,
88 		ARG_TABLE_DEV_NBR,
89 		ARG_TABLE_GDEV_NBR,
90 		ARG_TABLE_VERB,
91 		ARG_TABLE_SEED,
92 		ARG_TABLE_COLOR,
93 		ARG_TABLE_NOCOLOR,
94 		ARG_TABLE_FORCECOLOR,
95 		{
96 		.is_switch = true,
97 		.option = "nosim",
98 		.type = 'b',
99 		.dest = (void *)&nosim,
100 		.descript = "Do not connect to the Physical layer simulator"
101 		},
102 		BS_DUMP_FILES_ARGS,
103 		{
104 		.manual = true,
105 		.option = "argstest",
106 		.name = "arg",
107 		.type = 'l',
108 		.descript = "The arguments that follow will be passed straight to the testcase "
109 			"init function (Note: If more than 1 MCU is present, argtest corresponds "
110 			"to argstests" NSI_STRINGIFY(NSI_PRIMARY_MCU_N) " )"
111 		},
112 		{
113 		.manual = true,
114 		.option = "argstest<n>",
115 		.name = "arg",
116 		.type = 'l',
117 		.descript = "The arguments that follow will be passed straight to cpu<n>'s "
118 			"testcase init function), where 0 <= n < " NSI_STRINGIFY(NSI_N_CPUS)
119 			" is the cpu number"
120 		},
121 		{
122 		.manual = true,
123 		.option = "argsmain",
124 		.name = "arg",
125 		.type = 'l',
126 		.descript = "The arguments that follow will be passed to main (default)"
127 		},
128 		{
129 		.is_switch = true,
130 		.option = "cpu_print_info",
131 		.call_when_found = print_mcus_info,
132 		.type = 'b',
133 		.descript = "Print information about each MCUs",
134 		},
135 		ARG_TABLE_ENDMARKER
136 	};
137 #undef args
138 
139 	bs_add_dynargs(&args_struct, args_struct_toadd);
140 }
141 
142 NSI_TASK(bsim_register_basic_args, PRE_BOOT_1, 0);
143 
bsim_cleanup_args(void)144 static void bsim_cleanup_args(void)
145 {
146 	bs_cleanup_dynargs(&args_struct);
147 }
148 
149 NSI_TASK(bsim_cleanup_args, ON_EXIT_POST, 0);
150 
bs_add_extra_dynargs(bs_args_struct_t * args_struct_toadd)151 void bs_add_extra_dynargs(bs_args_struct_t *args_struct_toadd)
152 {
153 	bs_add_dynargs(&args_struct, args_struct_toadd);
154 }
155 
nsif_cpun_save_test_arg(int n,char * c)156 static void nsif_cpun_save_test_arg(int n, char *c)
157 {
158 	F_TRAMP_LIST(NATIVE_SIMULATOR_IF void nsif_cpu, _save_test_arg(char *argv))
159 
160 	void(*fptrs[])(char *) = {
161 		F_TRAMP_TABLE(nsif_cpu, _save_test_arg)
162 	};
163 
164 	fptrs[n](c);
165 }
166 
nsi_handle_one_cmdline_argument(char * argv)167 static void nsi_handle_one_cmdline_argument(char *argv)
168 {
169 	static enum {Main = 0, Test = 1} parsing = Main;
170 	static uint test_cpu_n;
171 
172 	if (bs_is_option(argv, "argstest", 0)) {
173 		parsing = Test;
174 		test_cpu_n = NSI_PRIMARY_MCU_N;
175 		return;
176 	} else if (bs_is_multi_opt(argv, "argstest", &test_cpu_n, 0)) {
177 		parsing = Test;
178 		return;
179 	} else if (bs_is_option(argv, "argsmain", 0)) {
180 		parsing = Main;
181 		return;
182 	}
183 
184 	if (parsing == Main) {
185 		if (!bs_args_parse_one_arg(argv, args_struct)) {
186 			bs_args_print_switches_help(args_struct);
187 			bs_trace_error_line("Incorrect option %s\n",
188 					    argv);
189 		}
190 	} else if (parsing == Test) {
191 		nsif_cpun_save_test_arg(test_cpu_n, argv);
192 	} else {
193 		bs_trace_error_line("Bad error\n");
194 	}
195 }
196 
197 /**
198  * Check the arguments provided in the command line: set args based on it or
199  * defaults, and check they are correct
200  */
nsi_handle_cmd_line(int argc,char * argv[])201 void nsi_handle_cmd_line(int argc, char *argv[])
202 {
203 	bs_args_set_defaults(args_struct);
204 	global_args.verb = 2;
205 	bs_trace_set_level(global_args.verb);
206 
207 	for (int i = 0; i < extra_argc; i++) {
208 		nsi_handle_one_cmdline_argument(extra_argv[i]);
209 	}
210 	for (int i = 1; i < argc; i++) {
211 		nsi_handle_one_cmdline_argument(argv[i]);
212 	}
213 }
214 
nsi_register_extra_args(int argc,char * argv[])215 void nsi_register_extra_args(int argc, char *argv[])
216 {
217 	int new_size = extra_argc + argc;
218 
219 	extra_argv = realloc(extra_argv, new_size*sizeof(char *));
220 	for (int i = 0; i < argc; i++) {
221 		memcpy(&extra_argv[extra_argc], argv, argc*sizeof(char *));
222 	}
223 	extra_argc += argc;
224 }
225 
clear_extra_args(void)226 static void clear_extra_args(void)
227 {
228 	free(extra_argv);
229 }
230 
231 NSI_TASK(clear_extra_args, ON_EXIT_PRE, 100);
232 
postcheck_cmd_line(void)233 static void postcheck_cmd_line(void)
234 {
235 	static const char *bogus_sim_id = "bogus";
236 	static const char default_phy[] = "2G4";
237 
238 	/**
239 	 * If the user did not set the simulation id or device number
240 	 * we assume he wanted to run with nosim (but warn him)
241 	 */
242 	if ((!nosim) && (global_args.s_id == NULL) && (global_args.device_nbr == UINT_MAX)) {
243 		print_no_sim_warning();
244 		nosim = true;
245 	}
246 	if (nosim) {
247 		if (global_args.s_id == NULL) {
248 			global_args.s_id = (char *)bogus_sim_id;
249 		}
250 		if (global_args.device_nbr == UINT_MAX) {
251 			global_args.device_nbr = 0;
252 		}
253 		hwll_set_nosim(true);
254 	}
255 
256 	if (global_args.device_nbr == UINT_MAX) {
257 		bs_args_print_switches_help(args_struct);
258 		bs_trace_error_line("The command line option <device number> "
259 				    "needs to be set\n");
260 	}
261 	if (global_args.global_device_nbr == UINT_MAX) {
262 		global_args.global_device_nbr = global_args.device_nbr;
263 		bs_trace_set_prefix_dev(global_args.global_device_nbr);
264 	}
265 	global_device_nbr = global_args.global_device_nbr;
266 	if (!global_args.s_id) {
267 		bs_args_print_switches_help(args_struct);
268 		bs_trace_error_line("The command line option <simulation ID> "
269 				    "needs to be set\n");
270 	}
271 	if (!global_args.p_id) {
272 		global_args.p_id = (char *)default_phy;
273 	}
274 
275 	if (global_args.rseed == UINT_MAX) {
276 		global_args.rseed = 0x1000 + global_args.device_nbr;
277 	}
278 
279 	bs_random_init(global_args.rseed);
280 }
281 
282 NSI_TASK(postcheck_cmd_line, PRE_BOOT_2, 0);
283 
284 /*
285  * Get the simulation id
286  */
bsim_args_get_simid(void)287 char *bsim_args_get_simid(void)
288 {
289 	return global_args.s_id;
290 }
291 
292 /*
293  * Get this device number in the simulation, as it is
294  * known in the overall simulation.
295  * In general this is the device number you want
296  */
bsim_args_get_global_device_nbr(void)297 unsigned int bsim_args_get_global_device_nbr(void)
298 {
299 	return global_args.global_device_nbr;
300 }
301 
302 /*
303  * Get this device number in the 2G4 Phy simulation
304  */
bsim_args_get_2G4_device_nbr(void)305 unsigned int bsim_args_get_2G4_device_nbr(void)
306 {
307 	return global_args.device_nbr;
308 }
309 
310 /*
311  * Get this device number in the 2G4 Phy simulation
312  */
bsim_args_get_2G4_phy_id(void)313 char *bsim_args_get_2G4_phy_id(void)
314 {
315 	return global_args.p_id;
316 }
317 
get_simid(void)318 char *get_simid(void)
319 {
320 	return bsim_args_get_simid();
321 }
322 
get_device_nbr(void)323 unsigned int get_device_nbr(void)
324 {
325 	return bsim_args_get_global_device_nbr();
326 }
327