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

📄 diag_os_linux.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.
 *
 *************************************************************************
 *
 *
 * 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 + -