BOROSOFTWAREAll work →
scantool/scantool_850.c 1636 lines
2 findings in this fileD22L666D23L755
1/*
2 * freediag - Vehicle Diagnostic Utility
3 *
4 *
5 * Copyright (C) 2017, 2023 Adam Goldman
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 - 850 subcommand
27 *
28 * Extended diagnostics for '96-'98 Volvo 850, S40, C70, S70, V70, XC70 and V90
29 *
30 */
31
32#include <stdbool.h>
33#include <stdio.h>
34#include <stdint.h>
35#include <stdlib.h>
36#include <string.h>
37#include <ctype.h>
38#include <errno.h>
39#include <stdarg.h>
40
41#include "diag.h"
42#include "diag_l1.h"
43#include "diag_l2.h"
44#include "diag_l7.h"
45#include "diag_err.h"
46#include "diag_os.h"
47
48#include "libcli.h"
49
50#include "diag_l7_d2.h"
51#include "diag_l7_kwp71.h"
52#include "scantool.h"
53#include "scantool_cli.h"
54
55#include "scantool_850/dtc.h"
56#include "scantool_850/ecu.h"
57
58static bool have_read_dtcs = false;
59static struct diag_msg *ecu_id = NULL;
60
61static bool live_display_running = false;
62static int live_data_lines;
63
64static enum cli_retval cmd_850_help(int argc, char **argv);
65static enum cli_retval cmd_850_connect(int argc, char **argv);
66static enum cli_retval cmd_850_disconnect(int argc, UNUSED(char **argv));
67static enum cli_retval cmd_850_ping(int argc, UNUSED(char **argv));
68static enum cli_retval cmd_850_sendreq(int argc, char **argv);
69static enum cli_retval cmd_850_peek(int argc, char **argv);
70static enum cli_retval cmd_850_dumpram(int argc, char **argv);
71static enum cli_retval cmd_850_read(int argc, char **argv);
72static enum cli_retval cmd_850_readnv(int argc, char **argv);
73static enum cli_retval cmd_850_adc(int argc, char **argv);
74static enum cli_retval cmd_850_id(int argc, UNUSED(char **argv));
75static enum cli_retval cmd_850_dtc(int argc, UNUSED(char **argv));
76static enum cli_retval cmd_850_cleardtc(int argc, UNUSED(char **argv));
77static enum cli_retval cmd_850_freeze(int argc, char **argv);
78static enum cli_retval cmd_850_resetsrl(int argc, UNUSED(char **argv));
79static enum cli_retval cmd_850_scan_all(int argc, UNUSED(char **argv));
80static enum cli_retval cmd_850_test(int argc, char **argv);
81
82const struct cmd_tbl_entry v850_cmd_table[] = {
83 { "help", "help [command]", "Gives help for a command",
84 cmd_850_help, 0, NULL},
85 { "?", "? [command]", "Gives help for a command",
86 cmd_850_help, 0, NULL},
87
88 { "connect", "connect <ecuname>", "Connect to ECU. Use '850 connect ?' to show ECU names.",
89 cmd_850_connect, 0, NULL},
90 { "disconnect", "disconnect", "Disconnect from ECU",
91 cmd_850_disconnect, 0, NULL},
92 { "scan-all", "scan-all", "Try connecting to all possible ECUs, print identification and DTCs",
93 cmd_850_scan_all, 0, NULL},
94 { "sendreq", "sendreq <byte0 [byte1 ...]>", "Send raw data to the ECU and print response",
95 cmd_850_sendreq, 0, NULL},
96 { "ping", "ping", "Verify communication with the ECU", cmd_850_ping,
97 0, NULL},
98 { "peek", "peek <addr1>[w|l][.addr2] [addr2 ...] [live|stream]", "Display contents of RAM, once or continuously",
99 cmd_850_peek, 0, NULL},
100 { "dumpram", "dumpram <filename> [fast]", "Dump entire RAM contents to file (Warning: takes 20+ minutes)",
101 cmd_850_dumpram, 0, NULL},
102 { "read", "read <id1>|*<addr1> [id2 ...] [live|stream]", "Display live data, once or continuously",
103 cmd_850_read, 0, NULL},
104 { "adc", "adc id1 [id2 ...]", "Display ADC readings, once or continuously",
105 cmd_850_adc, 0, NULL},
106 { "readnv", "readnv id1 [id2 ...]", "Display non-volatile data",
107 cmd_850_readnv, 0, NULL},
108 { "id", "id", "Display ECU identification",
109 cmd_850_id, 0, NULL},
110 { "dtc", "dtc [tips]", "Retrieve DTCs",
111 cmd_850_dtc, 0, NULL},
112 { "cleardtc", "cleardtc", "Clear DTCs from ECU",
113 cmd_850_cleardtc, 0, NULL},
114 { "freeze", "freeze dtc1|all [dtc2 ...]", "Display freeze frame(s)",
115 cmd_850_freeze, 0, NULL},
116 { "resetsrl", "resetsrl", "Reset the Service Reminder Light",
117 cmd_850_resetsrl, 0, NULL},
118 { "test", "test <testname>", "Test vehicle components",
119 cmd_850_test, 0, NULL},
120
121 CLI_TBL_BUILTINS,
122 CLI_TBL_END
123};
124
125static enum cli_retval cmd_850_help(int argc, char **argv) {
126 return cli_help_basic(argc, argv, v850_cmd_table);
127}
128
129/*
130 * Wrapper around printf. When live data display is running, increments the
131 * line count and clears old text remaining on the line we just printed.
132 * Appends a newline to the output.
133 */
134static int printf_livedata(const char *format, ...) {
135 va_list ap;
136 int rv;
137
138 va_start(ap, format);
139 rv = vprintf(format, ap);
140 va_end(ap);
141
142 if (live_display_running) {
143 live_data_lines++;
144 diag_os_clrtoeol();
145 }
146
147 putchar('\n');
148 return rv;
149}
150
151/*
152 * Capitalize the first letter of the supplied string.
153 * Returns a static buffer that will be reused on the next call.
154 */
155static char *capitalize(const char *in) {
156 static char buf[80];
157
158 strncpy(buf, in, sizeof(buf));
159 buf[sizeof(buf)-1] = '\0';
160
161 if (isalpha(buf[0]) && islower(buf[0])) {
162 buf[0] = toupper(buf[0]);
163 }
164 return buf;
165}
166
167
168/*
169 * Get an ECU's address by name.
170 */
171static int ecu_addr_by_name(const char *name) {
172 const struct ecu_info *ecu;
173 unsigned long int i;
174 char *p;
175
176 if (isdigit(name[0])) {
177 i = strtoul(name, &p, 0);
178 if (*p != '\0') {
179 return -1;
180 }
181 if (i > 0x7f) {
182 return -1;
183 }
184 return i;
185 }
186
187 ecu = ecu_info_by_name(name);
188 if (ecu == NULL) {
189 return -1;
190 }
191 return ecu->addr;
192}
193
194/*
195 * Get an ECU's description by address.
196 */
197static const char *ecu_desc_by_addr(uint8_t addr) {
198 const struct ecu_info *ecu;
199 static char buf[7];
200
201 ecu = ecu_info_by_addr(addr);
202 if (ecu) {
203 return ecu->desc;
204 }
205
206 sprintf(buf, "ECU %02X", addr);
207 return buf;
208}
209
210/*
211 * Get the description of the currently connected ECU.
212 */
213static const char *current_ecu_desc(void) {
214 uint8_t addr;
215
216 if (global_state < STATE_CONNECTED) {
217 return "???";
218 }
219
220 addr = global_l2_conn->diag_l2_destaddr;
221
222 if (addr > 0x7f) {
223 return "???";
224 }
225
226 return ecu_desc_by_addr(addr);
227}
228
229/*
230 * Get the printable designation (EFI-xxx, AT-xxx, etc) for a DTC by its raw
231 * byte value. Optionally, also get a description of the DTC.
232 * Returns a static buffer that will be reused on the next call.
233 */
234static char *dtc_printable_by_raw(uint8_t addr, uint8_t raw, const char **desc, const char **tips) {
235#define PRINTABLE_LEN 8 //including 0-termination
236 static char printable[PRINTABLE_LEN];
237 static char *empty="";
238 const struct ecu_info *ecu_entry;
239 const struct dtc_table_entry *dtc_entry;
240 const struct dtc_table_entry *dtc_map;
241 const char *prefix;
242 int16_t suffix;
243
244 prefix = "???";
245 if (tips != NULL) {
246 *tips = NULL;
247 }
248 ecu_entry = ecu_info_by_addr(addr);
249 if (ecu_entry) {
250 prefix = ecu_entry->dtc_prefix;
251 }
252 dtc_map = dtctable_by_addr(addr);
253 if (dtc_map) {
254 for (dtc_entry = dtc_map; dtc_entry->dtc_suffix != 0; dtc_entry++) {
255 if (dtc_entry->raw_value == raw) {
256 suffix = dtc_entry->dtc_suffix;
257 if (desc != NULL) {
258 *desc = dtc_entry->desc;
259 }
260 if (tips != NULL) {
261 *tips = dtc_entry->tips;
262 }
263 if (suffix > 999) {
264 suffix = 999;
265 }
266 if (suffix >= 0) {
267 snprintf(printable, PRINTABLE_LEN, "%s-%03d", prefix, suffix);
268 } else {
269 snprintf(printable, PRINTABLE_LEN, "%s-???", prefix);
270 }
271 return printable;
272 }
273 }
274 }
275
276 if (desc != NULL) {
277 *desc = empty;
278 }
279 snprintf(printable, PRINTABLE_LEN, "%s-???", prefix);
280 return printable;
281}
282
283/*
284 * Get the DTC prefix for the currently connected ECU.
285 */
286static const char *current_dtc_prefix(void) {
287 const struct ecu_info *ecu;
288
289 if (global_state < STATE_CONNECTED) {
290 return "???";
291 }
292
293 ecu = ecu_info_by_addr(global_l2_conn->diag_l2_destaddr);
294 if (ecu) {
295 return ecu->dtc_prefix;
296 }
297
298 return "???";
299}
300
301/*
302 * Get a DTC byte value by the printable designation. Returns 0xffff on
303 * failure. Where multiple matching DTCs exist, returns the first.
304 */
305static uint16_t dtc_raw_by_printable(const char *printable) {
306 char prefix[8];
307 uint16_t suffix;
308 char *p, *q, *r;
309 const struct dtc_table_entry *dtc_entry;
310 const struct dtc_table_entry *dtc_map;
311
312 /* extract prefix and suffix from string */
313 if (strlen(printable) > sizeof(prefix) - 1) {
314 return 0xffff; /* implausably long string */
315 }
316 strcpy(prefix, printable);
317 p = prefix;
318 while (isalpha(*p)) {
319 p++;
320 }
321 q = p;
322 if (*q == '-') {
323 q++;
324 }
325 suffix = strtoul(q, &r, 10);
326 if (*q == '\0' || *r != '\0') {
327 return 0xffff; /* no valid numeric suffix */
328 }
329 *p = '\0';
330
331 /* check prefix */
332 if (strcasecmp(prefix, current_dtc_prefix()) != 0) {
333 return 0xffff; /* doesn't match connected ecu prefix */
334 }
335
336 /* find suffix */
337 dtc_map = dtctable_by_addr(global_l2_conn->diag_l2_destaddr);
338 if (dtc_map == NULL) {
339 return 0xffff;
340 }
341 for (dtc_entry = dtc_map; dtc_entry->dtc_suffix != 0; dtc_entry++) {
342 if (dtc_entry->dtc_suffix == suffix) {
343 return dtc_entry->raw_value;
344 }
345 }
346 return 0xffff; /* suffix not found */
347}
348
349/*
350 * Print a list of known ECUs. Not all ECUs in this list are necessarily
351 * present in the vehicle.
352 */
353static void print_ecu_list(void) {
354 const struct ecu_info *ecu;
355
356 for (ecu = ecu_list; ecu->name != NULL; ecu++) {
357 printf(" %s\t%s\n", ecu->name, capitalize(ecu->desc));
358 }
359}
360
361enum connection_status {
362 NOT_CONNECTED, /* Not connected */
363 CONNECTED_D2, /* Connected with D2 over K-line */
364 CONNECTED_KWP71, /* Connected with KWP71 */
365 CONNECTED_EITHER, /* Connected with either D2 or KWP71 */
366 CONNECTED_OTHER /* Connected with non-Volvo protocol */
367};
368
369/*
370 * Indicates whether we're currently connected.
371 */
372static enum connection_status get_connection_status(void) {
373 if (global_state < STATE_CONNECTED) {
374 return NOT_CONNECTED;
375 }
376 if (global_l2_conn->l2proto->diag_l2_protocol == DIAG_L2_PROT_D2) {
377 return CONNECTED_D2;
378 }
379 if (global_l2_conn->l2proto->diag_l2_protocol == DIAG_L2_PROT_VAG) {
380 return CONNECTED_KWP71;
381 }
382 return CONNECTED_OTHER;
383}
384
385/*
386 * Check whether the number of arguments to a command is between the specified
387 * minimum and maximum. If not, print a message and return false.
388 */
389static bool valid_arg_count(int min, int argc, int max) {
390 if (argc < min) {
391 printf("Too few arguments\n");
392 return false;
393 }
394
395 if (argc > max) {
396 printf("Too many arguments\n");
397 return false;
398 }
399
400 return true;
401}
402
403/*
404 * Check whether the connection status matches the required connection status
405 * for this command. If not, print a message and return false.
406 */
407static bool valid_connection_status(unsigned int want) {
408 if (want == CONNECTED_EITHER) {
409 if (get_connection_status() == CONNECTED_D2 ||
410 get_connection_status() == CONNECTED_KWP71) {
411 return true;
412 }
413 } else if (get_connection_status() == want) {
414 return true;
415 }
416
417 switch (get_connection_status()) {
418 case NOT_CONNECTED:
419 printf("Not connected.\n");
420 return false;
421 case CONNECTED_OTHER:
422 if (want == NOT_CONNECTED) {
423 printf("Already connected with non-Volvo protocol. Please use 'diag disconnect'.\n");
424 } else {
425 printf("Connected with non-Volvo protocol.\n");
426 }
427 return false;
428 case CONNECTED_D2:
429 case CONNECTED_KWP71:
430 if (want == NOT_CONNECTED) {
431 printf("Already connected to %s. Please disconnect first.\n", current_ecu_desc());
432 } else {
433 printf("This function is not available with this protocol.\n");
434 }
435 return false;
436 default:
437 printf("Unexpected connection state!\n");
438 return false;
439 }
440}
441
442/*
443 * Send 3 pings with a delay between them to get the ELM used to the ECU's
444 * response time.
445 */
446static void adaptive_timing_workaround(void) {
447 int i;
448
449 for (i=0; i<3; i++) {
450 (void)diag_l7_d2_ping(global_l2_conn);
451 diag_os_millisleep(200);
452 }
453}
454
455/*
456 * Callback to store the ID block upon establishing a KWP71 connection
457 */
458static void ecu_id_callback(void *handle, struct diag_msg *in) {
459 struct diag_msg **out = (struct diag_msg **)handle;
460 *out = diag_dupmsg(in);
461}
462
463/*
464 * Connect to an ECU by name or address.
465 */
466static enum cli_retval cmd_850_connect(int argc, char **argv) {
467 int addr;
468 int rv;
469 struct diag_l0_device *dl0d;
470 struct diag_l2_data l2data;
471
472 if (!valid_arg_count(2, argc, 2)) {
473 return CMD_USAGE;
474 }
475
476 if (strcmp(argv[1], "?") == 0) {
477 printf("Known ECUs are:\n");
478 print_ecu_list();
479 printf("Can also specify target by numeric address.\n");
480 return CMD_USAGE;
481 }
482
483 if (!valid_connection_status(NOT_CONNECTED)) {
484 return CMD_OK;
485 }
486
487 addr = ecu_addr_by_name(argv[1]);
488 if (addr < 0) {
489 printf("Unknown ECU '%s'\n", argv[1]);
490 return CMD_OK;
491 }
492
493 dl0d = global_dl0d;
494
495 if (dl0d == NULL) {
496 printf("No global L0. Please select + configure L0 first\n");
497 return diag_iseterr(DIAG_ERR_GENERAL);
498 }
499
500 if (addr == 0x10) {
501 global_cfg.speed = 9600;
502 global_cfg.tgt = addr;
503 global_cfg.L1proto = DIAG_L1_ISO9141;
504 global_cfg.L2proto = DIAG_L2_PROT_VAG;
505 global_cfg.initmode = DIAG_L2_TYPE_SLOWINIT;
506 } else {
507 global_cfg.speed = 10400;
508 global_cfg.src = 0x13;
509 global_cfg.tgt = addr;
510 global_cfg.L1proto = DIAG_L1_ISO9141;
511 global_cfg.L2proto = DIAG_L2_PROT_D2;
512 global_cfg.initmode = DIAG_L2_TYPE_SLOWINIT;
513 }
514
515 rv = diag_l2_open(dl0d, global_cfg.L1proto);
516 if (rv) {
517 fprintf(stderr, "cmd_850_connect: diag_l2_open failed\n");
518 return diag_ifwderr(rv);
519 }
520
521 global_l2_conn = diag_l2_StartCommunications(dl0d, global_cfg.L2proto,
522 global_cfg.initmode & DIAG_L2_TYPE_INITMASK, global_cfg.speed,
523 global_cfg.tgt, global_cfg.src);
524 if (global_l2_conn == NULL) {
525 rv = diag_geterr();
526 diag_l2_close(dl0d);
527 return diag_iseterr(rv);
528 }
529
530 if (global_cfg.L2proto == DIAG_L2_PROT_VAG) {
531 (void)diag_l2_ioctl(global_l2_conn, DIAG_IOCTL_GET_L2_DATA, (void *)&l2data);
532 if (l2data.kb1!=0xab || l2data.kb2!=0x02) {
533 fprintf(stderr, FLFMT "_connect : wrong keybytes %02X%02X, expecting AB02\n", FL, l2data.kb1, l2data.kb2);
534 diag_l2_StopCommunications(global_l2_conn);
535 diag_l2_close(dl0d);
536 global_l2_conn = NULL;
537 global_state = STATE_IDLE;
538 return diag_iseterr(DIAG_ERR_WRONGKB);
539 }
540 }
541
542 global_state = STATE_CONNECTED;
543 printf("Connected to %s.\n", ecu_desc_by_addr(addr));
544 have_read_dtcs = false;
545
546 if (get_connection_status() == CONNECTED_D2) {
547 adaptive_timing_workaround();
548 } else {
549 printf("Warning: KWP71 communication is not entirely reliable yet.\n");
550 /*
551 * M4.4 doesn't accept ReadECUIdentification request, so save
552 * the identification block it sends at initial connection.
553 */
554 if (ecu_id != NULL) {
555 diag_freemsg(ecu_id);
556 }
557 ecu_id = NULL;
558 rv = diag_l2_recv(global_l2_conn, 300, ecu_id_callback, &ecu_id);
559 if (rv < 0) {
560 return diag_ifwderr(rv);
561 }
562 if (ecu_id == NULL) {
563 return diag_iseterr(DIAG_ERR_NOMEM);
564 }
565 }
566
567 return CMD_OK;
568}
569
570/*
571 * Close the current connection.
572 */
573static enum cli_retval cmd_850_disconnect(int argc, UNUSED(char **argv)) {
574 const char *desc;
575
576 if (!valid_arg_count(1, argc, 1)) {
577 return CMD_USAGE;
578 }
579
580 if (!valid_connection_status(CONNECTED_EITHER)) {
581 return CMD_OK;
582 }
583
584 desc = current_ecu_desc();
585
586 diag_l2_StopCommunications(global_l2_conn);
587 diag_l2_close(global_dl0d);
588
589 global_l2_conn = NULL;
590 global_state = STATE_IDLE;
591
592 printf("Disconnected from %s.\n", desc);
593 have_read_dtcs = false;
594 return CMD_OK;
595}
596
597/*
598 * Send a raw command and print the response.
599 */
600static enum cli_retval cmd_850_sendreq(int argc, char **argv) {
601 uint8_t data[MAXRBUF] = {0};
602 unsigned int len;
603 unsigned int i;
604 int rv;
605
606 if (!valid_arg_count(2, argc, sizeof(data) + 1)) {
607 return CMD_USAGE;
608 }
609
610 if (!valid_connection_status(CONNECTED_EITHER)) {
611 return CMD_OK;
612 }
613
614 len = argc - 1;
615 for (i = 0; i < len; i++) {
616 data[i] = (uint8_t) htoi(argv[i+1]);
617 }
618
619 rv = l2_do_send( global_l2_conn, data, len,
620 (void *)&_RQST_HANDLE_DECODE);
621
622 if (rv == DIAG_ERR_TIMEOUT) {
623 printf("No data received\n");
624 } else if (rv != 0) {
625 printf("sendreq: failed error %d\n", rv);
626 }
627
628 return CMD_OK;
629}
630
631/*
632 * Verify communication with the ECU.
633 */
634static enum cli_retval cmd_850_ping(int argc, UNUSED(char **argv)) {
635 int rv;
636
637 if (!valid_arg_count(1, argc, 1)) {
638 return CMD_USAGE;
639 }
640
641 if (!valid_connection_status(CONNECTED_EITHER)) {
642 return CMD_OK;
643 }
644
645 if (get_connection_status() == CONNECTED_D2) {
646 rv = diag_l7_d2_ping(global_l2_conn);
647 } else {
648 rv = diag_l7_kwp71_ping(global_l2_conn);
649 }
650
651 if (rv == 0) {
652 printf("Pong!\n");
653 } else {
654 printf("Ping failed.\n");
655 }
656
657 return CMD_OK;
658}
659
660#define CLAMPED_LOOKUP(table,index) table[MIN(ARRAY_SIZE(table)-1,(unsigned)(index))]
661
662/*
663 * If we know how to interpret a live data value, print out the description and
664 * scaled value.
665 */
666static void interpret_value(enum l7_namespace ns, uint16_t addr, UNUSED(int len), uint8_t *buf) {
667 static const char *mode_selector_positions[]={"Open","S","E","W","Unknown"};
668 static const char *driving_modes[]={"Economy","Sport","Winter","Unknown"};
669 static const char *warmup_states[]={"in progress or engine off","completed","not possible","status unknown"};
670 float volts;
671 int16_t deg_c;
672 uint8_t ecu = global_l2_conn->diag_l2_destaddr;
673
674 if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x0200) {
675 printf_livedata("Engine Coolant Temperature: %dC (%dF)", buf[1]-80, (buf[1]-80)*9/5+32);
676 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x0300) {
677 /*ECU pin A27, MCU P7.1 input, divider ratio 8250/29750, 5Vref*/
678 printf_livedata("Battery voltage: %.1f V", (float)buf[0]*29750/8250*5/255);
679 } else if (ns==NS_MEMORY && ecu==0x10 && addr==0x36) {
680 printf_livedata("Battery voltage: %.1f V", (float)buf[0]*29750/8250*5/255);
681 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x0A00) {
682 printf_livedata("Warm-up %s", CLAMPED_LOOKUP(warmup_states, (buf[0]>>2)&3));
683 printf_livedata("MIL %srequested by TCM", (buf[0]&0x10) ? "" : "not ");
684 /* Low 2 bits supposedly indicate drive cycle and trip
685 complete, but don't make sense - can get set without the
686 car ever moving */
687 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x1000) {
688 /* ECU pin A4, MCU P7.4 input, divider ratio 8250/9460 */
689 printf_livedata("MAF sensor signal: %.2f V", (float)buf[0]*9460/8250*5/255);
690 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x1800) {
691 printf_livedata("Short term fuel trim: %+.1f%%", (float)buf[0]*100/128-100);
692 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x1900) {
693 /* possibly in units of 0.004 milliseconds (injection time) */
694 printf_livedata("Long term fuel trim, additive (unscaled): %+d", (signed int)buf[0]-128);
695 } else if (ns==NS_LIVEDATA && ecu==0x7a && addr==0x1A00) {
696 printf_livedata("Long term fuel trim, multiplicative: %+.1f%%", (float)buf[0]*100/128-100);
697 } else if (ns==NS_LIVEDATA && ecu==0x6e && addr==0x0500) {
698 printf_livedata("Mode selector: MS1 %s, MS2 %s, switch position %s", (buf[0]&1) ? "low" : "high", (buf[0]&2) ? "low" : "high", CLAMPED_LOOKUP(mode_selector_positions, buf[0]));
699 printf_livedata("Driving mode: %s", CLAMPED_LOOKUP(driving_modes, buf[1]));
700 } else if (ns==NS_LIVEDATA && ecu==0x6e && addr==0x0C00) {
701 /* Full scale should be 1023, although highest value seen in
702 bench testing was 1020 */
703 volts = ((float)buf[0]*256+buf[1])*5/1023;
704 printf_livedata("ATF temperature sensor voltage: %.2f V", volts);
705 /* Avoid divide by zero below */
706 if (5.0f-volts == 0.0f) {
707 volts = 4.999;
708 }
709 /* Input has 1k to +5V, sensor acts as a potential divider */
710 printf_livedata("ATF temperature sensor resistance: %u ohms", (unsigned)((1000.0f*volts)/(5.0f-volts)));
711 /* Offs 11 (!) agrees with T vs R chart in Volvo Green Book */
712 deg_c = ((int16_t)buf[2]*256)+buf[3]-11;
713 printf_livedata("ATF temperature: %dC (%dF)", deg_c, deg_c*9/5+32);
714 }
715}
716
717/*
718 * Try to interpret all the live data values in the buffer.
719 */
720static void interpret_block(enum l7_namespace ns, uint16_t addr, int len, uint8_t *buf) {
721 int i;
722
723 if (ns != NS_MEMORY) {
724 addr <<= 8;
725 }
726
727 for (i=0; i<len; i++) {
728 interpret_value(ns, addr+i, len-i, buf+i);
729 }
730}
731
732/*
733 * Print one line of a hex dump, with an address followed by one or more
734 * values.
735 */
736static int print_hexdump_line(FILE *f, uint16_t addr, int addr_chars, const uint8_t *buf, uint16_t len) {
737 if (fprintf(f, "%0*X:", addr_chars, addr) < 0) {
738 return 1;
739 }
740 while (len--) {
741 if (fprintf(f, " %02X", *buf++) < 0) {
742 return 1;
743 }
744 }
745 if (live_display_running) {
746 diag_os_clrtoeol();
747 }
748 if (fputc('\n', f) == EOF) {
749 return 1;
750 }
751 return 0;
752}
753
754struct read_or_peek_item {
755 uint16_t start; /* starting address or identifier */
756 uint16_t end; /* ending address - for peeks only */
757 enum l7_namespace ns;
758};
759
760/*
761 * Parse an address argument on a peek command line.
762 */
763static int parse_peek_arg(const char *arg, struct read_or_peek_item *item) {
764 char *p, *q;
765
766 item->ns = NS_MEMORY;
767 item->start = strtoul(arg, &p, 0);
768 if (*p == '\0') {
769 item->end = item->start;
770 } else if ((p[0] == 'w' || p[0] == 'W') && p[1] == '\0') {
771 item->end = item->start + 1;
772 } else if ((p[0] == 'l' || p[0] == 'L') && p[1] == '\0') {
773 item->end = item->start + 3;
774 } else if ((p[0] == '.' || p[0] == '-') && p[1] != '\0') {
775 item->end = strtoul(p+1, &q, 0);
776 if (*q != '\0' || item->end < item->start) {
777 printf("Invalid address range '%s'\n", arg);
778 return 1;
779 }
780 } else {
781 printf("Invalid address '%s'\n", arg);
782 return 1;
783 }
784 return 0;
785}
786
787/*
788 * Parse an identifier argument on a read command line.
789 */
790static int parse_read_arg(const char *arg, struct read_or_peek_item *item) {
791 char *p;
792
793 if (arg[0] == '*') {
794 if (arg[1] == '\0') {
795 printf("Invalid identifier '%s'\n", arg);
796 return 1;
797 }
798 return parse_peek_arg(arg+1, item);
799 }
800
801 item->ns = NS_LIVEDATA;
802 item->start = strtoul(arg, &p, 0);
803 if (*p != '\0' || item->start > 0xff) {
804 printf("Invalid identifier '%s'\n", arg);
805 return 1;
806 }
807 return 0;
808}
809
810/*
811 * Parse an identifier argument on an adc command line.
812 */
813static int parse_adc_arg(const char *arg, struct read_or_peek_item *item) {
814 char *p;
815
816 item->ns = NS_ADC;
817 item->start = strtoul(arg, &p, 0);
818 if (*p != '\0' || item->start > 0xff) {
819 printf("Invalid identifier '%s'\n", arg);
820 return 1;
821 }
822 return 0;
823}
824
825/*
826 * Parse an identifier argument on a readnv command line.
827 */
828static int parse_readnv_arg(const char *arg, struct read_or_peek_item *item) {
829 char *p;
830
831 item->ns = NS_NV;
832 item->start = strtoul(arg, &p, 0);
833 if (*p != '\0' || item->start > 0xff) {
834 printf("Invalid identifier '%s'\n", arg);
835 return 1;
836 }
837 return 0;
838}
839
840/*
841 * Parse an identifier argument on a freeze command line.
842 */
843static int parse_freeze_arg(const char *arg, struct read_or_peek_item *item) {
844 char *p;
845
846 item->ns = NS_FREEZE;
847 if (isalpha(arg[0])) {
848 item->start = dtc_raw_by_printable(arg);
849 if (item->start == 0xffff) {
850 printf("Invalid identifier '%s'\n", arg);
851 return 1;
852 }
853 return 0;
854 }
855 item->start = strtoul(arg, &p, 0);
856 if (*p != '\0' || item->start > 0xff) {
857 printf("Invalid identifier '%s'\n", arg);
858 if (isdigit(arg[0]) && arg[0] != '0' && *p == '\0') {
859 printf("Did you mean %s-%s?\n",
860 current_dtc_prefix(), arg);
861 }
862 return 1;
863 }
864 if (isdigit(arg[0]) && arg[0]!='0') {
865 if (item->start < 100) {
866 printf("Warning: retrieving freeze frame by raw identifier %d (=%02X).\nDid you mean 0x%s?\n", item->start, item->start, arg);
867 } else {
868 printf("Warning: retrieving freeze frame by raw identifier %d (=%02X).\nDid you mean %s-%s?\n", item->start, item->start, current_dtc_prefix(), arg);
869 }
870 }
871 return 0;
872}
873
874/*
875 * Execute a read, peek or readnv command.
876 */
877static enum cli_retval read_family(int argc, char **argv, enum l7_namespace ns) {
878 int count;
879 int i, rv;
880 bool continuous;
881 struct read_or_peek_item *items;
882 uint8_t buf[20];
883 uint16_t addr, len;
884 int gotbytes;
885
886 if (!valid_arg_count(2, argc, 999)) {
887 return CMD_USAGE;
888 }
889
890 if (!valid_connection_status(CONNECTED_EITHER)) {
891 return CMD_OK;
892 }
893
894 continuous = false;
895 count = argc - 1;
896
897 if (ns!=NS_NV && ns!=NS_FREEZE && strcasecmp(argv[argc-1], "stream")==0) {
898 continuous = true;
899 count--;
900 if (count < 1) {
901 return CMD_USAGE;
902 }
903 }
904
905 if (ns!=NS_NV && ns!=NS_FREEZE && strcasecmp(argv[argc-1], "live")==0) {
906 if (continuous) {
907 return CMD_USAGE;
908 }
909 continuous = true;
910 live_display_running = true;
911 count--;
912 if (count < 1) {
913 return CMD_USAGE;
914 }
915 }
916
917 rv = diag_calloc(&items, count);
918 if (rv) {
919 live_display_running = false;
920 return diag_ifwderr(rv);
921 }
922
923 for (i=0; i<count; i++) {
924 switch (ns) {
925 case NS_MEMORY:
926 if (parse_peek_arg(argv[i + 1], &(items[i])) != 0) {
927 goto done;
928 }
929 break;
930 case NS_LIVEDATA:
931 if (parse_read_arg(argv[i + 1], &(items[i])) != 0) {
932 goto done;
933 }
934 break;
935 case NS_ADC:
936 if (parse_adc_arg(argv[i + 1], &(items[i])) != 0) {
937 goto done;
938 }
939 break;
940 case NS_NV:
941 if (parse_readnv_arg(argv[i + 1], &(items[i])) != 0) {
942 goto done;
943 }
944 break;
945 case NS_FREEZE:
946 if (parse_freeze_arg(argv[i + 1], &(items[i])) != 0) {
947 goto done;
948 }
949 break;
950 default:
951 fprintf(stderr, FLFMT "impossible ns value\n", FL);
952 goto done;
953 }
954 }
955
956 diag_os_ipending();
957 while (1) {
958 live_data_lines = 0;
959 for (i=0; i<count; i++) {
960 if (items[i].ns != NS_MEMORY) {
961 addr = items[i].start;
962 if (get_connection_status() == CONNECTED_D2) {
963 gotbytes = diag_l7_d2_read(global_l2_conn, items[i].ns, addr, sizeof(buf), buf);
964 } else {
965 gotbytes = diag_l7_kwp71_read(global_l2_conn, items[i].ns, addr, sizeof(buf), buf);
966 }
967 if (gotbytes < 0) {
968 printf("Error reading %02X\n", addr);
969 goto done;
970 }
971 if (items[i].ns == NS_FREEZE) {
972 printf("%s ",
973 dtc_printable_by_raw(
974 global_l2_conn
975 ->diag_l2_destaddr,
976 addr, NULL, NULL));
977 }
978 if (gotbytes == 0) {
979 printf_livedata("%02X: no data", addr);
980 } else if ((unsigned int)gotbytes > sizeof(buf)) {
981 print_hexdump_line(stdout, addr, 2, buf, sizeof(buf));
982 live_data_lines++;
983 printf_livedata(" (%d bytes received, only first %zu shown)", gotbytes, sizeof(buf));
984 interpret_block(items[i].ns, addr, sizeof(buf), buf);
985 } else {
986 print_hexdump_line(stdout, addr, 2, buf, gotbytes);
987 live_data_lines++;
988 interpret_block(items[i].ns, addr, gotbytes, buf);
989 }
990 } else {
991 addr = items[i].start;
992 len = (items[i].end - items[i].start) + 1;
993 while (len > 0) {
994 if (get_connection_status() == CONNECTED_D2) {
995 gotbytes = diag_l7_d2_read(global_l2_conn, NS_MEMORY, addr, (len<8) ? len : 8, buf);
996 } else {
997 gotbytes = diag_l7_kwp71_read(global_l2_conn, NS_MEMORY, addr, (len<8) ? len : 8, buf);
998 }
999 if (gotbytes == ((len<8) ? len : 8)) {
1000 print_hexdump_line(stdout, addr, 4, buf, (len<8) ? len : 8);
1001 live_data_lines++;
1002 interpret_block(NS_MEMORY, addr, (len<8) ? len : 8, buf);
1003 } else {
1004 printf("Error reading %s%04X\n", (ns==NS_LIVEDATA) ? "*" : "", addr);
1005 goto done;
1006 }
1007 len -= (len<8) ? len : 8;
1008 addr += 8;
1009 }
1010 }
1011 }
1012 if (!continuous || diag_os_ipending()) {
1013 break;
1014 }
1015 if (live_display_running) {
1016 diag_os_cursor_up(live_data_lines);
1017 }
1018 }
1019
1020done:
1021 live_display_running = false;
1022 free(items);
1023 return CMD_OK;
1024}
1025
1026/*
1027 * Read and display one or more values from RAM.
1028 *
1029 * Takes a list of addresses to read. Each address can have a suffix "w" or
1030 * "l" to indicate 2 or 4 bytes, respectively; otherwise a single byte is
1031 * read. Each item in the list can also be an address range with the starting
1032 * and ending addresses separated by ".".
1033 *
1034 * The word "live" can be added at the end to continuously read the
1035 * requested addresses and update the display until interrupted, or "stream"
1036 * to continuously read and scroll the display.
1037 */
1038static enum cli_retval cmd_850_peek(int argc, char **argv) {
1039 return read_family(argc, argv, NS_MEMORY);
1040}
1041
1042/*
1043 * Read and display one or more live data parameters.
1044 *
1045 * Takes a list of one-byte identifier values. If a value is prefixed with *,
1046 * it is treated as an address or address range to read from RAM instead of
1047 * a live data parameter identifier; in this way, a list of "read" and "peek"
1048 * operations can be done in a single command.
1049 *
1050 * The word "live" can be added at the end to continuously read the
1051 * requested addresses and update the display until interrupted, or "stream"
1052 * to continuously read and scroll the display.
1053 */
1054static enum cli_retval cmd_850_read(int argc, char **argv) {
1055 if (!valid_connection_status(CONNECTED_D2)) {
1056 return CMD_OK;
1057 }
1058
1059 return read_family(argc, argv, NS_LIVEDATA);
1060}
1061
1062/*
1063 * Read and display one or more ADC readings.
1064 *
1065 * Takes a list of one-byte channel identifiers.
1066 *
1067 * The word "live" can be added at the end to continuously read the
1068 * requested addresses and update the display until interrupted, or "stream"
1069 * to continuously read and scroll the display.
1070 */
1071static enum cli_retval cmd_850_adc(int argc, char **argv) {
1072 if (!valid_connection_status(CONNECTED_KWP71)) {
1073 return CMD_OK;
1074 }
1075
1076 return read_family(argc, argv, NS_ADC);
1077}
1078
1079
1080/*
1081 * Read and display one or more non-volatile parameters.
1082 *
1083 * Takes a list of one-byte identifier values.
1084 */
1085static enum cli_retval cmd_850_readnv(int argc, char **argv) {
1086 if (!valid_connection_status(CONNECTED_D2)) {
1087 return CMD_OK;
1088 }
1089
1090 return read_family(argc, argv, NS_NV);
1091}
1092
1093/*
1094 * Read and display freeze frames for all stored DTCs.
1095 */
1096static enum cli_retval cmd_850_freeze_all(void) {
1097 uint8_t dtcs[12];
1098 int count;
1099 char *argbuf;
1100 char **argvout;
1101 char *p;
1102 int rv;
1103 int i;
1104
1105 if (!valid_connection_status(CONNECTED_D2)) {
1106 return CMD_OK;
1107 }
1108
1109 rv = diag_l7_d2_dtclist(global_l2_conn, sizeof(dtcs), dtcs);
1110 if (rv < 0) {
1111 printf("Couldn't retrieve DTCs.\n");
1112 return CMD_OK;
1113 }
1114
1115 if (rv == 0) {
1116 printf("No stored DTCs.\n");
1117 return CMD_OK;
1118 }
1119
1120 count = rv;
1121
1122 rv = diag_calloc(&argbuf, 5);
1123 if (rv) {
1124 return diag_ifwderr(rv);
1125 }
1126
1127 rv = diag_calloc(&argvout, count+1);
1128 if (rv) {
1129 return diag_ifwderr(rv);
1130 }
1131
1132 p = argbuf;
1133 for (i=0; i<count; i++) {
1134 sprintf(p, "0x%x", dtcs[i]);
1135 argvout[i+1] = p;
1136 p += 5;
1137 }
1138
1139 rv = read_family(count+1, argvout, NS_FREEZE);
1140
1141 free(argvout);
1142 free(argbuf);
1143 return rv;
1144}
1145
1146/*
1147 * Read and display one or more freeze frames.
1148 *
1149 * Takes a list of DTCs, or the option "all" to retrieve freeze frames for all
1150 * stored DTCs. Each DTC can be specified either as a raw byte value or by its
1151 * EFI-xxx, AT-xxx, etc designation.
1152 */
1153static enum cli_retval cmd_850_freeze(int argc, char **argv) {
1154 if (!valid_connection_status(CONNECTED_D2)) {
1155 return CMD_OK;
1156 }
1157
1158 if (argc==2 && strcasecmp(argv[1], "all")==0) {
1159 return cmd_850_freeze_all();
1160 }
1161 return read_family(argc, argv, NS_FREEZE);
1162}
1163
1164/*
1165 * Query the ECU for identification and print the result.
1166 */
1167static enum cli_retval cmd_850_id_d2(void) {
1168 uint8_t buf[15];
1169 int rv;
1170 int i;
1171
1172 rv = diag_l7_d2_read(global_l2_conn, NS_NV, 0xf0, sizeof(buf), buf);
1173 if (rv < 0) {
1174 printf("Couldn't read identification.\n");
1175 return CMD_OK;
1176 }
1177 if (rv != sizeof(buf)) {
1178 printf("Identification response was %d bytes, expected %zu\n", rv, sizeof(buf));
1179 return CMD_OK;
1180 }
1181 if (buf[0] != 0) {
1182 printf("First identification response byte was %02X, expected 0\n", buf[0]);
1183 return CMD_OK;
1184 }
1185 if (!isprint(buf[5]) || !isprint(buf[6]) || !isprint(buf[7]) ||
1186 !isprint(buf[12]) || !isprint(buf[13]) || !isprint(buf[14])) {
1187 printf("Unexpected characters in version response\n");
1188 return CMD_OK;
1189 }
1190
1191 printf("Hardware ID: P%02X%02X%02X%02X revision %.3s\n", buf[1], buf[2], buf[3], buf[4], buf+5);
1192 printf("Software ID: %02X%02X%02X%02X revision %.3s\n", buf[8], buf[9], buf[10], buf[11], buf+12);
1193
1194 if (global_l2_conn->diag_l2_destaddr == 0x7a) {
1195 rv = diag_l7_d2_read(global_l2_conn, NS_NV, 1, sizeof(buf), buf);
1196 if (rv < 0) {
1197 return CMD_OK;
1198 }
1199 if (rv != 10) {
1200 printf("Identification response was %d bytes, expected %d\n", rv, 10);
1201 return CMD_OK;
1202 }
1203 for (i=0; i<10; i++) {
1204 if (!isdigit(buf[i])) {
1205 printf("Unexpected characters in identification block\n");
1206 return CMD_OK;
1207 }
1208 }
1209 printf("Order number: %c %.3s %.3s %.3s\n",buf[0], buf+1, buf+4, buf+7);
1210 }
1211
1212 return CMD_OK;
1213}
1214
1215/*
1216 * Print the ECU identification we received upon initial connection.
1217 */
1218static enum cli_retval cmd_850_id_kwp71(void) {
1219 int i;
1220 struct diag_msg *msg;
1221
1222 if (ecu_id == NULL) {
1223 printf("No stored ECU identification!\n");
1224 return CMD_OK;
1225 }
1226
1227 msg = ecu_id;
1228
1229 if (msg->len != 10) {
1230 printf("Identification block was %u bytes, expected %d\n", msg->len, 10);
1231 return CMD_OK;
1232 }
1233
1234 for (i=0; i<10; i++) {
1235 if (!isdigit(msg->data[i])) {
1236 printf("Unexpected characters in identification block\n");
1237 return CMD_OK;
1238 }
1239 }
1240
1241 printf("Order number: %c %.3s %.3s %.3s\n",msg->data[0], msg->data+1, msg->data+4, msg->data+7);
1242
1243 msg = msg->next;
1244 if (msg == NULL) {
1245 return CMD_OK;
1246 }
1247 /* Second block seems to be meaningless, don't print it. */
1248#if 0
1249 print_hexdump_line(stdout, msg->type, 2, msg->data, msg->len);
1250#endif
1251 msg = msg->next;
1252 if (msg == NULL) {
1253 return CMD_OK;
1254 }
1255
1256 if (msg->len != 10) {
1257 printf("Identification block was %u bytes, expected %d\n", msg->len, 10);
1258 return CMD_OK;
1259 }
1260
1261 for (i=0; i<7; i++) {
1262 if (!isdigit(msg->data[i])) {
1263 printf("Unexpected characters in identification block\n");
1264 return CMD_OK;
1265 }
1266 }
1267 printf("Hardware ID: P0%.7s\n", msg->data);
1268
1269 /* There's a fourth block but it seems to be meaningless. */
1270
1271 return CMD_OK;
1272}
1273
1274/*
1275 * Display ECU identification.
1276 */
1277static enum cli_retval cmd_850_id(int argc, UNUSED(char **argv)) {
1278 if (!valid_arg_count(1, argc, 1)) {
1279 return CMD_USAGE;
1280 }
1281
1282 if (!valid_connection_status(CONNECTED_EITHER)) {
1283 return CMD_OK;
1284 }
1285
1286 if (get_connection_status() == CONNECTED_D2) {
1287 return cmd_850_id_d2();
1288 }
1289 return cmd_850_id_kwp71();
1290}
1291
1292/*
1293 * Dump the entire contents of RAM to the specified file as a hex dump with
1294 * 8 bytes per line.
1295 *
1296 * ECUs may have holes in the memory map (example: Motronic M4.4 has RAM
1297 * at 0000-00FF and XRAM at F800-FFFF and nothing in between). So we try
1298 * reading in 8 byte chunks and if an attempt to read a given address fails,
1299 * just skip the hexdump line for that address and continue on to the next one.
1300 * If the "fast" option is specified on the command line, skip ahead to 0xF000
1301 * when a read attempt fails.
1302 */
1303static enum cli_retval cmd_850_dumpram(int argc, char **argv) {
1304 uint16_t addr;
1305 FILE *f;
1306 bool happy;
1307 uint8_t buf[8];
1308 bool fast;
1309
1310 if (argc == 2) {
1311 fast = false;
1312 } else if (argc == 3 && strcasecmp(argv[2], "fast") == 0) {
1313 fast = true;
1314 } else {
1315 return CMD_USAGE;
1316 }
1317
1318 if (!valid_connection_status(CONNECTED_D2)) {
1319 return CMD_OK;
1320 }
1321
1322 f = fopen(argv[1], "w");
1323 if (f == NULL) {
1324 perror("Can't open file");
1325 return CMD_OK;
1326 }
1327
1328 printf("Dumping RAM to %s...\n", argv[1]);
1329
1330 addr = 0;
1331 while (1) {
1332 if (diag_l7_d2_read(global_l2_conn, NS_MEMORY, addr, 8, buf) == 8) {
1333 happy = 1;
1334 errno = 0;
1335 if (print_hexdump_line(f, addr, 4, buf, 8) != 0) {
1336 if (errno != 0) {
1337 perror("\nError writing file");
1338 } else {
1339 /*error, but fprintf didn't set errno*/
1340 printf("\nError writing file");
1341 }
1342 return CMD_OK;
1343 }
1344 } else {
1345 happy = 0;
1346 }
1347 if ((addr&0x1f) == 0) {
1348 printf("\r%04X %s", addr, happy ? ":)" : ":/");
1349 fflush(stdout);
1350 }
1351 if (addr == 0xfff8) {
1352 break;
1353 }
1354 addr += 8;
1355
1356 if (fast && !happy && addr < 0xf000) {
1357 addr = 0xf000;
1358 }
1359 }
1360
1361 if (fclose(f) != 0) {
1362 perror("\nError writing file");
1363 return CMD_OK;
1364 }
1365
1366 printf("\r%04X :D\n", addr);
1367
1368 return CMD_OK;
1369}
1370
1371/*
1372 * Display list of stored DTCs.
1373 */
1374static enum cli_retval cmd_850_dtc(int argc, UNUSED(char **argv)) {
1375 uint8_t buf[12];
1376 int rv;
1377 int i;
1378 int span;
1379 char *code;
1380 const char *desc, *tips;
1381 bool unconfirmed = false;
1382 bool have_tips = false;
1383 bool show_tips = false;
1384
1385 if (!valid_arg_count(1, argc, 2)) {
1386 return CMD_USAGE;
1387 }
1388
1389 if (argc == 2 && strcasecmp(argv[1], "tips") == 0) {
1390 show_tips = true;
1391 } else if (argc == 2) {
1392 return CMD_USAGE;
1393 }
1394
1395 if (!valid_connection_status(CONNECTED_EITHER)) {
1396 return CMD_OK;
1397 }
1398
1399 if (get_connection_status() == CONNECTED_D2) {
1400 rv = diag_l7_d2_dtclist(global_l2_conn, sizeof(buf), buf);
1401 span = 1;
1402 } else {
1403 rv = diag_l7_kwp71_dtclist(global_l2_conn, sizeof(buf), buf);
1404 span = 5;
1405 }
1406
1407 if (rv < 0) {
1408 printf("Couldn't retrieve DTCs.\n");
1409 return CMD_OK;
1410 }
1411 have_read_dtcs = true;
1412
1413 if (rv == 0) {
1414 printf("No stored DTCs.\n");
1415 return CMD_OK;
1416 }
1417
1418 printf("Stored DTCs:\n");
1419 for (i=0; i<rv; i+=span) {
1420 code = dtc_printable_by_raw(global_l2_conn->diag_l2_destaddr, buf[i], &desc, &tips);
1421 printf("%s (%02X) %s\n", code, buf[i], desc);
1422 if (desc[0] != '\0' && desc[strlen(desc)-1] == '?') {
1423 unconfirmed = true;
1424 }
1425 if (tips != NULL) {
1426 have_tips = true;
1427 if (show_tips) {
1428 printf("%s\n", tips);
1429 }
1430 }
1431 }
1432
1433 if (unconfirmed) {
1434 printf("Warning: descriptions and DTC suffixes for lines ending in ? are unconfirmed\n");
1435 }
1436
1437 if (have_tips && !show_tips) {
1438 printf("Tips are available for %s. Use \"dtc tips\" to show them.\n", (rv > span) ? "some of these DTCs" : "this DTC");
1439 }
1440
1441 if (show_tips && !have_tips) {
1442 printf("No tips are available for %s.\n", (rv > span) ? "these DTCs" : "this DTC");
1443 }
1444
1445 return CMD_OK;
1446}
1447
1448/*
1449 * Clear stored DTCs.
1450 */
1451static enum cli_retval cmd_850_cleardtc(int argc, UNUSED(char **argv)) {
1452 char *input;
1453 int rv;
1454
1455 if (!valid_arg_count(1, argc, 1)) {
1456 return CMD_USAGE;
1457 }
1458
1459 if (!valid_connection_status(CONNECTED_EITHER)) {
1460 return CMD_OK;
1461 }
1462
1463 input = cli_basic_get_input("Are you sure you wish to clear the Diagnostic Trouble Codes (y/n) ? ", stdin);
1464 if (!input) {
1465 return CMD_OK;
1466 }
1467
1468 if ((strcasecmp(input, "yes") != 0) && (strcasecmp(input, "y")!=0)) {
1469 printf("Not done\n");
1470 goto done;
1471 }
1472
1473 if (!have_read_dtcs) {
1474 free(input);
1475 input = cli_basic_get_input("You haven't read the DTCs yet. Are you sure you wish to clear them (y/n) ? ", stdin);
1476 if (!input) {
1477 return CMD_OK;
1478 }
1479 if ((strcasecmp(input, "yes") != 0) && (strcasecmp(input, "y")!=0)) {
1480 printf("Not done\n");
1481 goto done;
1482 }
1483 }
1484
1485 if (get_connection_status() == CONNECTED_D2) {
1486 rv = diag_l7_d2_cleardtc(global_l2_conn);
1487 } else {
1488 rv = diag_l7_kwp71_cleardtc(global_l2_conn);
1489 }
1490
1491 if (rv == 0) {
1492 printf("No DTCs to clear!\n");
1493 } else if (rv == 1) {
1494 printf("Done\n");
1495 } else {
1496 printf("Failed\n");
1497 }
1498
1499done:
1500 free(input);
1501 return CMD_OK;
1502}
1503
1504/*
1505 * Reset the Service Reminder Light.
1506 */
1507static enum cli_retval cmd_850_resetsrl(int argc, UNUSED(char **argv)) {
1508 char *input;
1509 char *argvout[] = {"connect", "combi"};
1510 int rv;
1511 bool old_car;
1512
1513 if (!valid_arg_count(1, argc, 1)) {
1514 return CMD_USAGE;
1515 }
1516
1517 input = cli_basic_get_input("Are you sure you wish to reset the Service Reminder Light (y/n) ? ", stdin);
1518 if (!input) {
1519 return CMD_OK;
1520 }
1521
1522 if ((strcasecmp(input, "yes") != 0) && (strcasecmp(input, "y")!=0)) {
1523 printf("Not done\n");
1524 goto done;
1525 }
1526
1527 /* If talking to wrong ECU, disconnect first */
1528 if (get_connection_status() != NOT_CONNECTED && global_l2_conn->diag_l2_destaddr!=0x51) {
1529 printf("Disconnecting from %s first.\n", current_ecu_desc());
1530 cmd_850_disconnect(1, NULL);
1531 }
1532
1533 /* If not connected to combi, connect */
1534 if (get_connection_status() == NOT_CONNECTED) {
1535 if (cmd_850_connect(2, argvout) != CMD_OK) {
1536 printf("Couldn't connect to combined instrument panel.\n");
1537 goto done;
1538 }
1539 }
1540
1541 /* '96/'97 must be unlocked first, but '98 rejects unlock command */
1542 old_car = (diag_l7_d2_io_control(global_l2_conn, 0x30, 0) == 0);
1543
1544 rv = diag_l7_d2_run_routine(global_l2_conn, 0x30);
1545
1546 if (rv == 0) {
1547 printf("Done\n");
1548 } else if (rv == DIAG_ERR_TIMEOUT && old_car) {
1549 /* '96/'97 either don't respond after SRL reset, or respond
1550 after a long delay? */
1551 printf("Probably done\n");
1552 } else {
1553 printf("Failed\n");
1554 }
1555
1556done:
1557 free(input);
1558 return CMD_OK;
1559}
1560
1561/*
1562 * Try to connect to each possible ECU. Print identification and DTCs for each
1563 * successfully connected ECU.
1564 *
1565 * There will always be some unsuccessful connection attempts in a scan-all
1566 * because at least one ECU in our list will be missing from any given vehicle.
1567 * For example, MSA 15.7 and Motronic M4.4 will never both be present in the
1568 * same car.
1569 */
1570static enum cli_retval cmd_850_scan_all(int argc, UNUSED(char **argv)) {
1571 const struct ecu_info *ecu;
1572 char *argvout[2];
1573 char buf[4];
1574
1575 if (!valid_arg_count(1, argc, 1)) {
1576 return CMD_USAGE;
1577 }
1578
1579 if (!valid_connection_status(NOT_CONNECTED)) {
1580 return CMD_OK;
1581 }
1582
1583 printf("Scanning all ECUs.\n");
1584
1585 argvout[1] = buf;
1586 for (ecu = ecu_list; ecu->name != NULL; ecu++) {
1587 sprintf(buf, "%d", ecu->addr);
1588 if (ecu->addr == 0x10) {
1589 /* Skip Motronic M4.4 old protocol */
1590 } else if (cmd_850_connect(2, argvout) == CMD_OK) {
1591 cmd_850_id(1, NULL);
1592 cmd_850_dtc(1, NULL);
1593 cmd_850_disconnect(1, NULL);
1594 } else {
1595 printf("Couldn't connect to %s.\n", ecu->desc);
1596 }
1597 }
1598
1599 printf("Scan-all done.\n");
1600
1601 return CMD_OK;
1602}
1603
1604/*
1605 * Test the specified vehicle component.
1606 */
1607static enum cli_retval cmd_850_test(int argc, char **argv) {
1608 if (!valid_connection_status(CONNECTED_D2)) {
1609 return CMD_OK;
1610 }
1611
1612 if (argc==2 && strcasecmp(argv[1], "fan1")==0 && global_l2_conn->diag_l2_destaddr==0x7a) {
1613 if (diag_l7_d2_io_control(global_l2_conn, 0x0e, 3) == 0) {
1614 printf("Activating engine cooling fan.\n");
1615 } else {
1616 printf("Unable to activate fan.\n");
1617 }
1618 } else if (argc==2 && strcasecmp(argv[1], "fan2")==0 && global_l2_conn->diag_l2_destaddr==0x7a) {
1619 if (diag_l7_d2_io_control(global_l2_conn, 0x1f, 3) == 0) {
1620 printf("Activating engine cooling fan.\n");
1621 } else {
1622 printf("Unable to activate fan.\n");
1623 }
1624 } else {
1625 printf("Usage: test <testname>\n");
1626 if (global_l2_conn->diag_l2_destaddr == 0x7a) {
1627 printf("Available tests:\n");
1628 printf("fan1 - Activate engine cooling fan, half speed (please keep fingers clear)\n");
1629 printf("fan2 - Activate engine cooling fan, full speed (please keep fingers clear)\n");
1630 } else {
1631 printf("No available tests for this ECU.\n");
1632 }
1633 }
1634 return CMD_OK;
1635}
1636