/*
 io_serial.c
 Serial port input/output functions

 This file is part of the Unix driver for Towitoko smartcard readers
 Copyright (C) 2000 2001 Carlos Prados <cprados@yahoo.com>

 This version is modified by doz21 to work in a special manner ;)

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2 of the License, or (at your option) any later version.

 This library 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
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "../globals.h"
#ifdef WITH_CARDREADER

#if defined(__HPUX__)
#include <sys/modem.h>
#endif

#include <poll.h>

#if defined(__linux__) && !defined(__ANDROID__)
#include <linux/serial.h>
#endif

#if defined(__ANDROID__)
#include "../extapi/linux/serial.h"
#endif

#include "../oscam-time.h"
#include "icc_async.h"
#include "io_serial.h"

#if defined(__APPLE__)
#include <IOKIT/serial/ioss.h>
#endif

#define OK 0
#define ERROR 1

#define IO_SERIAL_FILENAME_LENGTH   32

/*
 * Internal functions declaration
 */

static int32_t IO_Serial_Bitrate(int32_t bitrate);

static bool IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us);

static int32_t oscam_sem;

void IO_Serial_Ioctl_Lock(struct s_reader *reader, int32_t flag)
{
	if((reader->typ != R_DB2COM1) && (reader->typ != R_DB2COM2)) { return; }
	if(!flag)
		{ oscam_sem = 0; }
	else while(oscam_sem != reader->typ)
		{
			while(oscam_sem)
				if(reader->typ == R_DB2COM1)
					{ cs_sleepms(6); }
				else
					{ cs_sleepms(8); }
			oscam_sem = reader->typ;
			cs_sleepms(1);
		}
}

bool IO_Serial_DTR_RTS(struct s_reader *reader, int32_t *dtr, int32_t *rts)
{
	const struct s_cardreader *crdr_ops = reader->crdr;
	if (!crdr_ops) return ERROR;

	if(crdr_ops->set_DTS_RTS)
		{ return crdr_ops->set_DTS_RTS(reader, dtr, rts); }

	uint32_t msr;
	uint32_t mbit;

	if(dtr)
	{
		mbit = TIOCM_DTR;
#if defined(TIOCMBIS) && defined(TIOBMBIC)
		if(ioctl(reader->handle, *dtr ? TIOCMBIS : TIOCMBIC, &mbit) < 0)
			{ return ERROR; }
#else
		if(ioctl(reader->handle, TIOCMGET, &msr) < 0)
			{ return ERROR; }
		if(*dtr)
			{ msr |= mbit; }
		else
			{ msr &= ~mbit; }
		if(ioctl(reader->handle, TIOCMSET, &msr) < 0)
			{ return ERROR; }
#endif
		rdr_log_dbg(reader, D_DEVICE, "Setting %s=%i", "DTR", *dtr);
	}

	if(rts)
	{
		mbit = TIOCM_RTS;
#if defined(TIOCMBIS) && defined(TIOBMBIC)
		if(ioctl(reader->handle, *rts ? TIOCMBIS : TIOCMBIC, &mbit) < 0)
			{ return ERROR; }
#else
		if(ioctl(reader->handle, TIOCMGET, &msr) < 0)
			{ return ERROR; }
		if(*rts)
			{ msr |= mbit; }
		else
			{ msr &= ~mbit; }
		if(ioctl(reader->handle, TIOCMSET, &msr) < 0)
			{ return ERROR; }
#endif
		rdr_log_dbg(reader, D_DEVICE, "Setting %s=%i", "RTS", *rts);
	}

	return OK;
}

/*
 * Public functions definition
 */

bool IO_Serial_SetBitrate(struct s_reader *reader, uint32_t bitrate, struct termios *tio)
{
	/* Set the bitrate */
#if !defined(__linux__)
#if !defined(__APPLE__)
	if(IO_Serial_Bitrate(bitrate) == B0)
	{
		rdr_log(reader, "Baudrate %u not supported", bitrate);
		return ERROR;
	}
	else
	{
		//no overclocking
		cfsetospeed(tio, IO_Serial_Bitrate(bitrate));
		cfsetispeed(tio, IO_Serial_Bitrate(bitrate));
		rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u",
					   reader->cardmhz, reader->mhz, bitrate);
	}
#endif
#endif

	/* Set the bitrate */
#if defined(__linux__)
	//FIXME workaround for Smargo until native mode works
	if((reader->mhz == reader->cardmhz) && (reader->smargopatch != 1) && IO_Serial_Bitrate(bitrate) != B0)
	{
		//no overclocking
		cfsetospeed(tio, IO_Serial_Bitrate(bitrate));
		cfsetispeed(tio, IO_Serial_Bitrate(bitrate));
		rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u",
					   reader->cardmhz, reader->mhz, bitrate);
	}
	else
	{
		//over or underclocking
		/* these structures are only available on linux */
		struct serial_struct nuts;
		// This makes valgrind happy, because it doesn't know what TIOCGSERIAL does
		// Without this there are lots of misleading errors of type:
		// "Conditional jump or move depends on uninitialised value(s)"
		nuts.baud_base = 0;
		nuts.custom_divisor = 0;
		ioctl(reader->handle, TIOCGSERIAL, &nuts);
		int32_t custom_baud_asked = bitrate * reader->mhz / reader->cardmhz;
		nuts.custom_divisor = (nuts.baud_base + (custom_baud_asked / 2)) / custom_baud_asked;
		int32_t custom_baud_delivered =  nuts.baud_base / nuts.custom_divisor;
		rdr_log_dbg(reader, D_DEVICE, "custom baudrate: cardmhz=%d mhz=%d custom_baud=%d baud_base=%d divisor=%d -> effective baudrate %d",
					   reader->cardmhz, reader->mhz, custom_baud_asked, nuts.baud_base, nuts.custom_divisor, custom_baud_delivered);
		int32_t baud_diff = custom_baud_delivered - custom_baud_asked;
		if(baud_diff < 0)
			{ baud_diff = (-baud_diff); }
		if(baud_diff  > 0.05 * custom_baud_asked)
		{
			rdr_log(reader, "WARNING: your card is asking for custom_baudrate = %i, but your configuration can only deliver custom_baudrate = %i", custom_baud_asked, custom_baud_delivered);
			rdr_log(reader, "You are over- or underclocking, try OSCam when running your reader at normal clockspeed as required by your card, and setting mhz and cardmhz parameters accordingly.");
			if(nuts.baud_base <= 115200)
				{ rdr_log(reader, "You are probably connecting your reader via a serial port, OSCam has more flexibility switching to custom_baudrates when using an USB->serial converter, preferably based on FTDI chip."); }
		}
		nuts.flags &= ~ASYNC_SPD_MASK;
		nuts.flags |= ASYNC_SPD_CUST;
		ioctl(reader->handle, TIOCSSERIAL, &nuts);
		cfsetospeed(tio, IO_Serial_Bitrate(38400));
		cfsetispeed(tio, IO_Serial_Bitrate(38400));
	}
#endif

	/* Set the bitrate */
#if defined(__APPLE__)
	if(IO_Serial_Bitrate(bitrate) == B0)
	{
		int32_t custom_baud_rate = bitrate * reader->mhz / reader->cardmhz;
		if(ioctl(reader->handle, IOSSIOSPEED, &custom_baud_rate) < 0)
		{
			rdr_log(reader, "Baudrate %u not supported", custom_baud_rate);
			return ERROR;
		}
		else
		{
			rdr_log_dbg(reader, D_DEVICE, "custom baudrate: cardmhz=%d mhz=%d -> effective baudrate %u",
						   reader->cardmhz, reader->mhz, custom_baud_rate);
		}
	}
	else
	{
		//no overclocking
		cfsetospeed(tio, IO_Serial_Bitrate(bitrate));
		cfsetispeed(tio, IO_Serial_Bitrate(bitrate));
		rdr_log_dbg(reader, D_DEVICE, "standard baudrate: cardmhz=%d mhz=%d -> effective baudrate %u",
					   reader->cardmhz, reader->mhz, bitrate);
	}
#endif
	return OK;
}

bool IO_Serial_SetParams(struct s_reader *reader, uint32_t bitrate, uint32_t bits, int32_t parity, uint32_t stopbits, int32_t *dtr, int32_t *rts)
{
	struct termios newtio;

	if(reader->typ == R_INTERNAL)
		{ return ERROR; }

	memset(&newtio, 0, sizeof(newtio));

	if(IO_Serial_SetBitrate(reader, bitrate, & newtio))
		{ return ERROR; }

	/* Set the character size */
	switch(bits)
	{
	case 5:
		newtio.c_cflag |= CS5;
		break;

	case 6:
		newtio.c_cflag |= CS6;
		break;

	case 7:
		newtio.c_cflag |= CS7;
		break;

	case 8:
		newtio.c_cflag |= CS8;
		break;
	}

	/* Set the parity */
	switch(parity)
	{
	case PARITY_ODD:
		newtio.c_cflag |= PARENB;
		newtio.c_cflag |= PARODD;
		break;

	case PARITY_EVEN:
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;
		break;

	case PARITY_NONE:
		newtio.c_cflag &= ~PARENB;
		break;
	}

	/* Set the number of stop bits */
	switch(stopbits)
	{
	case 1:
		newtio.c_cflag &= (~CSTOPB);
		break;
	case 2:
		newtio.c_cflag |= CSTOPB;
		break;
	}

	/* Selects raw (non-canonical) input and output */
	newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	newtio.c_oflag &= ~OPOST;
#if 1
	newtio.c_iflag |= IGNPAR;
	/* Ignore parity errors!!! Windows driver does so why shouldn't I? */
#endif
	/* Enable receiver, hang on close, ignore control line */
	newtio.c_cflag |= CREAD | HUPCL | CLOCAL;

	/* Read 1 byte minimun, no timeout specified */
	newtio.c_cc[VMIN] = 1;
	newtio.c_cc[VTIME] = 0;

	if(IO_Serial_SetProperties(reader, newtio))
		{ return ERROR; }

	reader->current_baudrate = bitrate;

	IO_Serial_Ioctl_Lock(reader, 1);
	IO_Serial_DTR_RTS(reader, dtr, rts);
	IO_Serial_Ioctl_Lock(reader, 0);
	return OK;
}

bool IO_Serial_SetProperties(struct s_reader *reader, struct termios newtio)
{
	if(reader->typ == R_INTERNAL)
		{ return OK; }

	if(tcsetattr(reader->handle, TCSANOW, &newtio) < 0)    // set terminal attributes.
		{ return ERROR; }

	int32_t mctl;
	rdr_log_dbg(reader, D_DEVICE, "Getting readerstatus...");
	if(ioctl(reader->handle, TIOCMGET, &mctl) >= 0)      // get reader statusbits
	{
		mctl &= ~TIOCM_RTS;
		rdr_log_dbg(reader, D_DEVICE, "Set reader ready to Send");
		ioctl(reader->handle, TIOCMSET, &mctl);   // set reader ready to send.
	}
	else { rdr_log(reader, "WARNING: Cant get readerstatus!"); }

	return OK;
}

int32_t IO_Serial_SetParity(struct s_reader *reader, unsigned char parity)
{
	struct termios tio;
	int32_t current_parity;
	// Get current parity
	if(tcgetattr(reader->handle, &tio) != 0)
	{
		rdr_log(reader, "ERROR: Could not get current parity, %s (errno=%d %s)", __func__, errno, strerror(errno));
		current_parity = 5; // set to unknown (5 is not predefined!)
	}
	else
	{
		if(((tio.c_cflag) & PARENB) == PARENB)
		{
			if(((tio.c_cflag) & PARODD) == PARODD)
				{ current_parity = PARITY_ODD; }
			else
				{ current_parity = PARITY_EVEN; }
		}
		else
		{
			current_parity = PARITY_NONE;
		}
	}

	if(current_parity != parity)
	{
		rdr_log_dbg(reader, D_IFD, "Setting parity from %s to %s",
					   current_parity == PARITY_ODD ? "Odd" :
					   current_parity == PARITY_NONE ? "None" :
					   current_parity == PARITY_EVEN ? "Even" : "Unknown",

					   parity == PARITY_ODD ? "Odd" :
					   parity == PARITY_NONE ? "None" :
					   parity == PARITY_EVEN ? "Even" : "Invalid");

		// Set the parity
		switch(parity)
		{
		case PARITY_ODD:
			tio.c_cflag |= PARENB;
			tio.c_cflag |= PARODD;
			break;

		case PARITY_EVEN:
			tio.c_cflag |= PARENB;
			tio.c_cflag &= ~PARODD;
			break;

		case PARITY_NONE:
			tio.c_cflag &= ~PARENB;
			break;
		}
		if(IO_Serial_SetProperties(reader, tio))
		{
			rdr_log_dbg(reader, D_IFD, "ERROR: could set parity!");
			return ERROR;
		}
	}

	return OK;
}

void IO_Serial_Flush(struct s_reader *reader)
{
	unsigned char b;
	uint8_t n = 0;
	tcflush(reader->handle, TCIOFLUSH);
	struct timeb starttotal, endtotal;
	struct timeb start, end;
	cs_ftimeus(&starttotal);
	endtotal = starttotal;
	cs_ftimeus(&start);
	end = start;
	int64_t gone = 0;
	while(!IO_Serial_Read(reader, 0, 75000, 1, &b)) // first appears between 9~75ms
	{
		n++;
		cs_ftimeus(&end);
		gone = comp_timebus(&end, &start);
		rdr_log_dbg(reader, D_DEVICE, "Flush readed byte Nr %d value %.2x time_us %"PRId64, n, b, gone);
		cs_ftimeus(&start); // Reset timer
		end = start;
	}
	cs_ftimeus(&endtotal);
	gone = comp_timebus(&endtotal, &starttotal);
	rdr_log_dbg(reader, D_DEVICE, "Buffers readed  %d bytes total time_us %"PRId64, n, gone);
}

void IO_Serial_Sendbreak(struct s_reader *reader, int32_t duration)
{
	tcsendbreak(reader->handle, duration / 1000);
}

bool IO_Serial_Read(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, unsigned char *data)
{
	uint32_t count = 0;

	if(timeout == 0)   // General fix for readers not communicating timeout and delay
	{
		if(reader->read_timeout != 0) { timeout = reader->read_timeout; }
		else { timeout = 9999000; }   // hope 9999000 is long enough!
		rdr_log_dbg(reader, D_DEVICE, "Warning: read timeout 0 changed to %d us", timeout);
	}

	rdr_log_dbg(reader, D_DEVICE, "Read timeout %d us, read delay %d us, to read %d char(s), chunksize %d char(s)", timeout, delay, size, size);

#if defined(WITH_STAPI) || defined(WITH_STAPI5) || defined(__SH4__) //internal stapi and sh4 readers need special treatment as they don't respond correctly to poll and some sh4 boxes only can read 1 byte at once
	if(reader->typ == R_INTERNAL)
	{
		int32_t readed;
#if defined(WITH_STAPI) || defined(WITH_STAPI5)
		const uint32_t chunksize = INT_MAX;
#elif defined(__SH4__)
		const uint32_t chunksize = 1;
#endif
		struct timeb start, end;
		cs_ftime(&start);
		end = start;
		int64_t gone = 0;
		int32_t timeout_ms = timeout / 1000;
		readed = 0;

		while(gone < timeout_ms)
		{
			readed = read(reader->handle, &data[count], size - count >= chunksize ? chunksize : size - count);
			cs_ftime(&end);
			if(readed > 0)
			{
				count += readed;
				cs_ftime(&start); // Reset timer
				end = start;
			}
			gone = comp_timeb(&end, &start);
			if(count < size)
			{
				if(readed < (int32_t)chunksize) { cs_sleepus(1); }
				continue;
			}
			else { break; }
		}
		if(count < size)
		{
			rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:");
			return ERROR;
		}
	}
	else
#endif  // read all chars at once for all other boxes
	{
		while(count < size)
		{
			int32_t readed = -1, errorcount = 0;
AGAIN:
			if(IO_Serial_WaitToRead(reader, delay, timeout))
			{
				rdr_log_dbg(reader, D_DEVICE, "Timeout in IO_Serial_WaitToRead, timeout=%d us", timeout);
				return ERROR;
			}

			while(readed < 0 && errorcount < 10)
			{
				readed = read(reader->handle, &data[count], size - count);
				if(readed < 0)
				{
					if(errno == EINTR) { continue; }  // try again in case of interrupt
					if(errno == EAGAIN) { goto AGAIN; }  //EAGAIN needs select procedure again
					rdr_log(reader, "ERROR: %s (errno=%d %s)", __func__, errno, strerror(errno));
					errorcount++;
				}
			}

			if(readed == 0)
			{
				rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:");
				rdr_log_dbg(reader, D_DEVICE, "Received End of transmission");
				return ERROR;
			}
			count += readed;
		}
	}
	rdr_log_dump_dbg(reader, D_DEVICE, data, count, "Receiving:");
	return OK;
}

int32_t IO_Serial_Receive(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t delay, uint32_t timeout)
{
	return IO_Serial_Read(reader, delay, timeout, size, buffer);
}

bool IO_Serial_Write(struct s_reader *reader, uint32_t delay, uint32_t timeout, uint32_t size, const unsigned char *data)
{
	const struct s_cardreader *crdr_ops = reader->crdr;
	if (!crdr_ops) return ERROR;

	if(timeout == 0)   // General fix for readers not communicating timeout and delay
	{
		if(reader->char_delay != 0) { timeout = reader->char_delay; }
		else { timeout = 1000000; }
		rdr_log_dbg(reader, D_DEVICE, "Warning: write timeout 0 changed to %d us", timeout);
	}
	uint32_t count, to_send, i_w;
	unsigned char data_w[MAX_ECM_SIZE];

	to_send = (delay ? 1 : size); // calculate chars to send at one
	rdr_log_dbg(reader, D_DEVICE, "Write timeout %d us, write delay %d us, to send %d char(s), chunksize %d char(s)", timeout, delay, size, to_send);

	for(count = 0; count < size; count += to_send)
	{
		if(count + to_send > size)
		{
			to_send = size - count;
		}
		uint16_t errorcount = 0, to_do = to_send;
		for(i_w = 0; i_w < to_send; i_w++)
			{ data_w [i_w] = data [count + i_w]; }
		rdr_log_dump_dbg(reader, D_DEVICE, data_w + (to_send - to_do), to_do, "Sending:");
AGAIN:
		if(!IO_Serial_WaitToWrite(reader, delay, timeout))
		{
			while(to_do != 0)
			{
				int32_t u = write(reader->handle, data_w + (to_send - to_do), to_do);
				if(u < 1)
				{
					if(errno == EINTR) { continue; }  //try again in case of Interrupted system call
					if(errno == EAGAIN) { goto AGAIN; }  //EAGAIN needs a select procedure again
					errorcount++;
					int16_t written = count + to_send - to_do;
					if(u != 0)
					{
						rdr_log(reader, "ERROR: %s: Written=%d of %d (errno=%d %s)",
								__func__, written , size, errno, strerror(errno));
					}
					if(errorcount > 10)   //exit if more than 10 errors
					{
						return ERROR;
					}
				}
				else
				{
					to_do -= u;
					errorcount = 0;
					if(crdr_ops->read_written)
						{ reader->written += u; } // these readers echo transmitted chars
				}
			}
		}
		else
		{
			rdr_log(reader, "Timeout in IO_Serial_WaitToWrite, delay=%d us, timeout=%d us", delay, timeout);
			if(crdr_ops->read_written && reader->written > 0)    // these readers need to read all transmitted chars before they can receive!
			{
				unsigned char buf[256];
				rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written);
				int32_t n = reader->written;
				if(IO_Serial_Read(reader, 0, 9990000, n, buf))   // use 9990000 = aprox 10 seconds (since written chars could be hughe!)
					{ return ERROR; }
				reader->written = 0;
				rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!");
			}
			return ERROR;
		}
	}
	if(crdr_ops->read_written && reader->written > 0)    // these readers need to read all transmitted chars before they can receive!
	{
		unsigned char buf[256];
		rdr_log_dbg(reader, D_DEVICE, "Reading %d echoed transmitted chars...", reader->written);
		int32_t n = reader->written;
		if(IO_Serial_Read(reader, 0, 9990000, n, buf))   // use 9990000 = aprox 10 seconds (since written chars could be hughe!)
			{ return ERROR; }
		reader->written = 0;
		rdr_log_dbg(reader, D_DEVICE, "Reading of echoed transmitted chars done!");
	}
	return OK;
}

#define MAX_TRANSMIT 255

int32_t IO_Serial_Transmit(struct s_reader *reader, unsigned char *buffer, uint32_t size, uint32_t UNUSED(expectedlen), uint32_t delay, uint32_t timeout)
{
	uint32_t sent, to_send;
	for(sent = 0; sent < size; sent = sent + to_send)
	{
		to_send = MIN(size, MAX_TRANSMIT);
		if(IO_Serial_Write(reader, delay, timeout , to_send, buffer + sent))
			{ return ERROR; }
	}
	return OK;
}

int32_t IO_Serial_Close(struct s_reader *reader)
{

	rdr_log_dbg(reader, D_DEVICE, "Closing serial port %s", reader->device);
	cs_sleepms(100); // maybe a dirty fix for the restart problem posted by wonderdoc
	if(reader->fdmc >= 0) { close(reader->fdmc); }
	if(reader->handle >= 0 && close(reader->handle) != 0)
		{ return ERROR; }

	reader->written = 0;

	return OK;
}

/*
 * Internal functions definition
 */

static int32_t IO_Serial_Bitrate(int32_t bitrate)
{
	static const struct BaudRates
	{
		int32_t real;
		speed_t apival;
	} BaudRateTab[] =
	{
#ifdef B230400
		{ 230400, B230400 },
#endif
#ifdef B115200
		{ 115200, B115200 },
#endif
#ifdef B76800
		{  76800, B76800  },
#endif
#ifdef B57600
		{  57600, B57600  },
#endif
#ifdef B38400
		{  38400, B38400  },
#endif
#ifdef B28800
		{  28800, B28800  },
#endif
#ifdef B19200
		{  19200, B19200  },
#endif
#ifdef B14400
		{  14400, B14400  },
#endif
#ifdef B9600
		{   9600, B9600   },
#endif
#ifdef B7200
		{   7200, B7200   },
#endif
#ifdef B4800
		{   4800, B4800   },
#endif
#ifdef B2400
		{   2400, B2400   },
#endif
#ifdef B1200
		{   1200, B1200   },
#endif
#ifdef B600
		{    600, B600    },
#endif
#ifdef B300
		{    300, B300    },
#endif
#ifdef B200
		{    200, B200    },
#endif
#ifdef B150
		{    150, B150    },
#endif
#ifdef B134
		{    134, B134    },
#endif
#ifdef B110
		{    110, B110    },
#endif
#ifdef B75
		{     75, B75     },
#endif
#ifdef B50
		{     50, B50     },
#endif
	};

	int32_t i;

	for(i = 0; i < (int)(sizeof(BaudRateTab) / sizeof(struct BaudRates)); i++)
	{
		int32_t b = BaudRateTab[i].real;
		int32_t d = ((b - bitrate) * 10000) / b;
		if(abs(d) <= 350)
		{
			return BaudRateTab[i].apival;
		}
	}
	return B0;
}

bool IO_Serial_WaitToRead(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us)
{
	struct pollfd ufds;
	struct timeb start, end;
	int32_t ret_val;
	int32_t in_fd;
	int64_t polltimeout = timeout_us / 1000;

	if(delay_us > 0)
		{ cs_sleepus(delay_us); }  // wait in us
	in_fd = reader->handle;

	ufds.fd = in_fd;
	ufds.events = POLLIN | POLLPRI;
	ufds.revents = 0x0000;
	cs_ftime(&start); // register start time
	while(1)
	{
		ret_val = poll(&ufds, 1, polltimeout);
		cs_ftime(&end); // register end time
		switch(ret_val)
		{
		case -1:
			if(errno == EINTR || errno == EAGAIN)
			{
				cs_sleepus(1);
				if(timeout_us > 0)
				{
					polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start);
					if(polltimeout < 0) { polltimeout = 0; }
				}
				continue;
			}
			rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno));
			return ERROR;
		default:
			if(ufds.revents & (POLLIN | POLLPRI))
				{ return OK; }
			else
				{ return ERROR; }
		}
	}
}

static bool IO_Serial_WaitToWrite(struct s_reader *reader, uint32_t delay_us, uint32_t timeout_us)
{
	struct pollfd ufds;
	struct timeb start, end;
	int32_t ret_val;
	int32_t out_fd;
	int64_t polltimeout = timeout_us / 1000;

#if !defined(WITH_COOLAPI) && !defined(WITH_COOLAPI2)
	if(reader->typ == R_INTERNAL) { return OK; }  // needed for internal readers, otherwise error!
#endif
	if(delay_us > 0)
		{ cs_sleepus(delay_us); }  // wait in us
	out_fd = reader->handle;

	ufds.fd = out_fd;
	ufds.events = POLLOUT;
	ufds.revents = 0x0000;
	cs_ftime(&start); // register start time
	while(1)
	{
		ret_val = poll(&ufds, 1, polltimeout);
		cs_ftime(&end); // register end time
		switch(ret_val)
		{
		case 0:
			rdr_log(reader, "ERROR: not ready to write, timeout=%"PRId64" ms", comp_timeb(&end, &start));
			return ERROR;
		case -1:
			if(errno == EINTR || errno == EAGAIN)
			{
				cs_sleepus(1);
				if(timeout_us > 0)
				{
					polltimeout = (timeout_us / 1000) - comp_timeb(&end, &start);
					if(polltimeout < 0) { polltimeout = 0; }
				}
				continue;
			}
			rdr_log(reader, "ERROR: %s: timeout=%"PRId64" ms (errno=%d %s)", __func__, comp_timeb(&end, &start), errno, strerror(errno));
			return ERROR;
		default:
			if(((ufds.revents) & POLLOUT) == POLLOUT)
				{ return OK; }
			else
				{ return ERROR; }
		}
	}
}

bool IO_Serial_InitPnP(struct s_reader *reader)
{
	uint32_t PnP_id_size = 0;
	unsigned char PnP_id[IO_SERIAL_PNPID_SIZE]; /* PnP Id of the serial device */
	int32_t dtr = IO_SERIAL_HIGH;
	int32_t cts = IO_SERIAL_LOW;

	if(IO_Serial_SetParams(reader, 1200, 7, PARITY_NONE, 1, &dtr, &cts))
		{ return ERROR; }

	while((PnP_id_size < IO_SERIAL_PNPID_SIZE) && !IO_Serial_Read(reader, 0, 200000, 1, &(PnP_id[PnP_id_size])))
		{ PnP_id_size++; }

	return OK;
}

int32_t IO_Serial_GetStatus(struct s_reader *reader, int32_t *status)
{
	uint32_t modembits = 0;
	if(ioctl(reader->handle, TIOCMGET, &modembits) == -1)
	{
		rdr_log(reader, "ERROR: %s: ioctl(TIOCMGET): %s", __func__, strerror(errno));
		return ERROR;
	}
	*status = 0;
	switch(reader->detect & 0x7f)
	{
	case 0:
		*status = modembits & TIOCM_CAR;
		break;
	case 1:
		*status = modembits & TIOCM_DSR;
		break;
	case 2:
		*status = modembits & TIOCM_CTS;
		break;
	case 3:
		*status = modembits & TIOCM_RNG;
		break;
	}
	if(!(reader->detect & 0x80))
		{ *status = !*status; }
	return OK;
}

int32_t IO_Serial_SetBaudrate(struct s_reader *reader, uint32_t baudrate)
{
	rdr_log_dbg(reader, D_IFD, "Setting baudrate to %u", baudrate);
	// Get current settings
	struct termios tio;
	call(tcgetattr(reader->handle, &tio) != 0);
	// Set new baudrate
	call(IO_Serial_SetBitrate(reader, baudrate, &tio));
	call(IO_Serial_SetProperties(reader, tio));
	reader->current_baudrate = baudrate; //so if update fails, reader->current_baudrate is not changed either
	return OK;
}

#endif
