/***************************************************************************
 *   Copyright (C) 2004 by EVER Sp. z o.o.                                 *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "ccomm.h"
#include "common.h"
#include <sys/ioctl.h>

#define _POSIX_SOURCE 1 /* POSIX compliant source */

CComm::CComm()
{
	device=NULL;
	fd=-1;
	bIsSimpleSig = false;
	bIsLocked = false;
	bIsInitialized = false;
}


CComm::~CComm()
{
	if (fd>=0)
		close();
	if (device!=NULL)
		free(device);
}

int CComm::open()
{
	if (device==NULL || baudrate==0)
		return COMM_FAILURE;

	fd=::open(device, O_RDWR | O_NOCTTY/* | O_NDELAY*/);
	lasterror=errno;
	if (fd<0)
		return COMM_FAILURE;
	else
		bIsInitialized = true;

	lock_port();
		
	fcntl(fd, F_SETFL, 0);

	/* save current port settings */
	tcgetattr(fd, &oldtio);
	memcpy(&newtio, &oldtio, sizeof(oldtio));
	/* mask new config */
#ifdef __FreeBSD__
	newtio.c_cflag &= ~(CSIZE|PARENB|CSTOPB|CRTSCTS);
	newtio.c_cflag |= (CS8|CLOCAL|CREAD);
#else/*__FreeBSD__*/
	newtio.c_cflag &= ~(CSIZE|CBAUD|PARENB|CSTOPB|CRTSCTS);
	newtio.c_cflag |= (baudrate|CS8|CLOCAL|CREAD);
#endif/*__FreeBSD__*/
	newtio.c_iflag &= ~(IXON|IXOFF|IXANY);
	newtio.c_iflag |= IGNPAR;
	/* set input mode to: non-canonical, no echo,... */
	newtio.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
	newtio.c_oflag &= ~(OPOST);
	
	newtio.c_cc[VTIME]=20;	// timeout=1*0.1 seconds
	newtio.c_cc[VMIN]=0;

	cfsetispeed(&newtio, baudrate);
	cfsetospeed(&newtio, baudrate);
	
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd, TCSANOW, &newtio);

//	int iocarg=TIOCM_DTR|TIOCM_RTS;
//	ioctl(fd, TIOCMBIC, &iocarg);
	
	return COMM_SUCCESS;
}

/* open port for simple signalling type of communication */
int CComm::open_simplesignalling()
{
	if (device==NULL)
		return COMM_FAILURE;

	fd=::open(device, O_RDWR | O_NOCTTY);
	lasterror=errno;
	if (fd<0)
		return COMM_FAILURE;
	else
		bIsInitialized = true;

	/* save current port settings */
	tcgetattr(fd, &oldtio);
	memcpy(&newtio, &oldtio, sizeof(oldtio));
	
	/* mask and set new config */
	newtio.c_cflag &= ~(CLOCAL);
	newtio.c_cflag |= CLOCAL;
	
	/* apply new config */
	tcsetattr(fd, TCSANOW, &newtio);
	
	return COMM_SUCCESS;
}

int CComm::close()
{
	if (fd<0) {
		return COMM_FAILURE;
	}
	/* restore last port settings */
	tcsetattr(fd, TCSANOW, &oldtio);
	
	unlock_port();
	
	flush(1, 1);
	/* close port */
	if (::close(fd)==-1) {
		lasterror=errno;
		return COMM_FAILURE;
	}
	bIsInitialized = false;
	fd=-1;
	return COMM_SUCCESS;
}

int CComm::getStatus()
{
	if (fd<0)
		return COMM_FAILURE;
	
	int iflags = 0;
	
	if (ioctl(fd, TIOCMGET, &iflags))
		return COMM_FAILURE;
	else
		return iflags;
}

int CComm::getStatus(int *pflags)
{
	if (fd<0 || (pflags==NULL))
		return COMM_FAILURE;
	
	if (ioctl(fd, TIOCMGET, pflags))
		return COMM_FAILURE;
	else
		return COMM_SUCCESS;
}

int CComm::setStatus(int flags)
{
	if (fd<0)
		return COMM_FAILURE;
	
	if (ioctl(fd, TIOCMSET, &flags))
		return COMM_FAILURE;
	else
		return COMM_SUCCESS;
}

int CComm::status_check(ecomm_status eFlags)
{
	int icurflags = 0;
	if (getStatus(&icurflags)!=COMM_FAILURE) {
		switch(eFlags)
		{
		case CTS:
			if (icurflags & TIOCM_CTS)
				return 1;
			break;
		case DSR:
			if (icurflags & TIOCM_DSR)
				return 1;
			break;
		case RING:
			if (icurflags & TIOCM_RNG)
				return 1;
			break;
		case RLSD:
			if (icurflags & TIOCM_CD)
				return 1;
			break;
		}
	} else {
		return -1;
	}
	return 0;
}

/* send one byte/character */
int CComm::send(unsigned char data)
{
	if (fd<0 || !bIsInitialized)
		return COMM_FAILURE;
	unsigned char data_out = data;
	//flush(1,0);
	int iResult=write(fd, &data_out, 1);
	lasterror=errno;
	if (iResult<=0)
		return COMM_FAILURE;
		
	return COMM_SUCCESS;
}

int CComm::send(unsigned char *pdata, int len)
{
	if (fd<0 || !bIsInitialized)
		return COMM_FAILURE;
	//flush(1,0);
	int iResult=write(fd, pdata, len);
	lasterror=errno;
	if (iResult<=0)
		return COMM_FAILURE;
		
	return COMM_SUCCESS;
}

/* send two byte/character */
int CComm::send2b(unsigned int *pdata)
{
	if (fd<0 || !bIsInitialized)
		return COMM_FAILURE;
	if((send(LOBYTE(*pdata))!=COMM_SUCCESS) ||
		(send(HIBYTE(*pdata))!=COMM_SUCCESS)) {
		lasterror=errno;
		return COMM_FAILURE;
	}
	lasterror=errno;
	return COMM_SUCCESS;
}

int CComm::send2b(unsigned int *pdata, int len2b)
{
	if (fd<0 || !bIsInitialized)
		return COMM_FAILURE;

	for (int ib=0; ib<len2b; ib++) {
		if (send2b((unsigned int *)(pdata+ib))!=COMM_SUCCESS) {
			lasterror=errno;
			return COMM_FAILURE;
		}
	}
	lasterror=errno;
	return COMM_SUCCESS;
}

// receive only one character and place it in the buffer
int CComm::receive(unsigned char *pdata)
{
	if (pdata==NULL || fd<0 || !bIsInitialized)
		return COMM_FAILURE;
		
	int iResult=read(fd, pdata, 1);
	lasterror=errno;
	if (iResult<=0)
		return COMM_FAILURE;
		
	return COMM_SUCCESS;
}

int CComm::receive(unsigned char *pdata, int len)
{
	if (pdata==NULL || fd<0 || !bIsInitialized)
		return COMM_FAILURE;
		
	int iResult=read(fd, pdata, len);
	lasterror=errno;
	if (iResult<=0)
		return COMM_FAILURE;
		
	return COMM_SUCCESS;
}

int CComm::receive2b(unsigned int *pdata, int len2b)
{
	if (pdata==NULL || fd<0 || !bIsInitialized)
		return COMM_FAILURE;
		
	unsigned char ucTmpBuf[2]="";
	int ib=0;
	for (ib=0; ib<len2b; ib++) {
		if (receive(ucTmpBuf, 2)==COMM_FAILURE)
			return COMM_FAILURE;
		pdata[ib]=(unsigned int)MAKEWORD(ucTmpBuf[0], ucTmpBuf[1]);
	}
	return COMM_SUCCESS;
}

int CComm::receive2brev(unsigned int *pdata, int len2b)
{
	if (pdata==NULL || fd<0 || !bIsInitialized)
		return COMM_FAILURE;
		
	unsigned char ucTmpBuf[2]="";
	int ib=0;
	for (ib=0; ib<len2b; ib++) {
		if (receive(ucTmpBuf, 2)==COMM_FAILURE)
			return COMM_FAILURE;
		pdata[ib]=(unsigned int)MAKEWORD(ucTmpBuf[1], ucTmpBuf[0]);
	}
	return COMM_SUCCESS;
}

int CComm::receive2b(unsigned int *pdata, int len2b, unsigned long *pcrc)
{
	if (pdata==NULL || fd<0 || !bIsInitialized || pcrc==NULL)
		return COMM_FAILURE;
		
	unsigned char ucTmpBuf[2]="";
	int ib=0;
	for (ib=0; ib<len2b; ib++) {
		if (receive(ucTmpBuf, 2)==COMM_FAILURE)
			return COMM_FAILURE;
		pdata[ib]=(unsigned int)MAKEWORD(ucTmpBuf[0], ucTmpBuf[1]);
		*pcrc+=ucTmpBuf[0]+ucTmpBuf[1];
	}
	return COMM_SUCCESS;
}

int CComm::receive2brev(unsigned int *pdata, int len2b, unsigned long *pcrc)
{
	if (pdata==NULL || fd<0 || !bIsInitialized || pcrc==NULL)
		return COMM_FAILURE;
		
	unsigned char ucTmpBuf[2]="";
	int ib=0;
	for (ib=0; ib<len2b; ib++) {
		if (receive(ucTmpBuf, 2)==COMM_FAILURE)
			return COMM_FAILURE;
		pdata[ib]=(unsigned int)MAKEWORD(ucTmpBuf[1], ucTmpBuf[0]);
		*pcrc+=ucTmpBuf[0]+ucTmpBuf[1];
	}
	return COMM_SUCCESS;
}

int CComm::lock_port()
{
	if (fd<0 || !bIsInitialized)
		return COMM_FAILURE;

#if defined(HAVE_FLOCK)

	if (flock(fd, LOCK_EX | LOCK_NB) != 0)
		return COMM_FAILURE;
		
#elif defined(HAVE_LOCKF)
	
	lseek(fd, 0L, SEEK_SET);
	if (lockf(fd, F_TLOCK, 0L))
		return COMM_FAILURE;

#endif
	return COMM_SUCCESS;
}

int CComm::unlock_port()
{
	if (fd<0 || !bIsInitialized || !bIsLocked)
		return COMM_FAILURE;

#if defined(HAVE_FLOCK)

	if (flock(fd, LOCK_UN) != 0)
		return COMM_FAILURE;
		
#elif defined(HAVE_LOCKF)
	
	lseek(fd, 0L, SEEK_SET);
	if (lockf(fd, F_ULOCK, 0L))
		return COMM_FAILURE;

#endif
	return COMM_SUCCESS;
}

int CComm::set_line(ecomm_lines eline)
{
	int iflags = getStatus();
	iflags &= ~(eline);
	iflags |= eline;
	setStatus(iflags);
	return getStatus();
}

int CComm::clr_line(ecomm_lines eline)
{
	int iflags = getStatus();
	iflags &= ~(eline);
	setStatus(iflags);
	return getStatus();
}

