📄 diag_l0_se.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 + -