⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 diag_l0_vw.c

📁 Freediag contains various drivers (ISO9141, ISO14230, SAEJ1850-VPW, SAEJ1850-PWM) for different adap
💻 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 + -