BOROSOFTWAREAll work →
scantool/scantool_obd.c 514 lines
1/* Copyright (C) 2001 Richard Almeida & Ibex Ltd ([email protected])
2 * Copyright (c) 2016 fenugrec
3 *
4 * Licensed under GPLv3
5 */
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include "diag.h"
15#include "diag_err.h"
16#include "diag_os.h"
17#include "diag_l2.h"
18#include "diag_l3.h"
19
20#include "libcli.h"
21
22#include "scantool.h"
23#include "scantool_cli.h"
24#include "scantool_obd.h"
25
26
27
28//cmd_watch : this creates a diag_l3_conn
29static enum cli_retval cmd_watch(int argc, char **argv) {
30 int rv;
31 struct diag_l2_conn *d_l2_conn;
32 struct diag_l3_conn *d_l3_conn=NULL;
33 struct diag_l0_device *dl0d = global_dl0d;
34 bool rawmode = 0;
35 bool nodecode = 0;
36 bool nol3 = 0;
37
38 if (argc > 1) {
39 if (strcasecmp(argv[1], "raw") == 0) {
40 rawmode = 1;
41 } else if (strcasecmp(argv[1], "nodecode") == 0) {
42 nodecode = 1;
43 } else if (strcasecmp(argv[1], "nol3") == 0) {
44 nol3 = 1;
45 } else {
46 printf("Didn't understand \"%s\"\n", argv[1]);
47 return CMD_USAGE;
48 }
49 }
50
51 if (!dl0d) {
52 printf("No global L0. Please select + configure L0 first\n");
53 return CMD_FAILED;
54 }
55
56 if (global_l2_conn) {
57 printf("L2 already connected, this won't work.\n");
58 return CMD_FAILED;
59 }
60
61 rv = diag_l2_open(dl0d, global_cfg.L1proto);
62 if (rv) {
63 printf("Failed to open hardware interface, ");
64 if (rv == DIAG_ERR_PROTO_NOTSUPP) {
65 printf("does not support requested L1 protocol\n");
66 } else if (rv == DIAG_ERR_BADIFADAPTER) {
67 printf("adapter probably not connected\n");
68 } else {
69 printf("%s\n", diag_errlookup(rv));
70 }
71 return CMD_FAILED;
72 }
73 if (rawmode) {
74 d_l2_conn = diag_l2_StartCommunications(dl0d, DIAG_L2_PROT_RAW,
75 0, global_cfg.speed,
76 global_cfg.tgt,
77 global_cfg.src);
78 } else {
79 d_l2_conn = diag_l2_StartCommunications(dl0d, global_cfg.L2proto,
80 DIAG_L2_TYPE_MONINIT, global_cfg.speed, global_cfg.tgt, global_cfg.src);
81 }
82
83 if (d_l2_conn == NULL) {
84 printf("Failed to connect to hardware in monitor mode\n");
85 diag_l2_close(dl0d);
86 return CMD_FAILED;
87 }
88 //here we have a valid d_l2_conn over dl0d.
89 (void) diag_os_ipending();
90
91 if (!rawmode) {
92 /* Put the SAE J1979 stack on top of the ISO device */
93
94 if (!nol3) {
95 d_l3_conn = diag_l3_start("SAEJ1979", d_l2_conn);
96 if (d_l3_conn == NULL) {
97 printf("Failed to enable SAEJ1979 mode\n");
98 diag_l2_StopCommunications(d_l2_conn);
99 diag_l2_close(dl0d);
100 return CMD_FAILED;
101 }
102 } else {
103 d_l3_conn = NULL;
104 }
105
106 printf("Monitoring started. Press Enter to end.\n");
107 while (1) {
108 if (diag_os_ipending()) {
109 break;
110 }
111
112 if (d_l3_conn != NULL) {
113 rv = diag_l3_recv(d_l3_conn, 10000,
114 j1979_watch_rcv,
115 (nodecode) ? NULL:(void *)d_l3_conn);
116 } else {
117 rv = diag_l2_recv(d_l2_conn, 10000,
118 j1979_watch_rcv, NULL);
119 }
120 if (rv == 0) {
121 continue;
122 }
123 if (rv == DIAG_ERR_TIMEOUT) {
124 continue;
125 }
126 }
127 } else {
128 //rawmode
129 /*
130 * And just read stuff, callback routine will print out the data
131 */
132 printf("Monitoring started. Press Enter to end.\n");
133 while (1) {
134 if (diag_os_ipending()) {
135 break;
136 }
137
138 rv = diag_l2_recv(d_l2_conn, 10000,
139 j1979_data_rcv, (void *)&_RQST_HANDLE_WATCH);
140 if (rv == 0) {
141 continue;
142 }
143 if (rv == DIAG_ERR_TIMEOUT) {
144 continue;
145 }
146 printf("recv returns %d\n", rv);
147 break;
148 }
149 }
150 if (d_l3_conn != NULL) {
151 diag_l3_stop(d_l3_conn);
152 }
153
154 diag_l2_StopCommunications(d_l2_conn);
155 diag_l2_close(dl0d);
156
157 return CMD_OK;
158}
159
160
161/*
162 * Print the monitorable data out, use SI units by default, or "english"
163 * units
164 */
165static void print_current_data(bool english) {
166 char buf[24];
167 ecu_data *ep;
168 unsigned int i;
169 unsigned int j;
170
171 printf("%-30.30s %-15.15s FreezeFrame\n",
172 "Parameter", "Current");
173
174 for (j = 0 ; get_pid(j) != NULL ; j++) {
175 const struct pid *p = get_pid(j);
176
177 for (i=0, ep=ecu_info; i<ecu_count; i++, ep++) {
178 if (DATA_VALID(p, ep->mode1_data) ||
179 DATA_VALID(p, ep->mode2_data)) {
180 printf("%-30.30s ", p->desc);
181
182 if (DATA_VALID(p, ep->mode1_data)) {
183 p->cust_snprintf(buf, sizeof(buf), english, p,
184 ep->mode1_data, 2);
185 } else {
186 snprintf(buf, sizeof(buf), "-----");
187 }
188
189 printf("%-15.15s ", buf);
190
191 if (DATA_VALID(p, ep->mode2_data)) {
192 p->cust_snprintf(buf, sizeof(buf), english, p,
193 ep->mode2_data, 3);
194 } else {
195 snprintf(buf, sizeof(buf), "-----");
196 }
197
198 printf("%-15.15s\n", buf);
199 }
200 }
201 }
202}
203
204static void log_response(int ecu, response *r) {
205 assert(global_logfp != NULL);
206
207 /* Only print good records */
208 if (r->type != TYPE_GOOD) {
209 return;
210 }
211
212 printf("%d: ", ecu);
213 diag_data_dump(global_logfp, r->data,r->len);
214 fprintf(global_logfp, "\n");
215}
216
217static void log_current_data(void) {
218 response *r;
219 ecu_data *ep;
220 unsigned int i;
221
222 if (!global_logfp) {
223 return;
224 }
225
226 log_timestamp("D");
227 fprintf(global_logfp, "MODE 1 DATA\n");
228 for (i=0, ep=ecu_info; i<ecu_count; i++, ep++) {
229 for (r = ep->mode1_data;
230 r < &ep->mode1_data[ARRAY_SIZE(ep->mode1_data)]; r++) {
231 log_response((int)i, r);
232 }
233 }
234
235 log_timestamp("D");
236 fprintf(global_logfp, "MODE 2 DATA\n");
237 for (i=0, ep=ecu_info; i<ecu_count; i++, ep++) {
238 for (r = ep->mode2_data;
239 r < &ep->mode2_data[ARRAY_SIZE(ep->mode2_data)]; r++) {
240 log_response((int)i, r);
241 }
242 }
243}
244
245static enum cli_retval cmd_monitor(int argc, char **argv) {
246 int rv;
247 bool english = 0;
248
249 if ((argc > 1) && (strcmp(argv[1], "?") == 0)) {
250 return CMD_USAGE;
251 }
252
253 if (global_state < STATE_SCANDONE) {
254 printf("SCAN has not been done, please do a scan\n");
255 return CMD_FAILED;
256 }
257
258 // If user states English or Metric, use that, else use config item
259 if (argc > 1) {
260 if (strcasecmp(argv[1], "english") == 0) {
261 english = 1;
262 } else if (strcasecmp(argv[1], "metric") == 0) {
263 english = 0;
264 } else {
265 return CMD_USAGE;
266 }
267 } else {
268 english = global_cfg.units;
269 }
270
271 printf("Monitoring. Press <enter> to stop.\n");
272
273 /*
274 * Now just receive data and log it for ever
275 */
276
277 while (1) {
278 rv = do_j1979_getdata(1);
279 /* Key pressed */
280 if (rv == 1 || rv<0) {
281 //enter was pressed to interrupt,
282 //or there was an error.
283 break;
284 }
285 /* print the data */
286 print_current_data(english);
287
288 /* Save the data */
289 log_current_data();
290
291 /* Get/Print current DTCs */
292 do_j1979_cms();
293 }
294 return CMD_OK;
295}
296
297// scan : use existing L3 J1979 connection, or establish a new one by trying all known protos.
298
299static enum cli_retval cmd_scan(UNUSED(int argc), UNUSED(char **argv)) {
300 int rv=DIAG_ERR_GENERAL;
301 if (argc > 1) {
302 return CMD_USAGE;
303 }
304
305 if (global_state == STATE_SCANDONE) {
306 printf("scan already done !\n");
307 return CMD_OK;
308 }
309 if (global_state == STATE_L3ADDED) {
310 if (global_l3_conn != NULL) {
311 if (strcmp(global_l3_conn->d_l3_proto->proto_name, "SAEJ1979") ==0) {
312 printf("Re-using active L3 connection.\n");
313 rv=0;
314 } else {
315 printf("L3 connection must be SAEJ1979 ! Try disconnecting and running scan again.\n");
316 return CMD_FAILED;
317 }
318 } else {
319 printf("Error: inconsistent global_state. Report this!\n");
320 return CMD_FAILED;
321 }
322 } else if (global_state >= STATE_CONNECTED) {
323 printf("Already connected, please disconnect first, or manually add SAEJ1979 L3 layer.\n");
324 return CMD_FAILED;
325 } else {
326 rv = ecu_connect();
327 }
328
329 if (rv == 0) {
330 printf("Connection to ECU established\n");
331
332 /* Now ask basic info from ECU */
333 do_j1979_basics();
334 /* Now get test results for continuously monitored systems */
335 do_j1979_cms();
336 /* And the non continuously monitored tests */
337 printf("Non-continuously monitored system tests (failures only): -\n");
338 do_j1979_ncms(0);
339 } else {
340 printf("Connection to ECU failed\n");
341 printf("Please check :\n");
342 printf("\tAdapter is connected to PC\n");
343 printf("\tCable is connected to Vehicle\n");
344 printf("\tVehicle is switched on\n");
345 printf("\tVehicle is OBDII compliant\n");
346 return CMD_FAILED;
347 }
348 return CMD_OK;
349}
350
351
352
353static enum cli_retval cmd_cleardtc(UNUSED(int argc), UNUSED(char **argv)) {
354 char *input;
355
356 if (global_state < STATE_CONNECTED) {
357 printf("Not connected to ECU\n");
358 return CMD_OK;
359 }
360
361 input = cli_basic_get_input("Are you sure you wish to clear the Diagnostic "
362 "Trouble Codes (y/n) ? ", stdin);
363 if (!input) {
364 return CMD_OK;
365 }
366
367 if ((strcasecmp(input, "yes") == 0) || (strcasecmp(input, "y")==0)) {
368 if (diag_cleardtc() == 0) {
369 printf("Done\n");
370 } else {
371 printf("Failed\n");
372 }
373 } else {
374 printf("Not done\n");
375 }
376
377 free(input);
378 return CMD_OK;
379}
380
381
382
383static enum cli_retval cmd_ecus(UNUSED(int argc), UNUSED(char **argv)) {
384 ecu_data *ep;
385 unsigned int i;
386
387 if (global_state < STATE_SCANDONE) {
388 printf("SCAN has not been done, please do a scan\n");
389 return CMD_OK;
390 }
391
392 printf("%u ECUs found\n", ecu_count);
393
394 for (i=0, ep=ecu_info; i<ecu_count; i++, ep++) {
395 printf("ECU %u: Address 0x%02X ", i, ep->ecu_addr & 0xff);
396 if (ep->supress) {
397 printf("output supressed for monitor mode\n");
398 } else {
399 printf("\n");
400 }
401 }
402 return CMD_OK;
403}
404
405static void print_resp_info(UNUSED(int mode), response *data) {
406
407 int i;
408 for (i=0; i<256; i++) {
409 if (data->type != TYPE_UNTESTED) {
410 if (data->type == TYPE_GOOD) {
411 printf("0x%02X: ", i );
412 diag_data_dump(stdout, data->data, data->len);
413 printf("\n");
414 } else {
415 printf("0x%02X: Failed 0x%X\n",
416 i, data->data[1]);
417 }
418 }
419 data++;
420 }
421}
422
423
424static enum cli_retval cmd_dumpdata(UNUSED(int argc), UNUSED(char **argv)) {
425 ecu_data *ep;
426 int i;
427
428 printf("Current Data\n");
429 for (i=0, ep=ecu_info; i<MAX_ECU; i++,ep++) {
430 if (ep->valid) {
431 printf("ECU 0x%02X:\n", ep->ecu_addr & 0xff);
432 print_resp_info(1, ep->mode1_data);
433 }
434 }
435
436 printf("Freezeframe Data\n");
437 for (i=0,ep=ecu_info; i<MAX_ECU; i++,ep++) {
438 if (ep->valid) {
439 printf("ECU 0x%02X:\n", ep->ecu_addr & 0xff);
440 print_resp_info(2, ep->mode2_data);
441 }
442 }
443
444 return CMD_OK;
445}
446
447
448
449
450/*print_pidinfo() : print supported PIDs (0 to 0x60) */
451static void print_pidinfo(int mode, uint8_t *pid_data) {
452 int i,j; /* j : # pid per line */
453
454 printf(" Mode %d:", mode);
455 for (i=0, j=0; i<=0x60; i++) {
456 if (j == 8) {
457 j = 0;
458 }
459
460 if (pid_data[i]) {
461 if (j == 0) {
462 printf("\n \t"); // once per line
463 }
464 printf("0x%02X ", i);
465 j++;
466 }
467 }
468 printf("\n");
469}
470
471
472static enum cli_retval cmd_pids(UNUSED(int argc), UNUSED(char **argv)) {
473 ecu_data *ep;
474 int i;
475
476 if (global_state < STATE_SCANDONE) {
477 printf("SCAN has not been done, please do a scan\n");
478 return CMD_OK;
479 }
480
481 for (i=0,ep=ecu_info; i<MAX_ECU; i++,ep++) {
482 if (ep->valid) {
483 printf("ECU %d address 0x%02X: Supported PIDs:\n",
484 i, ep->ecu_addr & 0xff);
485 print_pidinfo(1, ep->mode1_info);
486 print_pidinfo(2, ep->mode2_info);
487 print_pidinfo(5, ep->mode5_info);
488 print_pidinfo(6, ep->mode6_info);
489 print_pidinfo(8, ep->mode8_info);
490 print_pidinfo(9, ep->mode9_info);
491 }
492 }
493 printf("\n");
494
495 return CMD_OK;
496}
497
498const struct cmd_tbl_entry scantool_cmd_table[] = {
499 { "scan", "scan", "Start SCAN process", cmd_scan, 0, NULL},
500 { "monitor", "monitor [english/metric]", "Continuously monitor rpm etc",
501 cmd_monitor, 0, NULL},
502 { "cleardtc", "cleardtc", "Clear DTCs from ECU", cmd_cleardtc, 0, NULL},
503 { "ecus", "ecus", "Show ECU information", cmd_ecus, 0, NULL},
504 { "watch", "watch [raw/nodecode/nol3]",
505 "Watch the diagnostic bus and, if not in raw/nol3 mode, decode data",
506 cmd_watch, 0, NULL},
507 { "dumpdata", "dumpdata", "Show Mode1 Pid1/2 responses",
508 cmd_dumpdata, 0, NULL},
509 { "pids", "pids", "Shows PIDs supported by ECU",
510 cmd_pids, 0, NULL},
511 //no builtins for now since this table is appended with the one in scantool_cli
512 CLI_TBL_END
513};
514