BOROSOFTWAREAll work →
scantool/diag_l7_d2.c 401 lines
1 finding in this fileD24L116
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 * Diag
24 *
25 * Volvo D2 protocol application layer
26 *
27 * This protocol is used by the engine and chassis ECUs for extended
28 * diagnostics on the 1996-1998 Volvo 850, S40, C70, S70, V70, XC70, V90 and
29 * possibly other models. On the aforementioned models, it is used over the
30 * K-line (diag_l2_d2). It seems that the same protocol is also used over
31 * CAN bus on more recent models.
32 *
33 * Information on this protocol is available at:
34 * http://jonesrh.info/volvo850/volvo_850_obdii_faq.rtf
35 * Thanks to Richard H. Jones for sharing this information.
36 *
37 */
38
39#include <stdint.h>
40#include <stdbool.h>
41#include <string.h>
42#include <stdio.h>
43
44#include "diag.h"
45#include "diag_err.h"
46#include "diag_l2.h"
47#include "diag_l7_d2.h"
48
49/*
50 * Service Identifier hex values are in the manufacturer defined range.
51 * The service names used here are based on KWP2000. Original service names
52 * are unknown. Request and response message formats for these services are
53 * NOT according to KWP2000.
54 */
55enum {
56 stopDiagnosticSession = 0xA0,
57 testerPresent = 0xA1,
58 readDataByLocalIdentifier = 0xA5,
59 readDataByLongLocalIdentifier = 0xA6, /* CAN bus only? */
60 readMemoryByAddress = 0xA7,
61 readFreezeFrameByDTC = 0xAD,
62 readDiagnosticTroubleCodes = 0xAE,
63 clearDiagnosticInformation = 0xAF,
64 inputOutputControlByLocalIdentifier = 0xB0,
65 startRoutineByLocalIdentifier = 0xB2,
66 readNVByLocalIdentifier = 0xB9
67} service_id;
68
69/*
70 * Indicates whether a response was a positive acknowledgement of the request.
71 */
72static bool success_p(struct diag_msg *req, struct diag_msg *resp) {
73 if (resp->len < 1) {
74 return false;
75 }
76
77 if (resp->data[0] != (req->data[0] ^ 0x40)) {
78 return false;
79 }
80
81 if (resp->len > 2 && resp->data[1] != req->data[1]) {
82 return false;
83 }
84
85 return true;
86}
87
88/*
89 * Verify communication with the ECU.
90 */
91int diag_l7_d2_ping(struct diag_l2_conn *d_l2_conn) {
92 uint8_t req[] = { testerPresent };
93 int errval = 0;
94 struct diag_msg msg = {0};
95 struct diag_msg *resp = NULL;
96
97 msg.data = req;
98 msg.len = sizeof(req);
99
100 resp = diag_l2_request(d_l2_conn, &msg, &errval);
101 if (resp == NULL) {
102 return errval;
103 }
104
105 if (success_p(&msg, resp)) {
106 diag_freemsg(resp);
107 return 0;
108 }
109
110 diag_freemsg(resp);
111 return DIAG_ERR_ECUSAIDNO;
112}
113
114/* The request message for reading memory */
115static int read_MEMORY_req(uint8_t **msgout, unsigned int *msglen, uint16_t addr, uint8_t count) {
116 static uint8_t req[] = { readMemoryByAddress, 0, 99, 99, 1, 99 };
117
118 req[2] = (addr>>8)&0xff;
119 req[3] = addr&0xff;
120 req[5] = count;
121 *msgout = req;
122 *msglen = sizeof(req);
123 return 0;
124}
125
126/* The request message for reading live data by 1-byte identifier */
127static int read_LIVEDATA_req(uint8_t **msgout, unsigned int *msglen, uint16_t addr, UNUSED(uint8_t count)) {
128 static uint8_t req[] = { readDataByLocalIdentifier, 99, 1 };
129
130 req[1] = addr&0xff;
131 if (addr > 0xff) {
132 fprintf(stderr, FLFMT "read_LIVEDATA_req invalid address %x\n", FL, addr);
133 return DIAG_ERR_GENERAL;
134 }
135
136 *msgout = req;
137 *msglen = sizeof(req);
138 return 0;
139}
140
141/* The request message for reading live data by 2-byte ident (CAN bus only?) */
142static int read_LIVEDATA2_req(uint8_t **msgout, unsigned int *msglen, uint16_t addr, UNUSED(uint8_t count)) {
143 static uint8_t req[] = { readDataByLongLocalIdentifier, 99, 99, 1 };
144 req[1] = (addr>>8)&0xff;
145 req[2] = addr&0xff;
146 *msgout = req;
147 *msglen = sizeof(req);
148 return 0;
149}
150
151/* The request message for reading non-volatile data */
152static int read_NV_req(uint8_t **msgout, unsigned int *msglen, uint16_t addr, UNUSED(uint8_t count)) {
153 static uint8_t req[] = { readNVByLocalIdentifier, 99 };
154
155 req[1] = addr&0xff;
156 if (addr > 0xff) {
157 fprintf(stderr, FLFMT "read_NV_req invalid address %x\n", FL, addr);
158 return DIAG_ERR_GENERAL;
159 }
160
161 *msgout = req;
162 *msglen = sizeof(req);
163 return 0;
164}
165
166/* The request message for reading freeze frames */
167static int read_FREEZE_req(uint8_t **msgout, unsigned int *msglen, uint16_t addr, UNUSED(uint8_t count)) {
168 static uint8_t req[] = { readFreezeFrameByDTC, 99, 0 };
169
170 req[1] = addr&0xff;
171 if (addr > 0xff) {
172 fprintf(stderr, FLFMT "read_FREEZE_req invalid address %x\n", FL, addr);
173 return DIAG_ERR_GENERAL;
174 }
175
176 *msgout = req;
177 *msglen = sizeof(req);
178 return 0;
179}
180
181/*
182 * Read memory, live data or non-volatile data.
183 *
184 * Return value is actual byte count received, or negative on failure.
185 *
186 * For memory reads, a successful read always copies the exact number of bytes
187 * requested into the output buffer.
188 *
189 * For live data, non-volatile data and freeze frame reads, copies up to the
190 * number of bytes requested. Returns the actual byte count received, which may
191 * be more or less than the number of bytes requested.
192 */
193int diag_l7_d2_read(struct diag_l2_conn *d_l2_conn, enum l7_namespace ns, uint16_t addr, int buflen, uint8_t *out) {
194 struct diag_msg req = {0};
195 struct diag_msg *resp = NULL;
196 int datalen;
197 int rv;
198
199 switch (ns) {
200 case NS_MEMORY:
201 rv = read_MEMORY_req(&req.data, &req.len, addr, buflen);
202 break;
203 case NS_LIVEDATA:
204 rv = read_LIVEDATA_req(&req.data, &req.len, addr, buflen);
205 break;
206 case NS_LIVEDATA2:
207 rv = read_LIVEDATA2_req(&req.data, &req.len, addr, buflen);
208 break;
209 case NS_NV:
210 rv = read_NV_req(&req.data, &req.len, addr, buflen);
211 break;
212 case NS_FREEZE:
213 rv = read_FREEZE_req(&req.data, &req.len, addr, buflen);
214 break;
215 default:
216 fprintf(stderr, FLFMT "diag_l7_d2_read invalid namespace %d\n", FL, ns);
217 return DIAG_ERR_GENERAL;
218 }
219
220 if (rv != 0) {
221 return rv;
222 }
223
224 resp = diag_l2_request(d_l2_conn, &req, &rv);
225 if (resp == NULL) {
226 return rv;
227 }
228
229 if (resp->len<2 || !success_p(&req, resp)) {
230 diag_freemsg(resp);
231 return DIAG_ERR_ECUSAIDNO;
232 }
233
234 if (ns==NS_MEMORY) {
235 if (resp->len!=(unsigned int)buflen+4 || memcmp(req.data+1, resp->data+1, 3)!=0) {
236 diag_freemsg(resp);
237 return DIAG_ERR_ECUSAIDNO;
238 }
239 memcpy(out, resp->data+4, buflen);
240 diag_freemsg(resp);
241 return buflen;
242 }
243
244 datalen = resp->len - 2;
245 if (datalen > 0) {
246 memcpy(out, resp->data + 2,
247 (datalen > buflen) ? buflen : datalen);
248 }
249 diag_freemsg(resp);
250 return datalen;
251}
252
253/*
254 * Retrieve list of stored DTCs.
255 */
256int diag_l7_d2_dtclist(struct diag_l2_conn *d_l2_conn, int buflen, uint8_t *out) {
257 uint8_t req[] = { readDiagnosticTroubleCodes, 1 };
258 int errval = 0;
259 struct diag_msg msg = {0};
260 struct diag_msg *resp = NULL;
261 int count;
262
263 msg.data = req;
264 msg.len = sizeof(req);
265
266 resp = diag_l2_request(d_l2_conn, &msg, &errval);
267 if (resp == NULL) {
268 return errval;
269 }
270
271 if (resp->len<2 || !success_p(&msg, resp)) {
272 diag_freemsg(resp);
273 return DIAG_ERR_ECUSAIDNO;
274 }
275
276 count = resp->len - 2;
277 memcpy(out, resp->data+2, (buflen<count)?buflen:count);
278
279 if (resp->len == 14) {
280 /*
281 * If there are more than 12 DTCs, ECU will send multiple
282 * responses to a single readDiagnosticTroubleCodes request.
283 * Currently we just try to throw away any additional DTCs
284 * after the first response message.
285 */
286 (void)diag_l2_recv(d_l2_conn, 1000, NULL, NULL);
287 fprintf(stderr, "Warning: retrieving only first 12 DTCs\n");
288 }
289
290 diag_freemsg(resp);
291 return count;
292}
293
294/*
295 * Attempt to clear stored DTCs.
296 *
297 * Returns 0 if there were no DTCs, 1 if there was at least one DTC and the
298 * ECU returned positive acknowledgement for the clear request, <0 for errors.
299 */
300int diag_l7_d2_cleardtc(struct diag_l2_conn *d_l2_conn) {
301 uint8_t req[] = { clearDiagnosticInformation, 1 };
302 uint8_t buf[1];
303 struct diag_msg msg = {0};
304 struct diag_msg *resp = NULL;
305 int rv;
306
307 /*
308 * ECU will reject clearDiagnosticInformation unless preceded by
309 * readDiagnosticTroubleCodes.
310 */
311 rv = diag_l7_d2_dtclist(d_l2_conn, sizeof(buf), buf);
312 if (rv < 0) {
313 return rv;
314 }
315 if (rv == 0) {
316 return 0;
317 }
318
319 msg.data = req;
320 msg.len = sizeof(req);
321 resp = diag_l2_request(d_l2_conn, &msg, &rv);
322 if (resp == NULL) {
323 return rv;
324 }
325
326 if (resp->len==2 && success_p(&msg, resp)) {
327 diag_freemsg(resp);
328 return 1;
329 }
330
331 diag_freemsg(resp);
332 return DIAG_ERR_ECUSAIDNO;
333}
334
335/*
336 * Activate an output or substitute an input or internal value.
337 *
338 * The ECU will activate the specified output the requested number of times,
339 * or until the diagnostic session is stopped. The duration of each activation
340 * cycle depends on which output is specified.
341 */
342int diag_l7_d2_io_control(struct diag_l2_conn *d_l2_conn, uint8_t id, uint8_t reps) {
343 uint8_t long_req[] = { inputOutputControlByLocalIdentifier, id, 0x32, reps };
344 uint8_t short_req[] = { inputOutputControlByLocalIdentifier, id };
345 struct diag_msg msg = {0};
346 struct diag_msg *resp = NULL;
347 int rv;
348
349 if (reps > 0) {
350 msg.data = long_req;
351 msg.len = sizeof(long_req);
352 } else {
353 msg.data = short_req;
354 msg.len = sizeof(short_req);
355 }
356
357 resp = diag_l2_request(d_l2_conn, &msg, &rv);
358 if (resp == NULL) {
359 return rv;
360 }
361
362 if (resp->len==2 && success_p(&msg, resp)) {
363 diag_freemsg(resp);
364 return 0;
365 }
366
367 /*
368 * ECU returns 7F B0 11 for invalid ID, or 7F B0 21 if a
369 * previous inputOutputControlByLocalIdentifier is still in
370 * progress. For now we return DIAG_ERR_ECUSAIDNO for any
371 * error code.
372 */
373 diag_freemsg(resp);
374 return DIAG_ERR_ECUSAIDNO;
375}
376
377/*
378 * Start a routine.
379 */
380int diag_l7_d2_run_routine(struct diag_l2_conn *d_l2_conn, uint8_t id) {
381 uint8_t req[] = { startRoutineByLocalIdentifier, id };
382 struct diag_msg msg = {0};
383 struct diag_msg *resp = NULL;
384 int rv;
385
386 msg.data = req;
387 msg.len = sizeof(req);
388 resp = diag_l2_request(d_l2_conn, &msg, &rv);
389 if (resp == NULL) {
390 return rv;
391 }
392
393 if (resp->len==2 && success_p(&msg, resp)) {
394 diag_freemsg(resp);
395 return 0;
396 }
397
398 diag_freemsg(resp);
399 return DIAG_ERR_ECUSAIDNO;
400}
401