BOROSOFTWAREAll work →
scantool/diag_l0_dumb.c 773 lines
1/*
2 * freediag - Vehicle Diagnostic Utility
3 *
4 *
5 * Copyright (C) 2001 Richard Almeida & Ibex Ltd ([email protected])
6 * (c) fenugrec 2014-2016
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 *************************************************************************
23 *
24 * Diag, Layer 0, interface for VAGTool, Silicon Engines generic ISO9141/ISO14230
25 * and other compatible "dumb" K-line interfaces, such as Jeff Noxon's opendiag interface.
26 * Any RS232 interface with no microcontroller onboard should fall in this category.
27 *
28 * The dumbopts config option must be set according to particular type (VAGtool, SE)
29 * to enable certain features (L line on RTS, etc)
30 */
31
32#include <stdbool.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <assert.h>
37
38#include "diag.h"
39#include "diag_cfg.h"
40#include "diag_os.h"
41#include "diag_err.h"
42#include "diag_tty.h"
43#include "diag_l0.h"
44#include "diag_l1.h"
45
46struct dumb_device {
47 int protocol; //set in dumb_open with specified iProtocol
48 struct diag_serial_settings serial;
49
50 /* "dumbopts" flags. */
51 /* Using a struct cfgi for each of these really seems like overkill...
52 * current approach is to have a cfgi for an "int dumbopts", that is parsed into
53 * these flags in dumb_open(). Hence, the shortname + descriptions defined here are
54 * unused for the moment.
55 */
56 bool use_L;
57 #define DD_USEL "Use RTS to drive the L line for init. Interface must support this."
58 #define DS_USEL "USEL"
59 bool clr_dtr;
60 #define DD_CLRDTR "Always keep DTR cleared (neg. voltage). Unusual."
61 #define DS_CLRDTR "CLRDTR"
62 bool set_rts;
63 #define DD_SETRTS "Always keep RTS set (pos. voltage). Unusual; do not use with USE_L."
64 #define DS_SETRTS "SETRTS"
65 bool man_break;
66 #define DD_MANBRK "Send manual breaks (essential for USB-serial ICs that don't support 5bps.)"
67 #define DS_MANBRK "MANBRK"
68 bool lline_inv;
69 #define DD_INVL "Invert polarity of the L line. Unusual."
70 #define DS_INVL "INVL"
71 bool fast_break;
72 #define DD_FASTBK "Try alternate iso14230 fastinit code."
73 #define DS_FASTBK "FASTB"
74 bool blockduplex;
75 #define DD_BKDUPX "Use message-based half duplex removal if P4==0."
76 #define DS_BKDUPX "BLKDUP"
77
78 struct cfgi port;
79 struct cfgi dumbopts;
80 #define DUMBOPTS_SN "dumbopts"
81 #define DUMBOPTS_DESC "Dumb interface option flags; addition of the desired flags:\n" \
82 " 0x01 : USE_LLINE : use if the L line (driven by RTS) is required for init. Interface must support this\n" \
83 "\t(VAGTOOL for example).\n" \
84 " 0x02 : CLEAR_DTR : use if your interface needs DTR to be always clear (neg. voltage).\n" \
85 "\tThis is unusual. By default DTR will always be SET (pos. voltage)\n" \
86 " 0x04 : SET_RTS : use if your interface needs RTS to be always set (pos. voltage).\n" \
87 "\tThis is unusual. By default RTS will always be CLEAR (neg. voltage)\n" \
88 "\tThis option should not be used with USE_LLINE.\n" \
89 " 0x08 : MAN_BREAK : essential for USB-serial converters that don't support 5bps\n" \
90 "\tsuch as FTDI232*, P230* and other ICs (enabled by default).\n" \
91 " 0x10: LLINE_INV : Invert polarity of the L line. see\n" \
92 "\tdoc/dumb_interfaces.txt !! This is unusual.\n" \
93 " 0x20: FAST_BREAK : use alternate iso14230 fastinit code.\n" \
94 " 0x40: BLOCKDUPLEX : use message-based half duplex removal (if P4==0)\n\n" \
95 "ex.: \"dumbopts 9\" for MAN_BREAK and USE_LLINE.\n"
96
97
98 ttyp *tty_int; /** handle for tty stuff */
99
100};
101
102
103#define BPS_PERIOD 198 //length of 5bps bits. The idea is to make this configurable eventually by adding a user-settable offset
104#define WUPFLUSH 10 //should be between 5 and ~15 ms; this is the time we allow diag_tty_read to purge the break after a fast init
105#define WUPOFFSET 2 //shorten fastinit wake-up pattern by WUPOFFSET ms, to fine-tune tWUP. Another ugly bandaid that should be
106 //at least runtime-configurable
107
108// dumbopts flags set according to particular interface type (VAGtool vs SE etc.)
109#define USE_LLINE 0x01 //interface maps L line to RTS : setting RTS normally pulls L down to 0 .
110#define CLEAR_DTR 0x02 //have DTR cleared constantly (unusual, disabled by default)
111#define SET_RTS 0x04 //have RTS set constantly (also unusual, disabled by default).
112#define MAN_BREAK 0x08 //force bitbanged breaks for inits; enabled by default
113#define LLINE_INV 0x10 //invert polarity of the L line if set. see doc/dumb_interfaces.txt
114#define FAST_BREAK 0x20 //do we use diag_tty_fastbreak for iso14230-style fast init.
115#define BLOCKDUPLEX 0x40 //This allows half duplex removal on a whole message if P4==0 (see diag_l1_send())
116#define DUMBDEFAULTS (MAN_BREAK | BLOCKDUPLEX) //default set of flags
117
118
119
120extern const struct diag_l0 diag_l0_dumb;
121
122static void dumb_close(struct diag_l0_device *dl0d);
123
124/*
125 * Init must be callable even if no physical interface is
126 * present, it's just here for the code here to initialise its
127 * variables etc
128 */
129static int dumb_init(void) {
130 return 0;
131}
132
133/* fill & init new dl0d */
134static int dumb_new(struct diag_l0_device *dl0d) {
135 struct dumb_device *dev;
136 int rv;
137
138 assert(dl0d);
139
140 rv = diag_calloc(&dev, 1);
141 if (rv != 0) {
142 return diag_ifwderr(rv);
143 }
144
145 dl0d->l0_int = dev;
146
147 rv = diag_cfgn_tty(&dev->port);
148 if (rv != 0) {
149 free(dev);
150 return diag_ifwderr(rv);
151 }
152
153 rv = diag_cfgn_int(&dev->dumbopts, DUMBDEFAULTS, DUMBDEFAULTS);
154 if (rv != 0) {
155 diag_cfg_clear(&dev->port);
156 free(dev);
157 return diag_ifwderr(rv);
158 }
159
160 /* finish filling the dumbopts cfgi */
161 dev->dumbopts.shortname = DUMBOPTS_SN;
162 dev->dumbopts.descr = DUMBOPTS_DESC;
163 dev->port.next = &dev->dumbopts;
164 dev->dumbopts.next = NULL;
165
166 printf("Note concerning generic (dumb) interfaces : there are additional\n"
167 "options which can be set with \"set dumbopts\". By default\n"
168 "\"K-line only\" and \"MAN_BREAK\" are set. \n");
169
170 return 0;
171}
172
173static void dumb_del(struct diag_l0_device *dl0d) {
174 struct dumb_device *dev;
175
176 assert(dl0d);
177
178 dev = dl0d->l0_int;
179 if (!dev) {
180 return;
181 }
182
183 diag_cfg_clear(&dev->port);
184 diag_cfg_clear(&dev->dumbopts);
185 free(dev);
186 return;
187}
188
189static struct cfgi *dumb_getcfg(struct diag_l0_device *dl0d) {
190 struct dumb_device *dev;
191 if (dl0d == NULL) {
192 return diag_pseterr(DIAG_ERR_BADCFG);
193 }
194
195 dev = dl0d->l0_int;
196 return &dev->port;
197}
198
199
200static int dumb_open(struct diag_l0_device *dl0d, int iProtocol) {
201 struct dumb_device *dev;
202 int dumbopts;
203
204 assert(dl0d);
205 dev = dl0d->l0_int;
206
207 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_OPEN, DIAG_DBGLEVEL_V,
208 FLFMT "open port %s L1proto %d\n",
209 FL, dev->port.val.str, iProtocol);
210
211 /* try to open TTY */
212 dev->tty_int = diag_tty_open(dev->port.val.str);
213 if (dev->tty_int == NULL) {
214 return diag_iseterr(DIAG_ERR_GENERAL);
215 }
216 dev->protocol = iProtocol;
217
218 /* parse dumbopts to set flags */
219 dumbopts = dev->dumbopts.val.i;
220
221 dev->use_L = dumbopts & USE_LLINE;
222 dev->clr_dtr = dumbopts & CLEAR_DTR;
223 dev->set_rts = dumbopts & SET_RTS;
224 dev->man_break = dumbopts & MAN_BREAK;
225 dev->lline_inv = dumbopts & LLINE_INV;
226 dev->fast_break = dumbopts & FAST_BREAK;
227 dev->blockduplex = dumbopts & BLOCKDUPLEX;
228
229 /*
230 * We set RTS to low, and DTR high, because this allows some
231 * interfaces to work than need power from the DTR/RTS lines;
232 * this is altered according to dumbopts.
233 */
234 if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts) < 0) {
235 dumb_close(dl0d);
236 return diag_iseterr(DIAG_ERR_GENERAL);
237 }
238
239 (void)diag_tty_iflush(dev->tty_int); /* Flush unread input */
240
241 dl0d->opened = 1;
242
243 return 0;
244}
245
246//dumb_close : close TTY handle.
247static void dumb_close(struct diag_l0_device *dl0d) {
248 if (!dl0d) {
249 return;
250 }
251
252 struct dumb_device *dev = dl0d->l0_int;
253
254 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_CLOSE, DIAG_DBGLEVEL_V,
255 FLFMT "l0 link %p closing\n", FL, (void *)dl0d);
256
257 diag_tty_close(dev->tty_int);
258 dev->tty_int = NULL;
259 dl0d->opened = 0;
260
261 return;
262}
263
264/*
265 * Fastinit: ISO14230-2 sec 5.2.4.2.3
266 * Caller should have waited W5 (>300ms) before calling this (from _initbus only!)
267 * we assume the L line was at the correct state (1) during that time.
268 * returns 0 (success), 50ms after starting the wake-up pattern.
269 * Exceptionally we dont diag_iseterr on return since _initbus() takes care of that.
270 */
271static int dumb_fastinit(struct diag_l0_device *dl0d) {
272 struct dumb_device *dev = dl0d->l0_int;
273 int rv=0;
274 uint8_t cbuf[MAXRBUF];
275
276 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_INIT, DIAG_DBGLEVEL_V,
277 FLFMT "dl0d=%p fastinit\n", FL, (void *)dl0d);
278
279 //Tidle before break : W5 (>300ms) on poweron; P3 (>55ms) after a StopCommunication; or 0ms after a P3 timeout.
280 // We assume the caller took care of this.
281 /* Send 25/25 ms break as initialisation pattern (TiniL) */
282 //ISO14230-2 says we should send the same sync pattern on both L and K together.
283 // we do it almost perfectly; the L \_/ pulse starts before and ends after the K \_/ pulse.
284
285 if (dev->use_L) {
286 // do K+L only if the user wants to do both
287 if (dev->fast_break) {
288 //but we can't use diag_tty_fastbreak while doing the L-line.
289 fprintf(stderr, FLFMT "Warning : not using L line for FAST_BREAK.\n", FL);
290 rv=diag_tty_fastbreak(dev->tty_int, 50-WUPFLUSH);
291 } else {
292 unsigned long long tb0,tb1; //WUP adjustment
293 //normal fast break on K and L.
294 //note : if LLINE_INV is 1, then we need to clear RTS to pull L down !
295 rv = diag_tty_control(dev->tty_int, !(dev->clr_dtr), !(dev->lline_inv));
296 if (rv < 0) {
297 fprintf(stderr, FLFMT "fastinit: Failed to set L\\_\n", FL);
298 return diag_ifwderr(rv);
299 }
300 tb0 = diag_os_gethrt();
301 rv=diag_tty_break(dev->tty_int, 25); //K line low for 25ms
302 /* Now restore DTR/RTS */
303 if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts) < 0) {
304 fprintf(stderr, FLFMT "fastinit: Failed to restore DTR & RTS!\n",
305 FL);
306 }
307 tb1 = diag_os_gethrt() - tb0;
308 tb1 = diag_os_hrtus(tb1)/1000; //elapsed ms so far within tWUP
309 tb1 = (50 - WUPFLUSH) - tb1; //remaining time in WUP
310 if (tb1 > 25) {
311 return diag_iseterr(DIAG_ERR_GENERAL); // should never happen
312 }
313 diag_os_millisleep((unsigned int) tb1);
314 } //if FAST_BREAK
315 } else {
316 // do K line only
317 if (dev->fast_break) {
318 rv=diag_tty_fastbreak(dev->tty_int, 50-WUPFLUSH);
319 } else {
320 //normal break
321 unsigned long long tb0,tb1; //WUP adjustment
322 tb0 = diag_os_gethrt();
323 rv=diag_tty_break(dev->tty_int, 25); //K line low for 25ms
324
325 tb1 = diag_os_gethrt() - tb0;
326 tb1 = diag_os_hrtus(tb1)/1000; //elapsed ms so far within tWUP
327 tb1 = (50 - WUPFLUSH) - tb1; //remaining time in WUP
328 if (tb1 > 25) {
329 return diag_iseterr(DIAG_ERR_GENERAL); // should never happen
330 }
331 diag_os_millisleep((unsigned int) tb1);
332 }
333 } //if USE_LLINE
334 // here we have WUPFLUSH ms before tWUP is done; we use this
335 // short time to flush RX buffers. (L2 needs to send a StartComm
336 // request very soon.)
337
338 diag_tty_read(dev->tty_int, cbuf, sizeof(cbuf), WUPFLUSH);
339
340
341 //there may have been a problem in diag_tty_break, if so :
342 if (rv) {
343 fprintf(stderr, FLFMT " L0 fastinit : problem !\n", FL);
344 return diag_ifwderr(rv);
345 }
346 return 0;
347}
348
349
350/* Do the 5 BAUD L line stuff while the K line is twiddling */
351// Only called from slowinit if USE_LLINE && !MAN_BREAK.
352// Caller must have waited before calling; DTR and RTS should be set correctly beforehand.
353// Returns after stop bit is finished + time for diag_tty_read to flush echo. (max 20ms) (NOT the sync byte!)
354
355static void dumb_Lline(struct diag_l0_device *dl0d, uint8_t ecuaddr) {
356 /*
357 * The bus has been high for w0 ms already, now send the
358 * 8 bit ecuaddr at 5 baud LSB first
359 *
360 */
361 int i, rv=0;
362 struct dumb_device *dev = dl0d->l0_int;
363// uint8_t cbuf[10];
364
365 // We also toggle DTR to disable RXD (blocking it at logical 1).
366 // However, at least one system I tested doesn't react well to
367 // DTR-toggling.
368 /* Set RTS low, for 200ms (Start bit) */
369 if (diag_tty_control(dev->tty_int, (dev->clr_dtr), !(dev->lline_inv)) < 0) {
370 fprintf(stderr, FLFMT "_LLine: Failed to set DTR & RTS\n", FL);
371 return;
372 }
373 diag_os_millisleep(BPS_PERIOD); /* 200ms -5% */
374
375 for (i=0; i<8; i++) {
376 if (ecuaddr & (1<<i)) {
377 /* High */
378 rv |= diag_tty_control(dev->tty_int, (dev->clr_dtr), (dev->lline_inv));
379 } else {
380 /* Low */
381 rv |= diag_tty_control(dev->tty_int, (dev->clr_dtr), !(dev->lline_inv));
382 }
383
384 if (rv < 0) {
385 fprintf(stderr, FLFMT "_LLine: Failed to set DTR & RTS\n",
386 FL);
387 return;
388 }
389 diag_os_millisleep(BPS_PERIOD); /* 200ms -5% */
390 }
391 /* And set high for the stop bit */
392 if (diag_tty_control(dev->tty_int, (dev->clr_dtr), !(dev->lline_inv)) < 0) {
393 fprintf(stderr, FLFMT "_LLine: Failed to set DTR & RTS\n",
394 FL);
395 return;
396 }
397 diag_os_millisleep(BPS_PERIOD); /* 200ms -5% */
398
399 /* Now put DTR/RTS back correctly so RX side is enabled */
400 if (diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts) < 0) {
401 fprintf(stderr, FLFMT "_LLine: Failed to restore DTR & RTS\n",
402 FL);
403 }
404
405 /* And clear out the break XXX no, _slowinit will do this for us after calling dumb_Lline*/
406// diag_tty_read(dev->tty_int, cbuf, sizeof(cbuf), 20);
407
408 return;
409}
410
411/*
412 * Slowinit:
413 * We need to send a byte (the address) at 5 baud, then
414 * switch back to 10400 baud
415 * and then wait W1 (60-300ms) until we get Sync byte 0x55.
416 * Caller (in L2 typically) must have waited with bus=idle beforehand.
417 * This optionally does the L_line stuff according to dumbopts.
418 * Ideally returns 0 (success) immediately after receiving Sync byte.
419 * This one, like fastinit, doesnt diag_iseterr when returning errors
420 * since _initbus() takes care of that.
421 *
422 */
423static int dumb_slowinit(struct diag_l0_device *dl0d, struct diag_l1_initbus_args *in,
424 struct dumb_device *dev) {
425 uint8_t cbuf[10];
426 int rv;
427 unsigned int tout;
428 struct diag_serial_settings set;
429
430 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V,
431 FLFMT "slowinit dl0d=%p address 0x%X\n", FL, (void *)dl0d, in->addr);
432
433
434 //two methods of sending at 5bps. Most USB-serial converts don't support such a slow bitrate !
435 if (dev->man_break) {
436 //MAN_BREAK means we bitbang 5bps init on K and optionally L as well.
437
438 //send the byte at in->addr, bit by bit.
439 int bitcounter;
440 uint8_t tempbyte=in->addr;
441 bool curbit = 0; //startbit
442 for (bitcounter=0; bitcounter<=8; bitcounter++) {
443 //LSB first.
444 if (curbit) {
445 if (dev->use_L) {
446 //release L
447 diag_tty_control(dev->tty_int, !(dev->clr_dtr), (dev->lline_inv));
448 }
449 diag_os_millisleep(BPS_PERIOD);
450 } else {
451 unsigned int lowtime=BPS_PERIOD;
452 //to prevent spurious breaks if we have a sequence of 0's :
453 //this is an RLE of sorts...
454 for (; bitcounter <=7; bitcounter++) {
455 if (tempbyte & 1) {
456 // test bit 1; we just tested curbit before getting here.
457 break;
458 }
459 lowtime += BPS_PERIOD;
460 tempbyte = tempbyte >>1;
461 }
462 //this way, we know for sure the next bit is 1 (either bit 7==1 or stopbit==1 !)
463 if (dev->use_L) {
464 //L = 0
465 diag_tty_control(dev->tty_int, !(dev->clr_dtr), !(dev->lline_inv));
466 }
467 diag_tty_break(dev->tty_int, lowtime);
468 }
469 curbit = tempbyte & 1;
470 tempbyte = tempbyte >>1;
471 }
472 //at this point we just finished the last bit, we'll wait the duration of the stop bit.
473 if (dev->use_L) {
474 diag_tty_control(dev->tty_int, !(dev->clr_dtr), dev->set_rts); //release L
475 }
476 diag_os_millisleep(BPS_PERIOD); //stop bit
477
478 //at this point the stop bit just finished. We could just purge the input buffer ?
479 //Usually the next thing to happen is the ECU will send the sync byte (0x55) within W1
480 // (60 to 300ms)
481 // so we have 60ms to diag_tty_iflush, the risk is if it takes too long
482 // the "sync pattern byte" may be lost !
483 // TODO : add before+after timing check to see if diag_tty_iflush takes too long
484 diag_tty_iflush(dev->tty_int); //try it anyway
485 } else { //so it's not a man_break
486 /* Set to 5 baud, 8 N 1 */
487
488 set.speed = 5;
489 set.databits = diag_databits_8;
490 set.stopbits = diag_stopbits_1;
491 set.parflag = diag_par_n;
492
493 diag_tty_setup(dev->tty_int, &set);
494
495
496 /* Send the address as a single byte message */
497 diag_tty_write(dev->tty_int, &in->addr, 1);
498 //This supposes that diag_tty_write is non-blocking, i.e. returns before write is complete !
499 // 8 bits + start bit + stop bit @ 5bps = 2000 ms
500 /* Do the L line stuff as required*/
501 if (dev->use_L) {
502 dumb_Lline(dl0d, in->addr);
503 tout=400; //Shorter timeout to get back echo, as dumb_Lline exits after 5bps stopbit.
504 } else {
505 // If there's no manual L line to do, timeout needs to be longer to receive echo
506 tout=2400;
507 }
508 //TODO : try with diag_tty_iflush instead of trying to read a single echo byte?
509 //I expect the RX buffer might have more than 1 spurious byte in it at this point...
510 /*
511 * And read back the single byte echo, which shows TX completes
512 * - At 5 baud, it takes 2 seconds to send a byte ..
513 * - ECU responds within W1 = [60,300]ms after stop bit.
514 * We're not trying to get the sync byte yet, just the address byte
515 * echo
516 */
517
518 if (diag_tty_read(dev->tty_int, cbuf, 1,tout) != 1) {
519 fprintf(stderr, FLFMT "_slowinit: address echo error\n", FL);
520 return diag_iseterr(DIAG_ERR_TIMEOUT);
521 }
522
523 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V,
524 FLFMT "\tgot address echo 0x%X\n", FL, cbuf[0]);
525
526 rv = diag_tty_setup(dev->tty_int, &dev->serial);
527 if (rv) {
528 //reset original settings
529 fprintf(stderr, FLFMT "_slowinit: could not reset serial settings !\n", FL);
530 return diag_ifwderr(rv);
531 }
532 } //if !man_break
533 //Here, we sent 0x33 @ 5bps and read back the echo or purged it.
534 diag_os_millisleep(60-IFLUSH_TIMEOUT); //W1 minimum, less the time we spend in diag_tty_iflush
535 //Now the ECU is about to, or already, sending the sync byte 0x55.
536 /*
537 * Ideally we would now measure the length of the received
538 * 0x55 sync byte to work out the baud rate.
539 * not implemented, we'll just suppose the current serial settings
540 * are OK and read the 0x55.
541 * This is ok for iso9141, but in 14230 (5.2.4.2.2.2) we must detect
542 * between 1200 and 10400bps. One technique that *could* be tried
543 * (and implemented in diag_tty*.c) would be to set to a high baud
544 * rate (ideally > 10400*10, like 115.2k) and detect the incoming
545 * RXD_BREAK conditions as they come in
546 * (1 bit @ 10400 > complete byte @ 115200), and record + analyse
547 * timestamps for every RXD_BREAK falling edge. Windows has an
548 * EV_BREAK event for this; linux/unix may be more complex.
549 * Let's just forget about
550 * all this for now.
551 */
552
553 if (dev->protocol == DIAG_L1_ISO9141) {
554 tout = 241+50; /* maximum W1 + sync byte@10kbps - elapsed (60ms) + mud factor= 241ms + mud factor*/
555 } else if (dev->protocol== DIAG_L1_ISO14230) {
556 // probably ISO14230-2 sec 5.2.4.2.2
557 tout = 300; /* It should be the same thing, but let's put 300. */
558 } else {
559 fprintf(stderr, FLFMT "warning : using Slowinit with a strange L1 protocol !\n", FL);
560 tout=300; //but try anyway
561 }
562
563 rv = diag_tty_read(dev->tty_int, cbuf, 1, tout);
564 if (rv <= 0) {
565 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V,
566 FLFMT "\tdid not get Sync byte !\n", FL);
567
568 return diag_iseterr(DIAG_ERR_TIMEOUT);
569 }
570 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_PROTO, DIAG_DBGLEVEL_V,
571 FLFMT "\tgot sync byte 0x%X!\n", FL, cbuf[0]);
572
573 //If all's well at this point, we just read the sync pattern byte. L2 will take care
574 //of reading + echoing the keybytes
575 return 0;
576}
577
578/*
579 * Do wakeup on the bus
580 * return 0 on success, after reading of a sync byte, before receiving any keyword.
581 * since at the L0 level we have no knowledge of 9141 / 14230, the caller is
582 * responsible for waiting W5, W0 or Tidle before calling initbus().
583 */
584static int dumb_initbus(struct diag_l0_device *dl0d, struct diag_l1_initbus_args *in) {
585 int rv = DIAG_ERR_INIT_NOTSUPP;
586
587 struct dumb_device *dev;
588
589 dev = (struct dumb_device *)dl0d->l0_int;
590
591 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_IOCTL, DIAG_DBGLEVEL_V,
592 FLFMT "device link %p info %p initbus type %d\n", FL,
593 (void *)dl0d, (void *)dev, in->type);
594
595 if (!dev) {
596 return diag_iseterr(DIAG_ERR_GENERAL);
597 }
598
599 (void)diag_tty_iflush(dev->tty_int); /* Flush unread input */
600
601 switch (in->type) {
602 case DIAG_L1_INITBUS_FAST:
603 rv = dumb_fastinit(dl0d);
604 break;
605 case DIAG_L1_INITBUS_5BAUD:
606 rv = dumb_slowinit(dl0d, in, dev);
607 break;
608 case DIAG_L1_INITBUS_2SLOW:
609 // iso 9141 - 1989 style init, not implemented.
610 default:
611 return diag_iseterr(DIAG_ERR_INIT_NOTSUPP);
612 break;
613 }
614
615
616 if (rv) {
617 fprintf(stderr, FLFMT "L0 initbus failed (%s)\n", FL, diag_errlookup(rv));
618 return diag_ifwderr(rv);
619 }
620
621 return 0;
622
623}
624
625
626static int dumb_iflush(struct diag_l0_device *dl0d) {
627 struct dumb_device *dev = dl0d->l0_int;
628
629 return diag_tty_iflush(dev->tty_int);
630}
631
632/*
633 * Send a load of data
634 * this is "blocking", i.e. returns only when it's finished or it failed.
635 *
636 * Returns 0 on success
637 */
638
639static int dumb_send(struct diag_l0_device *dl0d,
640 const void *data, size_t len) {
641 /*
642 * This will be called byte at a time unless P4 timing parameter is zero
643 * as the L1 code that called this will be adding the P4 gap between
644 * bytes
645 */
646
647 struct dumb_device *dev = dl0d->l0_int;
648
649 if (len == 0) {
650 return diag_iseterr(DIAG_ERR_BADLEN);
651 }
652
653 DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_WRITE, DIAG_DBGLEVEL_V, data, len,
654 FLFMT "l0_send dl0d=%p len=%ld; ", FL, (void *)dl0d, (long)len);
655
656 if (diag_tty_write(dev->tty_int, data, len) != (int) len) {
657 fprintf(stderr, FLFMT "dumb_send: write error\n", FL);
658 return diag_iseterr(DIAG_ERR_GENERAL);
659 }
660
661 return 0;
662}
663
664/*
665 * Get data (blocking), returns number of bytes read, between 1 and len
666 * If timeout is set to 0, this becomes non-blocking
667 * returns # of bytes read if succesful
668 */
669
670static int dumb_recv(struct diag_l0_device *dl0d,
671 void *data, size_t len, unsigned int timeout) {
672 int rv;
673 struct dumb_device *dev = dl0d->l0_int;
674
675 if (len == 0) {
676 return diag_iseterr(DIAG_ERR_BADLEN);
677 }
678
679 DIAG_DBGM(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
680 FLFMT "_recv dl0d=%p req=%ld bytes timeout=%u\n", FL,
681 (void *)dl0d, (long)len, timeout);
682
683 if ((rv=diag_tty_read(dev->tty_int, data, len, timeout)) <= 0) {
684 if (rv == DIAG_ERR_TIMEOUT) {
685 return DIAG_ERR_TIMEOUT;
686 }
687 return diag_iseterr(DIAG_ERR_GENERAL);
688 }
689
690 DIAG_DBGMDATA(diag_l0_debug, DIAG_DEBUG_READ, DIAG_DBGLEVEL_V,
691 data, (size_t) rv, FLFMT "Got ", FL);
692
693 return rv;
694}
695
696/*
697 * Set speed/parity etc
698 * ret 0 if ok
699 */
700static int dumb_setspeed(struct diag_l0_device *dl0d,
701 const struct diag_serial_settings *pset) {
702 struct dumb_device *dev;
703
704 dev = (struct dumb_device *)dl0d->l0_int;
705
706 dev->serial = *pset;
707
708 return diag_tty_setup(dev->tty_int, &dev->serial);
709}
710
711
712static uint32_t dumb_getflags(struct diag_l0_device *dl0d) {
713 struct dumb_device *dev;
714 int flags=0;
715
716 dev = (struct dumb_device *)dl0d->l0_int;
717
718 if (dev->blockduplex) {
719 flags |= DIAG_L1_BLOCKDUPLEX;
720 }
721
722 switch (dev->protocol) {
723 case DIAG_L1_ISO14230:
724 flags |= DIAG_L1_FAST | DIAG_L1_PREFFAST | DIAG_L1_SLOW | DIAG_L1_HALFDUPLEX;
725 break;
726 case DIAG_L1_ISO9141:
727 flags |= DIAG_L1_SLOW | DIAG_L1_HALFDUPLEX;
728 break;
729 default:
730 break;
731 }
732
733 return flags;
734}
735
736static int dumb_ioctl(struct diag_l0_device *dl0d, unsigned cmd, void *data) {
737 int rv = 0;
738
739 switch (cmd) {
740 case DIAG_IOCTL_SETSPEED:
741 rv = dumb_setspeed(dl0d, (const struct diag_serial_settings *) data);
742 break;
743 case DIAG_IOCTL_INITBUS:
744 rv = dumb_initbus(dl0d, (struct diag_l1_initbus_args *)data);
745 break;
746 case DIAG_IOCTL_IFLUSH:
747 rv = dumb_iflush(dl0d);
748 break;
749 default:
750 rv = DIAG_ERR_IOCTL_NOTSUPP;
751 break;
752 }
753
754 return rv;
755}
756
757
758const struct diag_l0 diag_l0_dumb = {
759 "Generic dumb serial interface",
760 "DUMB",
761 DIAG_L1_ISO9141 | DIAG_L1_ISO14230 | DIAG_L1_RAW,
762 dumb_init,
763 dumb_new,
764 dumb_getcfg,
765 dumb_del,
766 dumb_open,
767 dumb_close,
768 dumb_getflags,
769 dumb_recv,
770 dumb_send,
771 dumb_ioctl,
772};
773