| 1 | /* |
| 2 | * freediag - Vehicle Diagnostic Utility |
| 3 | * |
| 4 | * (c) 2011-2016 fenugrec |
| 5 | * Diag, Layer 0, interface for Scantool.net's ELM32x Interface |
| 6 | * |
| 7 | * This is meant to support ELM323 & 327 devices; clones and originals. |
| 8 | * COM speed is "autodetected" (it tries 38400, 9600 and 115200bps) but can also be |
| 9 | * manually specified. |
| 10 | * |
| 11 | * ELM interfaces are particular in that they handle the header bytes + checksum internally. |
| 12 | * Data is transferred in ASCII hex format, i.e. 0x46 0xFE is sent and received as "46FE" |
| 13 | * This could be modified by enabling the "packed data" mode but I think ELM327 devices |
| 14 | * don't support that, and clones are equally hopeless. |
| 15 | * |
| 16 | * The ELM327 has non-volatile settings; this will require special treatment. Not |
| 17 | * completely implemented at the moment. |
| 18 | |
| 19 | * Note concerning non-volatile settings : it appears only these are stored to the EEPROM: |
| 20 | * -327: AT SP (set protocol) |
| 21 | * -327: PP 0C (custom baudrate) |
| 22 | * -323: these have no EEPROM so "ATZ" should really reset all to default |
| 23 | * |
| 24 | * |
| 25 | * Problems : |
| 26 | * * _recv() doesn't check if a good prompt '>' was received at the end |
| 27 | * * every time _open() is called it goes through the whole (long) init sequence |
| 28 | * * responses from multiple ECUs probably won't work |
| 29 | * |
| 30 | * Incomplete list of commands not supported by clones : {FI, SI, BD, KW} |
| 31 | */ |
| 32 | |
| 33 | #include <assert.h> |
| 34 | #include <stdio.h> |
| 35 | #include <stdint.h> |
| 36 | #include <stdlib.h> |
| 37 | #include <string.h> |
| 38 | |
| 39 | #include "diag.h" |
| 40 | #include "diag_cfg.h" |
| 41 | #include "diag_os.h" |
| 42 | #include "diag_err.h" |
| 43 | #include "diag_tty.h" |
| 44 | #include "diag_l0.h" |
| 45 | #include "diag_l1.h" |
| 46 | |
| 47 | |
| 48 | #define ELM_BUFSIZE 1000 //fit even max-length iso14230 frames, at 3 ASCII chars per byte. |
| 49 | #define ELM_SLOWNESS 100 //Add this many ms to read timeouts, because ELMs are sloooow |
| 50 | #define ELM_PURGETIME 400 //Time to wait (ms) for a response to "ATI" command |
| 51 | |
| 52 | struct elm_device { |
| 53 | int protocol; //current L1 protocol |
| 54 | |
| 55 | int elmflags; //see defines below |
| 56 | |
| 57 | struct cfgi port; |
| 58 | struct cfgi speed; //Host <-> ELM comms |
| 59 | |
| 60 | struct diag_serial_settings serial; |
| 61 | ttyp *tty_int; /** handle for tty stuff */ |
| 62 | |
| 63 | uint8_t kb1, kb2; // key bytes from 5 baud init |
| 64 | uint8_t atsh[3]; // current header setting for ISO9141 |
| 65 | struct diag_msg *wm; // custom wakeup message, if set |
| 66 | }; |
| 67 | |
| 68 | #define CFGSPEED_DESCR "Host <-> ELM comm speed (bps)" |
| 69 | #define CFGSPEED_SHORTN "elmspeed" |
| 70 | |
| 71 | |
| 72 | //flags for elmflags; set either 323_BASIC or 327_BASIC but not both; |
| 73 | // _CLONE can be set in addition to the basic type |
| 74 | #define ELM_323_BASIC 1 //device type is 323 |
| 75 | #define ELM_327_BASIC 2 //device type is 327 |
| 76 | #define ELM_32x_CLONE 4 //device is a clone; some commands will not be supported |
| 77 | #define ELM_INITDONE 0x10 //set when "BUS INIT" has happened. This is important for clones. |
| 78 | |
| 79 | |
| 80 | // possible error messages returned by the ELM IC |
| 81 | static const char *elm323_errors[] = {"BUS BUSY", "FB ERROR", "DATA ERROR", "<DATA ERROR", "NO DATA", "?", NULL}; |
| 82 | |
| 83 | static const char *elm327_errors[] = {"BUS BUSY", "FB ERROR", "DATA ERROR", "<DATA ERROR", "NO DATA", "?", |
| 84 | "ACT ALERT", "BUFFER FULL", "BUS ERROR", "CAN ERROR", "LP ALERT", |
| 85 | "LV RESET", "<RX ERROR", "STOPPED", "UNABLE TO CONNECT", "ERR", NULL}; |
| 86 | |
| 87 | /* authentic VS clone identification strings. |
| 88 | * I know of no elm323 clones. 327 clones may not support some commands (atfi, atsi, atkw) and thus need fallback methods. |
| 89 | * version strings should be ordered to test the longer strings first, e.g. '1.4a' before '1.4', as long |
| 90 | * as we use strstr() to match against the response. |
| 91 | */ |
| 92 | static const char *elm323_official[] = {"2.0",NULL}; //authentic 323 firmware versions, possibly incomplete list |
| 93 | static const char *elm323_clones[] = {NULL}; //known cloned versions |
| 94 | static const char *elm327_official[] = {"1.0a", "1.0", "1.1", "1.2a", "1.2", "1.3a", "1.3", "1.4b", "2.0", NULL}; |
| 95 | static const char *elm327_clones[] = {"1.4a", "1.4", "1.5a", "1.5", "2.1", NULL}; |
| 96 | |
| 97 | // baud rates for host to elm32x communication. Start with user-specified speed, then try common values |
| 98 | #define ELM_CUSTOMSPEED ((unsigned) -1) |
| 99 | static const unsigned elm_speeds[] = {ELM_CUSTOMSPEED, 38400, 9600, 115200, 0}; |
| 100 | |
| 101 | |
| 102 | extern const struct diag_l0 diag_l0_elm; |
| 103 | |
| 104 | static int elm_send(struct diag_l0_device *dl0d, const void *data, size_t len); |
| 105 | |
| 106 | static int elm_sendcmd(struct diag_l0_device *dl0d, |
| 107 | const uint8_t *data, size_t len, unsigned int timeout, uint8_t *resp); |
| 108 | |
| 109 | static int elm_purge(struct diag_l0_device *dl0d); |
| 110 | |
| 111 | static void elm_parse_cr(uint8_t *data, int len); //change 0x0A to 0x0D |
| 112 | static void elm_close(struct diag_l0_device *dl0d); |
| 113 | |
| 114 | /* |
| 115 | * Init must be callable even if no physical interface is |
| 116 | * present, it's just here for the code here to initialise its |
| 117 | * variables etc |
| 118 | */ |
| 119 | static int elm_init(void) { |
| 120 | static int elm_initdone=0; |
| 121 | |
| 122 | if (elm_initdone) { |
| 123 | return 0; |
| 124 | } |
| 125 | |
| 126 | elm_initdone = 1; |
| 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | int elm_new(struct diag_l0_device *dl0d) { |
| 131 | struct elm_device *dev; |
| 132 | int rv; |
| 133 | |
| 134 | assert(dl0d); |
| 135 | |
| 136 | rv = diag_calloc(&dev, 1); |
| 137 | if (rv != 0) { |
| 138 | return diag_ifwderr(rv); |
| 139 | } |
| 140 | |
| 141 | dl0d->l0_int = dev; |
| 142 | |
| 143 | rv = diag_cfgn_tty(&dev->port); |
| 144 | if (rv != 0) { |
| 145 | free(dev); |
| 146 | return diag_ifwderr(rv); |
| 147 | } |
| 148 | |
| 149 | rv = diag_cfgn_int(&dev->speed, 38400, 38400); |
| 150 | if (rv != 0) { |
| 151 | diag_cfg_clear(&dev->port); |
| 152 | free(dev); |
| 153 | return diag_ifwderr(rv); |
| 154 | } |
| 155 | dev->speed.descr = CFGSPEED_DESCR; |
| 156 | dev->speed.shortname = CFGSPEED_SHORTN; |
| 157 | |
| 158 | dev->port.next = &dev->speed; |
| 159 | dev->speed.next = NULL; |
| 160 | |
| 161 | return 0; |
| 162 | } |
| 163 | |
| 164 | struct cfgi *elm_getcfg(struct diag_l0_device *dl0d) { |
| 165 | struct elm_device *dev; |
| 166 | if (dl0d == NULL) { |
| 167 | return diag_pseterr(DIAG_ERR_BADCFG); |
| 168 | } |
| 169 | |
| 170 | dev = dl0d->l0_int; |
| 171 | return &dev->port; |
| 172 | } |
| 173 | |
| 174 | void elm_del(struct diag_l0_device *dl0d) { |
| 175 | struct elm_device *dev; |
| 176 | |
| 177 | assert(dl0d); |
| 178 | |
| 179 | dev = dl0d->l0_int; |
| 180 | if (!dev) { |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | diag_cfg_clear(&dev->port); |
| 185 | diag_cfg_clear(&dev->speed); |
| 186 | if (dev->wm != NULL) { |
| 187 | diag_freemsg(dev->wm); |
| 188 | } |
| 189 | free(dev); |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | |
| 194 | static void elm_close(struct diag_l0_device *dl0d) { |
| 195 | uint8_t buf[]="ATPC\x0D"; |
| 196 | struct elm_device *dev; |
| 197 | |
| 198 | assert(dl0d != NULL); |
| 199 | |
| 200 | if (dl0d->opened) { |
| 201 | elm_sendcmd(dl0d, buf, 5, 500, NULL); //close protocol. So clean ! |
| 202 | } |
| 203 | |
| 204 | dev = (struct elm_device *)dl0d->l0_int; |
| 205 | |
| 206 | /* If debugging, print to stderr */ |
| 207 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V, |
| 208 | FLFMT "link %p closing\n", FL, (void *)dl0d); |
| 209 | |
| 210 | diag_tty_close(dev->tty_int); |
| 211 | dev->tty_int = NULL; |
| 212 | dl0d->opened = 0; |
| 213 | |
| 214 | return; |
| 215 | } |
| 216 | |
| 217 | |
| 218 | //elm_parse_errors : look for known error messages in the reply. |
| 219 | // return any match or NULL if nothing found. |
| 220 | // data[] must be \0-terminated ! |
| 221 | static const char *elm_parse_errors(struct diag_l0_device *dl0d, uint8_t *data) { |
| 222 | struct elm_device *dev; |
| 223 | const char **elm_errors; //just used to select between the 2 error lists |
| 224 | int i; |
| 225 | |
| 226 | dev = (struct elm_device *) dl0d->l0_int; |
| 227 | |
| 228 | //pick the right set of error messages. Although we could just systematically |
| 229 | //use the vaster ELM327 set of error messages... |
| 230 | |
| 231 | if (dev->elmflags & ELM_323_BASIC) { |
| 232 | elm_errors=elm323_errors; |
| 233 | } else { |
| 234 | elm_errors = elm327_errors; |
| 235 | } |
| 236 | |
| 237 | for (i=0; elm_errors[i]; i++) { |
| 238 | if (strstr((char *)data, elm_errors[i])) { |
| 239 | //we found an error msg, return it. |
| 240 | return elm_errors[i]; |
| 241 | } |
| 242 | } |
| 243 | return NULL; |
| 244 | } |
| 245 | |
| 246 | //Send a command to ELM device and make sure no error occured. Data is passed on directly as a string; |
| 247 | //caller must make sure the string is \r-terminated , i.e. 0x0D-terminated. |
| 248 | //Sending 0A after 0D will cause ELM to interrupt what it's doing to "process" 0A, resulting in a |
| 249 | //failure. |
| 250 | //This func should not be used for commands that elicit a data response (i.e. all data destined to the OBD bus, |
| 251 | //hence not prefixed by "AT"). Response is dumped in *resp (0-terminated) for optional analysis by caller; *resp must be ELM_BUFSIZE long. |
| 252 | //returns 0 if (prompt_good) && ("OK" found anywhere in the response) && (no known error message was present) || |
| 253 | // (prompt_good && ATZ or ATKW command was sent) , since response doesn't contain "OK" for ATZ or ATKW. |
| 254 | //elm_sendcmd should not be called from outside diag_l0_elm.c. |
| 255 | static int elm_sendcmd(struct diag_l0_device *dl0d, const uint8_t *data, size_t len, unsigned int timeout, uint8_t *resp) { |
| 256 | //note : we better not request (len == (size_t) -1) bytes ! The casts between ssize_t and size_t are |
| 257 | // "muddy" in here |
| 258 | //ssize_t xferd; |
| 259 | int rv; |
| 260 | uint8_t ebuf[ELM_BUFSIZE]; //local buffer if caller provides none. |
| 261 | uint8_t *buf; |
| 262 | struct elm_device *dev; |
| 263 | const char *err_str; //hold a possible error message |
| 264 | |
| 265 | dev = (struct elm_device *)dl0d->l0_int; |
| 266 | //we need access to elm_device to access .elmflags |
| 267 | |
| 268 | if (resp == NULL) { |
| 269 | buf=ebuf; //use local buffer |
| 270 | } else { |
| 271 | buf = resp; // or caller-provided buffer |
| 272 | } |
| 273 | |
| 274 | if (!len) { |
| 275 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 276 | } |
| 277 | if (!dev) { |
| 278 | return diag_iseterr(DIAG_ERR_BADFD); |
| 279 | } |
| 280 | |
| 281 | if (data[len-1] != 0x0D) { |
| 282 | //Last byte is not a carriage return, this would die. |
| 283 | fprintf(stderr, FLFMT "elm_sendcmd: non-terminated command : %.*s\n", FL, (int) len, (char *) data); |
| 284 | //the %.*s is pure magic : limits the string length to len, even if the string is not null-terminated. |
| 285 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 286 | } |
| 287 | diag_tty_iflush(dev->tty_int); //currently the code often "forgets" data in the input buffer, especially if the previous |
| 288 | //transaction failed. Flushing the input increases the odds of not crashing soon |
| 289 | |
| 290 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 291 | FLFMT "elm_sendcmd: %.*s\n", FL, (int) len-1, (char *)data); |
| 292 | |
| 293 | rv = diag_tty_write(dev->tty_int, data, len); |
| 294 | if (rv != (int) len) { //XXX danger ! evil cast |
| 295 | fprintf(stderr, FLFMT "elm_sendcmd: write error\n", FL); |
| 296 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 297 | } |
| 298 | |
| 299 | //next, receive ELM response, within {ms} delay. |
| 300 | |
| 301 | rv=diag_tty_read(dev->tty_int, buf, ELM_BUFSIZE-1, timeout); //rv=# bytes read |
| 302 | |
| 303 | if (rv<1) { |
| 304 | //no data or error |
| 305 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 306 | FLFMT "ELM did not respond\n", FL); |
| 307 | |
| 308 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 309 | } |
| 310 | |
| 311 | elm_parse_cr(buf, rv); //debug output is prettier with this |
| 312 | DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V, buf, rv, |
| 313 | FLFMT "received %d bytes (%.*s\n); hex: ", FL, rv, rv, (char *)buf); |
| 314 | |
| 315 | buf[rv]=0; //terminate string |
| 316 | if (buf[rv-1] != '>') { |
| 317 | //if last character isn't the input prompt, there is a problem |
| 318 | fprintf(stderr, FLFMT "ELM not ready (no prompt received): %s\nhex: ", FL, buf); |
| 319 | diag_data_dump(stderr, buf, rv); |
| 320 | fprintf(stderr, "\n"); |
| 321 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 322 | } |
| 323 | //At this point we got a prompt but there may have been an error message. |
| 324 | //There is some ambiguity in the ELM datasheets on the exact format of the replies. |
| 325 | //1) the prompt character '>' is always alone on its line; |
| 326 | //2) depending on some parameters, it may be preceded by 0D or 0D 0A |
| 327 | //3) some errors ( "<DATA ERROR" and "<RX ERROR" ) can be appended after |
| 328 | //a reply; others should be at the beginning of the response (alone on a line) |
| 329 | |
| 330 | //ex. of good reply : "41 00 00 \r>", bad reply :"NO DATA\r>" |
| 331 | //let's just use strstr() to find occurences for each known error. |
| 332 | //it'll take a while but speed isn't normally critical when sending commands |
| 333 | |
| 334 | err_str = elm_parse_errors(dl0d, buf); |
| 335 | |
| 336 | if (err_str != NULL) { |
| 337 | fprintf(stderr, FLFMT "ELM returned error : %s\n", FL, err_str); |
| 338 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 339 | } |
| 340 | |
| 341 | //check if we either 1)got a positive response "OK" |
| 342 | //2)were sending ATZ (special case hack, it doesn't answer "OK") |
| 343 | if ((strstr((char *)buf, "OK") != NULL) || |
| 344 | (strstr((char *)data, "ATKW") != NULL) || |
| 345 | (strstr((char *)data, "ATZ") != NULL)) { |
| 346 | return 0; |
| 347 | } |
| 348 | |
| 349 | fprintf(stderr, FLFMT "Response not recognized ! Report this ! Got: \n", FL); |
| 350 | diag_data_dump(stderr, buf, rv); |
| 351 | fprintf(stderr, "\n"); |
| 352 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 353 | |
| 354 | } |
| 355 | /* |
| 356 | * Open the diagnostic device |
| 357 | * ELM settings used : no echo (E0), headers on (H1), linefeeds off (L0), mem off (M0) |
| 358 | */ |
| 359 | static int elm_open(struct diag_l0_device *dl0d, int iProtocol) { |
| 360 | int i,rv; |
| 361 | struct elm_device *dev; |
| 362 | struct diag_serial_settings sset; |
| 363 | const uint8_t *buf; |
| 364 | uint8_t rxbuf[ELM_BUFSIZE]; |
| 365 | |
| 366 | const char **elm_official; |
| 367 | const char **elm_clones; //point to elm323_ or elm327_ clone and official version string lists |
| 368 | |
| 369 | assert(dl0d); |
| 370 | dev = dl0d->l0_int; |
| 371 | |
| 372 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 373 | FLFMT "open port %s L1proto %d\n", |
| 374 | FL, dev->port.val.str, iProtocol); |
| 375 | |
| 376 | elm_init(); |
| 377 | |
| 378 | //sending ATZ to elm will wipe out current header setting |
| 379 | dev->atsh[0]=0; dev->atsh[1]=0; dev->atsh[2]=0; |
| 380 | |
| 381 | //throw away previous wakeup message setting, if any |
| 382 | if (dev->wm != NULL) { |
| 383 | diag_freemsg(dev->wm); |
| 384 | } |
| 385 | dev->wm = NULL; |
| 386 | |
| 387 | /* try to open TTY */ |
| 388 | dev->tty_int = diag_tty_open(dev->port.val.str); |
| 389 | if (dev->tty_int == NULL) { |
| 390 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 391 | } |
| 392 | dev->protocol = iProtocol; |
| 393 | |
| 394 | //try sending a command to elm327 at each possible speed until we get a response |
| 395 | sset.databits = diag_databits_8; |
| 396 | sset.stopbits = diag_stopbits_1; |
| 397 | sset.parflag = diag_par_n; |
| 398 | for (i=0; elm_speeds[i]; i++) { |
| 399 | sset.speed = elm_speeds[i]; |
| 400 | |
| 401 | // skip if custom speed was already tried: |
| 402 | if (sset.speed == (unsigned) dev->speed.val.i) { |
| 403 | continue; |
| 404 | } |
| 405 | |
| 406 | if (sset.speed == ELM_CUSTOMSPEED) { |
| 407 | //magic flag to retrieve custom speed |
| 408 | sset.speed = (unsigned) dev->speed.val.i; |
| 409 | } |
| 410 | fprintf(stderr, FLFMT "Sending ATI to ELM32x at %u...\n", FL, sset.speed); |
| 411 | |
| 412 | dev->serial = sset; |
| 413 | |
| 414 | if ((rv=diag_tty_setup(dev->tty_int, &sset))) { |
| 415 | fprintf(stderr, FLFMT "Error setting %u;8N1 on %s\n", |
| 416 | FL, sset.speed, dev->port.val.str); |
| 417 | elm_close(dl0d); |
| 418 | return diag_ifwderr(rv); |
| 419 | } |
| 420 | |
| 421 | diag_tty_iflush(dev->tty_int); /* Flush unread input */ |
| 422 | |
| 423 | //At this stage, the ELM has possibly been powered up for a while; |
| 424 | //it may have an unfinished command / garbage in its input buffer. We |
| 425 | //need to clear that before sending the real ATZ ==> ATI is quick and safe; elm_purge does this. |
| 426 | |
| 427 | dev->elmflags=0; //we know nothing yet |
| 428 | |
| 429 | rv=elm_purge(dl0d); |
| 430 | //if rv=0, we got a prompt so we know speed is set properly. |
| 431 | if (rv == 0) { |
| 432 | break; |
| 433 | } |
| 434 | } |
| 435 | if (elm_speeds[i]==0) { |
| 436 | fprintf(stderr, FLFMT "No response from ELM323/ELM327. Verify connection to ELM\n", FL); |
| 437 | elm_close(dl0d); |
| 438 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 439 | } |
| 440 | |
| 441 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 442 | FLFMT "elm_open : sending ATZ...\n", FL); |
| 443 | |
| 444 | //the command "ATZ" causes a full reset and the ELM replies with |
| 445 | //a string like "ELM32x vX.Xx\n>" |
| 446 | |
| 447 | buf=(uint8_t *)"ATZ\x0D"; |
| 448 | rv=elm_sendcmd(dl0d, buf, 4, 2000, rxbuf); |
| 449 | if (rv) { |
| 450 | fprintf(stderr, FLFMT "elm_open : ATZ failed !\n", FL); |
| 451 | elm_close(dl0d); |
| 452 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 453 | } |
| 454 | |
| 455 | //Correct prompt received; try to identify device. |
| 456 | // 1) guess 323 vs 327 |
| 457 | if (strstr((char *)rxbuf, "ELM323")!=NULL) { |
| 458 | dev->elmflags |= ELM_323_BASIC; |
| 459 | elm_official=elm323_official; |
| 460 | elm_clones=elm323_clones; |
| 461 | } else if (strstr((char *)rxbuf, "ELM327")!=NULL) { |
| 462 | dev->elmflags |= ELM_327_BASIC; |
| 463 | elm_official=elm327_official; |
| 464 | elm_clones=elm327_clones; |
| 465 | } else { |
| 466 | fprintf(stderr, FLFMT "no valid version string !! Report this !. Got:\n%s\n", FL, rxbuf); |
| 467 | //no point in continuing... |
| 468 | elm_close(dl0d); |
| 469 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 470 | } |
| 471 | // 2) identify valid VS clone devices. |
| 472 | rv=0; // temp "device identified" flag |
| 473 | for (i=0; elm_official[i]; i++) { |
| 474 | if (strstr((char *)rxbuf, elm_official[i])) { |
| 475 | printf("Official ELM found, v%s\n", elm_official[i]); |
| 476 | rv=1; |
| 477 | break; |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | if (rv==0) { |
| 482 | for (i=0; elm_clones[i]; i++) { |
| 483 | if (strstr((char *)rxbuf, elm_clones[i])) { |
| 484 | printf("Clone ELM found, v%s. Expect inferior performance\n", elm_clones[i]); |
| 485 | dev->elmflags |= ELM_32x_CLONE; |
| 486 | rv=1; |
| 487 | break; |
| 488 | } |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | if (rv==0) { |
| 493 | //still not identified : assume clone. |
| 494 | dev->elmflags |= ELM_32x_CLONE; |
| 495 | printf("ELM version not recognized! Please report this ! Response was:\n%s\n", rxbuf); |
| 496 | } |
| 497 | |
| 498 | if ((dev->elmflags & ELM_323_BASIC) && (dev->elmflags & ELM_32x_CLONE)) { |
| 499 | printf("A 323 clone ? Report this !\n"); |
| 500 | } |
| 501 | |
| 502 | |
| 503 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 504 | FLFMT "ELM reset success, elmflags=%#x\n", FL, dev->elmflags); |
| 505 | |
| 506 | dl0d->opened = 1; //TODO : use this flag to skip the clone detection next time |
| 507 | |
| 508 | //now send "ATE0\n" command to disable echo. |
| 509 | buf=(uint8_t *)"ATE0\x0D"; |
| 510 | if (elm_sendcmd(dl0d, buf, 5, 500, NULL)) { |
| 511 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 512 | FLFMT "sending \"ATE0\" failed\n", FL); |
| 513 | |
| 514 | elm_close(dl0d); |
| 515 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 516 | } |
| 517 | |
| 518 | //ATL0 : disable linefeeds |
| 519 | buf=(uint8_t *)"ATL0\x0D"; |
| 520 | if (elm_sendcmd(dl0d, buf, 5, 500, NULL)) { |
| 521 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 522 | FLFMT "sending \"ATL0\" failed\n", FL); |
| 523 | |
| 524 | elm_close(dl0d); |
| 525 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 526 | } |
| 527 | |
| 528 | //ATH1 : always show header bytes |
| 529 | buf=(uint8_t *)"ATH1\x0D"; |
| 530 | if (elm_sendcmd(dl0d, buf, 5, 500, NULL)) { |
| 531 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 532 | FLFMT "sending \"ATH1\" failed\n", FL); |
| 533 | |
| 534 | elm_close(dl0d); |
| 535 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 536 | } |
| 537 | |
| 538 | //for elm327 only: disable memory |
| 539 | if (dev->elmflags & ELM_327_BASIC) { |
| 540 | buf=(uint8_t *)"ATM0\x0D"; |
| 541 | if (elm_sendcmd(dl0d, buf, 5, 500, NULL)) { |
| 542 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 543 | FLFMT "sending \"ATM0\" failed\n", FL); |
| 544 | |
| 545 | elm_close(dl0d); |
| 546 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | //check if proto is really supported (323 supports only 9141 and 14230) |
| 551 | if ((dev->elmflags & ELM_323_BASIC) && |
| 552 | ((iProtocol != DIAG_L1_ISO9141) && |
| 553 | (iProtocol != DIAG_L1_ISO14230))) { |
| 554 | return diag_iseterr(DIAG_ERR_PROTO_NOTSUPP); |
| 555 | } |
| 556 | |
| 557 | //if 327, set proto when possible; we won't if 14230, because init type (fast vs slow) isn't decided yet |
| 558 | // CAN is also not implemented here |
| 559 | buf=NULL; |
| 560 | if (dev->elmflags & ELM_327_BASIC) { |
| 561 | switch (iProtocol) { |
| 562 | case DIAG_L1_J1850_PWM: |
| 563 | buf=(uint8_t *)"ATTP1\x0D"; |
| 564 | break; |
| 565 | case DIAG_L1_J1850_VPW: |
| 566 | buf=(uint8_t *)"ATTP2\x0D"; |
| 567 | break; |
| 568 | case DIAG_L1_ISO9141: |
| 569 | buf=(uint8_t *)"ATTP3\x0D"; |
| 570 | break; |
| 571 | default: |
| 572 | buf=NULL; |
| 573 | } |
| 574 | } |
| 575 | |
| 576 | if (buf != NULL) { |
| 577 | if (elm_sendcmd(dl0d, buf, 6, 500, NULL)) { |
| 578 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 579 | FLFMT "sending \"ATTPx\" failed\n", FL); |
| 580 | |
| 581 | elm_close(dl0d); |
| 582 | return diag_iseterr(DIAG_ERR_BADIFADAPTER); |
| 583 | } |
| 584 | } |
| 585 | |
| 586 | |
| 587 | //at this point : ELM is ready for further ops |
| 588 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V, |
| 589 | FLFMT "ELM ready.\n", FL); |
| 590 | |
| 591 | return 0; |
| 592 | } |
| 593 | |
| 594 | |
| 595 | /* |
| 596 | * Fastinit & slowinit: |
| 597 | * ELM should manage slow/fast init automatically... Until the code from levels L1 and up can deal with |
| 598 | * this, the bus will manually be initialized. |
| 599 | * Only callable internally. Not for export !! |
| 600 | * Unsupported by some (all?) clones !! (they handle the init internally, on demand) |
| 601 | */ |
| 602 | static int elm_fastinit(struct diag_l0_device *dl0d) { |
| 603 | uint8_t *cmds= (uint8_t *) "ATFI\x0D"; |
| 604 | |
| 605 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V, |
| 606 | FLFMT "ELM forced fastinit...\n", FL); |
| 607 | |
| 608 | //send command with 1000ms timeout (guessing) |
| 609 | if (elm_sendcmd(dl0d, cmds, 5, 1000, NULL)) { |
| 610 | fprintf(stderr, FLFMT "Command ATFI failed\n", FL); |
| 611 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 612 | } |
| 613 | return 0; |
| 614 | } |
| 615 | |
| 616 | |
| 617 | static int elm_slowinit(struct diag_l0_device *dl0d) { |
| 618 | uint8_t *cmds=(uint8_t *)"ATSI\x0D"; |
| 619 | |
| 620 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V, |
| 621 | FLFMT "ELM forced slowinit...\n", FL); |
| 622 | |
| 623 | //huge timeout of 2.8s. Not sure if this is adequate |
| 624 | if (elm_sendcmd(dl0d, cmds, 5, 2800, NULL)) { |
| 625 | fprintf(stderr, FLFMT "Command ATSI failed\n", FL); |
| 626 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 627 | } |
| 628 | return 0; |
| 629 | |
| 630 | } |
| 631 | |
| 632 | //elm_bogusinit : send a 01 00 request to force ELM to init bus. |
| 633 | //Only used to force clones to establish a connection... hack-grade because it assumes all ECUs support this. |
| 634 | // non-OBD ECUs may not work with this. ELM clones suck... |
| 635 | // TODO : add argument to allow either a 01 00 request (SID1 PID0, J1979) or 3E (iso14230 TesterPresent) request to force init. |
| 636 | static int elm_bogusinit(struct diag_l0_device *dl0d, unsigned int timeout) { |
| 637 | int rv; |
| 638 | struct elm_device *dev; |
| 639 | uint8_t buf[MAXRBUF]; |
| 640 | uint8_t generic_data[] = {0x01,0x00}; // J1979 request only |
| 641 | uint8_t iso9141_data[] = {0x68,0x6a,0xf1,0x01,0x00}; // with ISO9141 hdr |
| 642 | const char *err_str; |
| 643 | |
| 644 | dev = dl0d->l0_int; |
| 645 | if (dev->protocol & DIAG_L1_ISO9141) { |
| 646 | rv = elm_send(dl0d, iso9141_data, 5); |
| 647 | } else { |
| 648 | rv = elm_send(dl0d, generic_data, 2); |
| 649 | } |
| 650 | if (rv) { |
| 651 | return diag_ifwderr(rv); |
| 652 | } |
| 653 | |
| 654 | // receive everything; we're hoping for a prompt at the end and no error message. |
| 655 | rv=diag_tty_read(dev->tty_int, buf, MAXRBUF-5, timeout); //rv=# bytes read |
| 656 | elm_parse_cr(buf, rv); |
| 657 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V, |
| 658 | FLFMT "received %d bytes: %.*s: ", |
| 659 | FL, rv, rv, buf); |
| 660 | |
| 661 | if (rv<1) { |
| 662 | //no data or error |
| 663 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 664 | FLFMT "ELM did not respond\n", FL); |
| 665 | |
| 666 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 667 | } |
| 668 | buf[rv]=0; //terminate string |
| 669 | if (buf[rv-1] != '>') { |
| 670 | //if last character isn't the input prompt, there is a problem |
| 671 | fprintf(stderr, FLFMT "ELM not ready (no prompt received): %s\n", FL, buf); |
| 672 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 673 | } |
| 674 | |
| 675 | err_str = elm_parse_errors(dl0d, buf); |
| 676 | |
| 677 | if (err_str != NULL) { |
| 678 | fprintf(stderr, FLFMT "got error while forcing init: %s\n", FL, err_str); |
| 679 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 680 | } |
| 681 | |
| 682 | return 0; |
| 683 | |
| 684 | } |
| 685 | |
| 686 | // set wakeup message |
| 687 | static int elm_updatewm(struct diag_l0_device *dl0d) { |
| 688 | struct elm_device *dev; |
| 689 | uint8_t wm[18]; |
| 690 | |
| 691 | dev = (struct elm_device *)dl0d->l0_int; |
| 692 | sprintf((char *) wm, "ATWM %02X %02X %02X %02X\x0D", dev->wm->data[0], dev->wm->data[1], dev->wm->data[2], dev->wm->data[3]); |
| 693 | if (elm_sendcmd(dl0d, wm, 17, 500, NULL) < 0) { |
| 694 | fprintf(stderr, FLFMT "elm_updatewm: ATWM failed\n", FL); |
| 695 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 696 | } |
| 697 | return 0; |
| 698 | } |
| 699 | |
| 700 | /* |
| 701 | * Do manual wakeup on the bus, if supported |
| 702 | * ret 0 if ok |
| 703 | */ |
| 704 | static int elm_initbus(struct diag_l0_device *dl0d, struct diag_l1_initbus_args *in) { |
| 705 | const uint8_t *buf; |
| 706 | int rv = DIAG_ERR_INIT_NOTSUPP; |
| 707 | unsigned int timeout=0; //for bogus init |
| 708 | |
| 709 | struct elm_device *dev; |
| 710 | |
| 711 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 712 | FLFMT "ELM initbus type %d\n", FL, in->type); |
| 713 | |
| 714 | dev = (struct elm_device *)dl0d->l0_int; |
| 715 | |
| 716 | if (!dev) { |
| 717 | return diag_iseterr(rv); |
| 718 | } |
| 719 | |
| 720 | if (dev->elmflags & ELM_32x_CLONE) { |
| 721 | printf("Note : explicit bus init not available on clones. Errors here are ignored.\n"); |
| 722 | dev->elmflags &= ~ELM_INITDONE; //clear flag |
| 723 | } |
| 724 | |
| 725 | switch (in->type) { |
| 726 | case DIAG_L1_INITBUS_FAST: { |
| 727 | uint8_t fmt, src, tgt; |
| 728 | uint8_t setproto[]="ATTP5\x0D"; |
| 729 | uint8_t sethdr[15]; //format: "ATSH xx yy zz\x0D" |
| 730 | |
| 731 | fmt=(in->physaddr)? 0x81:0xC1; |
| 732 | src=in->testerid; |
| 733 | tgt=in->addr; |
| 734 | if (dev->elmflags & ELM_327_BASIC) { |
| 735 | //only needed for 327s (ATTP not available on ELM323) |
| 736 | //set proto; We assume iso14230 since it's a fast init |
| 737 | rv=elm_sendcmd(dl0d, setproto, 6, 500, NULL); |
| 738 | if (rv < 0) { |
| 739 | break; |
| 740 | } |
| 741 | } |
| 742 | |
| 743 | // set wakeup message if applicable |
| 744 | if (dev->wm != NULL) { |
| 745 | rv=elm_updatewm(dl0d); |
| 746 | if (rv < 0) { |
| 747 | return rv; |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | sprintf((char *) sethdr, "ATSH %02X %02X %02X\x0D", fmt, tgt, src); |
| 752 | rv=elm_sendcmd(dl0d, sethdr, 14, 500, NULL); |
| 753 | |
| 754 | //explicit init is not supported by clones, they wait for the first OBD request... |
| 755 | if ((dev->elmflags & ELM_32x_CLONE)==0) { |
| 756 | rv = elm_fastinit(dl0d); |
| 757 | if (!rv) { |
| 758 | dev->elmflags |= ELM_INITDONE; |
| 759 | } |
| 760 | } //if explicit init failed we'll try a bogus init anyway |
| 761 | timeout=1500; |
| 762 | } //case fastinit |
| 763 | break; //case fastinit |
| 764 | case DIAG_L1_INITBUS_5BAUD: |
| 765 | //if 327 : set proto first |
| 766 | if (dev->elmflags & ELM_327_BASIC) { |
| 767 | if (dev->protocol & DIAG_L1_ISO9141) { |
| 768 | buf=(uint8_t *) "ATTP3\x0D"; |
| 769 | } else if (dev->protocol & DIAG_L1_ISO14230) { |
| 770 | buf=(uint8_t *) "ATTP4\x0D"; |
| 771 | } else { // illegal combination ! |
| 772 | return diag_iseterr( |
| 773 | DIAG_ERR_INIT_NOTSUPP); |
| 774 | } |
| 775 | |
| 776 | rv=elm_sendcmd(dl0d, buf, 6, 500, NULL); |
| 777 | if (rv < 0) { |
| 778 | break; |
| 779 | } |
| 780 | } |
| 781 | |
| 782 | // set wakeup message if applicable |
| 783 | if (dev->wm != NULL) { |
| 784 | rv=elm_updatewm(dl0d); |
| 785 | if (rv < 0) { |
| 786 | return rv; |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | //set init address |
| 791 | if ((dev->elmflags & ELM_323_BASIC) && (in->addr != 0x33)) { |
| 792 | fprintf(stderr, FLFMT "elm_initbus: ELM323 doesn't support target address %02X", FL, in->addr); |
| 793 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 794 | } |
| 795 | if (in->addr != 0x33) { |
| 796 | uint8_t iia[15]; |
| 797 | sprintf((char *) iia, "ATIIA %02X\x0D", in->addr); |
| 798 | rv=elm_sendcmd(dl0d, iia, 9, 500, NULL); |
| 799 | if (rv < 0) { |
| 800 | fprintf(stderr, FLFMT "elm_initbus: ATIIA %02X failed\n", FL, in->addr); |
| 801 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 802 | } |
| 803 | } |
| 804 | |
| 805 | //accept nonstandard keybytes during init |
| 806 | if (dev->elmflags & ELM_327_BASIC) { |
| 807 | buf=(uint8_t *) "ATKW0\x0D"; |
| 808 | rv=elm_sendcmd(dl0d, buf, 6, 500, NULL); |
| 809 | if (rv < 0) { |
| 810 | fprintf(stderr, |
| 811 | FLFMT |
| 812 | "elm_initbus: ATKW0 failed, " |
| 813 | "continuing anyway\n", |
| 814 | FL); |
| 815 | } |
| 816 | } |
| 817 | |
| 818 | //explicit init is not supported by clones |
| 819 | if ((dev->elmflags & ELM_32x_CLONE)==0) { |
| 820 | rv = elm_slowinit(dl0d); |
| 821 | if (!rv) { |
| 822 | dev->elmflags |= ELM_INITDONE; |
| 823 | } |
| 824 | } |
| 825 | timeout=4200; //slow init is slow ! |
| 826 | |
| 827 | //query elm for keybytes |
| 828 | dev->kb1 = 0; dev->kb2 = 0; |
| 829 | if (dev->elmflags & ELM_INITDONE) { |
| 830 | uint8_t rxbuf[ELM_BUFSIZE]; |
| 831 | unsigned int kb1, kb2; |
| 832 | |
| 833 | buf=(uint8_t *) "ATKW\x0D"; |
| 834 | rv=elm_sendcmd(dl0d, buf, 5, 500, rxbuf); |
| 835 | if (rv < 0) { |
| 836 | fprintf(stderr, FLFMT "elm_initbus: ATKW failed, continuing anyway\n", FL); |
| 837 | // TODO: for clones without ATKW, can |
| 838 | // fall back to ATBD. If ATBD response |
| 839 | // starts with 06 addr 55 xx yy aa bb |
| 840 | // and aa=yy^0xff and bb=addr^0xff then |
| 841 | // xx and yy should be key bytes |
| 842 | rv = 0; |
| 843 | } else if (sscanf((char *)rxbuf, "1:%02X 2:%02X", &kb1, &kb2) == 2) { |
| 844 | dev->kb1 = kb1; |
| 845 | dev->kb2 = kb2; |
| 846 | in->kb1 = kb1; |
| 847 | in->kb2 = kb2; |
| 848 | } else { |
| 849 | fprintf(stderr, FLFMT "elm_initbus: ATKW failed, continuing anyway\n", FL); |
| 850 | rv = 0; |
| 851 | } |
| 852 | } |
| 853 | |
| 854 | // enable receipt of >7 byte messages, if possible |
| 855 | if (dev->elmflags & ELM_INITDONE) { |
| 856 | buf=(uint8_t *) "ATAL\x0D"; |
| 857 | rv=elm_sendcmd(dl0d, buf, 5, 500, NULL); |
| 858 | if (rv < 0) { |
| 859 | fprintf(stderr, FLFMT "elm_initbus: ATAL failed, continuing anyway\n", FL); |
| 860 | rv = 0; |
| 861 | } |
| 862 | } |
| 863 | |
| 864 | break; |
| 865 | default: |
| 866 | rv = DIAG_ERR_INIT_NOTSUPP; |
| 867 | break; |
| 868 | } |
| 869 | |
| 870 | if ((dev->elmflags & ELM_INITDONE)==0) { |
| 871 | // init not done, either because it's a clone (explicit init unsupported), or another reason. |
| 872 | // two choices : A) assume comms will be OBD compliant, so request "01 00" has to be supported |
| 873 | // B) tweak _recv() to skip the "BUS INIT: " line that ELM will send at the next request. |
| 874 | // A is easier for now... |
| 875 | // Note that since we don't check for "DIAG_ERR_INIT_NOTSUPP" here, we allow the ELM to (possibly) |
| 876 | // carry out an incorrect init. I can't see when this can be a problem. |
| 877 | // Note : clones suck |
| 878 | rv=elm_bogusinit(dl0d, timeout); |
| 879 | if (rv == 0) { |
| 880 | dev->elmflags |= ELM_INITDONE; |
| 881 | } |
| 882 | } |
| 883 | |
| 884 | return rv? diag_iseterr(rv):0; |
| 885 | |
| 886 | } |
| 887 | |
| 888 | //elm_purge : sends ATI command and checks for a valid prompt. This is faster than ATZ. |
| 889 | //use : if the ELM received garbage before ATI, ex.: "\xFF\xFFATI\r" it will just reject |
| 890 | //the invalid command but still give a valid prompt. |
| 891 | //Return 0 only if a valid prompt was received. |
| 892 | static int elm_purge(struct diag_l0_device *dl0d) { |
| 893 | uint8_t buf[ELM_BUFSIZE] = "ATI\x0D"; |
| 894 | int rv; |
| 895 | struct elm_device *dev = dl0d->l0_int; |
| 896 | |
| 897 | if (diag_tty_write(dev->tty_int, buf, 4) != 4) { |
| 898 | fprintf(stderr, FLFMT "elm_purge : write error\n", FL); |
| 899 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 900 | } |
| 901 | rv = diag_tty_read(dev->tty_int, buf, sizeof(buf), ELM_PURGETIME); |
| 902 | if (rv < 1) { |
| 903 | return DIAG_ERR_GENERAL; |
| 904 | } |
| 905 | |
| 906 | if (buf[rv-1] != '>') { |
| 907 | DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V, |
| 908 | buf, rv, FLFMT "elm_purge: got ", FL); |
| 909 | |
| 910 | return DIAG_ERR_GENERAL; |
| 911 | } |
| 912 | return 0; |
| 913 | } |
| 914 | |
| 915 | /* |
| 916 | * Send a load of data |
| 917 | * |
| 918 | * Directly send hex-ASCII; exit without receiving response. |
| 919 | * Upper levels don't append 0x0D / 0x0A at the end, we take care of adding the required 0x0D. |
| 920 | * "AT"* commands sould not be sent with this function. |
| 921 | * Returns 0 on success |
| 922 | */ |
| 923 | |
| 924 | static int elm_send(struct diag_l0_device *dl0d, const void *data, size_t len) { |
| 925 | uint8_t buf[ELM_BUFSIZE]; |
| 926 | struct elm_device *dev = dl0d->l0_int; |
| 927 | //ssize_t xferd; |
| 928 | int rv; |
| 929 | unsigned int i; |
| 930 | |
| 931 | if (len == 0) { |
| 932 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 933 | } |
| 934 | |
| 935 | if ((dev->protocol & DIAG_L1_ISO9141) && len <= 3) { |
| 936 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 937 | } |
| 938 | |
| 939 | if ((2*len)>(ELM_BUFSIZE-1)) { |
| 940 | //too much data for buffer size |
| 941 | fprintf(stderr, FLFMT "ELM: too much data for buffer (report this bug please!)\n", FL); |
| 942 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 943 | } |
| 944 | |
| 945 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 946 | FLFMT "ELM: sending %d bytes\n", FL, (int) len); |
| 947 | |
| 948 | if ((dev->protocol & DIAG_L1_ISO9141) && memcmp(dev->atsh, data, 3)) { |
| 949 | sprintf((char *)buf, "ATSH %02X %02X %02X\x0D", |
| 950 | (unsigned int)((uint8_t *)data)[0], |
| 951 | (unsigned int)((uint8_t *)data)[1], |
| 952 | (unsigned int)((uint8_t *)data)[2]); |
| 953 | rv=elm_sendcmd(dl0d, buf, 14, 500, NULL); |
| 954 | if (rv < 0) { |
| 955 | fprintf(stderr, FLFMT "elm_send: ATSH failed\n",FL); |
| 956 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 957 | } |
| 958 | |
| 959 | // if ISO9141 protocol setting with KWP message format, |
| 960 | // adjust receive filter |
| 961 | if ((dev->atsh[0] & 0x80) && |
| 962 | (dev->atsh[2] == (unsigned int)((uint8_t *)data)[2])) { |
| 963 | // already sent ATSR for this address |
| 964 | } else if ((unsigned int)((uint8_t *)data)[0] & 0x80) { |
| 965 | sprintf((char *)buf, "ATSR %02X\x0D", |
| 966 | (unsigned int)((uint8_t *)data)[2]); |
| 967 | rv=elm_sendcmd(dl0d, buf, 8, 500, NULL); |
| 968 | if (rv < 0) { |
| 969 | fprintf(stderr, FLFMT "elm_send: ATSR failed\n",FL); |
| 970 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 971 | } |
| 972 | } |
| 973 | |
| 974 | memcpy(dev->atsh, data, 3); |
| 975 | } |
| 976 | |
| 977 | for (i=0; i<len; i++) { |
| 978 | //fill buffer with ascii-fied hex data |
| 979 | snprintf((char *) &buf[2*i], 3, "%02X", (unsigned int)((uint8_t *)data)[i] ); |
| 980 | } |
| 981 | i=2*len; |
| 982 | buf[i]=0x0D; |
| 983 | buf[i+1]=0x00; //terminate string |
| 984 | |
| 985 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, |
| 986 | FLFMT "ELM: (sending string %s)\n", FL, (char *) buf); |
| 987 | |
| 988 | if (dev->protocol & DIAG_L1_ISO9141) { |
| 989 | i -= 6; |
| 990 | rv=diag_tty_write(dev->tty_int, buf+6, i+1); // skip header |
| 991 | } else { |
| 992 | rv=diag_tty_write(dev->tty_int, buf, i+1); |
| 993 | } |
| 994 | if (rv != (int) (i+1)) { //XXX danger ! evil cast ! |
| 995 | fprintf(stderr, FLFMT "elm_send:write error\n",FL); |
| 996 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 997 | } |
| 998 | return 0; |
| 999 | } |
| 1000 | |
| 1001 | /* |
| 1002 | * Get data (blocking), returns number of bytes read, between 1 and len |
| 1003 | * ELM returns a string with format "%02X %02X %02X[...]\n" . But it's slow so we add ELM_SLOWNESS ms to the specified timeout. |
| 1004 | * We convert this received ascii string to hex before returning. |
| 1005 | * note : "len" is the number of bytes read on the OBD bus, *NOT* the number of ASCII chars received on the serial link ! |
| 1006 | * TODO decode possible error strings ! Essential because ELM can reply |
| 1007 | * "FB ERROR" (FB) is a valid hex number |
| 1008 | * "ACT ALARM" (AC) is a valid hex number |
| 1009 | * technique: |
| 1010 | * -if any hexpair doesn't decode cleanly, discard message |
| 1011 | * -just before returning any data, search for an error message match |
| 1012 | * -still missing : if caller requests 1 byte, and the error is "FB ERROR", we'll still happily return 0xFB ! |
| 1013 | * |
| 1014 | * TODO: improve "len" semantics for L0 interfaces that do framing, such as this. Currently this returns max 1 message, to |
| 1015 | * let L2 do another call to get further messages (typical case of multiple responses) |
| 1016 | */ |
| 1017 | static int elm_recv(struct diag_l0_device *dl0d, void *data, size_t len, unsigned int timeout) { |
| 1018 | int rv, xferd; |
| 1019 | struct elm_device *dev = dl0d->l0_int; |
| 1020 | uint8_t rxbuf[3*MAXRBUF +1]; //I think some hotdog code in L2/L3 calls _recv with MAXRBUF so this needs to be huge. |
| 1021 | //the +1 is to \0-terminate the buffer for elm_parse_errors() to work |
| 1022 | |
| 1023 | unsigned long t0,tf; //manual timeout control |
| 1024 | int steplen; /* bytes per read */ |
| 1025 | int wp, rp; /* write & read indexes in rxbuf; a type of FIFO */ |
| 1026 | const char *err; |
| 1027 | |
| 1028 | if ((!len) || (len > MAXRBUF)) { |
| 1029 | return diag_iseterr(DIAG_ERR_BADLEN); |
| 1030 | } |
| 1031 | |
| 1032 | t0=diag_os_getms(); |
| 1033 | tf=t0+timeout + ELM_SLOWNESS; //timeout when tf is reached |
| 1034 | |
| 1035 | steplen=2; |
| 1036 | wp=0; |
| 1037 | rp=0; |
| 1038 | xferd=0; |
| 1039 | rxbuf[0] = 0x00; //null-terminate |
| 1040 | |
| 1041 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V, |
| 1042 | FLFMT "Expecting 3*%d bytes from ELM, %u ms timeout(+400)...", |
| 1043 | FL, (int)len, timeout); |
| 1044 | |
| 1045 | while (1) { |
| 1046 | unsigned long tcur; |
| 1047 | /* technique : try to read hexpairs (2 bytes); split messages according to |
| 1048 | * spacing chars (">\r\n") |
| 1049 | */ |
| 1050 | tcur = diag_os_getms(); |
| 1051 | if (tcur >= tf) { |
| 1052 | /* timed out : */ |
| 1053 | goto pre_exit; |
| 1054 | } |
| 1055 | timeout = tf - tcur; |
| 1056 | |
| 1057 | rv = diag_tty_read(dev->tty_int, rxbuf+wp, steplen, timeout); |
| 1058 | if (rv == DIAG_ERR_TIMEOUT) { |
| 1059 | goto pre_exit; |
| 1060 | } |
| 1061 | |
| 1062 | if (rv <= 0) { |
| 1063 | fprintf(stderr, FLFMT "elm_recv error\n", FL); |
| 1064 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 1065 | } |
| 1066 | |
| 1067 | wp += rv; /* position for next tty_read */ |
| 1068 | rxbuf[wp]=0; // '\0'-terminate "string" |
| 1069 | |
| 1070 | int skipc; /* chars to skip */ |
| 1071 | |
| 1072 | skipc = strspn((char *)(&rxbuf[rp]), " "); /* skip contig spaces */ |
| 1073 | rp += skipc; |
| 1074 | /* line end ? */ |
| 1075 | skipc=strspn((char *)(&rxbuf[rp]), "\r\n>"); |
| 1076 | rp += skipc; |
| 1077 | if (skipc > 0) { |
| 1078 | /* definitely a line-end / prompt ! return data so far, if any */ |
| 1079 | if (xferd > 0) { |
| 1080 | goto pre_exit; |
| 1081 | } |
| 1082 | } |
| 1083 | |
| 1084 | if (strlen((char *)(&rxbuf[rp])) < 2) { |
| 1085 | /* probably incomplete hexpair. */ |
| 1086 | steplen=1; |
| 1087 | continue; |
| 1088 | } |
| 1089 | |
| 1090 | unsigned int rbyte; |
| 1091 | if (sscanf((char *)(&rxbuf[rp]), "%02X", &rbyte) == 1) { |
| 1092 | /* good hexpair */ |
| 1093 | ((uint8_t *)data)[xferd]=(uint8_t) rbyte; |
| 1094 | xferd++; |
| 1095 | if ( (size_t)xferd==len) { |
| 1096 | goto pre_exit; |
| 1097 | } |
| 1098 | } else { |
| 1099 | /* didn't scanf properly... error message ? ex. "NO DATA\r>" |
| 1100 | * NO DATA is pretty inoffensive; skip printing that one */ |
| 1101 | if (!(rxbuf[rp] == 'N' && rxbuf[rp+1] =='O')) { |
| 1102 | fprintf(stderr, FLFMT "ELM sscanf failed to eat '%s'\n", FL, rxbuf); |
| 1103 | } |
| 1104 | /* finish pulling the error message or whatever garbage */ |
| 1105 | rv = diag_tty_read(dev->tty_int, rxbuf+wp, sizeof(rxbuf) - wp, ELM_SLOWNESS); |
| 1106 | if (rv >= 0) { |
| 1107 | rxbuf[wp + rv] = 0x00; |
| 1108 | } |
| 1109 | xferd = DIAG_ERR_GENERAL; |
| 1110 | goto pre_exit; |
| 1111 | } |
| 1112 | /* here, we just sscanf'd 2 bytes, so we read 1 more. */ |
| 1113 | /* we can't read 2 more, in case we're in a multi-message, for instance if we just decoded 0x00 in |
| 1114 | * "48 6A 01 00\n48 6A..." , reading two bytes "\n4" would corrupt the next message ! |
| 1115 | */ |
| 1116 | rp += 2; |
| 1117 | steplen=1; |
| 1118 | } // while (1) |
| 1119 | |
| 1120 | pre_exit: |
| 1121 | err = elm_parse_errors(dl0d, rxbuf); |
| 1122 | if (err) { |
| 1123 | if (strcmp(err, "NO DATA") == 0) { |
| 1124 | return DIAG_ERR_TIMEOUT; |
| 1125 | } |
| 1126 | fprintf(stderr, FLFMT "ELM error %s\n", FL, err); |
| 1127 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 1128 | } |
| 1129 | return (xferd>0)? xferd:DIAG_ERR_TIMEOUT; |
| 1130 | } |
| 1131 | |
| 1132 | |
| 1133 | /* set new wakeup message, if possible */ |
| 1134 | static int elm_setwm(struct diag_l0_device *dl0d, struct diag_msg *pmsg) { |
| 1135 | struct elm_device *dev; |
| 1136 | |
| 1137 | dev = (struct elm_device *)dl0d->l0_int; |
| 1138 | |
| 1139 | if (dev->elmflags & ELM_INITDONE) { |
| 1140 | /* |
| 1141 | * It's possible to change the wakeup message after init, |
| 1142 | * but we don't currently implement this. |
| 1143 | */ |
| 1144 | fprintf(stderr, FLFMT "elm_setwm: tried to set wakeup message after init\n", FL); |
| 1145 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 1146 | } |
| 1147 | |
| 1148 | if (dev->elmflags & ELM_323_BASIC) { |
| 1149 | fprintf(stderr, FLFMT "elm_setwm: ELM323 doesn't support setting wakeup message\n", FL); |
| 1150 | return diag_iseterr(DIAG_ERR_IOCTL_NOTSUPP); |
| 1151 | } |
| 1152 | |
| 1153 | if (pmsg->len != 4) { |
| 1154 | fprintf(stderr, FLFMT "elm_setwm: invalid message length %u\n", FL, pmsg->len); |
| 1155 | return diag_iseterr(DIAG_ERR_GENERAL); |
| 1156 | } |
| 1157 | |
| 1158 | if (dev->wm != NULL) { |
| 1159 | diag_freemsg(dev->wm); |
| 1160 | } |
| 1161 | dev->wm = diag_dupsinglemsg(pmsg); |
| 1162 | if (dev->wm == NULL) { |
| 1163 | return diag_iseterr(DIAG_ERR_NOMEM); |
| 1164 | } |
| 1165 | return 0; |
| 1166 | } |
| 1167 | |
| 1168 | |
| 1169 | static uint32_t elm_getflags(struct diag_l0_device *dl0d) { |
| 1170 | |
| 1171 | struct elm_device *dev; |
| 1172 | uint32_t flags=0; |
| 1173 | |
| 1174 | dev = (struct elm_device *)dl0d->l0_int; |
| 1175 | |
| 1176 | flags = DIAG_L1_DATAONLY | DIAG_L1_AUTOSPEED | DIAG_L1_DOESP4WAIT | |
| 1177 | DIAG_L1_DOESL2FRAME | DIAG_L1_DOESL2CKSUM | DIAG_L1_DOESFULLINIT | DIAG_L1_DOESKEEPALIVE; |
| 1178 | |
| 1179 | switch (dev->protocol) { |
| 1180 | case DIAG_L1_ISO9141: |
| 1181 | flags |= DIAG_L1_SLOW; |
| 1182 | flags &= ~DIAG_L1_DATAONLY; |
| 1183 | //flags |= DIAG_L1_NOHDRS; //probably not needed since we send ATH1 on init (enable headers) |
| 1184 | break; |
| 1185 | case DIAG_L1_ISO14230: |
| 1186 | flags |= DIAG_L1_SLOW | DIAG_L1_FAST | DIAG_L1_PREFFAST; |
| 1187 | //flags |= DIAG_L1_NOHDRS; |
| 1188 | break; |
| 1189 | default: |
| 1190 | break; |
| 1191 | } |
| 1192 | |
| 1193 | DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V, |
| 1194 | FLFMT "getflags link %p proto %d flags 0x%X\n", |
| 1195 | FL, (void *)dl0d, dev->protocol, flags); |
| 1196 | |
| 1197 | return flags; |
| 1198 | |
| 1199 | } |
| 1200 | |
| 1201 | //elm_parse_cr : change 0x0A to 0x0D in datastream. |
| 1202 | static void elm_parse_cr(uint8_t *data, int len) { |
| 1203 | int i=0; |
| 1204 | for (; i<len; i++) { |
| 1205 | if (*data == 0x0D) { |
| 1206 | *data = 0x0A; |
| 1207 | } |
| 1208 | data++; |
| 1209 | } |
| 1210 | return; |
| 1211 | } |
| 1212 | |
| 1213 | |
| 1214 | static int elm_ioctl(struct diag_l0_device *dl0d, unsigned cmd, void *data) { |
| 1215 | int rv = 0; |
| 1216 | |
| 1217 | switch (cmd) { |
| 1218 | case DIAG_IOCTL_IFLUSH: |
| 1219 | //do nothing |
| 1220 | rv = 0; |
| 1221 | break; |
| 1222 | case DIAG_IOCTL_INITBUS: |
| 1223 | rv = elm_initbus(dl0d, (struct diag_l1_initbus_args *)data); |
| 1224 | break; |
| 1225 | case DIAG_IOCTL_SETWM: |
| 1226 | rv = elm_setwm(dl0d, (struct diag_msg *)data); |
| 1227 | break; |
| 1228 | default: |
| 1229 | rv = DIAG_ERR_IOCTL_NOTSUPP; |
| 1230 | break; |
| 1231 | } |
| 1232 | |
| 1233 | return rv; |
| 1234 | } |
| 1235 | |
| 1236 | |
| 1237 | const struct diag_l0 diag_l0_elm = { |
| 1238 | "Scantool.net ELM32x Chipset Device", |
| 1239 | "ELM", |
| 1240 | DIAG_L1_ISO9141 | DIAG_L1_ISO14230 | DIAG_L1_J1850_PWM | DIAG_L1_J1850_VPW | DIAG_L1_CAN, |
| 1241 | elm_init, |
| 1242 | elm_new, |
| 1243 | elm_getcfg, |
| 1244 | elm_del, |
| 1245 | elm_open, |
| 1246 | elm_close, |
| 1247 | elm_getflags, |
| 1248 | elm_recv, |
| 1249 | elm_send, |
| 1250 | elm_ioctl |
| 1251 | }; |
| 1252 | |