BOROSOFTWAREAll work →
scantool/diag_l7_kwp71.c 289 lines
2 findings in this fileA11L124A12L236
1/*
2 * freediag - Vehicle Diagnostic Utility
3 *
4 *
5 * Copyright (C) 2017 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 * Diag
24 *
25 * KWP71 application layer
26 *
27 * KWP71 is used by Bosch ECUs in various European cars from the 1990s.
28 * KWP1281 is an extended(?) version of KWP71 with faster timing.
29 *
30 * KWP71 and KWP1281 are similar enough that this L7 can be used with
31 * freediag's VAG (KWP1281) L2 with at least some KWP71 capable ECUs.
32 *
33 */
34
35#include <stdint.h>
36#include <string.h>
37#include <stdio.h>
38
39#include "diag.h"
40#include "diag_os.h"
41#include "diag_err.h"
42#include "diag_l2.h"
43#include "diag_l7.h"
44
45/*
46 * The block title names used here are based on KWP2000. Original block titles
47 * are unknown. Request and response message formats for these blocks are
48 * NOT according to KWP2000.
49 *
50 * Not all block titles are supported by all ECUs.
51 */
52enum {
53 /* requests */
54 readMemoryByAddress = 0x01,
55 writeMemoryByAddress = 0x02,
56 readROMByAddress = 0x03,
57 clearDiagnosticInformation = 0x05,
58 stopDiagnosticSession = 0x06,
59 readDiagnosticTroubleCodes = 0x07,
60 readADC = 0x08,
61 /* responses - no numerical relation to corresponding request */
62 ack = 0x09, /* doubles as testerPresent request */
63 nak = 0x0A,
64 writeMemoryByAddress_resp = 0xED,
65 readADC_resp = 0xFB,
66 readDiagnosticTroubleCodes_resp = 0xFC,
67 readROMByAddress_resp = 0xFD,
68 readMemoryByAddress_resp = 0xFE,
69} block_titles;
70
71/*
72 * Verify communication with the ECU.
73 */
74int diag_l7_kwp71_ping(struct diag_l2_conn *d_l2_conn) {
75 int errval = 0;
76 struct diag_msg msg = {0};
77 struct diag_msg *resp = NULL;
78
79 msg.type = ack;
80
81 resp = diag_l2_request(d_l2_conn, &msg, &errval);
82 if (resp == NULL) {
83 return errval;
84 }
85
86 if (resp->type == ack) {
87 diag_freemsg(resp);
88 return 0;
89 }
90
91 diag_freemsg(resp);
92 return DIAG_ERR_ECUSAIDNO;
93}
94
95
96#define KWP71_REQSIZE 3
97/* Fill the request message for reading memory */
98static int read_MEMORY_req(struct diag_msg *msg, uint8_t *wantresp, uint16_t addr, uint8_t count) {
99 uint8_t *data=msg->data;
100
101 msg->type = readMemoryByAddress;
102 msg->len = 3;
103 data[0] = count;
104 data[1] = (addr>>8)&0xff;
105 data[2] = addr&0xff;
106 *wantresp = readMemoryByAddress_resp;
107 return 0;
108}
109
110/* Fill the request message for reading ROM */
111static int read_ROM_req(struct diag_msg *msg, uint8_t *wantresp, uint16_t addr, uint8_t count) {
112 uint8_t *data=msg->data;
113
114 msg->type = readROMByAddress;
115 msg->len = 3;
116 data[0] = count;
117 data[1] = (addr>>8)&0xff;
118 data[2] = addr&0xff;
119 *wantresp = readROMByAddress_resp;
120 return 0;
121}
122
123/* The request message for taking ADC readings */
124static int read_ADC_req(struct diag_msg *msg, uint8_t *wantresp, uint16_t addr) {
125 uint8_t *data=msg->data;
126
127 if (addr > 0xff) {
128 fprintf(stderr, FLFMT "read_ADC_req invalid address %x\n", FL, addr);
129 return DIAG_ERR_GENERAL;
130 }
131
132 msg->type = readADC;
133 msg->len = 3;
134 data[0] = addr;
135 *wantresp = readADC_resp;
136 return 0;
137}
138
139/*
140 * Read memory, ROM or ADC.
141 *
142 * Return value is actual byte count received, or negative on failure.
143 *
144 * For memory and ROM reads, a successful read always copies the exact number
145 * of bytes requested into the output buffer.
146 *
147 * For ADC reads, reads a single 2-byte value and copies up to the number of
148 * bytes requested. Returns the actual byte count received.
149 */
150int diag_l7_kwp71_read(struct diag_l2_conn *d_l2_conn, enum l7_namespace ns, uint16_t addr, int buflen, uint8_t *out) {
151 struct diag_msg req; //build request message in this
152 uint8_t request_data[KWP71_REQSIZE];
153 struct diag_msg *resp = NULL;
154 uint8_t wantresp;
155 int rv;
156
157 req.data = request_data;
158 switch (ns) {
159 case NS_MEMORY:
160 rv = read_MEMORY_req(&req, &wantresp, addr, buflen);
161 break;
162 case NS_ROM:
163 rv = read_ROM_req(&req, &wantresp, addr, buflen);
164 break;
165 case NS_ADC:
166 rv = read_ADC_req(&req, &wantresp, addr);
167 break;
168 default:
169 fprintf(stderr, FLFMT "diag_l7_kwp71_read invalid namespace %d\n", FL, ns);
170 return DIAG_ERR_GENERAL;
171 }
172
173 if (rv != 0) {
174 return rv;
175 }
176
177 resp = diag_l2_request(d_l2_conn, &req, &rv);
178 if (resp == NULL) {
179 return rv;
180 }
181
182 if (resp->type!=wantresp) {
183 diag_freemsg(resp);
184 return DIAG_ERR_ECUSAIDNO;
185 }
186
187 if (ns==NS_ADC && resp->len!=2) {
188 diag_freemsg(resp);
189 return DIAG_ERR_ECUSAIDNO;
190 }
191 if (ns != NS_ADC && resp->len != (unsigned int)buflen) {
192 diag_freemsg(resp);
193 return DIAG_ERR_ECUSAIDNO;
194 }
195 memcpy(out, resp->data, (resp->len>(unsigned int)buflen)?(unsigned int)buflen:resp->len);
196 rv = (resp->len > (unsigned int)buflen) ? (unsigned int)buflen:resp->len;
197 diag_freemsg(resp);
198 return rv;
199}
200
201/*
202 * Retrieve list of stored DTCs.
203 *
204 * Seems to return 5 bytes per DTC, but output format and size may vary by ECU.
205 *
206 * Returns the actual number of bytes read, even if the supplied buffer was too
207 * small for the full response.
208 */
209int diag_l7_kwp71_dtclist(struct diag_l2_conn *d_l2_conn, int buflen, uint8_t *out) {
210 int errval = 0;
211 struct diag_msg msg = {0};
212 struct diag_msg *resp = NULL;
213 int count;
214
215 msg.type = readDiagnosticTroubleCodes;
216
217 resp = diag_l2_request(d_l2_conn, &msg, &errval);
218 if (resp == NULL) {
219 return errval;
220 }
221
222 count = resp->len;
223
224 if (resp->type!=readDiagnosticTroubleCodes_resp) {
225 diag_freemsg(resp);
226 return DIAG_ERR_ECUSAIDNO;
227 }
228
229 if (count == 1 && resp->data[0] == 0) { /* No DTCs set */
230 count = 0;
231 }
232
233 if (count > 0) {
234 memcpy(out, resp->data, (buflen < count) ? buflen : count);
235 }
236 diag_freemsg(resp);
237
238 if (resp->next != NULL) {
239 /*
240 * If there are more than 2 DTCs, ECU will send multiple
241 * responses. For now, we only look at the DTCs in the first
242 * response.
243 */
244 fprintf(stderr, "Warning: retrieving only first %d DTCs\n", count/5);
245 }
246
247 return count;
248}
249
250/*
251 * Attempt to clear stored DTCs.
252 *
253 * Returns 0 if there were no DTCs, 1 if there was at least one DTC and the
254 * ECU returned positive acknowledgement for the clear request, <0 for errors.
255 */
256int diag_l7_kwp71_cleardtc(struct diag_l2_conn *d_l2_conn) {
257 uint8_t buf[1];
258 struct diag_msg msg = {0};
259 struct diag_msg *resp = NULL;
260 int rv;
261
262 /*
263 * Call readDiagnosticTroubleCodes first, even though it's not
264 * required in KWP71.
265 */
266 rv = diag_l7_kwp71_dtclist(d_l2_conn, sizeof(buf), buf);
267 if (rv < 0) {
268 return rv;
269 }
270 if (rv == 0) {
271 return 0;
272 }
273
274 diag_os_millisleep(500);
275
276 msg.type = clearDiagnosticInformation;
277 resp = diag_l2_request(d_l2_conn, &msg, &rv);
278 if (resp == NULL) {
279 return rv;
280 }
281
282 if (resp->type == ack) {
283 diag_freemsg(resp);
284 return 1;
285 }
286 diag_freemsg(resp);
287 return DIAG_ERR_ECUSAIDNO;
288}
289