📄 diag_l0_vw.c
字号:
/*
* freediag - Vehicle Diagnostic Utility
*
*
* Copyright (C) 2001 Richard Almeida & Ibex Ltd (rpa@ibex.co.uk)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*************************************************************************
*
* Diag, Layer 0, interface for VAGTool compatible interface
* such as Jeff Noxon's opendiag interface.
*
* This is also tested with the Silicon Engines K line only interface, but
* has some of the startup ECU checking removed. Dunno if it's worth keeping
* the separate SE drivers.
*/
/*
* Interface is 0-3 and represents serial devices connected
* to /dev/ttyS0 -> /dev/ttyS3 - ie com0 -> com3
*
* SubInterface isn't used (as we have no muxing ability on one of these devices)
*/
#include <stdlib.h>
#include <stdio.h>
#include "diag_os.h" /* operating specific includes */
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "diag.h"
#include "diag_l1.h"
#include "diag_err.h"
#include "diag_general.h"
static char *cvsid = "$Id: diag_l0_vw.c,v 1.4 2002/04/30 18:47:22 rpalmeida Exp $";
#define DIAG_MODULE "diag_l0_vw"
/*
* VAGTool compatible ISO-9141 'L and K' Line interface
* under Linux, connected to a standard COM port
*/
struct diag_l0_vwtool_device
{
int dev_fd;
char dev_name[1024]; /* device name */
int dev_protocol; /* L1 protocol we're using */
diag_ttystate_t dev_ttystate; /* Holds OS specific tty info */
int dev_speed; /* User settings */
int dev_bits;
int dev_stop_bits;
int dev_parflag;
};
#define DIAG_L1_VWTOOL_NUMDEV 4
struct diag_l0_vwtool_device diag_l0_vwtool_devices[DIAG_L1_VWTOOL_NUMDEV];
/* Global init flag */
int diag_l0_vwtool_initdone = 0;
/*
* Init must be callable even if no physical interface is
* present, it's just here for the code here to initialise its
* variables etc
*/
int
diag_l0_vwtool_init(void)
{
int i;
struct sched_param p;
if (diag_l0_vwtool_initdone)
return (0);
diag_l0_vwtool_initdone = 1;
/* FD == -1 is magic unused flag */
for (i = 0; i < DIAG_L1_VWTOOL_NUMDEV; i++)
diag_l0_vwtool_devices[i].dev_fd = -1;
/* Set real time UNIX scheduling */
p.sched_priority = 1;
if ( sched_setscheduler(getpid(), SCHED_FIFO, &p) < 0)
{
perror("sched_setscheduler");
}
return (0);
}
struct diag_l0_vwtool_device *
diag_l0_vwtool_finddev(int fd)
{
int i;
struct diag_l0_vwtool_device *dev;
for (i = 0; i < DIAG_L1_VWTOOL_NUMDEV; i++)
{
dev = &diag_l0_vwtool_devices[i];
if (dev->dev_fd == fd)
return(dev);
}
return (NULL) ;
}
/*
* Open the diagnostic device, returns a file descriptor
* records original state of term interface so we can restore later
*/
int
diag_l0_vwtool_open(int iInterface, int iProtocol)
{
struct diag_l0_vwtool_device *dev;
struct serial_struct sinfo;
struct termios tinfo;
int flags;
u_int8_t buf[8];
if (diag_l0_debug & DIAG_DEBUG_OPEN)
{
fprintf(stderr, "%s: open subinterface %d protocol %d\n",
DIAG_MODULE, iInterface, iProtocol);
}
diag_l0_vwtool_init();
dev = diag_l0_vwtool_finddev(-1); /* Find an unused diag block */
if (dev == NULL)
return (-1); /* No free device blocks (coding error ...) */
(void)sprintf(dev->dev_name, "/dev/ttyS%d", iInterface);
/*
* Synchronous open, so writes only return once data is
* sent out. NB this doesn't work .. writes still return
* immediately
*/
dev->dev_fd = open(dev->dev_name, O_RDWR | O_SYNC);
if (dev->dev_fd >= 0)
{
if (diag_l0_debug & DIAG_DEBUG_OPEN)
fprintf(stderr, "%s: Device %s opened, fd %d\n",
DIAG_MODULE, dev->dev_name, dev->dev_fd);
}
else
return(-1);
dev->dev_protocol = iProtocol;
if (diag_os_tty_init(dev->dev_fd, &dev->dev_ttystate) < 0)
{
fprintf(stderr, "%s: tty_init failed for %s\n",
DIAG_MODULE, dev->dev_name);
return(-1);
}
/*
* We set RTS to low, and DTR high, because this allows some
* interfaces to work than need power from the DTR/RTS lines
*/
if (diag_os_tty_control(dev->dev_fd, &dev->dev_ttystate, 1, 0) < 0)
{
fprintf(stderr, "%s: open: Ioctl TIOCMBIS failed %d\n", DIAG_MODULE, errno);
}
/* Read any old data hanging about on the port */
(void)diag_os_read(dev->dev_fd, buf, sizeof(buf), 5);
return (dev->dev_fd) ;
}
int
diag_l0_vwtool_close(int fd)
{
struct diag_l0_vwtool_device *dev;
dev = diag_l0_vwtool_finddev(fd); /* Find an unused diag block */
if (diag_l0_debug & DIAG_DEBUG_CLOSE)
fprintf(stderr, "%s: device dev %d fd %x closing\n",
DIAG_MODULE, dev, fd);
if (dev)
{
(void) diag_os_tty_restore(fd, &dev->dev_ttystate);
close(dev->dev_fd);
dev->dev_fd = -1;
}
return (0);
}
/*
* Do wakeup on the bus
*/
int
diag_l0_vwtool_initbus(int fd, struct diag_l1_init *in)
{
int rv = DIAG_ERR_INIT_NOTSUPP;
u_int8_t cbuf[1024];
struct diag_l0_vwtool_device *dev;
dev = diag_l0_vwtool_finddev(fd); /* Find an unused diag block */
if (diag_l0_debug & DIAG_DEBUG_IOCTL)
fprintf(stderr, "%s: device fd %d info %x initbus type %d\n",
DIAG_MODULE, fd, dev, in->type);
if (!dev)
return(-1);
/* Empty our Receive buffer and wait for idle bus */
(void)diag_os_read( fd, cbuf, sizeof(cbuf), 200);
/* Wait the idle time (Tidle > 300ms) */
usleep(300000);
switch (in->type)
{
case DIAG_L1_INITBUS_FAST:
rv = diag_l0_vwtool_fastinit(fd, in, dev);
break;
case DIAG_L1_INITBUS_5BAUD:
rv = diag_l0_vwtool_slowinit(fd, in, dev);
break;
default:
rv = DIAG_ERR_INIT_NOTSUPP;
break;
}
/*
* return the baud rate etc to what the user had set
* because the init routines will have messed them up
*/
(void)diag_os_tty_setup(fd, &dev->dev_ttystate,
dev->dev_speed, dev->dev_bits, dev->dev_stop_bits,
dev->dev_parflag);
return(rv);
}
/*
* Fastinit:
* We need to send an 25ms (+/- 1ms) break
* and then wait 25ms. We must have waited Tidle (300ms) first
*
* The trouble here is Linux doesn't let us be that accurate sending
* a break - so we do it by sending a '0x00' at a baud rate that means
* the output is low for 25ms, then we wait for 25ms, using busy
* waits (which we get from being in real-time mode and doing nanosleeps
* of less than 2ms each)
*/
diag_l0_vwtool_fastinit(int fd, struct diag_l1_init *in,
struct diag_l0_vwtool_device *dev)
{
struct serial_struct sinfo;
struct termios tinfo;
char cbuf;
int cnt;
/* Send 25 ms break as initialisation pattern (TiniL) */
/* Set baud rate etc to 360 baud, 8, N, 1 */
diag_os_tty_setup(fd, &dev->dev_ttystate, 360, 8, 1,
DIAG_L1_PAR_N);
/* Send a 0x00 byte message */
write(fd, "", 1);
/* And read back the single byte echo, which shows TX completes */
while ( (cnt = diag_os_read(fd, &cbuf, 1, 1000)) <= 0)
{
if (cnt == DIAG_ERR_TIMEOUT)
return (DIAG_ERR_TIMEOUT);
if (cnt == 0)
{
/* Error, EOF */
fprintf(stderr, "%s: read returned EOF !!\n", DIAG_MODULE);
return(-1);
}
if (errno != EINTR)
{
/* Error, EOF */
perror("read");
fprintf(stderr, "%s: read returned error %d !!\n", DIAG_MODULE, errno);
return(-1);
}
}
/* Now wait 24-26 ms so Twup expires */
diag_os_millisleep(24);
/* Now let the caller send a startCommunications message */
return (0);
}
/* Do the 5 BAUD L line stuff while the K line is twiddling */
void
diag_l0_vwtool_Lline(int fd, struct diag_l0_vwtool_device *dev,
u_int8_t ecuaddr)
{
/*
* The bus has been high for w0 ms already, now send the
* 8 bit ecuaddr at 5 baud LSB first
*
* NB, Linux usleep() can be extended by the O/S or by
* the time to do the system call.
* The baudrate must be 5baud +/ 5%, so we use the -5% value
* and let the system extend as needed
*/
int i, rv;
int flags;
u_int8_t cbuf;
/* Initial state, DTR High, RTS low */
/*
* Set DTR low during this, receive circuitry
* will see a break for that time, that we'll clear out later
*/
if (diag_os_tty_control(fd, &dev->dev_ttystate, 0, 1) < 0)
{
fprintf(stderr, "%s: open: Failed to set modem control lines\n",
DIAG_MODULE);
}
/* Set RTS low, for 200ms (Start bit) */
if (diag_os_tty_control(fd, &dev->dev_ttystate, 0, 0) < 0)
{
fprintf(stderr, "%s: open: Failed to set modem control lines\n",
DIAG_MODULE);
return;
}
usleep(190000); /* 200ms -5% */
for (i=0; i<8; i++)
{
if (ecuaddr & (1<<i))
{
/* High */
rv = diag_os_tty_control(fd, &dev->dev_ttystate, 0, 1);
}
else
{
/* Low */
rv = diag_os_tty_control(fd, &dev->dev_ttystate, 0, 0);
}
if (rv < 0)
{
fprintf(stderr, "%s: open: Failed to set modem control lines\n",
DIAG_MODULE);
return;
}
usleep(190000); /* 200ms -5% */
}
/* And set high for the stop bit */
if (diag_os_tty_control(fd, &dev->dev_ttystate, 0, 1) < 0)
{
fprintf(stderr, "%s: open: Failed to set modem control lines\n",
DIAG_MODULE);
return;
}
usleep(190000); /* 200ms -5% */
/* Now put DTR/RTS back correctly so RX side is enabled */
if (diag_os_tty_control(fd, &dev->dev_ttystate, 1, 0) < 0)
{
fprintf(stderr, "%s: open: Failed to set modem control lines\n",
DIAG_MODULE);
}
/* And clear out the break */
(void) diag_os_read(fd, &cbuf, 1, 20);
return;
}
/*
* Slowinit:
* We need to send a byte (the address) at 5 baud, then
* switch back to 10400 baud
* and then wait 25ms. We must have waited Tidle (300ms) first
*
* We can use the main chip to do this on the K line but on VAGtool interfaces
* we also need to do this on the L line which is done by twiddling the RTS
* line.
*/
diag_l0_vwtool_slowinit(int fd, struct diag_l1_init *in,
struct diag_l0_vwtool_device *dev)
{
struct serial_struct sinfo;
struct termios tinfo;
char cbuf[1024];
int cnt, rv, i;
int baud, tout;
struct timeval first, last;
int diffus;
u_int8_t ecuaddr;
if (diag_l0_debug & DIAG_DEBUG_PROTO)
{
fprintf(stderr, "%s: slowinit fd %d address 0x%x\n",
DIAG_MODULE, fd, in->addr);
}
/* Set to 5 baud, 8 N 1 */
diag_os_tty_setup(fd, &dev->dev_ttystate, 5, 8, 1, DIAG_L1_PAR_N);
/* Wait W0 (2ms or longer) leaving the bus at logic 1 */
usleep(2000);
/* Send the address as a single byte message */
write(fd, &in->addr, 1);
#if notdef
/* Do the L line stuff */
diag_l0_vwtool_Lline(fd, &dev->dev_ttystate, in->addr);
#endif
/*
* And read back the single byte echo, which shows TX completes
* - At 5 baud, it takes 2 seconds to send a byte ..
*/
while ( (cnt = diag_os_read(fd, &cbuf[0], 1, 2750)) <= 0)
{
if (cnt == DIAG_ERR_TIMEOUT)
{
if (diag_l0_debug & DIAG_DEBUG_PROTO)
fprintf(stderr, "%s: slowinit fd %d echo read timeout\n",
DIAG_MODULE, fd);
return (DIAG_ERR_TIMEOUT);
}
if (cnt == 0)
{
/* Error, EOF */
fprintf(stderr, "%s: read returned EOF !!\n", DIAG_MODULE);
return(-1);
}
if (errno != EINTR)
{
/* Error, EOF */
perror("read");
fprintf(stderr, "%s: read returned error %d !!\n", DIAG_MODULE, errno);
return(-1);
}
}
/*
* Ideally we would now measure the length of the received
* 0x55 sync character to work out the baud rate.
* However, we cant do that yet, so we just set the
* baud rate to what the user requested and read the 0x55
*/
(void)diag_os_tty_setup(fd, &dev->dev_ttystate,
dev->dev_speed, dev->dev_bits, dev->dev_stop_bits,
dev->dev_parflag);
if (dev->dev_protocol == DIAG_L1_ISO9141)
tout = 750; /* 2s is too long */
else
tout = 300; /* 300ms according to ISO14230-2 */
rv = diag_os_read(fd, cbuf, 1, tout);
if (rv < 0)
{
if (diag_l0_debug & DIAG_DEBUG_PROTO)
fprintf(stderr, "%s: slowinit fd %d read timeout\n",
DIAG_MODULE, fd);
return(rv);
}
return (0);
}
/*
* Send a load of data
*
* Returns 0 on success, -1 on failure
*/
diag_l0_vwtool_send(int fd, int subinterface, char *data, int len)
{
/*
* This will be called byte at a time unless P4 timing parameter is zero
* as the L1 code that called this will be adding the P4 gap between
* bytes
*/
int cnt;
if (diag_l0_debug & DIAG_DEBUG_WRITE)
{
fprintf(stderr, "%s: device fd %d send %d bytes ",
DIAG_MODULE, fd, len);
if (diag_l0_debug & DIAG_DEBUG_DATA)
{
int i;
for (i=0; i<len; i++)
fprintf(stderr, "0x%x ", data[i] & 0xff);
}
}
while ((cnt = write(fd, data, len)) != len)
{
/* Partial write */
if (cnt < 0)
{
/* error */
if (errno != EINTR)
{
perror("write");
fprintf(stderr, "%s: write returned error %d !!\n", DIAG_MODULE, errno);
return(-1);
}
/* errno - EINTR is OK */
}
/* Successfully wrote cnt bytes (or 0 && EINTR), so inc pointers and continue */
len -= cnt;
data += cnt;
}
if ( (diag_l0_debug & (DIAG_DEBUG_WRITE|DIAG_DEBUG_DATA)) ==
(DIAG_DEBUG_WRITE|DIAG_DEBUG_DATA) )
{
fprintf(stderr, "\n", fd, len);
}
return(0);
}
/*
* Get data (blocking), returns number of chars read, between 1 and len
* If timeout is set to 0, this becomes non-blocking
*/
diag_l0_vwtool_recv(int fd , int subinterface, char *data, int len, int timeout)
{
int cnt;
if (diag_l0_debug & DIAG_DEBUG_READ)
fprintf(stderr,
"%s: fd %d recv upto %d bytes timeout %d\n",
DIAG_MODULE, fd, len, timeout);
while ( (cnt = diag_os_read(fd, data, len, timeout)) <= 0)
{
if (cnt == DIAG_ERR_TIMEOUT)
return (DIAG_ERR_TIMEOUT);
if (cnt == 0)
{
/* Error, EOF */
fprintf(stderr, "%s: read returned EOF !!\n", DIAG_MODULE);
return(-1);
}
if (errno != EINTR)
{
/* Error, EOF */
fprintf(stderr, "%s: read returned error %d !!\n", DIAG_MODULE, errno);
return(-1);
}
}
return(cnt);
}
/*
* Set speed/parity etc
*/
diag_l0_vwtool_setspeed(int fd, int speed, int bits,
int stop_bits, int parflag )
{
struct diag_l0_vwtool_device *dev;
dev = diag_l0_vwtool_finddev(fd); /* Find device */
dev->dev_speed = speed;
dev->dev_bits = bits;
dev->dev_stop_bits = stop_bits;
dev->dev_parflag = parflag;
return (diag_os_tty_setup(fd, &dev->dev_ttystate, speed, bits,
stop_bits, parflag) );
return (0);
}
int
diag_l0_vwtool_getflags(int fd)
{
/* All interface types here use same flags */
return(
DIAG_L1_SLOW | DIAG_L1_FAST | DIAG_L1_PREFFAST |
DIAG_L1_HALFDUPLEX
);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -