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

📄 diag_l0_se.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 Silicon Engines generic ISO 9141 interface
 *
 * We purposely haven't defined an structure that is used externally by this
 * interface, just a file descriptor because it's not so easy to do for
 * different devices, and potentially different operating systems.
 *
 *
 * This code is written to handle interruptible system calls (which happens
 * on SYSV)
 *
 * This driver is *very* similar to the VAGtool interface driver, all that
 * is different is that the VAGtool device sets RTS low for normal operation
 * and the VAGtool then twiddles RTS for doing 5 baud L line initialisation
 * - this code has a couple of extra bits of checking around that startup
 * code and therefore I've kept it, but it's probably worth discarding this
 * driver at some point and using the VAGtool code for all
 */


/*
 * 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_se.c,v 1.5 2002/04/30 18:47:22 rpalmeida Exp $";


#define DIAG_MODULE	"diag_l0_sileng"


/*
 * Silicon Engines ISO-9141 'K' Line interface
 * under Linux, connected to a standard COM port
 *
 * We don't use any of the 5 baud init features of the device,
 * just ignore RTS/CTS and then transmit/receive as normal. The device
 * is half duplex so we get an echo (ISO9141 is half duplex)
 *
 * I'd imagine many other K line interfaces will work with this code
 */

#define DIAG_MODULE	"diag_l0_sileng"

struct diag_l0_sileng_device
{
	int dev_fd;
	char dev_name[1024];	/* device name */

	int dev_protocol;	/* L1 protocol were using */

	diag_ttystate_t	dev_ttystate;	/* Holds OS specific tty info */

	int dev_speed;
	int dev_bits;
	int dev_stop_bits;
	int dev_parflag;
};

#define DIAG_L1_SILENG_NUMDEV 4
struct diag_l0_sileng_device	diag_l0_sileng_devices[DIAG_L1_SILENG_NUMDEV];

/* Global init flag */
int diag_l0_sileng_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_sileng_init(void)
{
	int i;
	struct sched_param p;

	if (diag_l0_sileng_initdone)
		return (0);
	diag_l0_sileng_initdone = 1;

	/* FD == -1 is magic unused flag */
	for (i = 0; i < DIAG_L1_SILENG_NUMDEV; i++)
		diag_l0_sileng_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_sileng_device *
diag_l0_sileng_finddev(int fd)
{
	int i;
	struct diag_l0_sileng_device *dev;

	for (i = 0; i < DIAG_L1_SILENG_NUMDEV; i++)
	{
		dev = &diag_l0_sileng_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_sileng_open(int iInterface, int iProtocol)
{
	struct diag_l0_sileng_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_sileng_init();

	dev = diag_l0_sileng_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
	 */
	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 need to ensure that DTR is high, or interface thinks it's in
	 * special 5 baud mode.
	 *
	 * We set RTS to low, 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_sileng_close(int fd)
{
	struct diag_l0_sileng_device *dev;

	dev = diag_l0_sileng_finddev(fd);	/* Find an unused diag block */
		
	if (diag_l0_debug & DIAG_DEBUG_CLOSE)
		fprintf(stderr, "%s: device fd %d info %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_sileng_initbus(int fd, struct diag_l1_init *in)
{
	int rv = DIAG_ERR_INIT_NOTSUPP;
	u_int8_t cbuf[1024];

	struct diag_l0_sileng_device *dev;

	dev = diag_l0_sileng_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_sileng_fastinit(fd, in, dev);
		break;

	case DIAG_L1_INITBUS_5BAUD:
		rv = diag_l0_sileng_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);

	if (diag_l0_debug & DIAG_DEBUG_IOCTL)
		fprintf(stderr, "%s: initbus device fd %d returning %d\n",
			DIAG_MODULE, fd, rv);
	
	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_sileng_fastinit(int fd, struct diag_l1_init *in,
	struct diag_l0_sileng_device *dev)
{
	struct serial_struct sinfo;
	struct termios tinfo;
	char cbuf;
	int cnt;

	if (diag_l0_debug & DIAG_DEBUG_IOCTL)
		fprintf(stderr, "%s: device fd %d fastinit\n",
			DIAG_MODULE, fd);

	/* 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);
}

/*
 * 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
 *
 */
diag_l0_sileng_slowinit(int fd, struct diag_l1_init *in,
	struct diag_l0_sileng_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;

	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);

	/*
	 * 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_sileng_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_sileng_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",
			DIAG_MODULE, fd, len, timeout);

	while ( (cnt = diag_os_read(fd, data, len, timeout)) <= 0)
	{
		if (cnt == DIAG_ERR_TIMEOUT)
		{
			if (diag_l0_debug & DIAG_DEBUG_READ)
				fprintf(stderr, "\n");
			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);
		}
	}
	if (diag_l0_debug & DIAG_DEBUG_READ)
	{
		int i;
		for (i=0; i<cnt; i++)
		{
			fprintf(stderr, " 0x%02x", data[i] & 0xff);
		}
		fprintf(stderr, "\n");
	}
	return(cnt);
}

/*
 * Set speed/parity etc
 */
diag_l0_sileng_setspeed(int fd, int speed, int bits,
		int stop_bits, int parflag )
{
	struct diag_l0_sileng_device *dev;

	dev = diag_l0_sileng_finddev(fd);	/* Find dev for this fd */

	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) );
}

int
diag_l0_sileng_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 + -