29 #include "cfgtool_util.h" 30 #include "cfgtool_cfg2ubx.h" 31 #include "cfgtool_rx2cfg.h" 32 #include "cfgtool_cfg2rx.h" 33 #include "cfgtool_dump.h" 34 #include "cfgtool_uc2cfg.h" 35 #include "cfgtool_cfginfo.h" 36 #include "cfgtool_parse.h" 37 #include "cfgtool_reset.h" 38 #include "cfgtool_status.h" 54 const char *(*help)(void);
72 const char *resetType;
82 static int rx2cfg(
void) {
return rx2cfgRun( gArgs.rxPort, gArgs.cfgLayer, gArgs.useUnknown); }
83 static int rx2list(
void) {
return rx2listRun(gArgs.rxPort, gArgs.cfgLayer, gArgs.useUnknown); }
84 static int cfg2rx(
void) {
return cfg2rxRun( gArgs.rxPort, gArgs.cfgLayer, gArgs.resetType, gArgs.applyConfig); }
85 static int cfg2ubx(
void) {
return cfg2ubxRun(gArgs.cfgLayer, gArgs.extraInfo); }
86 static int cfg2hex(
void) {
return cfg2hexRun(gArgs.cfgLayer, gArgs.extraInfo); }
87 static int cfg2c(
void) {
return cfg2cRun( gArgs.cfgLayer, gArgs.extraInfo); }
88 static int uc2cfg(
void) {
return uc2cfgRun(); }
89 static int cfginfo(
void) {
return cfginfoRun(); }
90 static int dump(
void) {
return dumpRun( gArgs.rxPort, gArgs.extraInfo, gArgs.noProbe); }
91 static int parse(
void) {
return parseRun( gArgs.extraInfo); }
92 static int reset(
void) {
return resetRun( gArgs.rxPort, gArgs.resetType); }
93 static int status(
void) {
return statusRun( gArgs.rxPort, gArgs.extraInfo, gArgs.noProbe); }
97 { .name =
"cfg2rx", .info =
"Configure a receiver from a configuration file", .help = cfg2rxHelp, .run = cfg2rx,
98 .need_i =
true, .need_o =
false, .need_p =
true, .need_l =
true, .may_r =
true, .may_n =
false },
100 { .name =
"rx2cfg", .info =
"Create configuration file from config in a receiver", .help = rx2cfgHelp, .run = rx2cfg,
101 .need_i =
false, .need_o =
true, .need_p =
true, .need_l =
true, .need_r =
false, .may_n =
false },
103 { .name =
"rx2list", .info =
"Like rx2cfg but output a flat list of key-value pairs", .help = rx2listHelp, .run = rx2list,
104 .need_i =
false, .need_o =
true, .need_p =
true, .need_l =
true, .need_r =
false, .may_n =
false },
106 { .name =
"cfg2ubx", .info =
"Convert config file to UBX-CFG-VALSET message(s)", .help = cfg2ubxHelp, .run = cfg2ubx,
107 .need_i =
true, .need_o =
true, .need_p =
false, .need_l =
true, .need_r =
false, .may_n =
false },
109 { .name =
"cfg2hex", .info =
"Like cfg2ubx but prints a hex dump of the message(s)", .help = NULL, .run = cfg2hex,
110 .need_i =
true, .need_o =
true, .need_p =
false, .need_l =
true, .need_r =
false, .may_n =
false },
112 { .name =
"cfg2c", .info =
"Like cfg2ubx but prints a c source code of the message(s)", .help = NULL, .run = cfg2c,
113 .need_i =
true, .need_o =
true, .need_p =
false, .need_l =
true, .need_r =
false, .may_n =
false },
115 { .name =
"uc2cfg", .info =
"Convert u-center config file to sane config file", .help = uc2cfgHelp, .run = uc2cfg,
116 .need_i =
true, .need_o =
true, .need_p =
false, .need_l =
false, .need_r =
false, .may_n =
false },
118 { .name =
"cfginfo", .info =
"Print information about known configuration items etc.", .help = cfginfoHelp, .run = cfginfo,
119 .need_i =
false, .need_o =
true, .need_p =
false, .need_l =
false, .need_r =
false, .may_n =
false },
121 { .name =
"dump", .info =
"Connects to receiver and prints received message frames", .help = dumpHelp, .run = dump,
122 .need_i =
false, .need_o =
true, .need_p =
true, .need_l =
false, .need_r =
false, .may_n =
true },
124 { .name =
"parse", .info =
"Parse file and output message frames", .help = parseHelp, .run = parse,
125 .need_i =
true, .need_o =
true, .need_p =
false, .need_l =
false, .need_r =
false, .may_n =
false },
127 { .name =
"reset", .info =
"Reset receiver", .help = resetHelp, .run = reset,
128 .need_i =
false, .need_o =
false, .need_p =
true, .need_l =
false, .need_r =
true, .may_n =
false },
130 { .name =
"status", .info =
"Connects to receiver and prints status", .help = statusHelp, .run = status,
131 .need_i =
false, .need_o =
true, .need_p =
true, .need_l =
false, .need_r =
false, .may_n =
true },
135 const char *
const kTitleStr =
137 "cfgtool "CONFIG_VERSION
" -- u-blox 9 configuration interface tool\n" 140 const char *
const kCopyrightStr =
142 " Copyright (c) 2020 Philippe Kehl (flipflip at oinkzwurgl dot org)\n" 143 " https://oinkzwurgl.org/hacking/ubloxcfg/\n" 146 const char *
const kVersionStr =
148 " version: "CONFIG_VERSION
", git hash: "CONFIG_GITHASH
", build date and time: "CONFIG_DATE
" "CONFIG_TIME
"\n" 151 const char *
const kHelpStr =
155 " cfgtool -h | -H | -h <command> | -V\n" 156 " cfgtool [-v] [-q] [...] <command>\n" 161 " -h Prints help summary\n" 162 " -H Prints full help\n" 163 " -h <command> Prints help for a command\n" 164 " -V Displays version and license information\n" 166 " -v / -q Increases / decreases verbosity\n" 168 " <command> Command\n" 170 " And depending on the <command>:\n" 172 " -i <infile> Input file (default: '-', i.e. standard input)\n" 173 " -o <outfile> Output file (default: '-', i.e. standard output)\n" 174 " -y Overwrite output file if it already exists\n" 175 " -p <serial> Serial port where the receiver is connected:\n" 176 " [ser://]<device>[:<baudrate>]\n" 177 " tcp://<host>:<port>[:<baudrate>]\n" 178 " telnet://<host>:<port>[:<baudrate>]\n" 179 " -l <layer(s)> Configuration layer(s) to use:\n" 180 " RAM, BBR, Flash, Default\n" 181 " -r <reset> Reset mode to use to reset the receiver:\n" 182 " soft, hard, hot, warm, cold, default, factory,\n" 183 " stop, start, gnss\n" 184 " -u Use unknown (undocumented) configuation items\n" 185 " -x Output extra information (comments, hex dumps, ...)\n" 186 " -a Activate configuration after storing\n" 187 " -n Do not probe/autobaud receiver, use passive reading only\n" 190 " Available <commands>s:\n" 193 const char *
const kLicenseHelp =
197 " This program is free software: you can redistribute it and/or modify it\n" 198 " under the terms of the GNU General Public License as published by the Free\n" 199 " Software Foundation, either version 3 of the License, or (at your option)\n" 200 " any later version.\n" 202 " This program is distributed in the hope that it will be useful, but WITHOUT\n" 203 " ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n" 204 " FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for\n" 207 " You should have received a copy of the GNU General Public License along with\n" 208 " this program. If not, see https://www.gnu.org/licenses/.\n" 210 "Third-party code:\n" 212 " This program includes CRC24Q routines from the GPSD project, under a\n" 213 " BSD-2-Clause license. See source code or https://gitlab.com/gpsd/.\n" 216 const char *
const kPortHelp =
219 " Local serial ports: [ser://]<device>[@<baudrate>], where:\n" 222 " <device> COM1, COM23, etc.\n" 224 " <device> /dev/ttyUSB0, /dev/ttyACM1, /dev/serial/..., etc.\n" 226 " <baudrate> Baudrate (optional)\n" 228 " Note that 'ser://' is the default and can be omitted. If no <baudrate>\n" 229 " is specified, it is be automatically detected. That is, '-p <device>'\n" 230 " works in most cases.\n" 233 " It is recommended to use /dev/serial/by-path/... device names for USB\n" 234 " (CDC ACM) connections as the names remain after a hardware reset and\n" 235 " USB re-enumeration. See the 'reset' command.\n" 238 " Raw TCP/IP ports: tcp://<addr>:<port>, where:\n" 240 " <addr> Hostname or IP address\n" 241 " <port> Port number\n" 243 " Telnet TCP/IP ports: telnet://<addr>:<port>[@<baudrate>], where\n" 245 " <addr> Hostname or IP address\n" 246 " <port> Port number\n" 247 " <baudrate> Baudrate (optional)\n" 249 " This uses telnet (RFC854, etc.) in-band control using com port control\n" 250 " option (RFC2217) to set the baudrate on the remote serial port. This\n" 251 " works for example with ser2net(8) and some hardware RS232 servers.\n" 253 " A minimal ser2net command line that should work is:\n" 254 " ser2net -d -C \"12345:telnet:0:/dev/ttyUSB0: remctl\"\n" 255 " This should allow using '-p telnet://localhost:12345'.\n" 258 const char *
const kLayersHelp =
260 "Configuration layers:\n" 262 " RAM Current(ly used) configuration, has all items\n" 263 " BBR Battery-backed RAM item storage, may have no items at all\n" 264 " Flash Flash (if available) items storage, may have no items at all\n" 265 " Default Default configuration, has all items\n" 268 const char *
const kExitHelp =
271 " Command successful: "STRINGIFY(EXIT_SUCCESS)
"\n" 272 " Bad command-line arguments: "STRINGIFY(EXIT_BADARGS)
"\n" 273 " Detecting receiver failed: "STRINGIFY(EXIT_RXFAIL)
"\n" 274 " No data from receiver: "STRINGIFY(EXIT_RXNODATA)
"\n" 275 " Unspecified error: "STRINGIFY(EXIT_OTHERFAIL)
"\n" 278 const char *
const kGreeting =
279 "Happy hacking! :-)\n" 282 #define _ARGS_STR(_flag_, _var_) \ 283 else if (strcmp(_flag_, argv[argIx]) == 0) \ 285 if ((argIx + 1) < argc) \ 287 _var_ = argv[argIx + 1]; \ 296 #define _ARGS_BOOL(_flag_, _var_, _ARGS_) \ 297 else if (strcmp(_flag_, argv[argIx]) == 0) \ 302 void printHelp(
const bool full)
304 fputs(kTitleStr, stdout);
305 fputs(kCopyrightStr, stdout);
306 fputs(kHelpStr, stdout);
307 for (
int ix = 0; ix < NUMOF(kCmds); ix++)
309 fprintf(stdout,
" %-14s %s\n", kCmds[ix].name, kCmds[ix].info);
314 fputs(kLicenseHelp, stdout);
315 fputs(kPortHelp, stdout);
316 fputs(kLayersHelp, stdout);
317 fputs(kExitHelp, stdout);
318 for (
int ix = 0; ix < NUMOF(kCmds); ix++)
320 if (kCmds[ix].help != NULL)
322 fputs(kCmds[ix].help(), stdout);
326 fputs(kGreeting, stdout);
329 void printVersion(
void)
331 fputs(kTitleStr, stdout);
332 fputs(
"Version:\n\n", stdout);
333 fputs(kVersionStr, stdout);
334 fputs(
"Copyright:\n\n", stdout);
335 fputs(kCopyrightStr, stdout);
336 fputs(kLicenseHelp, stdout);
337 fputs(kGreeting, stdout);
340 int main(
int argc,
char **argv)
342 memset(&gArgs, 0,
sizeof(gArgs));
344 DEBUG_CFG_t debugCfg =
346 .level = DEBUG_LEVEL_PRINT,
350 .colour = isatty(fileno(stderr)) == 1,
356 debugSetup(&debugCfg);
359 for (
int argIx = 1; argIx < argc; argIx++)
362 if (strcmp(
"-h", argv[argIx]) == 0)
366 else if (strcmp(
"-H", argv[argIx]) == 0)
371 else if (strcmp(
"-V", argv[argIx]) == 0)
376 else if (strcmp(
"-v", argv[argIx]) == 0)
380 else if (strcmp(
"-q", argv[argIx]) == 0)
384 _ARGS_STR(
"-i", gArgs.inName)
385 _ARGS_STR("-o", gArgs.outName)
386 _ARGS_STR("-p", gArgs.rxPort)
387 _ARGS_STR("-l", gArgs.cfgLayer)
388 _ARGS_STR("-r", gArgs.resetType)
389 _ARGS_BOOL("-u", gArgs.useUnknown, true)
390 _ARGS_BOOL("-x", gArgs.extraInfo, true)
391 _ARGS_BOOL("-a", gArgs.applyConfig, true)
392 _ARGS_BOOL("-y", gArgs.outOverwrite, true)
393 _ARGS_BOOL("-n", gArgs.noProbe, true)
394 else if (gArgs.cmd != NULL)
400 for (
int ix = 0; ix < NUMOF(kCmds); ix++)
402 if (strcmp(kCmds[ix].name, argv[argIx]) == 0)
404 gArgs.cmd = &kCmds[ix];
408 if (gArgs.cmd == NULL)
416 WARNING(
"Illegal argument '%s'!", argv[argIx]);
423 if (gArgs.cmd != NULL)
425 debugCfg.mark = gArgs.cmd->name;
427 debugSetup(&debugCfg);
431 if (gArgs.cmd != NULL)
433 fputs(kTitleStr, stdout);
434 if (gArgs.cmd->help != NULL)
436 fputs(gArgs.cmd->help(), stdout);
438 fputs(kGreeting, stdout);
448 if (gArgs.cmd == NULL)
450 WARNING(
"Try '%s -h'.", argv[0]);
455 if (res && gArgs.cmd->need_i)
457 if ( (gArgs.inName == NULL) || ((gArgs.inName[0] ==
'-') && (gArgs.inName[1] ==
'\0')) )
460 gArgs.inFile = stdin;
461 DEBUG(
"input from stdin");
463 int fd = fileno(gArgs.inFile);
464 const int flags = fcntl(fd, F_GETFL, 0);
465 int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
466 if ( ((flags < 0)) || (r < 0) )
468 WARNING(
"Failed setting stdin to non-blocking: %s", strerror(errno));
475 else if (gArgs.inName[0] ==
'\0')
477 WARNING(
"Need '-i <infile>' argument!");
482 DEBUG(
"Opening input: %s", gArgs.inName);
483 gArgs.inFile = fopen(gArgs.inName,
"r");
484 if (gArgs.inFile == NULL)
486 WARNING(
"Failed opening '%s' for reading: %s!\n",
487 gArgs.inName, strerror(errno));
492 else if ( (gArgs.cmd != NULL) && !gArgs.cmd->need_i && (gArgs.inName != NULL) )
494 WARNING(
"Illegal argument '-i %s'!", gArgs.inName);
497 ioSetInput(gArgs.inName, gArgs.inFile);
500 if (res && gArgs.cmd->need_o)
502 if ( (gArgs.outName == NULL) || ((gArgs.outName[0] ==
'-') && (gArgs.outName[1] ==
'\0')) )
505 gArgs.outFile = stdout;
506 DEBUG(
"output to stdout");
508 else if (gArgs.outName[0] ==
'\0')
510 WARNING(
"Need '-o <infile>' argument!");
518 ioSetOutput(gArgs.outName, gArgs.outFile, gArgs.outOverwrite);
521 if ( (gArgs.cmd != NULL) && gArgs.cmd->need_p )
523 if ( (gArgs.rxPort == NULL) || (gArgs.rxPort[0] ==
'\0') )
525 WARNING(
"Need '-p <port>' argument!");
529 else if ( (gArgs.cmd != NULL) && !gArgs.cmd->need_p && (gArgs.rxPort != NULL) )
531 WARNING(
"Illegal argument '-p %s'!", gArgs.rxPort);
536 if ((gArgs.cmd != NULL) && gArgs.cmd->need_l)
538 if ( (gArgs.cfgLayer == NULL) || (gArgs.cfgLayer[0] ==
'\0') )
540 WARNING(
"Need '-l <layer(s)>' argument!");
544 else if ( (gArgs.cmd != NULL) && !gArgs.cmd->need_l && (gArgs.cfgLayer != NULL) )
546 WARNING(
"Illegal argument '-l %s'!", gArgs.cfgLayer);
551 if ( (gArgs.cmd != NULL) && gArgs.cmd->need_r )
553 if ( (gArgs.resetType == NULL) || (gArgs.resetType[0] ==
'\0') )
555 WARNING(
"Need '-r <mode>' argument!");
559 else if ( (gArgs.cmd != NULL) && !(gArgs.cmd->need_r || gArgs.cmd->may_r) && (gArgs.resetType != NULL) )
561 WARNING(
"Illegal argument '-r %s'!", gArgs.resetType);
566 if ( (gArgs.cmd != NULL) && (!gArgs.cmd->may_n && gArgs.noProbe) )
568 WARNING(
"Illegal argument '-n'!");
579 DEBUG(
"args: inName=%s outName=%s rxPort=%s cfgLayer=%s useUnknown=%d",
580 gArgs.inName, gArgs.outName, gArgs.rxPort, gArgs.cfgLayer, gArgs.useUnknown);
581 const int exitCode = gArgs.cmd->run();