1 /*
2  * Copyright 2018 Oticon A/S
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "p2G4_args.h"
7 #include <stdlib.h>
8 #include <string.h>
9 #include <limits.h>
10 #include "bs_tracing.h"
11 #include "bs_types.h"
12 #include "bs_oswrap.h"
13 
14 char executable_name[] = "bs_2G4_phy_v1";
15 
component_print_post_help()16 void component_print_post_help(){
17   fprintf(stdout,"\n"
18 " Note regarding multiple modems: the default modem (and the default modem arguments)\n"
19 " are assigned to any modem which does not get assigned any other modem with\n"
20 " -modem<nbr>=<modem>.\n"
21 "\n"
22 "bs_2G4_phy_v1: Emulates the physical layer, including all devices modem's and the radio channel\n");
23 }
24 
25 #define MAXPARAMS_LIBRARIES 1024
26 
27 static char default_channel[] = "NtNcable";
28 static char default_modem[]   = "Magic";
29 
allocate_modems_params(p2G4_args_t * args)30 static inline void allocate_modems_params(p2G4_args_t *args){
31   args->modem_name = (char **)bs_calloc(args->n_devs, sizeof(char*));
32   args->modem_argc = (uint *)bs_calloc(args->n_devs, sizeof(uint));
33   args->modem_argv = (char ***)bs_calloc(args->n_devs, sizeof(char**));
34 }
35 
36 p2G4_args_t *args_g;
37 
cmd_trace_lvl_found(char * argv,int offset)38 static void cmd_trace_lvl_found(char * argv, int offset){
39   bs_trace_set_level(args_g->verb);
40 }
phy_id_found(char * argv,int offset)41 static void phy_id_found(char * argv, int offset){
42   bs_trace_set_prefix_phy(args_g->p_id);
43 }
44 double sim_length;
sim_length_found(char * argv,int offset)45 static void sim_length_found(char * argv, int offset){
46   args_g->sim_length = sim_length;
47   bs_trace_raw(9,"cmdarg: sim_length set to %"PRItime"\n", args_g->sim_length);
48 }
stop_found(char * argv,int offset)49 static void stop_found(char * argv, int offset){
50   args_g->compare = true;
51 }
dump_found(char * argv,int offset)52 static void dump_found(char * argv, int offset){
53   args_g->dont_dump = false;
54   bs_trace_raw(9,"cmdarg: dump mode set\n");
55 }
channel_found(char * argv,int offset)56 static void channel_found(char * argv, int offset){
57   bs_trace_raw(9,"cmdarg: channel set to libChannel_%s.so\n",args_g->channel_name);
58 }
defmodem_found(char * argv,int offset)59 static void defmodem_found(char * argv, int offset){
60   bs_trace_raw(9,"cmdarg: defmodem set to libModem_%s.so\n",args_g->defmodem_name);
61 }
62 /**
63  * Check the arguments provided in the command line: set args based on it
64  * or defaults, and check they are correct
65  */
p2G4_argsparse(int argc,char * argv[],p2G4_args_t * args)66 void p2G4_argsparse(int argc, char *argv[], p2G4_args_t *args)
67 {
68 
69   args_g = args;
70   bs_args_struct_t args_struct[] = {
71       ARG_TABLE_S_ID,
72       { false, false , false,  "p_id",       "phy_id",       's', (void*)&args->p_id,  phy_id_found,   "(2G4) String which uniquely identifies the phy inside the simulation"},
73       /*manual,mandatory,switch,option,     name ,           type,       destination,         callback,             , description*/
74       { true,  true  , false,  "D",        "number_devices", 'u', (void*)&args->n_devs,  NULL,         "Number of devices which will connect in this phy"},
75       { false, false  , false, "sim_length",  "sim_length",  'f', (void*)&sim_length, sim_length_found,"In us, length of the simulation"},
76       ARG_TABLE_VERB,
77       ARG_TABLE_SEED,
78       ARG_TABLE_COLOR,
79       ARG_TABLE_NOCOLOR,
80       ARG_TABLE_FORCECOLOR,
81       { false, false  , true,  "nodump",    "no_dump",  'b', (void*)&args->dont_dump,      NULL,         "Will not dump (or compare) any files"},
82       { false, false  , true,  "dump_imm",  "dump_imm", 'b', (void*)&args->dump_imm,       NULL,         "When dumping, do not buffer more than a line"},
83       { false, false  , true,  "dump",      "dump",     'b', (void*)NULL,                 dump_found,    "Revert -nodump option (note that the last -nodump/dump set in the command line prevails)"},
84       { false, false  , true,  "crcerr_data","crcerr",  'b', (void*)&args->crcerr_data,    NULL,         "Provide uncorrupted packet to device attempting to receive even if packet has a CRC error or reception is aborted midway (disabled by default)"},
85       { false, false  , true,  "c",          "compare", 'b', (void*)&args->compare,        NULL,         "Run in compare mode: will compare instead of dumping"},
86       { false, false  , true,  "stop_on_diff","stop",   'b', (void*)&args->stop_on_diff,  stop_found,    "Run in compare mode, but stop as soon as a difference is found"},
87       { false, false  , false, "channel",    "channel", 's', (void*)&args->channel_name,  channel_found, "Which channel will be used ( lib/lib_2G4Channel_<channel>.so ). By default NtNcable"},
88       { false, false  , false, "defmodem",   "modem",   's', (void*)&args->defmodem_name, defmodem_found,"Which modem will be used by default for all devices ( lib/lib_2G4Modem_<modem>.so ). By default Magic"},
89       { true,  false  , false, "modem<nbr>", "modem",   's', (void*)NULL,                  NULL,         "Which modem will be used for the device <nbr> ( lib/lib_2G4Modem_<modem>.so )"},
90       { true,  false  , false, "argschannel","arg",     'l', (void*)NULL,                  NULL,         "Following arguments (until end or new -args*) will be passed to the channel"},
91       { true,  false  , false, "argsdefmodem","arg",    'l', (void*)NULL,                  NULL,         "Following arguments (until end or new -args*) will be passed to the modems set to be the default modem"},
92       { true,  false  , false, "argsmodem<nbr>","arg",  'l', (void*)NULL,                  NULL,         "Following arguments (until end or new -args*) will be passed to the modem of the device <nbr>"},
93       { true,  false  , false, "argsmain","arg",        'l', (void*)NULL,                  NULL,         "Following arguments (until end or new -args*) will be passed to the phy itself (default)"},
94       ARG_TABLE_ENDMARKER
95   };
96 
97 #define MAIN       0
98 #define CHANNEL    1
99 #define DEFMODEM   2
100 #define MODEM   1000
101 
102   uint parsing = MAIN;
103   static char default_phy[] ="2G4";
104 
105   args->verb       = 2;
106   bs_trace_set_level(args->verb);
107   args->rseed      = 0xFFFF;
108   args->sim_length = TIME_NEVER - 1000000000 ; //1Ksecond before never by default
109 
110   args->channel_argv    = bs_calloc(MAXPARAMS_LIBRARIES*2, sizeof(char *));
111   args->channel_argc    = 0;
112   args->channel_name    = default_channel;
113 
114   args->defmodem_argv   = args->channel_argv + MAXPARAMS_LIBRARIES;
115   args->defmodem_argc   = 0;
116   args->defmodem_name   = default_modem;
117 
118   args->modem_argv = NULL;
119   args->modem_argc = NULL;
120   args->modem_name = NULL;
121 
122   char trace_prefix[] = "cmdarg: ";
123   bs_args_set_trace_prefix(trace_prefix);
124 
125   int offset;
126   uint modem_nbr;
127 
128   for (int i=1; i<argc; i++){
129 
130     //First we check if we are getting the option to switch the type of args:
131     if ((strncmp(argv[i], "-args",5)==0) || (strncmp(argv[i], "--args",6)==0)) {
132       if (bs_is_option(argv[i], "argschannel", 0)) {
133         parsing = CHANNEL;
134       } else if ( bs_is_option(argv[i], "argsdefmodem", 0)) {
135         parsing = DEFMODEM;
136       } else if ( bs_is_option(argv[i], "argsmain", 0)) {
137         parsing = MAIN;
138       } else if (bs_is_multi_opt(argv[i], "argsmodem", &modem_nbr ,0)) {
139         if ( args->n_devs == 0 ) {
140           bs_trace_error_line("cmdarg: tried to set the modem args for a device"
141                               " (%i) before setting the number of devices (-D="
142                               "<nbr>) (%s)\n\n""\n",
143                               modem_nbr, args->n_devs, argv[i]);
144         }
145         if ( modem_nbr >= args->n_devs ) {
146           bs_trace_error_line("cmdarg: tried to set the modem args for a device "
147                               "%i >= %i number of avaliable devices (%s)\n\n""\n",
148                               modem_nbr, args->n_devs, argv[i]);
149         }
150         if ( args->modem_name[modem_nbr] == NULL ) {
151           bs_trace_error_line("cmdarg: tried to set the modem args for a modem "
152                               "(%i) not yet specified (%s)\n\n""\n",
153                               modem_nbr, argv[i]);
154         }
155         parsing = MODEM + modem_nbr;
156       } else {
157         bs_args_print_switches_help(args_struct);
158         bs_trace_error_line("Incorrect args option '%s'\n",argv[i]);
159       }
160       continue;
161     }
162 
163     //Otherwise, depending on what we are parsing now, we handle things:
164     if ( parsing == MAIN ){
165       if ( !bs_args_parse_one_arg(argv[i], args_struct) ){
166         if ((offset = bs_is_option(argv[i], "D", 1)) > 0) {
167           if (args->n_devs != 0) {
168             bs_trace_error_line("The number of devices (-D) can only be "
169                                 "specified once, found as argument %i: %s\n",
170                                 i, argv[i]);
171           }
172           bs_read_optionparam(&argv[i][offset], (void*)&(args->n_devs), 'u', "nbr_devices");
173           allocate_modems_params(args);
174 
175         } else if ((offset = bs_is_multi_opt(argv[i], "modem", &modem_nbr, 1))>0) {
176           if ( args->n_devs == 0 ) {
177             bs_trace_error_line("cmdarg: tried to set the modem for a device "
178                                 "(%i) before setting the number of devices (-D"
179                                 "=<nbr>) (%s)\n",
180                                 modem_nbr, args->n_devs, argv[i]);
181           }
182           if ( modem_nbr >= args->n_devs ) {
183             bs_trace_error_line("cmdarg: tried to set the modem for a device "
184                                 "%i >= %i number of avaliable devices (%s)\n",
185                                 modem_nbr, args->n_devs, argv[i]);
186           }
187           args->modem_name[modem_nbr] = &argv[i][offset];
188           bs_trace_raw(9, "cmdarg: modem[%u] set to libModem_%s.so\n",
189                        modem_nbr, args->modem_name[modem_nbr]);
190         }
191         else {
192           bs_args_print_switches_help(args_struct);
193           bs_trace_error_line("Incorrect phy main option %s\n",argv[i]);
194         }
195       }
196 
197     } else if ( parsing == CHANNEL ){
198       if ( args->channel_argc >= MAXPARAMS_LIBRARIES ) {
199         bs_trace_error_line("Too many channel command line parameters (at '%s'), maximum is %i\n", argv[i],MAXPARAMS_LIBRARIES);
200       } else {
201         args->channel_argv[ args->channel_argc ] = argv[i];
202         bs_trace_raw(9,"cmdarg: adding '%s' to channel args[%i]\n", argv[i], args->channel_argc);
203         args->channel_argc++;
204       }
205 
206     } else if ( parsing == DEFMODEM ){
207       if ( args->channel_argc >= MAXPARAMS_LIBRARIES ) {
208         bs_trace_error_line("Too many defmodem command line parameters (at '%s'), maximum is %i\n", argv[i],MAXPARAMS_LIBRARIES);
209       } else {
210         args->defmodem_argv[ args->defmodem_argc ] = argv[i];
211         bs_trace_raw(9,"cmdarg: adding '%s' to defmodem args[%i]\n", argv[i], args->defmodem_argc);
212         args->defmodem_argc++;
213       }
214 
215     } else if ( parsing >= MODEM ){
216       uint modem_nbr = parsing - MODEM;
217       if ( args->modem_argc[modem_nbr] >= MAXPARAMS_LIBRARIES ) {
218         bs_trace_error_line("Too many modem%i command line parameters (at '%s'), maximum is %i\n",modem_nbr, argv[i],MAXPARAMS_LIBRARIES);
219       } else {
220         if ( args->modem_argc[modem_nbr] == 0 ){
221           args->modem_argv[modem_nbr]  = bs_calloc(MAXPARAMS_LIBRARIES, sizeof(char *));
222         }
223         args->modem_argv[modem_nbr][ args->modem_argc[modem_nbr] ] = argv[i];
224         bs_trace_raw(9,"cmdarg: adding '%s' to modem%i args[%i]\n", argv[i], modem_nbr, args->modem_argc[modem_nbr]);
225         args->modem_argc[modem_nbr]++;
226       }
227     }
228   }
229 
230   if ( args->s_id == NULL ) {
231     bs_args_print_switches_help(args_struct);
232     bs_trace_error_line("The command line option <simulation ID> needs to be set\n");
233   }
234   if ( args->p_id == NULL ){
235     args->p_id = default_phy;
236     bs_trace_set_prefix_phy(args->p_id);
237   }
238   if ( args->n_devs == 0 ) {
239     bs_args_print_switches_help(args_struct);
240     bs_trace_error_line("The command line option <number_devices> needs to be set\n");
241   }
242   //for all modems which didn't get set to something else, we set them to the default modem
243   for (uint dev = 0; dev < args->n_devs ; dev ++){
244     if (args->modem_name[dev] == NULL) { //if we didnt set for this modem already
245       args->modem_name[dev] = args->defmodem_name;
246       args->modem_argv[dev] = args->defmodem_argv;
247       args->modem_argc[dev] = args->defmodem_argc;
248     }
249   }
250 }
251 
p2G4_clear_args_struct(p2G4_args_t * args)252 void p2G4_clear_args_struct(p2G4_args_t *args){
253   for (uint n = 0; n < args->n_devs ; n++) {
254     if ((args->modem_argv[n] != NULL) && (args->modem_argv[n] != args->defmodem_argv)) {
255       free(args->modem_argv[n]);
256     }
257   }
258 
259   if ( args->channel_argv != NULL ){
260     free(args->channel_argv);
261   }
262   if (args->modem_name != NULL) {
263     free(args->modem_name);
264   }
265 
266   if (args->modem_argc != NULL) {
267       free(args->modem_argc);
268   }
269 
270   if (args->modem_argv != NULL) {
271       free(args->modem_argv);
272   }
273 }
274