888 lines
23 KiB
C
Executable File
888 lines
23 KiB
C
Executable File
/*
|
|
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
|