/* * Safe write routine; return 0 on success */ static int diag_l0_muleng_write(struct diag_l0_device *dl0d, const void *dp, size_t txlen) { if (txlen <=0) return diag_iseterr(DIAG_ERR_BADLEN); if ( (diag_l0_debug & (DIAG_DEBUG_WRITE|DIAG_DEBUG_DATA)) == (DIAG_DEBUG_WRITE|DIAG_DEBUG_DATA) ) { fprintf(stderr, FLFMT "device link %p sending to ME device: ", FL, (void *)dl0d); diag_data_dump(stderr, dp, txlen); fprintf(stderr, "\n"); } /* * And send it to the interface */ if (diag_tty_write(dl0d, dp, txlen) != (int) txlen) { fprintf(stderr, FLFMT "muleng_write error!!\n", FL); return diag_iseterr(DIAG_ERR_GENERAL); } return 0; }
static int diag_l0_br_write(struct diag_l0_device *dl0d, const void *dp, size_t txlen) { ssize_t xferd; while ((size_t)(xferd = diag_tty_write(dl0d, dp, txlen)) != txlen) { if (xferd < 0) { /* error */ if (errno != EINTR) { fprintf(stderr, FLFMT "write returned error %s.\n", FL, strerror(errno)); return diag_iseterr(DIAG_ERR_GENERAL); } xferd = 0; /* Interrupted read, nothing transferred. */ errno = 0; } /* * Successfully wrote xferd bytes (or 0 bytes if EINTR), * so increment the pointers and continue */ txlen -= (size_t) xferd; dp = (const void *)((const char *)dp + xferd); } return 0; }
static int diag_l0_br_write(struct diag_l0_device *dl0d, const void *dp, size_t txlen) { if (txlen <=0) return diag_iseterr(DIAG_ERR_BADLEN); if (diag_tty_write(dl0d, dp, txlen) != (int) txlen) { fprintf(stderr, FLFMT "br_write error\n", FL); return diag_iseterr(DIAG_ERR_GENERAL); } return 0; }
static int diag_l0_elm_send(struct diag_l0_device *dl0d, UNUSED(const char *subinterface), const void *data, size_t len) { uint8_t buf[ELM_BUFSIZE]; //ssize_t xferd; int rv; unsigned int i; if (len <= 0) return diag_iseterr(DIAG_ERR_BADLEN); if ((2*len)>(ELM_BUFSIZE-1)) { //too much data for buffer size fprintf(stderr, FLFMT "ELM: too much data for buffer (report this bug please!)\n", FL); return diag_iseterr(DIAG_ERR_BADLEN); } if (diag_l0_debug & DIAG_DEBUG_WRITE) { fprintf(stderr, FLFMT "ELM: sending %d bytes\n", FL, (int) len); } for (i=0; i<len; i++) { //fill buffer with ascii-fied hex data snprintf((char *) &buf[2*i], 3, "%02X", (unsigned int)((uint8_t *)data)[i] ); } i=2*len; buf[i]=0x0D; buf[i+1]=0x00; //terminate string if ((diag_l0_debug & DIAG_DEBUG_WRITE) && (diag_l0_debug & DIAG_DEBUG_DATA)) { fprintf(stderr, FLFMT "ELM: (sending string %s)\n", FL, (char *) buf); } rv=diag_tty_write(dl0d, buf, i+1); if (rv != (int) (i+1)) { //XXX danger ! evil cast ! fprintf(stderr, FLFMT "elm_send:write error\n",FL); return diag_iseterr(DIAG_ERR_GENERAL); } return 0; }
//elm_purge : sends ATI command and checks for a valid prompt. This is faster than ATZ. //use : if the ELM received garbage before ATI, ex.: "\xFF\xFFATI\r" it will just reject //the invalid command but still give a valid prompt. //Return 0 only if a valid prompt was received. static int elm_purge(struct diag_l0_device *dl0d) { uint8_t buf[ELM_BUFSIZE] = "ATI\x0D"; int rv; if (diag_tty_write(dl0d, buf, 4) != 4) { fprintf(stderr, FLFMT "elm_purge : write error\n", FL); return diag_iseterr(DIAG_ERR_GENERAL); } rv = diag_tty_read(dl0d, buf, sizeof(buf), ELM_PURGETIME); if (rv < 1) { return DIAG_ERR_GENERAL; } if (buf[rv-1] != '>') { if (diag_l0_debug & DIAG_DEBUG_DATA) { fprintf(stderr, FLFMT "elm_purge: got ", FL); diag_data_dump(stderr, buf, rv); fprintf(stderr, "\n"); } return DIAG_ERR_GENERAL; } return 0; }
//Send a command to ELM device and make sure no error occured. Data is passed on directly as a string; //caller must make sure the string is \r-terminated , i.e. 0x0D-terminated. //Sending 0A after 0D will cause ELM to interrupt what it's doing to "process" 0A, resulting in a //failure. //This func should not be used for commands that elicit a data response (i.e. all data destined to the OBD bus, //hence not prefixed by "AT"). Response is dumped in *resp (0-terminated) for optional analysis by caller; *resp must be ELM_BUFSIZE long. //returns 0 if (prompt_good) && ("OK" found anywhere in the response) && (no known error message was present) || // (prompt_good && ATZ command was sent) , since response doesn't contain "OK" for ATZ. //elm_sendcmd should not be called from outside diag_l0_elm.c. static int elm_sendcmd(struct diag_l0_device *dl0d, const uint8_t *data, size_t len, unsigned int timeout, uint8_t *resp) { //note : we better not request (len == (size_t) -1) bytes ! The casts between ssize_t and size_t are // "muddy" in here //ssize_t xferd; int rv; uint8_t ebuf[ELM_BUFSIZE]; //local buffer if caller provides none. uint8_t *buf; struct diag_l0_elm_device *dev; const char *err_str; //hold a possible error message dev = (struct diag_l0_elm_device *)dl0d->l0_int; //we need access to diag_l0_elm_device to access .elmflags if (resp==NULL) buf=ebuf; //use local buffer else buf=resp; //or caller-provided buffer if (!len) return diag_iseterr(DIAG_ERR_BADLEN); if (!dev) return diag_iseterr(DIAG_ERR_BADFD); if (data[len-1] != 0x0D) { //Last byte is not a carriage return, this would die. fprintf(stderr, FLFMT "elm_sendcmd: non-terminated command : %.*s\n", FL, (int) len, (char *) data); //the %.*s is pure magic : limits the string length to len, even if the string is not null-terminated. return diag_iseterr(DIAG_ERR_GENERAL); } diag_tty_iflush(dl0d); //currently the code often "forgets" data in the input buffer, especially if the previous //transaction failed. Flushing the input increases the odds of not crashing soon if (diag_l0_debug & DIAG_DEBUG_WRITE) { fprintf(stderr, FLFMT "elm_sendcmd: %.*s\n", FL, (int) len-1, (char *)data); } rv = diag_tty_write(dl0d, data, len); if (rv != (int) len) { //XXX danger ! evil cast fprintf(stderr, FLFMT "elm_sendcmd: write error\n", FL); return diag_iseterr(DIAG_ERR_GENERAL); } //next, receive ELM response, within {ms} delay. rv=diag_tty_read(dl0d, buf, ELM_BUFSIZE-1, timeout); //rv=# bytes read if (rv<1) { //no data or error if (diag_l0_debug & DIAG_DEBUG_WRITE) { fprintf(stderr, FLFMT "ELM did not respond\n", FL); } return diag_iseterr(DIAG_ERR_GENERAL); } if (diag_l0_debug & DIAG_DEBUG_READ) { elm_parse_cr(buf, rv); //debug output is prettier with this fprintf(stderr, FLFMT "received %d bytes (%.*s\n); hex: ", FL, rv, rv, (char *)buf); if (diag_l0_debug & DIAG_DEBUG_DATA) { diag_data_dump(stderr, buf, rv); fprintf(stderr, "\n"); } } buf[rv]=0; //terminate string if (buf[rv-1] != '>') { //if last character isn't the input prompt, there is a problem fprintf(stderr, FLFMT "ELM not ready (no prompt received): %s\nhex: ", FL, buf); diag_data_dump(stderr, buf, rv); fprintf(stderr, "\n"); return diag_iseterr(DIAG_ERR_GENERAL); } //At this point we got a prompt but there may have been an error message. //There is some ambiguity in the ELM datasheets on the exact format of the replies. //1) the prompt character '>' is always alone on its line; //2) depending on some parameters, it may be preceded by 0D or 0D 0A //3) some errors ( "<DATA ERROR" and "<RX ERROR" ) can be appended after //a reply; others should be at the beginning of the response (alone on a line) //ex. of good reply : "41 00 00 \r>", bad reply :"NO DATA\r>" //let's just use strstr() to find occurences for each known error. //it'll take a while but speed isn't normally critical when sending commands err_str = elm_parse_errors(dl0d, buf); if (err_str != NULL) { fprintf(stderr, FLFMT "ELM returned error : %s\n", FL, err_str); return diag_iseterr(DIAG_ERR_GENERAL); } //check if we either 1)got a positive response "OK" //2)were sending ATZ (special case hack, it doesn't answer "OK") if ((strstr((char *)buf, "OK") != NULL) || (strstr((char *)data, "ATZ") != NULL)) { return 0; } fprintf(stderr, FLFMT "Response not recognized ! Report this ! Got: \n", FL); diag_data_dump(stderr, buf, rv); fprintf(stderr, "\n"); return diag_iseterr(DIAG_ERR_GENERAL); }
/* * diag_tty_break */ int diag_tty_break(struct diag_l0_device *dl0d, const int ms) { char cbuf; struct timeval tv; int xferd; struct diag_serial_settings set; /* * We need to send an 25ms (+/- 1ms) break * and then wait 25ms. We must have waited Tidle (300ms) first * * The trouble here is Linux doesn't let us be that accurate sending * a break - so we do it by sending a '0x00' at a baud rate that means * the output is low for 25ms, then we wait for 25ms, using busy * waits (which we get from being in real-time mode and doing nanosleeps * of less than 2ms each) * */ /* Set baud rate etc to 360 baud, 8, N, 1 */ set.speed = 360; set.databits = diag_databits_8; set.stopbits = diag_stopbits_1; set.parflag = diag_par_n; diag_tty_setup(dl0d, &set); gettimeofday(&tv,NULL); /* Send a 0x00 byte message */ diag_tty_write(dl0d, "", 1); if (diag_l0_debug & DIAG_DEBUG_TIMER) { fprintf(stderr, FLFMT "%04ld.%03ld : break start\n", FL, tv.tv_sec, tv.tv_usec); } /* * And read back the single byte echo, which shows TX completes */ while ( (xferd = diag_tty_read(dl0d, &cbuf, 1, 1000)) <= 0) { if (xferd == DIAG_ERR_TIMEOUT) return diag_iseterr(DIAG_ERR_TIMEOUT); if (xferd == 0) { /* Error, EOF */ fprintf(stderr, FLFMT "read returned EOF.\n", FL); return diag_iseterr(DIAG_ERR_GENERAL); } if (errno != EINTR) { /* Error, EOF */ fprintf(stderr, FLFMT "read returned error %s.\n", FL, strerror(errno)); return diag_iseterr(DIAG_ERR_GENERAL); } } /* Now wait the requested number of ms */ gettimeofday(&tv,NULL); diag_os_millisleep(ms); if (diag_l0_debug & DIAG_DEBUG_TIMER) { fprintf(stderr, FLFMT "%04ld.%03ld : end of break_L\n", FL, tv.tv_sec, tv.tv_usec); } return 0; }