| 1 | /* |
| 2 | * freediag - Vehicle Diagnostic Utility |
| 3 | * |
| 4 | * |
| 5 | * Copyright (C) 2001 Richard Almeida & Ibex Ltd ([email protected]) |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 20 | * |
| 21 | ************************************************************************* |
| 22 | * |
| 23 | * |
| 24 | * Mostly ODBII Compliant Scan Tool (as defined in SAE J1978) |
| 25 | * |
| 26 | * CLI routines - "set" commands |
| 27 | * |
| 28 | * |
| 29 | */ |
| 30 | #include <stdbool.h> |
| 31 | #include <stdint.h> |
| 32 | #include <stdio.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <string.h> |
| 35 | |
| 36 | #include "diag.h" |
| 37 | #include "diag_cfg.h" //for cfgi |
| 38 | #include "diag_l0.h" |
| 39 | #include "diag_l1.h" |
| 40 | #include "diag_l2.h" |
| 41 | |
| 42 | #include "libcli.h" |
| 43 | #include "scantool_cli.h" |
| 44 | #include "utlist.h" |
| 45 | |
| 46 | /** Global parameters **/ |
| 47 | /* struct global_cfg contains all global parameters */ |
| 48 | struct globcfg global_cfg; |
| 49 | |
| 50 | /* SET sub menu */ |
| 51 | static enum cli_retval cmd_set_custom(int argc, char **argv); |
| 52 | static enum cli_retval cmd_set_help(int argc, char **argv); |
| 53 | static enum cli_retval cmd_set_show(int argc, char **argv); |
| 54 | static enum cli_retval cmd_set_speed(int argc, char **argv); |
| 55 | static enum cli_retval cmd_set_testerid(int argc, char **argv); |
| 56 | static enum cli_retval cmd_set_destaddr(int argc, char **argv); |
| 57 | static enum cli_retval cmd_set_addrtype(int argc, char **argv); |
| 58 | static enum cli_retval cmd_set_l1protocol(int argc, char **argv); |
| 59 | static enum cli_retval cmd_set_l2protocol(int argc, char **argv); |
| 60 | static enum cli_retval cmd_set_initmode(int argc, char **argv); |
| 61 | static enum cli_retval cmd_set_display(int argc, char **argv); |
| 62 | static enum cli_retval cmd_set_interface(int argc, char **argv); |
| 63 | |
| 64 | const struct cmd_tbl_entry set_cmd_table[] = { |
| 65 | { "help", "help [command]", "Gives help for a command", |
| 66 | cmd_set_help, 0, NULL}, |
| 67 | { "?", "help [command]", "Gives help for a command", |
| 68 | cmd_set_help, CLI_CMD_HIDDEN, NULL}, |
| 69 | |
| 70 | { "interface", "interface [NAME]", "Interface to use. Use set interface ? to get a list of names", |
| 71 | cmd_set_interface, 0, NULL}, |
| 72 | |
| 73 | { "display", "display [english/metric]", "English or metric display", |
| 74 | cmd_set_display, 0, NULL}, |
| 75 | |
| 76 | { "speed", "speed [speed]", "ECU communications speed", |
| 77 | cmd_set_speed, 0, NULL}, |
| 78 | { "testerid", "testerid [testerid]", "Source ID/address", |
| 79 | cmd_set_testerid, 0, NULL}, |
| 80 | { "destaddr", "destaddr [destaddr]","Destination ID/address", |
| 81 | cmd_set_destaddr, 0, NULL}, |
| 82 | |
| 83 | { "addrtype", "addrtype [func/phys]", "Address type, physical or functional.", |
| 84 | cmd_set_addrtype, 0, NULL}, |
| 85 | |
| 86 | { "l1protocol", "l1protocol [protocolname]", "Hardware (L1) protocol to use. Use 'set l1protocol ?' to show valid choices.", |
| 87 | cmd_set_l1protocol, 0, NULL}, |
| 88 | |
| 89 | { "l2protocol", "l2protocol [protocolname]", "Software (L2) protocol to use. Use 'set l2protocol ?' to show valid choices.", |
| 90 | cmd_set_l2protocol, 0, NULL}, |
| 91 | |
| 92 | { "initmode", "initmode [modename]", "Bus initialisation mode to use. Use 'set initmode ?' to show valid choices.", |
| 93 | cmd_set_initmode, 0, NULL}, |
| 94 | |
| 95 | { "show", "show", "Shows all settable values, including L0-specific items", |
| 96 | cmd_set_show, 0, NULL}, |
| 97 | |
| 98 | CLI_TBL_BUILTINS, |
| 99 | |
| 100 | { "", "", "", |
| 101 | cmd_set_custom, CLI_CMD_CUSTOM | CLI_CMD_HIDDEN, NULL}, |
| 102 | |
| 103 | CLI_TBL_END |
| 104 | }; |
| 105 | |
| 106 | const char *const l1_names[] = { //these MUST be in the same order as they are listed in diag_l1.h !! |
| 107 | "ISO9141", "ISO14230", |
| 108 | "J1850-VPW", "J1850-PWM", "CAN", "", "", "RAW", NULL |
| 109 | }; |
| 110 | |
| 111 | //These MUST match the DIAG_L2_TYPE_* flags in diag_l2.h so that |
| 112 | // l2_initmodes[DIAG_L2_TYPE_XX] == "XX" !! |
| 113 | const char *const l2_initmodes[] = { |
| 114 | "5BAUD", "FAST", "CARB", NULL |
| 115 | }; |
| 116 | |
| 117 | |
| 118 | /* workaround to use cmd_set_interface() properly: set up fake command args */ |
| 119 | static char *default_cmd = "interface"; |
| 120 | static char *default_iface = "DUMB"; |
| 121 | |
| 122 | int set_init(void) { |
| 123 | /* Reset parameters to defaults. */ |
| 124 | |
| 125 | global_cfg.speed = 10400; /* Comms speed; ECUs will probably send at 10416 bps (96us per bit) */ |
| 126 | |
| 127 | global_cfg.src = 0xf1; /* Our tester ID */ |
| 128 | global_cfg.addrtype = 1; /* Use functional addressing */ |
| 129 | global_cfg.tgt = 0x33; /* Dest ECU address */ |
| 130 | |
| 131 | global_cfg.L1proto = DIAG_L1_ISO9141; /* L1 protocol type */ |
| 132 | |
| 133 | global_cfg.L2idx = 0; |
| 134 | global_cfg.L2proto = l2proto_list[0]->diag_l2_protocol; /* cannot guarantee 9141 was compiled... DIAG_L2_PROT_ISO9141; */ |
| 135 | |
| 136 | global_cfg.initmode = DIAG_L2_TYPE_FASTINIT; |
| 137 | |
| 138 | global_cfg.units = 0; /* English (1), or Metric (0) */ |
| 139 | |
| 140 | char *garbage_args[] = {default_cmd, default_iface}; |
| 141 | cmd_set_interface(2, garbage_args); /* Default H/w interface to use */ |
| 142 | |
| 143 | return 0; |
| 144 | } |
| 145 | |
| 146 | void set_close(void) { |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | // handle dynamic options (L0-specific). |
| 151 | // argv[0] is the config shortname, etc. |
| 152 | // if argv[0] is '?' (special case), this prints available subcommands. |
| 153 | static enum cli_retval cmd_set_custom(int argc, char **argv) { |
| 154 | struct cfgi *cfgp; |
| 155 | char *setstr; |
| 156 | bool helping=0; |
| 157 | bool show_current=0; |
| 158 | int newval; |
| 159 | |
| 160 | if (!global_dl0d) { |
| 161 | // no L0 selected yet |
| 162 | if (strcmp(argv[0], "?") == 0) { |
| 163 | return CMD_OK; |
| 164 | } |
| 165 | printf("No such item !\nAdditional items may be available after setting the interface type.\nUse \"set interface NAME\" to set the interface type.\n"); |
| 166 | return CMD_FAILED; |
| 167 | } |
| 168 | |
| 169 | if (strcmp(argv[0], "?") == 0) { |
| 170 | //list available custom commands for the current L0. |
| 171 | LL_FOREACH(diag_l0_getcfg(global_dl0d), cfgp) { |
| 172 | printf("\t%s\n", cfgp->shortname); |
| 173 | } |
| 174 | return CMD_OK; |
| 175 | } |
| 176 | |
| 177 | if (argc >= 2) { |
| 178 | if (strcmp(argv[1], "?") == 0) { |
| 179 | helping = 1; |
| 180 | } |
| 181 | } else { |
| 182 | // no args: display current settings |
| 183 | show_current = 1; |
| 184 | } |
| 185 | |
| 186 | /* find the config item */ |
| 187 | LL_FOREACH(diag_l0_getcfg(global_dl0d), cfgp) { |
| 188 | if (strcasecmp(cfgp->shortname, argv[0]) == 0) { |
| 189 | break; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | if (!cfgp) { |
| 194 | printf("No such item !\n"); |
| 195 | return CMD_FAILED; |
| 196 | } |
| 197 | |
| 198 | if (show_current) { |
| 199 | char *val = diag_cfg_getstr(cfgp); |
| 200 | printf("%s: %s\n", argv[0], val); |
| 201 | free(val); |
| 202 | return CMD_OK; |
| 203 | } |
| 204 | |
| 205 | if (helping) { |
| 206 | printf("%s\n", cfgp->descr); |
| 207 | diag_cfg_refresh(cfgp); |
| 208 | printf("Available options:\n"); |
| 209 | if (cfgp->numopts > 0) { |
| 210 | int i; |
| 211 | for (i=0; i < cfgp->numopts; i++) { |
| 212 | printf("\t\t%s\n", cfgp->opt[i]); |
| 213 | } |
| 214 | } |
| 215 | return CMD_OK; |
| 216 | } |
| 217 | |
| 218 | /* TODO : move this to diag_cfg.* if it works */ |
| 219 | newval = htoi(argv[1]); |
| 220 | switch (cfgp->type) { |
| 221 | case CFGT_STR: |
| 222 | diag_cfg_setstr(cfgp, argv[1]); |
| 223 | break; |
| 224 | case CFGT_U8: |
| 225 | diag_cfg_setu8(cfgp, (uint8_t) newval); |
| 226 | break; |
| 227 | case CFGT_INT: |
| 228 | diag_cfg_setint(cfgp, newval); |
| 229 | break; |
| 230 | case CFGT_BOOL: |
| 231 | diag_cfg_setbool(cfgp, (bool) newval); |
| 232 | break; |
| 233 | default: |
| 234 | return CMD_FAILED; |
| 235 | } |
| 236 | |
| 237 | setstr = diag_cfg_getstr(cfgp); |
| 238 | printf("%s set to: %s\n", cfgp->shortname, setstr); |
| 239 | free(setstr); |
| 240 | return CMD_OK; |
| 241 | } |
| 242 | |
| 243 | static enum cli_retval cmd_set_show(UNUSED(int argc), UNUSED(char **argv)) { |
| 244 | /* Show stuff; calling the cmd_set_*() functions with argc=0 displays the current setting. */ |
| 245 | cmd_set_interface(0,NULL); |
| 246 | cmd_set_speed(0, NULL); |
| 247 | cmd_set_display(0,NULL); |
| 248 | cmd_set_testerid(0,NULL); |
| 249 | cmd_set_addrtype(0,NULL); |
| 250 | cmd_set_destaddr(0,NULL); |
| 251 | cmd_set_l1protocol(0,NULL); |
| 252 | cmd_set_l2protocol(0,NULL); |
| 253 | cmd_set_initmode(0,NULL); |
| 254 | |
| 255 | /* Parse L0-specific config items */ |
| 256 | if (global_dl0d) { |
| 257 | struct cfgi *cfgp; |
| 258 | printf("L0 options:\n"); |
| 259 | LL_FOREACH(diag_l0_getcfg(global_dl0d), cfgp) { |
| 260 | char *cs = diag_cfg_getstr(cfgp); |
| 261 | if (cfgp->shortname == NULL || cs == NULL) { |
| 262 | continue; |
| 263 | } |
| 264 | |
| 265 | printf("\t%s=%s\n",cfgp->shortname, cs); |
| 266 | free(cs); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | return CMD_OK; |
| 271 | } |
| 272 | |
| 273 | |
| 274 | static enum cli_retval cmd_set_interface(int argc, char **argv) { |
| 275 | const struct diag_l0 *iter; |
| 276 | |
| 277 | if (argc <= 1 || argv == NULL) { |
| 278 | printf("interface: using %s\n", |
| 279 | global_cfg.l0name); |
| 280 | return CMD_OK; |
| 281 | } |
| 282 | if (argc > 2) { |
| 283 | printf("Too many arguments !\n"); |
| 284 | return CMD_USAGE; |
| 285 | } |
| 286 | |
| 287 | if (global_dl0d) { |
| 288 | if (global_dl0d->opened) { |
| 289 | printf("Error : L0 busy or connected; please disconnect first\n"); |
| 290 | return CMD_FAILED; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | int i, helping = 0, found = 0; |
| 295 | if (strcmp(argv[1], "?") == 0) { |
| 296 | helping = 1; |
| 297 | printf("Valid interface names (not case sensitive): \n\t"); |
| 298 | } |
| 299 | for (i=0; l0dev_list[i]; i++) { |
| 300 | iter = l0dev_list[i]; |
| 301 | //loop through l0 interface names, either printing or comparing to argv[1] |
| 302 | if (helping) { |
| 303 | printf("%s ", iter->shortname); |
| 304 | } else if (strcasecmp(argv[1], iter->shortname) == 0) { |
| 305 | global_cfg.l0name = iter->shortname; |
| 306 | found = 1; |
| 307 | break; // no use in continuing |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | if (helping) { |
| 312 | printf("\n"); |
| 313 | return CMD_USAGE; |
| 314 | } |
| 315 | |
| 316 | if (!found) { |
| 317 | printf("interface: invalid interface %s\n", argv[1]); |
| 318 | printf("interface: use \"set interface ?\" to see list of names\n"); |
| 319 | return CMD_FAILED; |
| 320 | } |
| 321 | |
| 322 | printf("interface is now %s\n", global_cfg.l0name); |
| 323 | |
| 324 | /* close + free current global dl0d. */ |
| 325 | if (global_dl0d) { |
| 326 | /* XXX warn before breaking a (possibly) active L0-L2 chain */ |
| 327 | diag_l0_close(global_dl0d); |
| 328 | diag_l0_del(global_dl0d); |
| 329 | } |
| 330 | |
| 331 | global_dl0d = diag_l0_new(global_cfg.l0name); |
| 332 | if (!global_dl0d) { |
| 333 | printf("Error loading interface %s.\n", global_cfg.l0name); |
| 334 | } |
| 335 | |
| 336 | return CMD_OK; |
| 337 | } |
| 338 | |
| 339 | static enum cli_retval cmd_set_display(int argc, char **argv) { |
| 340 | if (argc > 1) { |
| 341 | if (strcasecmp(argv[1], "english") == 0) { |
| 342 | global_cfg.units = 1; |
| 343 | } else if (strcasecmp(argv[1], "metric") == 0) { |
| 344 | global_cfg.units = 0; |
| 345 | } else { |
| 346 | return CMD_USAGE; |
| 347 | } |
| 348 | } else { |
| 349 | printf("display: %s units\n", global_cfg.units?"english":"metric"); |
| 350 | } |
| 351 | |
| 352 | return CMD_OK; |
| 353 | } |
| 354 | |
| 355 | static enum cli_retval cmd_set_speed(int argc, char **argv) { |
| 356 | if (argc > 1) { |
| 357 | if (strcmp(argv[1], "?") == 0) { |
| 358 | return CMD_USAGE; |
| 359 | } |
| 360 | |
| 361 | global_cfg.speed = htoi(argv[1]); |
| 362 | } |
| 363 | printf("Connect speed: %u\n", global_cfg.speed); |
| 364 | |
| 365 | return CMD_OK; |
| 366 | } |
| 367 | |
| 368 | static enum cli_retval cmd_set_testerid(int argc, char **argv) { |
| 369 | if (argc > 1) { |
| 370 | int tmp; |
| 371 | if (strncmp(argv[1], "?", 1) == 0) { |
| 372 | return CMD_USAGE; |
| 373 | } |
| 374 | tmp = htoi(argv[1]); |
| 375 | if ( (tmp < 0) || (tmp > 0xff)) { |
| 376 | printf("testerid: must be between 0 and 0xff\n"); |
| 377 | return CMD_FAILED; |
| 378 | } |
| 379 | global_cfg.src = (uint8_t) tmp; |
| 380 | } |
| 381 | printf("testerid: using 0x%02X\n", (unsigned) global_cfg.src); |
| 382 | |
| 383 | return CMD_OK; |
| 384 | } |
| 385 | |
| 386 | static enum cli_retval cmd_set_destaddr(int argc, char **argv) { |
| 387 | if (argc > 1) { |
| 388 | int tmp; |
| 389 | if (strncmp(argv[1], "?", 1) == 0) { |
| 390 | return CMD_USAGE; |
| 391 | } |
| 392 | tmp = htoi(argv[1]); |
| 393 | if ( (tmp < 0) || (tmp > 0xff)) { |
| 394 | printf("destaddr: must be between 0 and 0xff\n"); |
| 395 | return CMD_FAILED; |
| 396 | } |
| 397 | global_cfg.tgt = (uint8_t) tmp; |
| 398 | } |
| 399 | printf("destaddr: using 0x%02X\n", |
| 400 | (unsigned) global_cfg.tgt); |
| 401 | |
| 402 | return CMD_OK; |
| 403 | } |
| 404 | |
| 405 | static enum cli_retval cmd_set_addrtype(int argc, char **argv) { |
| 406 | if (argc > 1) { |
| 407 | if (strncmp(argv[1], "func", 4) == 0) { |
| 408 | global_cfg.addrtype = 1; |
| 409 | } else if (strncmp(argv[1], "phys", 4) == 0) { |
| 410 | global_cfg.addrtype = 0; |
| 411 | } else { |
| 412 | return CMD_USAGE; |
| 413 | } |
| 414 | } else { |
| 415 | printf("addrtype: %s addressing\n", |
| 416 | global_cfg.addrtype ? "functional" : "physical"); |
| 417 | } |
| 418 | |
| 419 | return CMD_OK; |
| 420 | } |
| 421 | |
| 422 | static enum cli_retval cmd_set_l2protocol(int argc, char **argv) { |
| 423 | if (argc > 1) { |
| 424 | int i, helping = 0, found = 0; |
| 425 | if (strcmp(argv[1], "?") == 0) { |
| 426 | helping = 1; |
| 427 | printf("L2 protocol: valid names are "); |
| 428 | } |
| 429 | for (i=0; l2proto_list[i] != NULL; i++) { |
| 430 | const struct diag_l2_proto *d2p=l2proto_list[i]; |
| 431 | if (helping) { |
| 432 | printf("%s ", d2p->shortname); |
| 433 | continue; |
| 434 | } |
| 435 | if (strcasecmp(argv[1], d2p->shortname) == 0) { |
| 436 | found = 1; |
| 437 | global_cfg.L2idx = i; |
| 438 | global_cfg.L2proto = d2p->diag_l2_protocol; |
| 439 | break; |
| 440 | } |
| 441 | } |
| 442 | if (helping) { |
| 443 | printf("\n"); |
| 444 | return CMD_USAGE; |
| 445 | } |
| 446 | if (!found) { |
| 447 | printf("l2protocol: invalid protocol %s\n", argv[1]); |
| 448 | printf("l2protocol: use \"set l2protocol ?\" to see list of protocols\n"); |
| 449 | } |
| 450 | } else { |
| 451 | printf("l2protocol: Layer 2 protocol to use %s\n", |
| 452 | l2proto_list[global_cfg.L2idx]->shortname); |
| 453 | } |
| 454 | return CMD_OK; |
| 455 | } |
| 456 | |
| 457 | static enum cli_retval cmd_set_l1protocol(int argc, char **argv) { |
| 458 | if (argc > 1) { |
| 459 | int i, helping = 0, found = 0; |
| 460 | if (strcmp(argv[1], "?") == 0) { |
| 461 | helping = 1; |
| 462 | printf("L1 protocol: valid names are "); |
| 463 | } |
| 464 | for (i=0; l1_names[i] != NULL; i++) { |
| 465 | if (helping && *l1_names[i]) { |
| 466 | printf("%s ", l1_names[i]); |
| 467 | } else if (strcasecmp(argv[1], l1_names[i]) == 0) { |
| 468 | global_cfg.L1proto = 1 << i; |
| 469 | found = 1; |
| 470 | } |
| 471 | } |
| 472 | if (helping) { |
| 473 | printf("\n"); |
| 474 | return CMD_USAGE; |
| 475 | } |
| 476 | if (!found) { |
| 477 | printf("L1protocol: invalid protocol %s\n", argv[1]); |
| 478 | printf("l1protocol: use \"set l1protocol ?\" to see list of protocols\n"); |
| 479 | } |
| 480 | return CMD_OK; |
| 481 | } |
| 482 | |
| 483 | int offset; |
| 484 | |
| 485 | for (offset=0; offset < 8; offset++) { |
| 486 | if (global_cfg.L1proto == (1 << offset)) { |
| 487 | break; |
| 488 | } |
| 489 | } |
| 490 | printf("l1protocol: Layer 1 (H/W) protocol to use %s\n", |
| 491 | l1_names[offset]); |
| 492 | |
| 493 | return CMD_OK; |
| 494 | } |
| 495 | |
| 496 | static enum cli_retval cmd_set_initmode(int argc, char **argv) { |
| 497 | if (argc > 1) { |
| 498 | int i, helping = 0, found = 0; |
| 499 | if (strcmp(argv[1], "?") == 0) { |
| 500 | helping = 1; |
| 501 | } |
| 502 | for (i=0; l2_initmodes[i] != NULL; i++) { |
| 503 | if (helping) { |
| 504 | printf("%s ", l2_initmodes[i]); |
| 505 | } else { |
| 506 | if (strcasecmp(argv[1], l2_initmodes[i]) == 0) { |
| 507 | found = 1; |
| 508 | global_cfg.initmode = i; |
| 509 | } |
| 510 | } |
| 511 | } |
| 512 | if (helping) { |
| 513 | printf("\n"); |
| 514 | return CMD_USAGE; |
| 515 | } |
| 516 | if (!found) { |
| 517 | printf("initmode: invalid mode %s\n", argv[1]); |
| 518 | printf("initmode: use \"set initmode ?\" to see list of initmodes\n"); |
| 519 | } |
| 520 | return CMD_OK; |
| 521 | } |
| 522 | |
| 523 | printf("initmode: Initmode to use with above protocol is %s\n", |
| 524 | l2_initmodes[global_cfg.initmode]); |
| 525 | |
| 526 | return CMD_OK; |
| 527 | } |
| 528 | |
| 529 | static enum cli_retval cmd_set_help(int argc, char **argv) { |
| 530 | return cli_help_basic(argc, argv, set_cmd_table); |
| 531 | } |
| 532 | |