| 1 | /* |
| 2 | * freediag - Vehicle Diagnostic Utility |
| 3 | |
| 4 | ************************************************************************* |
| 5 | * dumbtest (c) 2014-2015 fenugrec |
| 6 | * Diag, Layer 0, interface tester. This should *NOT* be used while |
| 7 | * a vehicle is connected; only for debugging the electrical interface itself. |
| 8 | |
| 9 | * This is a dummy l0 driver : most functions do nothing except |
| 10 | * _open which opens the interface, runs the specified test, and closes everything |
| 11 | * before returning. |
| 12 | */ |
| 13 | |
| 14 | |
| 15 | #include <assert.h> |
| 16 | #include <stdbool.h> |
| 17 | #include <stdint.h> |
| 18 | #include <stdio.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <string.h> //for memcmp |
| 21 | |
| 22 | #include "diag.h" |
| 23 | #include "diag_cfg.h" |
| 24 | #include "diag_err.h" |
| 25 | #include "diag_os.h" |
| 26 | #include "diag_tty.h" |
| 27 | #include "diag_l0.h" |
| 28 | #include "diag_l1.h" |
| 29 | |
| 30 | struct dt_device { |
| 31 | int protocol; //set in dt_open with specified iProtocol |
| 32 | struct diag_serial_settings serial; |
| 33 | |
| 34 | /* "dumbopts" flags. Copied from l0_dumb driver */ |
| 35 | /* Using a struct cfgi for each of these really seems like overkill... |
| 36 | * current approach is to have a cfgi for an "int dumbopts", that is parsed into |
| 37 | * these flags in dumb_open(). Hence, the shortname + descriptions defined here are |
| 38 | * unused for the moment. |
| 39 | */ |
| 40 | bool use_L; |
| 41 | #define DD_USEL "Use RTS to drive the L line for init. Interface must support this." |
| 42 | #define DS_USEL "USEL" |
| 43 | bool clr_dtr; |
| 44 | #define DD_CLRDTR "Always keep DTR cleared (neg. voltage). Unusual." |
| 45 | #define DS_CLRDTR "CLRDTR" |
| 46 | bool set_rts; |
| 47 | #define DD_SETRTS "Always keep RTS set (pos. voltage). Unusual; do not use with USE_L." |
| 48 | #define DS_SETRTS "SETRTS" |
| 49 | bool man_break; |
| 50 | #define DD_MANBRK "Send manual breaks (essential for USB-serial ICs that don't support 5bps.)" |
| 51 | #define DS_MANBRK "MANBRK" |
| 52 | bool lline_inv; |
| 53 | #define DD_INVL "Invert polarity of the L line. Unusual." |
| 54 | #define DS_INVL "INVL" |
| 55 | bool fast_break; |
| 56 | #define DD_FASTBK "Try alternate iso14230 fastinit code." |
| 57 | #define DS_FASTBK "FASTB" |
| 58 | bool blockduplex; |
| 59 | #define DD_BKDUPX "Use message-based half duplex removal if P4==0." |
| 60 | #define DS_BKDUPX "BLKDUP" |
| 61 | |
| 62 | struct cfgi port; |
| 63 | struct cfgi dumbopts; |
| 64 | #define DUMBOPTS_SN "dumbopts" |
| 65 | #define DUMBOPTS_DESC "Dumb interface option flags; addition of the desired flags:\n" \ |
| 66 | " 0x01 : USE_LLINE : use if the L line (driven by RTS) is required for init. Interface must support this\n" \ |
| 67 | "\t(VAGTOOL for example).\n" \ |
| 68 | " 0x02 : CLEAR_DTR : use if your interface needs DTR to be always clear (neg. voltage).\n" \ |
| 69 | "\tThis is unusual. By default DTR will always be SET (pos. voltage)\n" \ |
| 70 | " 0x04 : SET_RTS : use if your interface needs RTS to be always set (pos. voltage).\n" \ |
| 71 | "\tThis is unusual. By default RTS will always be CLEAR (neg. voltage)\n" \ |
| 72 | "\tThis option should not be used with USE_LLINE.\n" \ |
| 73 | " 0x08 : MAN_BREAK : essential for USB-serial converters that don't support 5bps\n" \ |
| 74 | "\tsuch as FTDI232*, P230* and other ICs (enabled by default).\n" \ |
| 75 | " 0x10: LLINE_INV : Invert polarity of the L line. see\n" \ |
| 76 | "\tdoc/dumb_interfaces.txt !! This is unusual.\n" \ |
| 77 | " 0x20: FAST_BREAK : use alternate iso14230 fastinit code.\n" \ |
| 78 | " 0x40: BLOCKDUPLEX : use message-based half duplex removal (if P4==0)\n\n" \ |
| 79 | "ex.: \"dumbopts 9\" for MAN_BREAK and USE_LLINE.\n" |
| 80 | |
| 81 | |
| 82 | ttyp *tty_int; /** handle for tty stuff */ |
| 83 | }; |
| 84 | |
| 85 | |
| 86 | // dumbopts flags set according to particular interface type (VAGtool vs SE etc.) |
| 87 | #define USE_LLINE 0x01 //interface maps L line to RTS : setting RTS normally pulls L down to 0 . |
| 88 | #define CLEAR_DTR 0x02 //have DTR cleared constantly (unusual, disabled by default) |
| 89 | #define SET_RTS 0x04 //have RTS set constantly (also unusual, disabled by default). |
| 90 | #define MAN_BREAK 0x08 //force bitbanged breaks for inits; enabled by default |
| 91 | #define LLINE_INV 0x10 //invert polarity of the L line if set. see doc/dumb_interfaces.txt |
| 92 | #define FAST_BREAK 0x20 //do we use diag_tty_fastbreak for iso14230-style fast init. |
| 93 | #define BLOCKDUPLEX 0x40 //This allows half duplex removal on a whole message if P4==0 (see diag_l1_send()) |
| 94 | #define DUMBDEFAULTS (MAN_BREAK | BLOCKDUPLEX) //default set of flags |
| 95 | |
| 96 | |
| 97 | extern const struct diag_l0 diag_l0_dumbtest; |
| 98 | |
| 99 | static int dt_send(struct diag_l0_device *dl0d, |
| 100 | const void *data, size_t len); |
| 101 | |
| 102 | /* |
| 103 | * Init must be callable even if no physical interface is |
| 104 | * present, it's just here for the code here to initialise its |
| 105 | * variables etc |
| 106 | */ |
| 107 | static int dt_init(void) { |
| 108 | /* Global init flag */ |
| 109 | static int dt_initdone=0; |
| 110 | |
| 111 | if (dt_initdone) { |
| 112 | return 0; |
| 113 | } |
| 114 | |
| 115 | dt_initdone = 1; |
| 116 | |
| 117 | return 0; |
| 118 | } |
| 119 | |
| 120 | |
| 121 | |
| 122 | |
| 123 | //dtest_1 : slow pulse TXD with diag_tty_break; 400ms / 200ms cycles. |
| 124 | |
| 125 | static void dtest_1(struct diag_l0_device *dl0d) { |
| 126 | int i; |
| 127 | struct dt_device *dev = dl0d->l0_int; |
| 128 | |
| 129 | fprintf(stderr, "Starting test 1: pulsing TXD=1, 1s, TXD=0, 500ms:"); |
| 130 | for (i=0; i<=4; i++) { |
| 131 | diag_os_millisleep(1000); |
| 132 | if (diag_tty_break(dev->tty_int, 500)) { |
| 133 | break; |
| 134 | } |
| 135 | fprintf(stderr, "."); |
| 136 | } |
| 137 | fprintf(stderr, "\n"); |
| 138 | return; |
| 139 | } |
| 140 | |
| 141 | //dtest_2 : fast pulse TXD by sending 0x55 @ 10.4kps with 5ms interbyte |
| 142 | static void dtest_2(struct diag_l0_device *dl0d) { |
| 143 | int i, pc=0; |
| 144 | const int iters=300; |
| 145 | uint8_t patternbyte=0x55; |
| 146 | struct dt_device *dev = dl0d->l0_int; |
| 147 | |
| 148 | fprintf(stderr, "Starting test 2: sending 0x55 with P4=5ms:"); |
| 149 | for (i=0; i<=iters; i++) { |
| 150 | if (diag_tty_write(dev->tty_int, &patternbyte, 1) != 1) { |
| 151 | fprintf(stderr, "write error\n"); |
| 152 | break; |
| 153 | } |
| 154 | if ((10*i/iters) != pc) { |
| 155 | pc +=1; |
| 156 | fprintf(stderr, "."); |
| 157 | } |
| 158 | diag_os_millisleep(5); |
| 159 | } |
| 160 | fprintf(stderr, "\n"); |
| 161 | return; |
| 162 | } |
| 163 | |
| 164 | //dtest_3: slow pulse RTS |
| 165 | static void dtest_3(struct diag_l0_device *dl0d) { |
| 166 | int i; |
| 167 | struct dt_device *dev = dl0d->l0_int; |
| 168 | fprintf(stderr, "Starting test 3: pulsing RTS=1, 1s, RTS=0, 500ms:"); |
| 169 | |
| 170 | for (i=0; i<=4; i++) { |
| 171 | if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), 1)) { |
| 172 | break; |
| 173 | } |
| 174 | diag_os_millisleep(1000); |
| 175 | if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), 0)) { |
| 176 | break; |
| 177 | } |
| 178 | diag_os_millisleep(500); |
| 179 | fprintf(stderr, "."); |
| 180 | } |
| 181 | diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts); |
| 182 | fprintf(stderr, "\n"); |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | //dtest_4: slow pulse DTR |
| 187 | static void dtest_4(struct diag_l0_device *dl0d) { |
| 188 | int i; |
| 189 | struct dt_device *dev = dl0d->l0_int; |
| 190 | fprintf(stderr, "Starting test 4: pulsing DTR=1, 1s, DTR=0, 500ms:"); |
| 191 | |
| 192 | for (i=0; i<=4; i++) { |
| 193 | if (diag_tty_control(dev->tty_int, 1, dev->set_rts)) { |
| 194 | break; |
| 195 | } |
| 196 | diag_os_millisleep(1000); |
| 197 | if (diag_tty_control(dev->tty_int, 0, dev->set_rts)) { |
| 198 | break; |
| 199 | } |
| 200 | diag_os_millisleep(500); |
| 201 | fprintf(stderr, "."); |
| 202 | } |
| 203 | diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts); |
| 204 | fprintf(stderr, "\n"); |
| 205 | return; |
| 206 | } |
| 207 | |
| 208 | |
| 209 | //dtest_5 : fast pulse TXD with diag_tty_break; |
| 210 | |
| 211 | static void dtest_5(struct diag_l0_device *dl0d) { |
| 212 | int i, pc=0; |
| 213 | struct dt_device *dev = dl0d->l0_int; |
| 214 | const int iters=40; |
| 215 | fprintf(stderr, "Starting test 5: pulsing TXD=1, 50, TXD=0, 25ms:"); |
| 216 | for (i=0; i<=iters; i++) { |
| 217 | diag_os_millisleep(50); |
| 218 | if (diag_tty_break(dev->tty_int, 25)) { |
| 219 | fprintf(stderr, "break error\n"); |
| 220 | break; |
| 221 | } |
| 222 | if ((10*i/iters) != pc) { |
| 223 | pc +=1; |
| 224 | fprintf(stderr, "."); |
| 225 | } |
| 226 | } |
| 227 | fprintf(stderr, "\n"); |
| 228 | return; |
| 229 | } |
| 230 | |
| 231 | //dtest_6 : fast pulse TXD with diag_tty_fastbreak; |
| 232 | |
| 233 | static void dtest_6(struct diag_l0_device *dl0d) { |
| 234 | int i, pc=0; |
| 235 | struct dt_device *dev = dl0d->l0_int; |
| 236 | const int iters=50; |
| 237 | fprintf(stderr, "Starting test 6: pulsing TXD=1, 50ms, TXD=0, 25ms:"); |
| 238 | for (i=0; i<=iters; i++) { |
| 239 | if (diag_tty_fastbreak(dev->tty_int, 50)) { |
| 240 | fprintf(stderr, "fastbreak error\n"); |
| 241 | break; |
| 242 | } |
| 243 | if ((10*i/iters) != pc) { |
| 244 | pc +=1; |
| 245 | fprintf(stderr, "."); |
| 246 | } |
| 247 | } |
| 248 | fprintf(stderr, "\n"); |
| 249 | return; |
| 250 | } |
| 251 | |
| 252 | //dtest_7 : half duplex echo removal 1 : send bytes and remove echo |
| 253 | //one by one; P4=0. Print per-byte time to do this; use _dumb_send() instead |
| 254 | // of diag_tty directly, like l1_send(). |
| 255 | static void dtest_7(struct diag_l0_device *dl0d) { |
| 256 | uint8_t i, pc=0, echo; |
| 257 | int rv, badechos=0; |
| 258 | unsigned long long ti, tf=0; //measure inner time |
| 259 | struct dt_device *dev = dl0d->l0_int; |
| 260 | |
| 261 | #define DT7_ITERS 100 |
| 262 | fprintf(stderr, "Starting test 7: half duplex single echo removal:"); |
| 263 | |
| 264 | for (i=0; i<DT7_ITERS; i++) { |
| 265 | echo=i-1; //init to bad value |
| 266 | ti=diag_os_gethrt(); //get starting time. |
| 267 | if (dt_send(dl0d, &i, 1)) { |
| 268 | break; |
| 269 | } |
| 270 | |
| 271 | rv = diag_tty_read(dev->tty_int, &echo, 1, 1000); |
| 272 | if (rv != 1) { |
| 273 | fprintf(stderr, "\ndt7: tty_read rets %d.\n", rv); |
| 274 | break; |
| 275 | } |
| 276 | tf = tf + diag_os_gethrt() - ti; |
| 277 | //check echo |
| 278 | if (echo != i) { |
| 279 | badechos++; |
| 280 | } |
| 281 | |
| 282 | if ((10*i/DT7_ITERS) != pc) { |
| 283 | pc +=1; |
| 284 | fprintf(stderr, "."); |
| 285 | } |
| 286 | } //for |
| 287 | fprintf(stderr, "\n"); |
| 288 | tf = tf / DT7_ITERS; //average time per byte |
| 289 | printf("Average speed : %d us/byte. %d good; %d bad echos received.\n", (int) diag_os_hrtus(tf), i, badechos); |
| 290 | |
| 291 | |
| 292 | return; |
| 293 | } //dtest_7 |
| 294 | |
| 295 | //dtest_8 : block half duplex removal; send 10 bytes per message |
| 296 | static void dtest_8(struct diag_l0_device *dl0d) { |
| 297 | #define DT8_MSIZE 10 |
| 298 | uint8_t tx[DT8_MSIZE], echo[DT8_MSIZE]; |
| 299 | int i, rv = -1, badechos=0; |
| 300 | unsigned long long ti, tf=0; |
| 301 | struct dt_device *dev = dl0d->l0_int; |
| 302 | |
| 303 | #define DT8_ITERS 10 |
| 304 | fprintf(stderr, "Starting test 8: half duplex block echo removal:"); |
| 305 | //fill i[] first |
| 306 | for (i = 0; i < DT8_MSIZE; i++) { |
| 307 | tx[i] = (uint8_t)i; |
| 308 | } |
| 309 | |
| 310 | for (i=0; i<=DT8_ITERS; i++) { |
| 311 | ti=diag_os_gethrt(); //get starting time. |
| 312 | if (dt_send(dl0d, tx, DT8_MSIZE)) { |
| 313 | break; |
| 314 | } |
| 315 | |
| 316 | rv = diag_tty_read(dev->tty_int, echo, DT8_MSIZE, 100 + 5*DT8_MSIZE); |
| 317 | if (rv != DT8_MSIZE) { |
| 318 | fprintf(stderr, "\ndt8: tty_read rets %d.\n", rv); |
| 319 | break; |
| 320 | } |
| 321 | tf = tf + diag_os_gethrt() - ti; |
| 322 | //check echo |
| 323 | if ( memcmp(tx, echo, DT8_MSIZE) == 0) { |
| 324 | //ok |
| 325 | rv=0; |
| 326 | } else { |
| 327 | badechos++; |
| 328 | } |
| 329 | fprintf(stderr, "."); |
| 330 | } //for |
| 331 | |
| 332 | tf = tf / (DT8_ITERS * DT8_MSIZE); //average time per byte |
| 333 | fprintf(stderr, "\n"); |
| 334 | if (rv != 0) { |
| 335 | printf("Error, test did not complete.\n"); |
| 336 | } else { |
| 337 | printf("Average speed : %d us/byte. %d bad echos received.\n", (int) diag_os_hrtus(tf), badechos); |
| 338 | } |
| 339 | |
| 340 | return; |
| 341 | } //dtest_8 |
| 342 | |
| 343 | //dtest_9 : test accuracy of read timeouts. |
| 344 | static void dtest_9(struct diag_l0_device *dl0d) { |
| 345 | #define DT9_ITERS 4 |
| 346 | unsigned int i; |
| 347 | int iters; |
| 348 | uint8_t garbage[MAXRBUF]; |
| 349 | unsigned long long t0, tf; |
| 350 | struct dt_device *dev = dl0d->l0_int; |
| 351 | |
| 352 | fprintf(stderr, "Starting test 9: checking accuracy of read timeouts:\n"); |
| 353 | diag_tty_iflush(dev->tty_int); //purge before starting |
| 354 | |
| 355 | for (i=10; i<=200; i += 20) { |
| 356 | t0=diag_os_gethrt(); |
| 357 | for (iters=0; iters < DT9_ITERS; iters++) { |
| 358 | diag_tty_read(dev->tty_int, garbage, MAXRBUF, i); |
| 359 | } |
| 360 | tf = (diag_os_gethrt() - t0) / DT9_ITERS; //average measured timeout |
| 361 | printf("Timeout=%u: avg=%dms\n", i, (int) (diag_os_hrtus(tf)/1000)); |
| 362 | } |
| 363 | |
| 364 | return; |
| 365 | } //dtest_9 |
| 366 | |
| 367 | //dtest_10 == dtest_2 with a different speed |
| 368 | |
| 369 | //dtest_11 : test incomplete read timeout (needs half-duplex connection) |
| 370 | static void dtest_11(struct diag_l0_device *dl0d) { |
| 371 | #define DT11_ITERS 4 |
| 372 | unsigned int i; |
| 373 | int iters,rv; |
| 374 | uint8_t garbage[MAXRBUF]; |
| 375 | unsigned long long t0, tf; |
| 376 | struct dt_device *dev = dl0d->l0_int; |
| 377 | |
| 378 | fprintf(stderr, "Starting test 11: half-duplex incomplete read timeout accuracy:\n"); |
| 379 | diag_tty_iflush(dev->tty_int); //purge before starting |
| 380 | |
| 381 | for (i=10; i<=180; i += 20) { |
| 382 | tf=0; |
| 383 | for (iters=0; iters < DT11_ITERS; iters++) { |
| 384 | uint8_t tc = i; |
| 385 | if ((rv = dt_send(dl0d, &tc, 1))) { |
| 386 | goto failed; |
| 387 | } |
| 388 | t0=diag_os_gethrt(); |
| 389 | if ((rv=diag_tty_read(dev->tty_int, garbage, MAXRBUF, i)) != 1) { |
| 390 | // failed: purge + try next timeout value |
| 391 | fprintf(stderr, "failed @ timeout=%u : %s\n", i, diag_errlookup(rv)); |
| 392 | diag_tty_iflush(dev->tty_int); |
| 393 | break; |
| 394 | } |
| 395 | tf = tf + diag_os_gethrt() - t0; |
| 396 | } |
| 397 | tf = tf / DT11_ITERS; |
| 398 | printf("Timeout=%u: avg=%dms\n", i, (int) (diag_os_hrtus(tf)/1000)); |
| 399 | } |
| 400 | return; |
| 401 | failed: |
| 402 | fprintf(stderr, "Problem during test! %s\n",diag_errlookup(rv)); |
| 403 | return; |
| 404 | } //dtest_11 |
| 405 | |
| 406 | //dtest_12 : diag_tty_write() duration |
| 407 | static void dtest_12(struct diag_l0_device *dl0d) { |
| 408 | #define DT12_ITERS 4 |
| 409 | unsigned int i; |
| 410 | int iters; |
| 411 | uint8_t garbage[MAXRBUF]; |
| 412 | unsigned long long t0, tf; //measure inner time |
| 413 | unsigned long long ts1, ts2; //measure overall loop |
| 414 | struct dt_device *dev = dl0d->l0_int; |
| 415 | |
| 416 | fprintf(stderr, "Starting test 12: diag_tty_write() duration:\n"); |
| 417 | diag_tty_iflush(dev->tty_int); //purge before starting |
| 418 | |
| 419 | for (i=1; i<=50; i += 5) { |
| 420 | tf=0; |
| 421 | printf("len=%u:", i); |
| 422 | ts1=diag_os_gethrt(); |
| 423 | for (iters=0; iters < DT12_ITERS; iters++) { |
| 424 | unsigned long long tt1; |
| 425 | t0 = diag_os_gethrt(); |
| 426 | if (dt_send(dl0d, garbage, i)) { |
| 427 | goto failed; |
| 428 | } |
| 429 | tt1 = diag_os_gethrt(); |
| 430 | tf = tf + (tt1 - t0); |
| 431 | printf("\t%luus", (long unsigned int) (diag_os_hrtus(tt1-t0))); |
| 432 | (void) diag_tty_read(dev->tty_int, garbage, MAXRBUF, 5); |
| 433 | } |
| 434 | ts2= (diag_os_gethrt() - ts1) / DT12_ITERS; |
| 435 | tf = tf / DT12_ITERS; |
| 436 | printf(" => avg=%dms / %dms\n", (int) (diag_os_hrtus(tf)/1000), (int) (diag_os_hrtus(ts2)/1000)); |
| 437 | if (i == 1) { |
| 438 | i = 0; |
| 439 | } |
| 440 | } |
| 441 | return; |
| 442 | failed: |
| 443 | fprintf(stderr, "Problem during test!\n"); |
| 444 | return; |
| 445 | } |
| 446 | |
| 447 | //dtest_13 : simulate 14230 fastinit : 25ms low, tWUP=50ms, then send 0xAA @ 10.4k; with diag_tty_fastbreak |
| 448 | |
| 449 | static void dtest_13(struct diag_l0_device *dl0d) { |
| 450 | int i, pc=0; |
| 451 | const int iters=50; |
| 452 | const uint8_t db=0xAA; |
| 453 | struct dt_device *dev = dl0d->l0_int; |
| 454 | |
| 455 | fprintf(stderr, "Starting test 6: simulate fastinit:"); |
| 456 | for (i=0; i<=iters; i++) { |
| 457 | if (diag_tty_fastbreak(dev->tty_int, 50)) { |
| 458 | fprintf(stderr, "fastbreak error\n"); |
| 459 | break; |
| 460 | } |
| 461 | if (diag_tty_write(dev->tty_int, &db, 1) != 1) { |
| 462 | fprintf(stderr, "tty_write error\n"); |
| 463 | break; |
| 464 | } |
| 465 | diag_tty_iflush(dev->tty_int); //purge echo(s) |
| 466 | if ((10*i/iters) != pc) { |
| 467 | pc +=1; |
| 468 | fprintf(stderr, "."); |
| 469 | } |
| 470 | } |
| 471 | fprintf(stderr, "\n"); |
| 472 | return; |
| 473 | } |
| 474 | |
| 475 | static int dt_new(struct diag_l0_device *dl0d) { |
| 476 | struct dt_device *dev; |
| 477 | int rv; |
| 478 | |
| 479 | assert(dl0d); |
| 480 | |
| 481 | rv = diag_calloc(&dev, 1); |
| 482 | if (rv != 0) { |
| 483 | return diag_ifwderr(rv); |
| 484 | } |
| 485 | |
| 486 | dl0d->l0_int = dev; |
| 487 | |
| 488 | rv = diag_cfgn_tty(&dev->port); |
| 489 | if (rv != 0) { |
| 490 | free(dev); |
| 491 | return diag_ifwderr(rv); |
| 492 | } |
| 493 | |
| 494 | rv = diag_cfgn_int(&dev->dumbopts, DUMBDEFAULTS, DUMBDEFAULTS); |
| 495 | if (rv != 0) { |
| 496 | diag_cfg_clear(&dev->port); |
| 497 | free(dev); |
| 498 | return diag_ifwderr(rv); |
| 499 | } |
| 500 | |
| 501 | /* finish filling the dumbopts cfgi */ |
| 502 | dev->dumbopts.shortname = DUMBOPTS_SN; |
| 503 | dev->dumbopts.descr = DUMBOPTS_DESC; |
| 504 | dev->port.next = &dev->dumbopts; |
| 505 | dev->dumbopts.next = NULL; |
| 506 | |
| 507 | printf("*** Warning ! The DUMBT driver is only for electrical ***\n" |
| 508 | "*** testing ! Do NOT use while connected to a vehicle! ***\n" |
| 509 | "*** refer to doc/scantool-manual.html ***\n"); |
| 510 | |
| 511 | return 0; |
| 512 | } |
| 513 | |
| 514 | static void dt_del(struct diag_l0_device *dl0d) { |
| 515 | struct dt_device *dev; |
| 516 | |
| 517 | assert(dl0d); |
| 518 | |
| 519 | dev = dl0d->l0_int; |
| 520 | if (!dev) { |
| 521 | return; |
| 522 | } |
| 523 | |
| 524 | diag_cfg_clear(&dev->port); |
| 525 | diag_cfg_clear(&dev->dumbopts); |
| 526 | free(dev); |
| 527 | return; |
| 528 | } |
| 529 | |
| 530 | static struct cfgi *dt_getcfg(struct diag_l0_device *dl0d) { |
| 531 | struct dt_device *dev; |
| 532 | if (dl0d == NULL) { |
| 533 | return diag_pseterr(DIAG_ERR_BADCFG); |
| 534 | } |
| 535 | |
| 536 | dev = dl0d->l0_int; |
| 537 | return &dev->port; |
| 538 | } |
| 539 | |
| 540 | |
| 541 | /* |
| 542 | * Open the diagnostic device, returns a file descriptor |
| 543 | * records original state of term interface so we can restore later |
| 544 | */ |
| 545 | static int dt_open(struct diag_l0_device *dl0d, int testnum) { |
| 546 | struct dt_device *dev; |
| 547 | struct diag_serial_settings pset; |
| 548 | int dumbopts; |
| 549 | |
| 550 | assert(dl0d); |
| 551 | dev = dl0d->l0_int; |
| 552 | |
| 553 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 554 | FLFMT "open port %s test # %d\n", FL, dev->port.val.str, testnum); |
| 555 | |
| 556 | dt_init(); //make sure it is initted |
| 557 | |
| 558 | /* try to open TTY */ |
| 559 | dev->tty_int = diag_tty_open(dev->port.val.str); |
| 560 | if (dev->tty_int == NULL) { |
| 561 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 562 | } |
| 563 | |
| 564 | dev->protocol = DIAG_L1_RAW; //cheat ! |
| 565 | |
| 566 | switch (testnum) { |
| 567 | case 10: |
| 568 | pset.speed = 15000; |
| 569 | break; |
| 570 | case 14: |
| 571 | pset.speed = 360; |
| 572 | break; |
| 573 | default: |
| 574 | pset.speed=10400; |
| 575 | } |
| 576 | |
| 577 | pset.databits = diag_databits_8; |
| 578 | pset.stopbits = diag_stopbits_1; |
| 579 | pset.parflag = diag_par_n; |
| 580 | |
| 581 | if (diag_tty_setup(dev->tty_int, &pset)) { |
| 582 | diag_tty_close(dev->tty_int); |
| 583 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 584 | } |
| 585 | |
| 586 | /* parse dumbopts to set flags */ |
| 587 | dumbopts = dev->dumbopts.val.i; |
| 588 | |
| 589 | dev->use_L = dumbopts & USE_LLINE; |
| 590 | dev->clr_dtr = dumbopts & CLEAR_DTR; |
| 591 | dev->set_rts = dumbopts & SET_RTS; |
| 592 | dev->man_break = dumbopts & MAN_BREAK; |
| 593 | dev->lline_inv = dumbopts & LLINE_INV; |
| 594 | dev->fast_break = dumbopts & FAST_BREAK; |
| 595 | dev->blockduplex = dumbopts & BLOCKDUPLEX; |
| 596 | |
| 597 | //set initial DTR and RTS lines before starting tests; |
| 598 | if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts) < 0) { |
| 599 | diag_tty_close(dev->tty_int); |
| 600 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 601 | } |
| 602 | |
| 603 | (void)diag_tty_iflush(dev->tty_int); /* Flush unread input */ |
| 604 | |
| 605 | //printf("Press <enter> to stop the test.\n"); |
| 606 | //Currently these run for a fixed time... |
| 607 | switch (testnum) { |
| 608 | case 1: |
| 609 | dtest_1(dl0d); |
| 610 | break; |
| 611 | case 2: |
| 612 | dtest_2(dl0d); |
| 613 | break; |
| 614 | case 3: |
| 615 | dtest_3(dl0d); |
| 616 | break; |
| 617 | case 4: |
| 618 | dtest_4(dl0d); |
| 619 | break; |
| 620 | case 5: |
| 621 | dtest_5(dl0d); |
| 622 | break; |
| 623 | case 6: |
| 624 | dtest_6(dl0d); |
| 625 | break; |
| 626 | case 7: |
| 627 | dtest_7(dl0d); |
| 628 | break; |
| 629 | case 8: |
| 630 | dtest_8(dl0d); |
| 631 | break; |
| 632 | case 9: |
| 633 | dtest_9(dl0d); |
| 634 | break; |
| 635 | case 10: |
| 636 | dtest_2(dl0d); //same test, different speed |
| 637 | break; |
| 638 | case 11: |
| 639 | dtest_11(dl0d); |
| 640 | break; |
| 641 | case 12: |
| 642 | dtest_12(dl0d); |
| 643 | break; |
| 644 | case 13: |
| 645 | dtest_13(dl0d); |
| 646 | break; |
| 647 | case 14: |
| 648 | dtest_7(dl0d); //same test, different speed |
| 649 | break; |
| 650 | default: |
| 651 | break; |
| 652 | } |
| 653 | |
| 654 | diag_tty_close(dev->tty_int); |
| 655 | |
| 656 | fprintf(stderr, "L0 test finished. Ignore the following error.\n"); |
| 657 | return DIAG_ERR_GENERAL; |
| 658 | } |
| 659 | |
| 660 | |
| 661 | static void dt_close(UNUSED(struct diag_l0_device *dl0d)) { |
| 662 | return; |
| 663 | } |
| 664 | |
| 665 | |
| 666 | /* |
| 667 | * Send a load of data |
| 668 | * this is "blocking", i.e. returns only when it's finished or it failed. |
| 669 | * |
| 670 | * Returns 0 on success, -1 on failure |
| 671 | */ |
| 672 | |
| 673 | static int dt_send(struct diag_l0_device *dl0d, |
| 674 | const void *data, size_t len) { |
| 675 | struct dt_device *dev = dl0d->l0_int; |
| 676 | |
| 677 | /* |
| 678 | * This will be called byte at a time unless P4 timing parameter is zero |
| 679 | * as the L1 code that called this will be adding the P4 gap between |
| 680 | * bytes |
| 681 | */ |
| 682 | if (len == 0) { |
| 683 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 684 | } |
| 685 | |
| 686 | DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, data, len, |
| 687 | FLFMT "dt_send dl0d=%p , len=%ld. ", FL, (void *)dl0d, (long)len); |
| 688 | |
| 689 | if (diag_tty_write(dev->tty_int, data, len) != (int) len) { |
| 690 | fprintf(stderr, FLFMT "dt_send: write error\n", FL); |
| 691 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 692 | } |
| 693 | |
| 694 | return 0; |
| 695 | } |
| 696 | |
| 697 | /* dummy dt_recv |
| 698 | */ |
| 699 | |
| 700 | static int dt_recv(struct diag_l0_device *dl0d, |
| 701 | UNUSED(void *data), size_t len, unsigned int timeout) { |
| 702 | fprintf(stderr, |
| 703 | FLFMT "link %p recv upto %ld bytes timeout %u; doing nothing.\n", |
| 704 | FL, (void *)dl0d, (long)len, timeout); |
| 705 | |
| 706 | return diag_iseterr(DIAG_ERR_TIMEOUT); |
| 707 | } |
| 708 | |
| 709 | /* |
| 710 | * Set speed/parity etc |
| 711 | */ |
| 712 | static int dt_setspeed(struct diag_l0_device *dl0d, |
| 713 | const struct diag_serial_settings *pset) { |
| 714 | struct dt_device *dev; |
| 715 | |
| 716 | dev = (struct dt_device *)(dl0d->l0_int); |
| 717 | |
| 718 | dev->serial = *pset; |
| 719 | |
| 720 | return diag_tty_setup(dev->tty_int, &dev->serial); |
| 721 | } |
| 722 | |
| 723 | |
| 724 | static uint32_t dt_getflags(UNUSED(struct diag_l0_device *dl0d)) { |
| 725 | return DIAG_L1_HALFDUPLEX; |
| 726 | } |
| 727 | |
| 728 | static int dt_ioctl(struct diag_l0_device *dl0d, unsigned cmd, void *data) { |
| 729 | int rv = 0; |
| 730 | |
| 731 | switch (cmd) { |
| 732 | case DIAG_IOCTL_IFLUSH: |
| 733 | //do nothing |
| 734 | rv = 0; |
| 735 | break; |
| 736 | case DIAG_IOCTL_SETSPEED: |
| 737 | rv = dt_setspeed(dl0d, (const struct diag_serial_settings *) data); |
| 738 | break; |
| 739 | default: |
| 740 | rv = DIAG_ERR_IOCTL_NOTSUPP; |
| 741 | break; |
| 742 | } |
| 743 | |
| 744 | return rv; |
| 745 | } |
| 746 | |
| 747 | |
| 748 | const struct diag_l0 diag_l0_dumbtest = { |
| 749 | "Dumb interface test suite", |
| 750 | "DUMBT", |
| 751 | -1, //support "all" L1 protos... |
| 752 | dt_init, |
| 753 | dt_new, |
| 754 | dt_getcfg, |
| 755 | dt_del, |
| 756 | dt_open, |
| 757 | dt_close, |
| 758 | dt_getflags, |
| 759 | dt_recv, |
| 760 | dt_send, |
| 761 | dt_ioctl |
| 762 | }; |
| 763 | |