📄 diag_os_linux.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.
*
*************************************************************************
*
*
* LINUX specific stuff
*
*
* This "stuff" tweaks the Unix to behave how we want it to, and does
* very LINUX specific stuff
*
* We run the process in
* (1) Real time mode , as we need to do some very accurate sleeps
* for fast init purposes
* (2) As root in order to establish (1)
* (3) The os specific and IO driver code allows "interruptible syscalls"
* BSD and Linux defaults is that signals don't interrupt syscalls
* (i.e restartable system calls)
* SYSV does, so you see lots of code that copes with EINTR
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/types.h>
#include "diag_os.h" /* includes for this file */
#include "diag.h"
#include "diag_err.h"
static char *cvsid = "$Id: diag_os_linux.c,v 1.6 2002/05/01 22:00:35 rpalmeida Exp $";
#define DIAG_MODULE "diag_os"
int diag_os_init_done = 0;
/*
* Like alarm, but shorter
*/
int
diag_os_millialarm(int ms)
{
struct itimerval tv, otv;
tv.it_interval.tv_sec = 0;
tv.it_interval.tv_usec = 0;
tv.it_value.tv_sec = ms/1000;
tv.it_value.tv_usec = (ms%1000) * 1000;
setitimer(ITIMER_REAL, &tv, &otv);
}
/*
* SIGALRM handler
*/
void
diag_os_sigalrm()
{
diag_l3_timer(); /* Call L3 Timer */
diag_l2_timer(); /* Call L2 timers, which will call L1 timer */
}
int
diag_os_init()
{
int i;
struct sigaction stNew;
struct itimerval tv, otv;
if (diag_os_init_done)
return(0);
diag_os_init_done = 1;
/*
* Check privileges
*/
if (getuid() != 0)
{
fprintf(stderr,"WARNING: Not running as superuser\n");
fprintf(stderr,"WARNING: Need to set real-time mode - Things will not work correctly\n");
}
/*
* Start alarm handler
*/
#if OLD_WAY
signal(SIGALRM, diag_os_sigalrm);
#else
memset(&stNew, 0, sizeof(stNew));
stNew.sa_handler = diag_os_sigalrm;
stNew.sa_flags = 0;
stNew.sa_flags = SA_RESTART;
sigaction(SIGALRM, &stNew, NULL);
#endif
/* Repeating timer */
tv.it_interval.tv_sec = 0;
tv.it_interval.tv_usec = 500000; /* 500ms */
tv.it_value.tv_sec = 0;
tv.it_value.tv_usec = 500000; /* 500ms */
setitimer(ITIMER_REAL, &tv, &otv); /* Set timer */
return(0);
}
/*
* Microsleep for N milliseconds
*
* This is aimed at small waits as it does very accurate "busy wait"
* sleeping 2ms at a time (nanosleep does busy wait sleeps for <=2ms requests)
*/
int
diag_os_millisleep(int ms)
{
struct timespec rqst, resp;
while (ms)
{
if (ms > 2)
{
rqst.tv_nsec = 2000000;
ms -= 2;
}
else
{
rqst.tv_nsec = ms * 1000000;
ms = 0;
}
rqst.tv_sec = 0;
while (nanosleep(&rqst, &resp) != 0)
{
if (errno == EINTR)
{
/* Interrupted, continue */
memcpy(&rqst, &resp, sizeof(resp));
}
else
return(-1); /* Some other failure */
}
}
return(0);
}
/*
* Non interruptible, sleeping posix like read() with a timeout
* timeout is in ms. If timeout is 0, this acts as a non-blocking
* read
*/
ssize_t
diag_os_read(int fd, void *buf, size_t count, int timeout)
{
fd_set set;
int rv;
struct timeval tv;
FD_ZERO(&set);
FD_SET(fd, &set);
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
/*
* Do a select() with a timeout
* - Portability :- select on linux updates the timeout when
* it is interrupted. It does get interrupted because of the timer
* running regularly.
*/
while ( (rv = select(fd + 1, &set, NULL, NULL, &tv)) < 0 )
{
if (errno == EINTR)
continue;
}
switch (rv)
{
case 0:
/* Timeout */
return (DIAG_ERR_TIMEOUT);
case 1:
/* Ready for read */
rv = 0;
if (count)
rv = read(fd, buf, count);
return (rv);
default:
/* Unspecific Error */
return (DIAG_ERR_GENERAL);
}
}
/*
* TTY handling routines
*/
/* Record current tty state */
int diag_os_tty_init(int fd, diag_ttystate_t *dt)
{
/*
* Save original settings so can reset
* device on close - we also set "current" settings to
* those we just read aswell
*/
if (ioctl(fd, TIOCGSERIAL, &dt->dt_osinfo) < 0)
{
fprintf(stderr, "%s: open: Ioctl TIOCGSERIAL failed %d\n", DIAG_MODULE, errno);
return (-1);
}
memcpy(&dt->dt_sinfo, &dt->dt_osinfo, sizeof(dt->dt_osinfo));
if (ioctl(fd, TIOCMGET, &dt->dt_modemflags) < 0)
{
fprintf(stderr, "%s: open: Ioctl TIOCMGET failed %d\n", DIAG_MODULE, errno);
return (-1);
}
if (ioctl(fd, TCGETS, &dt->dt_otinfo) < 0)
{
fprintf(stderr, "%s: open: Ioctl TCGETS failed %d\n", DIAG_MODULE, errno);
return (-1);
}
memcpy(&dt->dt_tinfo, &dt->dt_otinfo, sizeof(dt->dt_otinfo));
return(0);
}
/* Restore ttystate - used before leaving */
int diag_os_tty_restore(int fd, diag_ttystate_t *dt)
{
(void)ioctl(fd, TIOCSSERIAL, &dt->dt_osinfo);
(void)ioctl(fd, TCSETS, &dt->dt_otinfo);
(void)ioctl(fd, TIOCMSET, &dt->dt_modemflags);
return(0);
}
/*
* Set speed/parity etc
*/
int diag_os_tty_setup(int fd, diag_ttystate_t *dt, int speed, int bits,
int stop_bits, int parflag )
{
/*
* This sets the interface to the speed closest to that requested.
* As it happens, all speeds on a Linux tty are calculated as a divisor
* of the base speed - for instance, base speed is normally 115200
* on a 16550 standard serial port
* this allows us to get
* 10472 baud (115200 / 11)
* - NB, this is not +/- 0.5% as defined in ISO 14230 for
* a tester, but it is if it was an ECU ... but that's a
* limitation of the PC serial port ...
* 9600 baud (115200 / 12)
* 5 baud (115200 / 23040 )
*/
if (diag_l0_debug & DIAG_DEBUG_IOCTL)
{
fprintf(stderr, "%s: setup: device fd %d dt %x ",
DIAG_MODULE, fd, dt);
fprintf(stderr, "speed %d bits %d stopbits %d parity %d\n",
speed, bits, stop_bits, parflag);
}
/* Copy original settings to "current" settings */
memcpy(&dt->dt_sinfo, &dt->dt_osinfo, sizeof(dt->dt_osinfo));
/* Now, mod the "current" settings */
dt->dt_sinfo.custom_divisor = dt->dt_sinfo.baud_base / speed ;
/* Turn of other speed flags */
dt->dt_sinfo.flags &= ~ASYNC_SPD_MASK;
/*
* Turn on custom speed flags and low latency mode
*/
dt->dt_sinfo.flags |= ASYNC_SPD_CUST | ASYNC_LOW_LATENCY;
/* And tell the kernel the new settings */
if (ioctl(fd, TIOCSSERIAL, &dt->dt_sinfo) < 0)
{
fprintf(stderr, "%s: Ioctl TIOCSSERIAL failed %d\n", DIAG_MODULE, errno);
return (-1);
}
/* Now do the same for the parity etc settings */
memcpy(&dt->dt_tinfo, &dt->dt_otinfo, sizeof(dt->dt_otinfo));
/* Like stty raw */
dt->dt_tinfo.c_iflag &= ~ (IGNBRK | BRKINT | IGNPAR | PARMRK
| INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF
| IUCLC | IXANY | IMAXBEL | OPOST | ISIG | ICANON | XCASE );
dt->dt_tinfo.c_oflag &= ~( OPOST ) ;
dt->dt_tinfo.c_lflag &= ~( ECHO | ECHOCTL | ECHOE | ECHOK |
ECHOKE | ECHONL | ECHOPRT | ICANON | ISIG ) ;
/*
* Set the baud rate and force speed to 38400 so that the
* custom baud rate stuff set above works
*/
dt->dt_tinfo.c_cflag &= ~CBAUD;
dt->dt_tinfo.c_cflag |= B38400;
/* Turn off RTS/CTS, and loads of others, similar to "stty raw" */
dt->dt_tinfo.c_cflag &= ~( CRTSCTS ) ;
/* Turn on ... */
dt->dt_tinfo.c_cflag |= (CLOCAL);
dt->dt_tinfo.c_cflag &= ~CSIZE;
switch (bits)
{
case 8:
dt->dt_tinfo.c_cflag |= CS8;
break;
case 7:
dt->dt_tinfo.c_cflag |= CS7;
break;
case 6:
dt->dt_tinfo.c_cflag |= CS6;
break;
case 5:
dt->dt_tinfo.c_cflag |= CS5;
break;
default:
fprintf(stderr, "%s bad bit setting used (%d)\n",
DIAG_MODULE, bits);
return(-1);
}
switch (stop_bits)
{
case 2:
dt->dt_tinfo.c_cflag |= CSTOPB;
break;
case 1:
dt->dt_tinfo.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr, "%s bad stopbit setting used (%d)\n",
DIAG_MODULE, stop_bits);
return(-1);
}
switch (parflag)
{
case DIAG_L1_PAR_E:
dt->dt_tinfo.c_cflag |= PARENB;
dt->dt_tinfo.c_cflag &= ~PARODD;
break;
case DIAG_L1_PAR_O:
dt->dt_tinfo.c_cflag |= (PARENB | PARODD);
break;
case DIAG_L1_PAR_N:
dt->dt_tinfo.c_cflag &= ~PARENB;
break;
default:
fprintf(stderr, "%s bad parity setting used (%d)\n", DIAG_MODULE, parflag);
return(-1);
}
if (ioctl(fd, TCSETS, &dt->dt_tinfo) < 0)
{
fprintf(stderr, "%s: Ioctl TCSETS failed %d\n", DIAG_MODULE, errno);
return (-1);
}
return (0);
}
/*
* Set/Clear DTR and RTS lines
* as specified
*/
int
diag_os_tty_control(int fd, diag_ttystate_t *dt, int dtr, int rts)
{
int setflags = 0, clearflags = 0;
if (dtr)
setflags = TIOCM_DTR;
else
clearflags = TIOCM_DTR;
if (rts)
setflags = TIOCM_RTS;
else
clearflags = TIOCM_RTS;
if (ioctl(fd, TIOCMBIC, &clearflags) < 0)
{
fprintf(stderr, "%s: open: Ioctl TIOCMBIC failed %d\n", DIAG_MODULE, errno);
return (-1);
}
if (ioctl(fd, TIOCMBIS, &setflags) < 0)
{
fprintf(stderr, "%s: open: Ioctl TIOCMBIS failed %d\n", DIAG_MODULE, errno);
return (-1);
}
return(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -