/*
    NanoStack: MCU software and PC tools for IP-based wireless sensor networking.
		
    Copyright (C) 2006-2007 Sensinode Ltd.

    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.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

		Address:
		Sensinode Ltd.
		Teknologiantie 6	
		90570 Oulu, Finland

		E-mail:
		info@sensinode.com
*/


#include <sys/poll.h>

#include "port.h"

int port_open(port_t **port, char *device)
{
	port_t *new_port = (port_t *) malloc(sizeof(port_t));
	char err_string[128];
	
	*port = new_port;
	
	new_port->device = 0;
	new_port->handle = 0;

	new_port->handle = open(device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);

	if (new_port->handle <= 0)
	{
		strerror_r(errno, err_string, 128);
		
		printf("Serial port open failed with error message: %s.\n", err_string);
		return(-1);
	}
	else
	{
		tcgetattr(new_port->handle, &(new_port->old_params));
	
		fcntl(new_port->handle, F_SETFL, FASYNC);
		printf("Serial port %s opened succesfully.\n", device);

		return(0);
	}	
}

int port_close(port_t *port)
{
	if (!port)
		return(-1);
	
	if ((port->handle) > 0)
	{
		tcflush(port->handle, TCIFLUSH);
		tcsetattr(port->handle,TCSANOW,&(port->old_params));
	
		close(port->handle);
		port->handle = 0;
	}

	if (port->device) free(port->device);
	port->device = 0;
	free(port);
	
	return(1);
}


/** @todo port_write() function probably needs mutexes -mjs */

int port_write(port_t *port, unsigned char *buffer, size_t buflen)
{
	int i=0;

	if (!port) return -1;
	
	/** @todo The write to serial port is at the moment done one octet at a time with 10ms interval between each write operation due to some minor problems in MCU interrupts. -mjs */
	while(i < buflen)
	{		
		write(port->handle, &(buffer[i]), 1);
		tcflush(port->handle, TCIFLUSH);
		i++;
	}

/*		write(port->handle, &(buffer[i]), buflen);*/

	tcflush(port->handle, TCIFLUSH);
	
	return(0);
}

int port_read(port_t *port, unsigned char *buffer, size_t buflen)
{
	unsigned int l = 0;
	l = read(port->handle, buffer, buflen);
	return(l);
}

int port_set_params(port_t *port, uint32_t speed, uint8_t rtscts)
{
	int rate = B115200;
	struct termios newtio;
	
	if (!port) return -1;
	
	switch (speed)
	{
		case 230400:
			rate = B230400;
			break;
			
		case 0:
		case 115200:
			rate = B115200;
			break;
			
		case 57600:
			rate = B57600;
			break;
			
		case 38400:
			rate = B38400;
			break;
			
		case 19200:
			rate = B19200;
			break;
			
		case 9600:
			rate = B9600;
			break;
		
		default:
			return -1;
			
	}
	bzero(&newtio, sizeof(newtio));

	if (speed == 9600)
	{	
		newtio.c_cflag |= CS8 | CSTOPB | CLOCAL | CREAD;
	}
	else
	{
		newtio.c_cflag |= CS8 |  CLOCAL | CREAD;
	}
	if (rtscts)
	{
		newtio.c_cflag |= CRTSCTS;
	}
	newtio.c_iflag = IGNPAR;
	
	cfsetispeed(&newtio, rate);
	cfsetospeed(&newtio, rate);
	
	newtio.c_cc[VTIME] = 0;
	newtio.c_cc[VMIN] = 1;
	
#if 0	
	newtio.c_cflag = rate | CS8 | CLOCAL | CREAD | CSTOPB;
/*	if (rts_cts) newtio.c_cflag |= CRTSCTS;*/
	
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;

	newtio.c_lflag = 0;
	
	newtio.c_cc[VTIME] = 0;
	newtio.c_cc[VMIN] = 1;
#endif

	tcflush(port->handle, TCIFLUSH);
	tcsetattr(port->handle,TCSANOW,&newtio);

	return(0);
}

int port_dtr_set(port_t *port)
{
	int port_state = TIOCM_DTR;
	
	if (!port) return(-1);
	
/*	error = ioctl(port->handle, TIOCMGET, &port_state);
	port_state |= TIOCM_RTS;
	ioctl(port->handle, TIOCMSET, &port_state);*/
	
	ioctl(port->handle, TIOCMBIS, &port_state);
	return 0;
}

int port_dtr_clear(port_t *port)
{
	int port_state = TIOCM_DTR;
	
	if (!port) return(-1);
	
	ioctl(port->handle, TIOCMBIC, &port_state);
	return 0;
}

int port_rts_set(port_t *port)
{
	int port_state = TIOCM_RTS;
	
	if (!port) return(-1);
	
	ioctl(port->handle, TIOCMBIS, &port_state);
	return 0;
}

int port_rts_clear(port_t *port)
{
	int port_state = TIOCM_RTS;
	
	if (!port) return(-1);
	
	ioctl(port->handle, TIOCMBIC, &port_state);
	return 0;
}

int port_get(port_t *port, unsigned char *buffer, int timeout)
{
	struct pollfd pfds;
	unsigned int nfds = 1;
	int bytes = 0;
	int rval;

	pfds.fd = (int)(port->handle);
	pfds.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;

	rval = poll(&pfds, nfds, timeout);
	
	if((rval & POLLIN) != POLLIN)
	{
		return(-1);
	}
	else
	{
		bytes = port_read(port, buffer, 1);
	}
	return bytes;
}

int port_readline(port_t *port, unsigned char *buffer, int buf_size, int timeout)
{
	int length = 0;
	struct pollfd pfds;
	unsigned int nfds = 1;
	int bytes = 0;
	int rval;

	pfds.fd = (int)(port->handle);
	pfds.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;

	do
	{
		rval = poll(&pfds, nfds, timeout);
		if((rval & POLLIN) != POLLIN)
		{
			return(length);
		}
		else
		{
			bytes = port_read(port, &(buffer[length]), 1);
			if (buffer[length] == '\n')
			{ 
				buf_size = length;
			}
			length += bytes;
		}

	}while(length < buf_size);
	
	buffer[length] = 0;
	
	if(length != 0)
		return length;
	else
		return(-1);
}

int port_write_echo(port_t *port, char *string)
{
	int length = 0;
	int retry = 0;
	unsigned char byte;
	
	while( (string[length]) && (retry < 100) )
	{
		port_write(port, (unsigned char *) &string[length], 1);
		while (retry++ < 100) 
		{
			if (port_read(port, &byte, 1) == 1)
			{
/*				printf("%c",byte);*/
				if (byte == string[length])
				{
					retry = 0;
					length++;
					break;
				}
				else retry = 100;
			}
			else	usleep(1000);
		}
	}
	if ((string[strlen(string)-1] == '\r') && (retry < 100) )
	{ /*wait for \n*/
		retry = 0;
		while (retry++ < 100) 
		{
			if (port_read(port, &byte, 1) == 1)
			{
/*				printf("%c",byte);*/
				break;
			}
			else	usleep(1000);
		}
	}
	
	if (retry >= 100) return 0;
	else return length;
}


int port_write_8byte_no_echo(port_t *port, int dlen, char *string)
{
	int length = 0;
	int total_len;
	int wrbytes = 4;
	
	total_len = dlen;

/*	printf("total: %d, length: %d, dlen: %d.\n", total_len, length, dlen);	*/
	while(total_len > length)
	{
		if((total_len - length) >= wrbytes)
		{
			port_write(port, (unsigned char *)&string[length], wrbytes);
			length +=  wrbytes;
		}
		else
		{
			port_write(port, (unsigned char *)&string[length], total_len - length);
			length +=  total_len - length;
		}
		usleep(1250);
	
	}

	return(length);
}