| 1 | /* |
| 2 | * freediag - Vehicle Diagnostic Utility |
| 3 | * |
| 4 | * |
| 5 | * Copyright (C) 2001 Richard Almeida & Ibex Ltd ([email protected]) |
| 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 - test subcommand |
| 27 | */ |
| 28 | |
| 29 | #include <stdbool.h> |
| 30 | #include <stdio.h> |
| 31 | #include <string.h> |
| 32 | |
| 33 | #include "diag.h" /* operating specific includes */ |
| 34 | #include "diag_l3.h" /* operating specific includes */ |
| 35 | |
| 36 | #include "libcli.h" |
| 37 | |
| 38 | #include "scantool.h" |
| 39 | #include "scantool_cli.h" |
| 40 | |
| 41 | #include "utlist.h" |
| 42 | |
| 43 | static enum cli_retval cmd_test_help(int argc, char **argv); |
| 44 | static enum cli_retval cmd_test_rvi(int argc, char **argv); |
| 45 | static enum cli_retval cmd_test_cms(int argc, char **argv); |
| 46 | static enum cli_retval cmd_test_ncms(int argc, char **argv); |
| 47 | static enum cli_retval cmd_test_readiness(int argc, char **argv); |
| 48 | |
| 49 | const struct cmd_tbl_entry test_cmd_table[] = { |
| 50 | { "help", "help [command]", "Gives help for a command", |
| 51 | cmd_test_help, 0, NULL}, |
| 52 | { "?", "? [command]", "Gives help for a command", |
| 53 | cmd_test_help, CLI_CMD_HIDDEN, NULL}, |
| 54 | { "rvi", "rvi", "Send request vehicle info commands to the ECU", |
| 55 | cmd_test_rvi, 0, NULL}, |
| 56 | { "cms", "cms", |
| 57 | "Get test results for continuously monitored systems", |
| 58 | cmd_test_cms, 0, NULL}, |
| 59 | { "ncms", "ncms", |
| 60 | "Get test results for non-continuously monitored systems", |
| 61 | cmd_test_ncms, 0, NULL}, |
| 62 | { "readiness", "readiness", |
| 63 | "Do readiness tests", |
| 64 | cmd_test_readiness, 0, NULL}, |
| 65 | |
| 66 | CLI_TBL_BUILTINS, |
| 67 | CLI_TBL_END |
| 68 | }; |
| 69 | |
| 70 | static enum cli_retval cmd_test_help(int argc, char **argv) { |
| 71 | return cli_help_basic(argc, argv, test_cmd_table); |
| 72 | } |
| 73 | |
| 74 | |
| 75 | /* |
| 76 | * Guts of routine to ask for VIN/CID/CVN |
| 77 | * return data length (excluding 0x00 termination) if ok (data written to *obuf) |
| 78 | */ |
| 79 | static unsigned get_vit_info(struct diag_l3_conn *d_conn, uint8_t itype, uint8_t *obuf, unsigned buflen) { |
| 80 | struct diag_msg *msg, *msgcur; |
| 81 | int rv; |
| 82 | unsigned offset; |
| 83 | |
| 84 | /* Now request the VIN */ |
| 85 | rv = l3_do_j1979_rqst(d_conn, 9, itype, 0, 0, 0, 0, 0, (void *)&_RQST_HANDLE_NORMAL); |
| 86 | if (rv < 0) { |
| 87 | printf("Failed to get infotype 0x%X info\n", itype); |
| 88 | return 0; |
| 89 | } |
| 90 | |
| 91 | msg = find_ecu_msg(0, 0x49); |
| 92 | if (msg == NULL) { |
| 93 | printf("No Mode 9 response\n"); |
| 94 | return 0; |
| 95 | } |
| 96 | |
| 97 | offset = 0; |
| 98 | LL_FOREACH(msg, msgcur) { |
| 99 | memcpy(&obuf[offset], &msgcur->data[3], 4); |
| 100 | offset += 4; |
| 101 | if (offset >= buflen ) { |
| 102 | offset = buflen - 1; |
| 103 | printf("Clipped Mode 9 response\n"); |
| 104 | break; |
| 105 | } |
| 106 | } |
| 107 | obuf[offset] = 0; |
| 108 | return offset; |
| 109 | } |
| 110 | |
| 111 | |
| 112 | /* Request Vehicle Info */ |
| 113 | |
| 114 | static enum cli_retval cmd_test_rvi(UNUSED(int argc), UNUSED(char **argv)) { |
| 115 | struct diag_l3_conn *d_conn; |
| 116 | |
| 117 | if (global_state < STATE_SCANDONE) { |
| 118 | printf("SCAN has not been done, please do a scan\n"); |
| 119 | return CMD_OK; |
| 120 | } |
| 121 | |
| 122 | d_conn = global_l3_conn; |
| 123 | |
| 124 | ecu_data *ep; |
| 125 | unsigned i; |
| 126 | bool merged_mode9_info[0x100]; |
| 127 | #define MODE9_INFO_MAXLEN 0x100 |
| 128 | uint8_t infostring[MODE9_INFO_MAXLEN]; |
| 129 | |
| 130 | |
| 131 | /* merge all infotypes supported by all ECUs */ |
| 132 | memset(merged_mode9_info, 0, sizeof(merged_mode9_info)); |
| 133 | for (i=0, ep=ecu_info; i<ecu_count; i++, ep++) { |
| 134 | unsigned j; |
| 135 | for (j=0; j<sizeof(ep->mode9_info); j++) { |
| 136 | merged_mode9_info[j] |= ep->mode9_info[j]; |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | if (merged_mode9_info[2]) { |
| 141 | if (get_vit_info(d_conn, 2, infostring, MODE9_INFO_MAXLEN) > 3) { |
| 142 | printf("VIN: %s\n", (char *) &infostring[3]); //skip padding ! |
| 143 | } |
| 144 | } else { |
| 145 | printf("ECU doesn't support VIN request\n"); |
| 146 | } |
| 147 | |
| 148 | if (merged_mode9_info[4]) { |
| 149 | if (get_vit_info(d_conn, 4, infostring, MODE9_INFO_MAXLEN)) { |
| 150 | printf("Calibration ID: %s\n", (char *) infostring); |
| 151 | } |
| 152 | } else { |
| 153 | printf("ECU doesn't support Calibration ID request\n"); |
| 154 | } |
| 155 | |
| 156 | if (merged_mode9_info[6]) { |
| 157 | unsigned cvn_len = get_vit_info(d_conn, 6, infostring, MODE9_INFO_MAXLEN); |
| 158 | if (cvn_len) { |
| 159 | printf("CVN: "); |
| 160 | diag_data_dump(stdout, infostring, cvn_len); |
| 161 | printf("\n"); |
| 162 | } |
| 163 | } else { |
| 164 | printf("ECU doesn't support CVN request\n"); |
| 165 | } |
| 166 | |
| 167 | return CMD_OK; |
| 168 | } |
| 169 | |
| 170 | |
| 171 | |
| 172 | |
| 173 | static enum cli_retval cmd_test_cms(UNUSED(int argc), UNUSED(char **argv)) { |
| 174 | if (global_state < STATE_SCANDONE) { |
| 175 | printf("SCAN has not been done, please do a scan\n"); |
| 176 | return CMD_OK; |
| 177 | } |
| 178 | do_j1979_cms(); |
| 179 | return CMD_OK; |
| 180 | } |
| 181 | |
| 182 | |
| 183 | static enum cli_retval cmd_test_ncms(UNUSED(int argc), UNUSED(char **argv)) { |
| 184 | if (global_state < STATE_SCANDONE) { |
| 185 | printf("SCAN has not been done, please do a scan\n"); |
| 186 | return CMD_OK; |
| 187 | } |
| 188 | do_j1979_ncms(1); |
| 189 | return CMD_OK; |
| 190 | } |
| 191 | |
| 192 | |
| 193 | static enum cli_retval cmd_test_readiness(UNUSED(int argc), UNUSED(char **argv)) { |
| 194 | int rv; |
| 195 | struct diag_l3_conn *d_conn; |
| 196 | ecu_data *ep; |
| 197 | unsigned int i, j; |
| 198 | const char *text; |
| 199 | uint16_t incomplete_monitors; |
| 200 | int incomplete_continuous, incomplete_trip; |
| 201 | |
| 202 | const char *test_names[] = { |
| 203 | "Misfire Monitoring", |
| 204 | "Fuel System Monitoring", |
| 205 | "Comprehensive Component Monitoring", |
| 206 | NULL, |
| 207 | "Catalyst Monitoring", |
| 208 | "Heated Catalyst Monitoring", |
| 209 | "Evaporative System Monitoring", |
| 210 | "Secondary Air System Monitoring", |
| 211 | "A/C System Refrigerant Monitoring", |
| 212 | "Oxygen Sensor Monitoring", |
| 213 | "Oxygen Sensor Heater Monitor", |
| 214 | "EGR System Monitoring" |
| 215 | }; |
| 216 | |
| 217 | d_conn = global_l3_conn; |
| 218 | |
| 219 | if (global_state < STATE_CONNECTED) { |
| 220 | printf("Not connected to ECU\n"); |
| 221 | return CMD_OK; |
| 222 | } |
| 223 | |
| 224 | /* Do Mode 1 Pid 1 request */ |
| 225 | rv = l3_do_j1979_rqst(d_conn, 1, 1, 0x00, |
| 226 | 0x00, 0x00, 0x00, 0x00, |
| 227 | (void *)&_RQST_HANDLE_READINESS); |
| 228 | |
| 229 | if ((rv < 0) || (find_ecu_msg(0, 0x41) == NULL)) { |
| 230 | printf("Mode 1 PID 1 request failed\n"); |
| 231 | return CMD_OK; |
| 232 | } |
| 233 | |
| 234 | /* And process results */ |
| 235 | incomplete_monitors = 0; |
| 236 | for (j=0, ep=ecu_info; j<ecu_count; j++, ep++) { |
| 237 | if (ep->mode1_data[1].type == TYPE_GOOD) { |
| 238 | int supported, value; |
| 239 | |
| 240 | for (i=0; i<12; i++) { |
| 241 | text = test_names[i]; |
| 242 | if (text == NULL) { |
| 243 | continue; |
| 244 | } |
| 245 | if (i<4) { |
| 246 | supported = (ep->mode1_data[1].data[3]>>i)&1; |
| 247 | value = (ep->mode1_data[1].data[3]>>(i+4))&1; |
| 248 | } else { |
| 249 | supported = (ep->mode1_data[1].data[4]>>(i-4))&1; |
| 250 | value = (ep->mode1_data[1].data[5]>>(i-4))&1; |
| 251 | } |
| 252 | if (supported && value) { |
| 253 | incomplete_monitors |= 1<<i; |
| 254 | } |
| 255 | if (ecu_count > 1) { |
| 256 | printf("ECU %u: ", j); |
| 257 | } |
| 258 | printf("%s: ", text); |
| 259 | if (supported) { |
| 260 | printf("%sComplete\n", value?"NOT ":""); |
| 261 | } else { |
| 262 | printf("Not Supported\n"); |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | incomplete_continuous = 0; |
| 269 | incomplete_trip = 0; |
| 270 | for (i=0; i<12; i++) { |
| 271 | if (incomplete_monitors & (1<<i)) { |
| 272 | if (i<4) { |
| 273 | incomplete_continuous++; |
| 274 | } else { |
| 275 | incomplete_trip++; |
| 276 | } |
| 277 | } |
| 278 | } |
| 279 | printf("Number of incomplete continuous monitors: "); |
| 280 | printf(incomplete_continuous?"%d\n":"None\n", incomplete_continuous); |
| 281 | printf("Number of incomplete non-continuous monitors: "); |
| 282 | printf(incomplete_trip?"%d\n":"None\n", incomplete_trip); |
| 283 | printf("Total number of incomplete monitors: "); |
| 284 | printf((incomplete_continuous+incomplete_trip)?"%d\n":"None\n", (incomplete_continuous+incomplete_trip)); |
| 285 | |
| 286 | return CMD_OK; |
| 287 | } |
| 288 | |