BOROSOFTWAREAll work →
scantool/diag_l0_sim.c 819 lines
1/*
2 * freediag - Vehicle Diagnostic Utility
3 *
4 *
5 * Copyright (C) 2004 Vasco Nevoa.
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 * This is not an interface driver like all the other L0 files.
24 * It implements a "Car Simulator" instead of a true ECU interface.
25 * The intention is to free freediag from the need of an actual car,
26 * when all you want to do is to test a protocol stack or a client
27 * application.
28 *
29 * This is implemented as L0 and not L1, 2, or 3, because this way it allows
30 * us to test the protocol stack as well, not just the applications.
31 *
32 * In this L0 "pseudo-driver", the serial port is not used, and in its
33 * place is a simple file, such as "freediag_carsim.db". This file holds
34 * one or more responses for each OBDII request. Feel free to enlarge
35 * that file with valid information for your case, customise it at will
36 * for your own tests. The format is pretty raw (message bytes in hexadecimal),
37 * with allowance for comments (lines started with "#") and a very small and
38 * rigid syntax (check the comments in the file).
39 *
40 */
41
42#include <assert.h>
43#include <stdint.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h> // str**()
47#include <ctype.h>
48#include <stdbool.h>
49#include <math.h> // sin()
50
51#include "diag.h"
52#include "diag_err.h"
53#include "diag_os.h"
54#include "diag_l0.h"
55#include "diag_l1.h"
56#include "diag_cfg.h"
57
58#include "utlist.h"
59
60
61/**************************************************/
62// LOCAL DATATYPES AND GLOBALS:
63/**************************************************/
64
65
66extern const struct diag_l0 diag_l0_sim;
67
68static const char *simfile_default=DB_FILE; //default filename
69
70#define MAX_RESP_LEN 260 //iso14230 can be 4bytes header + 255 bytes payload, + 1 checksum
71
72// ECU responses linked list:
73struct sim_ecu_response {
74 char *text; // unparsed text for the response!
75 uint8_t *data; // parsed final response.
76 unsigned len; // final response length.
77 struct sim_ecu_response *next;
78};
79
80/* Internal state (struct diag_l0_device->l0_int) */
81struct sim_device {
82 int protocol;
83 FILE *fp; // DB file pointer.
84 // Configuration variables.
85 // These affect the kind of flags we should return.
86 // This makes the simulator configurable towards using
87 // or not the L2 framing and CRC/Checksums.
88 // These boolean flags are to be programmed with values
89 // from the DB file in use.
90 bool dataonly; /* messages are sent/received without headers or checksums; required for J1850 */
91 bool nocksum; /* messages are sent/received without checksums */
92 bool framed; /* responses must be considered as complete frames; dataonly and nocksum imply this */
93 bool fullinit; /* indicate that l0 does full init */
94
95 int proto_restrict; /* (optional) only accept connections matching this proto */
96
97 struct cfgi simfile;
98
99 uint8_t sim_last_ecu_request[MAX_RESP_LEN]; // Copy of most recent request.
100 struct sim_ecu_response *sim_last_ecu_responses; // For keeping all the responses to the last request.
101};
102
103
104
105/**************************************************/
106// FORWARD DECLARATIONS:
107/**************************************************/
108
109
110int sim_send(struct diag_l0_device *dl0d,
111 const void *data, const size_t len);
112
113int sim_recv(struct diag_l0_device *dl0d,
114 void *data, size_t len, unsigned int timeout);
115
116void sim_close(struct diag_l0_device *dl0d);
117
118/**************************************************/
119// LOCAL FUNCTIONS:
120/**************************************************/
121
122
123// Allocates one new ecu response and fills it with given text.
124static struct sim_ecu_response *sim_new_ecu_response_txt(const char *text) {
125 struct sim_ecu_response *resp;
126 int rv;
127
128 if ((rv = diag_calloc(&resp, 1))) {
129 return diag_pfwderr(rv);
130 }
131
132 resp->data = NULL;
133 resp->len = 0;
134 resp->text = NULL;
135 resp->next = NULL;
136
137 if ((text != NULL) && strlen(text)) {
138 if ((rv=diag_calloc(&(resp->text), strlen(text)+1))) {
139 free(resp);
140 return diag_pfwderr(rv);
141 }
142
143 strncpy(resp->text, text, strlen(text)); //using strlen() defeats the purpose of strncpy ...
144 }
145
146 return resp;
147}
148
149
150// Frees an ecu response and returns the next one in the list.
151static struct sim_ecu_response *sim_free_ecu_response(struct sim_ecu_response **resp) {
152 struct sim_ecu_response* next_resp = NULL;
153
154 if (resp && *resp) {
155
156 //get pointer to next one.
157 next_resp = (*resp)->next;
158
159 //free this one.
160 if ((*resp)->data) {
161 free((*resp)->data);
162 }
163 if ((*resp)->text) {
164 free((*resp)->text);
165 }
166 free(*resp);
167 *resp = NULL;
168 }
169
170 //return next one.
171 return next_resp;
172}
173
174
175// Frees all responses from the given one until the end of the list.
176static void sim_free_ecu_responses(struct sim_ecu_response **resp_pp) {
177 struct sim_ecu_response **temp_resp_pp = resp_pp;
178 uint8_t count = 0;
179
180 if ((resp_pp == NULL) || (*resp_pp == NULL)) {
181 return;
182 }
183
184 while (*temp_resp_pp != NULL) {
185 *temp_resp_pp = sim_free_ecu_response(temp_resp_pp);
186 count++;
187 }
188
189 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V,
190 FLFMT " %d responses freed from queue.\n", FL, count);
191
192 return;
193}
194
195// for debug purposes.
196static void sim_dump_ecu_responses(struct sim_ecu_response *resp_p) {
197 struct sim_ecu_response *tresp;
198 uint8_t count = 0;
199
200 LL_FOREACH(resp_p, tresp) {
201 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_DATA, DIAG_DBGLEVEL_V,
202 FLFMT "response #%d: %s", FL, count, tresp->text);
203 count++;
204 }
205
206 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_DATA, DIAG_DBGLEVEL_V,
207 FLFMT "%d responses in queue.\n", FL, count);
208 return;
209}
210
211
212// Builds a list of responses for a request, by finding them in the DB file.
213static void sim_find_responses(struct sim_ecu_response **resp_pp, FILE *fp, const uint8_t *data, const unsigned len) {
214#define TAG_REQUEST "RQ"
215#define TAG_RESPONSE "RP"
216#define VALUE_DONTCARE "XXXX"
217#define REQBYTES MAX_RESP_LEN //number of request bytes analyzed
218
219 uint8_t resp_count = 0;
220 uint8_t new_resp_count = 0;
221 uint8_t synth_req[REQBYTES];
222 char line_buf[(MAX_RESP_LEN * 5)+ 6]; // 260 response bytes * 5 ("0xYY ") + tolerance for a token ("abc1 ")
223 int end_responses = 0;
224 int request_found = 0;
225 struct sim_ecu_response *resp_p;
226
227
228 // walk to the end of the list (last valid item).
229 LL_FOREACH(*resp_pp, resp_p) {
230 resp_count++;
231 }
232
233 // go to the beginning of the DB file.
234 rewind(fp);
235
236 // search for the given request.
237 while (!request_found && !end_responses) {
238 // get a line from DB file.
239 if (fgets(line_buf, sizeof(line_buf), fp) == NULL) {
240 // EOF reached.
241 break;
242 }
243 // ignore all lines except requests.
244 if (strncmp(line_buf, TAG_REQUEST, strlen(TAG_REQUEST)) != 0) {
245 continue;
246 }
247 // synthesize up to REQBYTES bytes from DB request line.
248 unsigned int i, num;
249 char *p, *q;
250 p = line_buf + 3;
251 for (i=0; i < REQBYTES; i++) {
252 while (isspace(*p)) {
253 p++;
254 }
255 if (*p == '\0') {
256 break;
257 }
258 if (strncmp(p, VALUE_DONTCARE, strlen(VALUE_DONTCARE)) == 0) {
259 if (i < len) {
260 synth_req[i] = data[i];
261 }
262 p += strlen(VALUE_DONTCARE);
263 } else {
264 synth_req[i] = (uint8_t)strtoul(p, &q, 16);
265 if (p == q) {
266 break;
267 }
268 p = q;
269 if (!isspace(*p)) {
270 break;
271 }
272 }
273 }
274 num = i;
275 // compare given request with synthesized DB file request.
276 if ((len >= num) && (memcmp(data, synth_req, MIN(len, num)) == 0)) {
277 // got a match, now cycle the following lines for responses.
278 request_found = 1;
279 while (!end_responses) {
280 // get a line from file.
281 if (fgets(line_buf, sizeof(line_buf), fp) == NULL) {
282 // EOF reached.
283 end_responses = 1;
284 break;
285 }
286 // ignore all lines except responses.
287 if (strncmp(line_buf, TAG_RESPONSE, strlen(TAG_RESPONSE)) != 0) {
288 // if it's another request, then end the list.
289 if (strncmp(line_buf, TAG_REQUEST, strlen(TAG_REQUEST)) == 0) {
290 end_responses = 1;
291 break;
292 }
293 continue;
294 }
295 // add the new response (without the tag).
296 if (resp_count + new_resp_count == 0) {
297 // create the root of the list.
298 resp_p = *resp_pp = sim_new_ecu_response_txt(line_buf + strlen(TAG_RESPONSE) + 1);
299 if (!resp_p) {
300 fprintf(stderr, FLFMT "Could not add new response \"%s\"\n", FL, line_buf + strlen(TAG_RESPONSE) + 1);
301 end_responses=1;
302 }
303 } else {
304 // add to the end of the list.
305 resp_p->next = sim_new_ecu_response_txt(line_buf + strlen(TAG_RESPONSE) + 1);
306 if (!(resp_p->next)) {
307 fprintf(stderr, FLFMT "Could not add new response \"%s\"\n", FL, line_buf + strlen(TAG_RESPONSE) + 1);
308 end_responses=1;
309 }
310 resp_p = resp_p->next;
311 }
312 new_resp_count++;
313 }
314 }
315 }
316
317 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
318 FLFMT "%d responses queued for receive, %d new.\n",
319 FL, resp_count + new_resp_count, new_resp_count);
320 return;
321}
322
323
324// Returns a value between 0x00 and 0xFF calculated as the trigonometric
325// sine of the current system time (with a period of one second).
326static uint8_t sine1(UNUSED(uint8_t *data), UNUSED(uint8_t pos)) {
327 unsigned long now=diag_os_getms();
328 //sin() returns a float between -1.0 and 1.0
329 return (uint8_t) (0x7F * sin(now * 6.283185 / 1000));
330}
331
332// Returns a value between 0x00 and 0xFF directly proportional
333// to the value of the current system time (with a period of one second).
334static uint8_t sawtooth1(UNUSED(uint8_t *data), UNUSED(uint8_t pos)) {
335 unsigned long now=diag_os_getms();
336 return (uint8_t) (0xFF * (now % 1000));
337}
338
339// Returns a value copied from the specified position in the request.
340static uint8_t requestbyten(UNUSED(uint8_t *data), char *s, const uint8_t req[]) {
341 int index;
342 bool increment = 0;
343 bool bogus = 0;
344 uint8_t value;
345 char *p;
346
347 index = (uint8_t)strtoul(s, &p, 10);
348 if (*s == '\0') {
349 bogus = 1;
350 } else if (p[0]=='+' && p[1]=='\0') {
351 increment = 1;
352 } else if (*p != '\0') {
353 bogus = 1;
354 }
355
356 index--;
357 if (index < 0 || index >= MAX_RESP_LEN) {
358 bogus = 1;
359 }
360
361 if (bogus) {
362 fprintf(stderr, FLFMT "Invalid req* token in response\n", FL);
363 return 0;
364 }
365
366 value = req[index];
367 if (increment) {
368 value++;
369 }
370 return value;
371}
372
373// Parses a response's text to data.
374// Replaces special tokens with function results. This mangles resp_p->text, which shouldn't be a problem
375static void sim_parse_response(struct sim_ecu_response *resp_p, uint8_t req[]) {
376#define TOKEN_SINE1 "sin1"
377#define TOKEN_SAWTOOTH1 "swt1"
378#define TOKEN_ISO9141CS "cks1"
379#define TOKEN_REQUESTBYTE "req"
380#define SRESP_SIZE MAX_RESP_LEN
381
382 uint8_t synth_resp[SRESP_SIZE]; // response bytes.
383 char *cur_tok = NULL; //current token
384 char *rptr = resp_p->text; //working copy of the ptr. We will mangle resp_p->text
385 int ret;
386 int pos = 0;
387
388 // extract byte values from response line, splitting tokens at whitespace / EOL.
389 while ((cur_tok = strtok(rptr, " \t\r\n")) != NULL) {
390 if (pos >= MAX_RESP_LEN) {
391 fprintf(stderr, "Malformed db file, too many bytes on one line !");
392 break;
393 }
394 // try replacing a token with a calculated value.
395 if (strcmp(cur_tok, TOKEN_SINE1) == 0) {
396 synth_resp[pos] = sine1(synth_resp, pos);
397 } else if (strcmp(cur_tok, TOKEN_SAWTOOTH1) == 0) {
398 synth_resp[pos] = sawtooth1(synth_resp, pos);
399 } else if (strcmp(cur_tok, TOKEN_ISO9141CS) == 0) {
400 synth_resp[pos] = diag_cks1(synth_resp, pos);
401 } else if (strncmp(cur_tok, TOKEN_REQUESTBYTE,
402 strlen(TOKEN_REQUESTBYTE)) == 0) {
403 synth_resp[pos] = requestbyten(synth_resp, cur_tok + strlen(TOKEN_REQUESTBYTE), req);
404 } else {
405 // failed. try scanning element as an Hex byte.
406 unsigned int tempbyte;
407 ret = sscanf(cur_tok, "%X", &tempbyte); //can't scan direct to uint8 !
408 if (ret != 1) {
409 fprintf(stderr, FLFMT "Error parsing line: %s at position %d.\n", FL, resp_p->text, pos*5);
410 break;
411 }
412 synth_resp[pos] = (uint8_t) tempbyte;
413 }
414 pos++;
415 rptr = NULL; //strtok: continue parsing
416 }
417
418 // copy to user.
419 if (diag_calloc(&(resp_p->data),pos)) {
420 fprintf(stderr, FLFMT "Error parsing response\n", FL);
421 return;
422 }
423 memcpy(resp_p->data, synth_resp, pos);
424 resp_p->len = pos;
425}
426
427// Reads the configuration options from the file.
428// Stores them in globals.
429static void sim_read_cfg(struct sim_device *dev) {
430 FILE *fp = dev->fp;
431 char *p; // temp string pointer.
432 char line_buf[21]; // 20 chars generally enough for a config token.
433
434#define TAG_CFG "CFG"
435#define CFG_DATAONLY "DATAONLY"
436#define CFG_NOL2CKSUM "NOL2CKSUM"
437#define CFG_FRAMED "FRAMED"
438#define CFG_FULLINIT "FULLINIT"
439#define CFG_P9141 "P_9141"
440#define CFG_P14230 "P_14230"
441#define CFG_P1850P "P_J1850P"
442#define CFG_P1850V "P_J1850V"
443#define CFG_PCAN "P_CAN"
444#define CFG_PRAW "P_RAW"
445
446 dev->dataonly = 0;
447 dev->nocksum = 0;
448 dev->fullinit = 0;
449 dev->proto_restrict = 0;
450
451 // search for all config lines.
452 while (1) {
453 // get a line from DB file.
454 if (fgets(line_buf, 20, fp) == NULL) {
455 // EOF reached.
456 break;
457 }
458 // ignore all lines except configs.
459 if (strncmp(line_buf, TAG_CFG, strlen(TAG_CFG)) != 0) {
460 continue;
461 }
462 // get the config values.
463 p = line_buf + strlen(TAG_CFG) + 1;
464
465 if (strncmp(p, CFG_DATAONLY, strlen(CFG_DATAONLY)) == 0) {
466 dev->dataonly = 1;
467 continue;
468 }
469 if (strncmp(p, CFG_NOL2CKSUM, strlen(CFG_NOL2CKSUM)) == 0) {
470 dev->nocksum = 1;
471 continue;
472 }
473 if (strncmp(p, CFG_FRAMED, strlen(CFG_FRAMED)) == 0) {
474 dev->framed = 1;
475 } else if (strncmp(p, CFG_FULLINIT, strlen(CFG_FULLINIT)) == 0) {
476 dev->fullinit = 1;
477 } else if (strncmp(p, CFG_P9141, strlen(CFG_P9141)) == 0) {
478 dev->proto_restrict=DIAG_L1_ISO9141;
479 continue;
480 } else if (strncmp(p, CFG_P14230, strlen(CFG_P14230)) == 0) {
481 dev->proto_restrict=DIAG_L1_ISO14230;
482 continue;
483 } else if (strncmp(p, CFG_P1850P, strlen(CFG_P1850P)) == 0) {
484 dev->proto_restrict=DIAG_L1_J1850_PWM;
485 continue;
486 } else if (strncmp(p, CFG_P1850V, strlen(CFG_P1850V)) == 0) {
487 dev->proto_restrict=DIAG_L1_J1850_VPW;
488 continue;
489 } else if (strncmp(p, CFG_PCAN, strlen(CFG_PCAN)) == 0) {
490 dev->proto_restrict=DIAG_L1_CAN;
491 continue;
492 } else if (strncmp(p, CFG_PRAW, strlen(CFG_PRAW)) == 0) {
493 dev->proto_restrict=DIAG_L1_RAW;
494 continue;
495 }
496 }
497}
498
499/**************************************************/
500// INTERFACE FUNCTIONS:
501/**************************************************/
502
503
504// Initializes the simulator.
505int sim_init(void) {
506 return 0;
507}
508
509/* fill & init new dl0d */
510int sim_new(struct diag_l0_device *dl0d) {
511 int rv;
512 struct sim_device *dev;
513
514 // Create sim_device:
515 if ((rv = diag_calloc(&dev, 1))) {
516 return diag_ifwderr(rv);
517 }
518
519 dl0d->l0_int = dev;
520
521 //init configurable params:
522 if (diag_cfgn_str(&dev->simfile, simfile_default,
523 "Simulation file to use as data input", "simfile")) {
524 free(dev);
525 return diag_iseterr(DIAG_ERR_GENERAL);
526 }
527 dev->simfile.next = NULL; //mark as first/only/last item in the list
528 return 0;
529}
530
531/* Clear + free the contents of dl0d; assumes L0 was closed first */
532void sim_del(struct diag_l0_device *dl0d) {
533 struct sim_device *dev;
534
535 assert(dl0d !=NULL);
536
537 dev = (struct sim_device *)dl0d->l0_int;
538
539 if (!dev) {
540 return;
541 }
542
543 diag_cfg_clear(&dev->simfile);
544 free(dev);
545
546 return;
547}
548
549// Opens the simulator DB file
550int sim_open(struct diag_l0_device *dl0d, int iProtocol) {
551 struct sim_device *dev;
552 const char *simfile;
553
554 assert(dl0d != NULL);
555
556 dev = (struct sim_device *) dl0d->l0_int;
557 simfile = dev->simfile.val.str;
558
559 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V,
560 FLFMT "open simfile %s proto=%d\n", FL, simfile, iProtocol);
561
562 dev->protocol = iProtocol;
563 dev->sim_last_ecu_responses = NULL;
564
565 // Open the DB file:
566 if ((dev->fp = fopen(simfile, "r")) == NULL) {
567 fprintf(stderr, FLFMT "Unable to open file \"%s\": ", FL, simfile);
568 return diag_iseterr(DIAG_ERR_GENERAL);
569 }
570
571 rewind(dev->fp);
572
573 // Read the configuration flags from the db file:
574 sim_read_cfg(dev);
575
576 /* if a specific proto was set, refuse a mismatched connection */
577 if (dev->proto_restrict) {
578 if (dev->proto_restrict != iProtocol) {
579 sim_close(dl0d);
580 return diag_iseterr(DIAG_ERR_PROTO_NOTSUPP);
581 }
582 }
583
584 dl0d->opened = 1;
585 return 0;
586}
587
588
589// Closes the simulator DB file; cleanup after _sim_open()
590void sim_close(struct diag_l0_device *dl0d) {
591 assert(dl0d != NULL);
592 //if (!dl0d) return;
593
594 struct sim_device *dev = (struct sim_device *)dl0d->l0_int;
595 assert(dev != NULL);
596
597 // If debugging, print to stderr.
598 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V,
599 FLFMT "dl0d=%p closing simfile\n", FL, (void *)dl0d);
600
601 sim_free_ecu_responses(&dev->sim_last_ecu_responses);
602
603
604 if (dev->fp != NULL) {
605 fclose(dev->fp);
606 dev->fp = NULL;
607 }
608
609 dl0d->opened = 0;
610 return;
611}
612
613
614// Simulates the bus initialization.
615static int sim_initbus(struct diag_l0_device *dl0d, struct diag_l1_initbus_args *in) {
616 struct sim_device *dev;
617 uint8_t synch_patt[1];
618 const uint8_t sim_break = 0x00;
619
620 dev = (struct sim_device *)dl0d->l0_int;
621
622 sim_free_ecu_responses(&dev->sim_last_ecu_responses);
623
624 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
625 FLFMT "device link %p info %p initbus type %d\n",
626 FL, (void *)dl0d, (void *)dev, in->type);
627
628 if (!dev) {
629 return diag_iseterr(DIAG_ERR_INIT_NOTSUPP);
630 }
631
632 if (dev->fullinit) {
633 return 0;
634 }
635
636 switch (in->type) {
637 case DIAG_L1_INITBUS_FAST:
638 // Send break.
639 // We simulate a break with a single "0x00" char.
640 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_INIT, DIAG_DBGLEVEL_V,
641 FLFMT "Sending: BREAK!\n", FL);
642 sim_send(dl0d, &sim_break, 1);
643 break;
644 case DIAG_L1_INITBUS_5BAUD:
645 // Send Service Address (as if it was at 5baud).
646 sim_send(dl0d, &in->addr, 1);
647 // Receive Synch Pattern (as if it was at 10.4kbaud).
648 sim_recv(dl0d, synch_patt, 1, 0);
649 break;
650 default:
651 return diag_iseterr(DIAG_ERR_INIT_NOTSUPP);
652 break;
653 }
654
655 return 0;
656}
657
658
659// Simulates the send of a request.
660// Returns 0 on success, -1 on failure.
661// Should be called with the full message to send, because
662// CARSIM behaves like a smart interface (does P4).
663// Gets the list of responses from the DB file for the given request.
664int sim_send(struct diag_l0_device *dl0d,
665 const void *data, const size_t len) {
666 struct sim_device *dev = dl0d->l0_int;
667
668 if (len == 0) {
669 return diag_iseterr(DIAG_ERR_BADLEN);
670 }
671
672 if (len > MAX_RESP_LEN) {
673 fprintf(stderr, FLFMT "Error : calling sim_send with len (%u) > MAX_RESP_LEN !\n", FL, (unsigned int) len);
674 return diag_iseterr(DIAG_ERR_GENERAL);
675 }
676
677 if (dev->sim_last_ecu_responses != NULL) {
678 fprintf(stderr, FLFMT "AAAHHH!!! You're sending a new request before reading all previous responses!!! \n", FL);
679 return diag_iseterr(DIAG_ERR_GENERAL);
680 }
681
682 DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, data, len,
683 FLFMT "dl0d=%p sending %u bytes; ", FL, (void *)dl0d, (unsigned int)len);
684
685 // Store a copy of this request for use by req* function tokens.
686 memcpy(dev->sim_last_ecu_request, data, len);
687
688 // Build the list of responses for this request.
689 sim_find_responses(&dev->sim_last_ecu_responses, dev->fp, data, (unsigned) len);
690
691 sim_dump_ecu_responses(dev->sim_last_ecu_responses);
692
693 return 0;
694}
695
696
697// Gets present ECU response from the prepared list.
698// Returns ECU response with parsed data (if applicable).
699// Returns number of bytes read.
700int sim_recv(struct diag_l0_device *dl0d,
701 void *data, size_t len, unsigned int timeout) {
702 size_t xferd;
703 struct sim_ecu_response *resp_p = NULL;
704 struct sim_device *dev = dl0d->l0_int;
705
706 if (!len) {
707 return diag_iseterr(DIAG_ERR_BADLEN);
708 }
709 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
710 FLFMT "link %p recv upto %ld bytes timeout %u\n",
711 FL, (void *)dl0d, (long)len, timeout);
712
713 // "Receive from the ECU" a response.
714 resp_p = dev->sim_last_ecu_responses;
715 if (resp_p != NULL) {
716 // Parse the response (replace simulated values if needed).
717 sim_parse_response(resp_p, dev->sim_last_ecu_request);
718 // Copy to client.
719 xferd = MIN(resp_p->len, len);
720 memcpy(data, resp_p->data, xferd);
721 // Free the present response in the list (and walk to the next one).
722 dev->sim_last_ecu_responses = sim_free_ecu_response(&dev->sim_last_ecu_responses);
723 } else {
724 // Nothing to receive, simulate timeout on return.
725 xferd = 0;
726 memset(data, 0, len);
727 }
728
729 DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V, data, xferd,
730 FLFMT "dl0d=%p recv %d bytes; ", FL, (void *)dl0d, (int) len);
731
732 return (xferd == 0 ? DIAG_ERR_TIMEOUT : (int) xferd);
733}
734
735
736
737// Returns the interface's physical flags.
738// The simulator doesn't need half-duplex or
739// P4 timing, and implements all types of init.
740uint32_t sim_getflags(struct diag_l0_device *dl0d) {
741 struct sim_device *dev = dl0d->l0_int;
742 int ret;
743
744 ret = DIAG_L1_SLOW |
745 DIAG_L1_FAST |
746 DIAG_L1_PREFFAST |
747 DIAG_L1_DOESP4WAIT |
748 DIAG_L1_AUTOSPEED |
749 DIAG_L1_NOTTY;
750
751 /* both "no checksum" and "dataonly" modes take care of checksums, and imply framing. */
752 if (dev->nocksum || dev->dataonly) {
753 ret |= DIAG_L1_DOESL2CKSUM | DIAG_L1_STRIPSL2CKSUM |
754 DIAG_L1_DOESL2FRAME;
755 }
756
757 if (dev->dataonly) {
758 ret |= DIAG_L1_NOHDRS | DIAG_L1_DATAONLY | DIAG_L1_DOESL2FRAME;
759 }
760
761 if (dev->framed) {
762 ret |= DIAG_L1_DOESL2FRAME;
763 }
764
765 if (dev->fullinit) {
766 ret |= DIAG_L1_DOESFULLINIT;
767 }
768
769 return ret;
770}
771
772
773struct cfgi *sim_getcfg(struct diag_l0_device *dl0d) {
774 struct sim_device *dev;
775 if (dl0d == NULL) {
776 return diag_pseterr(DIAG_ERR_BADCFG);
777 }
778
779 dev = (struct sim_device *)dl0d->l0_int;
780 return &dev->simfile;
781}
782
783
784int sim_ioctl(struct diag_l0_device *dl0d, unsigned cmd, void *data) {
785 int rv = 0;
786
787 switch (cmd) {
788 case DIAG_IOCTL_INITBUS:
789 rv = sim_initbus(dl0d, (struct diag_l1_initbus_args *)data);
790 break;
791 default:
792 rv = DIAG_ERR_IOCTL_NOTSUPP;
793 break;
794 }
795
796 return rv;
797}
798
799
800// Declares the interface's protocol flags
801// and pointers to functions.
802// Like any simulator, it "implements" all protocols
803// (it only depends on the content of the DB file).
804const struct diag_l0 diag_l0_sim = {
805 "Car Simulator interface",
806 "CARSIM",
807 DIAG_L1_J1850_VPW | DIAG_L1_J1850_PWM | DIAG_L1_ISO9141 | DIAG_L1_ISO14230 | DIAG_L1_RAW,
808 sim_init,
809 sim_new,
810 sim_getcfg,
811 sim_del,
812 sim_open,
813 sim_close,
814 sim_getflags,
815 sim_recv,
816 sim_send,
817 sim_ioctl
818};
819