/*
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 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 "defines.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_POLL
#include <sys/poll.h>
#else
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <sys/ioctl.h>
#include <time.h>
#include "io_serial.h"
#define IO_SERIAL_FILENAME_LENGTH 32
/*
* Internal functions declaration
*/
static int
IO_Serial_Bitrate(int bitrate);
static bool
IO_Serial_WaitToRead (int hnd, unsigned delay_ms, unsigned timeout_ms);
static bool
IO_Serial_WaitToWrite (int hnd, unsigned delay_ms, unsigned timeout_ms);
static void
IO_Serial_DeviceName (unsigned com, bool usbserial, char * filename, unsigned length);
static bool
IO_Serial_InitPnP (IO_Serial * io);
static void
IO_Serial_Clear (IO_Serial * io);
static bool
IO_Serial_GetPropertiesCache(IO_Serial * io, IO_Serial_Properties * props);
static void
IO_Serial_SetPropertiesCache(IO_Serial * io, IO_Serial_Properties * props);
static void
IO_Serial_ClearPropertiesCache (IO_Serial * io);
/*
* Public functions definition
*/
IO_Serial *
IO_Serial_New (void)
{
IO_Serial *io;
io = (IO_Serial *) malloc (sizeof (IO_Serial));
if (io != NULL)
IO_Serial_Clear (io);
return io;
}
bool IO_Serial_Init (IO_Serial * io, unsigned com, bool usbserial, bool pnp)
{
char filename[IO_SERIAL_FILENAME_LENGTH];
IO_Serial_DeviceName (com, usbserial, filename, IO_SERIAL_FILENAME_LENGTH);
#ifdef DEBUG_IO
printf ("IO: Opening serial port %s\n", filename);
#endif
if (com < 1)
return FALSE;
io->com = com;
io->fd = open (filename, O_RDWR | O_NOCTTY);
if (io->fd < 0)
return FALSE;
if (pnp)
IO_Serial_InitPnP (io);
io->usbserial=usbserial;
return TRUE;
}
bool
IO_Serial_GetProperties (IO_Serial * io, IO_Serial_Properties * props)
{
struct termios currtio;
speed_t i_speed, o_speed;
unsigned int mctl;
if (IO_Serial_GetPropertiesCache(io, props))
return TRUE;
if (tcgetattr (io->fd, &currtio) != 0)
return FALSE;
o_speed = cfgetospeed (&currtio);
switch (o_speed)
{
#ifdef B0
case B0:
props->output_bitrate = 0;
break;
#endif
#ifdef B50
case B50:
props->output_bitrate = 50;
break;
#endif
#ifdef B75
case B75:
props->output_bitrate = 75;
break;
#endif
#ifdef B110
case B110:
props->output_bitrate = 110;
break;
#endif
#ifdef B134
case B134:
props->output_bitrate = 134;
break;
#endif
#ifdef B150
case B150:
props->output_bitrate = 150;
break;
#endif
#ifdef B200
case B200:
props->output_bitrate = 200;
break;
#endif
#ifdef B300
case B300:
props->output_bitrate = 300;
break;
#endif
#ifdef B600
case B600:
props->output_bitrate = 600;
break;
#endif
#ifdef B1200
case B1200:
props->output_bitrate = 1200;
break;
#endif
#ifdef B1800
case B1800:
props->output_bitrate = 1800;
break;
#endif
#ifdef B2400
case B2400:
props->output_bitrate = 2400;
break;
#endif
#ifdef B4800
case B4800:
props->output_bitrate = 4800;
break;
#endif
#ifdef B9600
case B9600:
props->output_bitrate = 9600;
break;
#endif
#ifdef B19200
case B19200:
props->output_bitrate = 19200;
break;
#endif
#ifdef B38400
case B38400:
props->output_bitrate = 38400;
break;
#endif
#ifdef B57600
case B57600:
props->output_bitrate = 57600;
break;
#endif
#ifdef B115200
case B115200:
props->output_bitrate = 115200;
break;
#endif
#ifdef B230400
case B230400:
props->output_bitrate = 230400;
break;
#endif
default:
props->output_bitrate = 1200;
break;
}
i_speed = cfgetispeed (&currtio);
switch (i_speed)
{
#ifdef B0
case B0:
props->input_bitrate = 0;
break;
#endif
#ifdef B50
case B50:
props->input_bitrate = 50;
break;
#endif
#ifdef B75
case B75:
props->input_bitrate = 75;
break;
#endif
#ifdef B110
case B110:
props->input_bitrate = 110;
break;
#endif
#ifdef B134
case B134:
props->input_bitrate = 134;
break;
#endif
#ifdef B150
case B150:
props->input_bitrate = 150;
break;
#endif
#ifdef B200
case B200:
props->input_bitrate = 200;
break;
#endif
#ifdef B300
case B300:
props->input_bitrate = 300;
break;
#endif
#ifdef B600
case B600:
props->input_bitrate = 600;
break;
#endif
#ifdef B1200
case B1200:
props->input_bitrate = 1200;
break;
#endif
#ifdef B1800
case B1800:
props->input_bitrate = 1800;
break;
#endif
#ifdef B2400
case B2400:
props->input_bitrate = 2400;
break;
#endif
#ifdef B4800
case B4800:
props->input_bitrate = 4800;
break;
#endif
#ifdef B9600
case B9600:
props->input_bitrate = 9600;
break;
#endif
#ifdef B19200
case B19200:
props->input_bitrate = 19200;
break;
#endif
#ifdef B38400
case B38400:
props->input_bitrate = 38400;
break;
#endif
#ifdef B57600
case B57600:
props->input_bitrate = 57600;
break;
#endif
#ifdef B115200
case B115200:
props->input_bitrate = 115200;
break;
#endif
#ifdef B230400
case B230400:
props->input_bitrate = 230400;
break;
#endif
default:
props->input_bitrate = 1200;
break;
}
switch (currtio.c_cflag & CSIZE)
{
case CS5:
props->bits = 5;
break;
case CS6:
props->bits = 6;
break;
case CS7:
props->bits = 7;
break;
case CS8:
props->bits = 8;
break;
}
if (((currtio.c_cflag) & PARENB) == PARENB)
if (((currtio.c_cflag) & PARODD) == PARODD)
props->parity = IO_SERIAL_PARITY_ODD;
else
props->parity = IO_SERIAL_PARITY_EVEN;
else
props->parity = IO_SERIAL_PARITY_NONE;
if (((currtio.c_cflag) & CSTOPB) == CSTOPB)
props->stopbits = 2;
else
props->stopbits = 1;
#if !defined(OS_CYGWIN32) && !defined(OS_HPUX)
if (ioctl (io->fd, TIOCMGET, &mctl) < 0)
return FALSE;
props->dtr = ((mctl & TIOCM_DTR) ? IO_SERIAL_HIGH : IO_SERIAL_LOW);
props->rts = ((mctl & TIOCM_RTS) ? IO_SERIAL_HIGH : IO_SERIAL_LOW);
#else
props->dtr = IO_SERIAL_HIGH;
props->rts = IO_SERIAL_HIGH;
#endif
IO_Serial_SetPropertiesCache (io, props);
#ifdef DEBUG_IO
printf
("IO: Getting properties: %ld bps; %d bits/byte; %s parity; %d stopbits; dtr=%d; rts=%d\n",
props->input_bitrate, props->bits,
props->parity == IO_SERIAL_PARITY_EVEN ? "Even" : props->parity ==
IO_SERIAL_PARITY_ODD ? "Odd" : "None", props->stopbits, props->dtr,
props->rts);
#endif
return TRUE;
}
bool
IO_Serial_SetProperties (IO_Serial * io, IO_Serial_Properties * props)
{
struct termios newtio;
unsigned int modembits;
#if 1
#if !defined(OS_CYGWIN32) && !defined(OS_HPUX)
modembits = TIOCM_DTR;
if (props->dtr == IO_SERIAL_HIGH)
{
if (ioctl (io->fd, TIOCMBIS, &modembits) < 0)
return FALSE;
}
else if (props->dtr == IO_SERIAL_LOW)
{
if (ioctl (io->fd, TIOCMBIC, &modembits) < 0)
return FALSE;
}
modembits = TIOCM_RTS;
if (props->rts == IO_SERIAL_HIGH)
{
if (ioctl (io->fd, TIOCMBIS, &modembits) < 0)
return FALSE;
}
else if (props->rts == IO_SERIAL_LOW)
{
if (ioctl (io->fd, TIOCMBIC, &modembits) < 0)
return FALSE;
}
#endif
#endif
memset (&newtio, 0, sizeof (newtio));
/* Set the bitrate */
cfsetispeed (&newtio, IO_Serial_Bitrate (props->input_bitrate));
cfsetospeed (&newtio, IO_Serial_Bitrate (props->output_bitrate));
/* Set the character size */
switch (props->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 (props->parity)
{
case IO_SERIAL_PARITY_ODD:
newtio.c_cflag |= PARENB;
newtio.c_cflag |= PARODD;
break;
case IO_SERIAL_PARITY_EVEN:
newtio.c_cflag |= PARENB;
newtio.c_cflag &= ~PARODD;
break;
case IO_SERIAL_PARITY_NONE:
newtio.c_cflag &= ~PARENB;
}
/* Set the number of stop bits */
switch (props->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
/* Ignore parity errors!!! Windows driver does so why shouldn't I? */
newtio.c_iflag |= IGNPAR;
#endif
/* Enable receiber, 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 (tcsetattr (io->fd, TCSANOW, &newtio) < 0)
return FALSE;
if (tcflush (io->fd, TCIFLUSH) < 0)
return FALSE;
IO_Serial_SetPropertiesCache (io, props);
#ifdef DEBUG_IO
printf
("IO: Setting properties: %ld bps; %d bits/byte; %s parity; %d stopbits; dtr=%d; rts=%d\n",
props->input_bitrate, props->bits,
props->parity == IO_SERIAL_PARITY_EVEN ? "Even" : props->parity ==
IO_SERIAL_PARITY_ODD ? "Odd" : "None", props->stopbits, props->dtr,
props->rts);
#endif
return TRUE;
}
void
IO_Serial_GetPnPId (IO_Serial * io, BYTE * pnp_id, unsigned *length)
{
(*length) = io->PnP_id_size;
memcpy (pnp_id, io->PnP_id, io->PnP_id_size);
}
unsigned
IO_Serial_GetCom (IO_Serial * io)
{
return io->com;
}
bool
IO_Serial_Read (IO_Serial * io, unsigned timeout, unsigned size, BYTE * data)
{
BYTE c;
int count = 0;
#ifdef DEBUG_IO
printf ("IO: Receiving: ");
fflush (stdout);
#endif
for (count = 0; count < size; count++)
{
if (IO_Serial_WaitToRead (io->fd, 0, timeout))
{
if (read (io->fd, &c, 1) != 1)
{
#ifdef DEBUG_IO
printf ("ERROR\n");
fflush (stdout);
#endif
return FALSE;
}
data[count] = c;
#ifdef DEBUG_IO
printf ("%X ", c);
fflush (stdout);
#endif
}
else
{
#ifdef DEBUG_IO
printf ("TIMEOUT\n");
fflush (stdout);
#endif
/* tcflush (io->fd, TCIFLUSH); */
return FALSE;
}
}
#ifdef DEBUG_IO
printf ("\n");
fflush (stdout);
#endif
return TRUE;
}
bool
IO_Serial_Write (IO_Serial * io, unsigned delay, unsigned size, BYTE * data)
{
unsigned count, to_send;
#ifdef DEBUG_IO
unsigned i;
printf ("IO: Sending: ");
fflush (stdout);
#endif
/* Discard input data from previous commands */
tcflush (io->fd, TCIFLUSH);
for (count = 0; count < size; count += to_send)
{
to_send = (delay? 1: size);
if (IO_Serial_WaitToWrite (io->fd, delay, 1000))
{
if (write (io->fd, data + count, to_send) != to_send)
{
#ifdef DEBUG_IO
printf ("ERROR\n");
fflush (stdout);
#endif
return FALSE;
}
#ifdef DEBUG_IO
for (i=0; i<to_send; i++)
printf ("%X ", data[count + i]);
fflush (stdout);
#endif
}
else
{
#ifdef DEBUG_IO
printf ("TIMEOUT\n");
fflush (stdout);
#endif
/* tcflush (io->fd, TCIFLUSH); */
return FALSE;
}
}
#ifdef DEBUG_IO
printf ("\n");
fflush (stdout);
#endif
return TRUE;
}
bool IO_Serial_Close (IO_Serial * io)
{
char filename[IO_SERIAL_FILENAME_LENGTH];
IO_Serial_DeviceName (io->com, io->usbserial, filename,
IO_SERIAL_FILENAME_LENGTH);
#ifdef DEBUG_IO
printf ("IO: Clossing serial port %s\n", filename);
#endif
if (close (io->fd) != 0)
return FALSE;
IO_Serial_ClearPropertiesCache (io);
IO_Serial_Clear (io);
return TRUE;
}
void
IO_Serial_Delete (IO_Serial * io)
{
if (io->props != NULL)
free (io->props);
free (io);
}
/*
* Internal functions definition
*/
static int
IO_Serial_Bitrate(int bitrate)
{
#ifdef B230400
if ((bitrate)>=230400) return B230400;
#endif
#ifdef B115200
if ((bitrate)>=115200) return B115200;
#endif
#ifdef B57600
if ((bitrate)>=57600) return B57600;
#endif
#ifdef B38400
if ((bitrate)>=38400) return B38400;
#endif
#ifdef B19200
if ((bitrate)>=19200) return B19200;
#endif
#ifdef B9600
if ((bitrate)>=9600) return B9600;
#endif
#ifdef B4800
if ((bitrate)>=4800) return B4800;
#endif
#ifdef B2400
if ((bitrate)>=2400) return B2400;
#endif
#ifdef B1800
if ((bitrate)>=1800) return B1800;
#endif
#ifdef B1200
if ((bitrate)>=1200) return B1200;
#endif
#ifdef B600
if ((bitrate)>=600) return B600;
#endif
#ifdef B300
if ((bitrate)>=300) return B300;
#endif
#ifdef B200
if ((bitrate)>=200) return B200;
#endif
#ifdef B150
if ((bitrate)>=150) return B150;
#endif
#ifdef B134
if ((bitrate)>=134) return B134;
#endif
#ifdef B110
if ((bitrate)>=110) return B110;
#endif
#ifdef B75
if ((bitrate)>=75) return B75;
#endif
#ifdef B50
if ((bitrate)>=50) return B50;
#endif
#ifdef B0
if ((bitrate)>=0) return B0;
#endif
return 0; /* Should never get here */
}
static bool
IO_Serial_WaitToRead (int hnd, unsigned delay_ms, unsigned timeout_ms)
{
int rval;
#ifdef HAVE_POLL
struct pollfd ufds;
#else
fd_set rfds;
struct timeval tv;
#endif
if (delay_ms > 0)
{
#ifdef HAVE_NANOSLEEP
struct timespec req_ts;
req_ts.tv_sec = delay_ms / 1000;
req_ts.tv_nsec = (delay_ms % 1000) * 1000000L;
nanosleep (&req_ts, NULL);
#else
usleep ((unsigned long) (delay_ms * 1000L));
#endif
}
#ifdef HAVE_POLL
ufds.fd = hnd;
ufds.events = POLLIN;
ufds.revents = 0x0000;
rval = poll (&ufds, 1, timeout_ms);
if (rval != 1)
return (FALSE);
return (((ufds.revents) & POLLIN) == POLLIN);
#else
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000L;
FD_ZERO (&rfds);
FD_SET (hnd, &rfds);
rval = select (hnd + 1, &rfds, NULL, NULL, &tv);
return FD_ISSET (hnd, &rfds);
#endif
}
static bool
IO_Serial_WaitToWrite (int hnd, unsigned delay_ms, unsigned timeout_ms)
{
int rval;
#ifdef HAVE_POLL
struct pollfd ufds;
#else
fd_set rfds;
struct timeval tv;
#endif
if (delay_ms > 0)
{
#ifdef HAVE_NANOSLEEP
struct timespec req_ts;
req_ts.tv_sec = delay_ms / 1000;
req_ts.tv_nsec = (delay_ms % 1000) * 1000000L;
nanosleep (&req_ts, NULL);
#else
usleep ((unsigned long) (delay_ms * 1000L));
#endif
}
#ifdef HAVE_POLL
ufds.fd = hnd;
ufds.events = POLLOUT;
ufds.revents = 0x0000;
rval = poll (&ufds, 1, timeout_ms);
if (rval != 1)
return (FALSE);
return (((ufds.revents) & POLLOUT) == POLLOUT);
#else
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000L;
FD_ZERO (&rfds);
FD_SET (hnd, &rfds);
rval = select (hnd + 1, NULL, &rfds, NULL, &tv);
return FD_ISSET (hnd, &rfds);
#endif
}
static void
IO_Serial_Clear (IO_Serial * io)
{
io->fd = -1;
io->props = NULL;
io->com = 0;
memset (io->PnP_id, 0, IO_SERIAL_PNPID_SIZE);
io->PnP_id_size = 0;
io->usbserial = FALSE;
}
static void
IO_Serial_SetPropertiesCache(IO_Serial * io, IO_Serial_Properties * props)
{
if (io->props == NULL)
io->props = (IO_Serial_Properties *) malloc (sizeof (IO_Serial_Properties));
#ifdef DEBUG_IO
printf ("IO: Catching properties\n");
#endif
memcpy (io->props, props, sizeof (IO_Serial_Properties));
}
static bool
IO_Serial_GetPropertiesCache(IO_Serial * io, IO_Serial_Properties * props)
{
if (io->props != NULL)
{
memcpy (props, io->props, sizeof (IO_Serial_Properties));
#if 0
#ifdef DEBUG_IO
printf
("IO: Getting properties (catched): %ld bps; %d bits/byte; %s parity; %d stopbits; dtr=%d; rts=%d\n",
props->input_bitrate, props->bits,
props->parity == IO_SERIAL_PARITY_EVEN ? "Even" : props->parity ==
IO_SERIAL_PARITY_ODD ? "Odd" : "None", props->stopbits, props->dtr,
props->rts);
#endif
#endif
return TRUE;
}
return FALSE;
}
static void
IO_Serial_ClearPropertiesCache (IO_Serial * io)
{
#ifdef DEBUG_IO
printf ("IO: Clearing properties cache\n");
#endif
if (io->props != NULL)
{
free (io->props);
io->props = NULL;
}
}
static void
IO_Serial_DeviceName (unsigned com, bool usbserial, char * filename, unsigned length)
{
#ifdef IO_ENABLE_USB
if (usbserial)
#ifdef OS_LINUX
#ifdef IO_ENABLE_DEVFS
snprintf (filename, length, "/dev/usb/tts/%d", com - 1);
#else
snprintf (filename, length, "/dev/ttyUSB%d", com - 1);
#endif
#endif /* OS_LINUX */
else
#endif /* IO_ENABLE_USB */
#ifdef IO_ENABLE_DEVPCSC
snprintf (filename, length, "/dev/pcsc/%d", com);
#else
#ifdef OS_LINUX
#ifdef IO_ENABLE_DEVFS
snprintf (filename, length, "/dev/tts/%d", com - 1);
#else
snprintf (filename, length, "/dev/ttyS%d", com - 1);
#endif
#endif /* OS_LINUX */
#ifdef OS_SOLARIS
snprintf (filename, length, "/dev/cua/%c", 'a' + com - 1);
#endif /* OS_SOLARIS */
#ifdef OS_CYGWIN32
sprintf (filename, "COM%d", com);
#endif /* OS_CYGWIN32 */
#ifdef OS_HPUX
snprintf (filename, length, "/dev/tty%dp0", com);
#endif /* OS_HPUX */
#ifdef OS_IRIX
snprintf (filename, length, "/dev/ttyf%d", com);
#endif /* OS_IRIX */
#ifdef OS_DIGITAL_UNIX
snprintf (filename, length, "/dev/tty%02d", com);
#endif /* OS_DIGITAL_UNIX */
#ifdef OS_AIX
snprintf (filename, length, "/dev/tty%d", com - 1);
#endif /* OS_AIX */
#ifdef OS_SCO
snprintf (filename, length, "/dev/tty%da", com);
#endif /* OS_SCO */
#ifdef OS_FREEBSD
/* support for for FreeBSD added by Martin Preuss<libchipcard@aquamaniac.de> */
snprintf (filename, length, "/dev/cuaa%d", com - 1);
#endif /* OS_FREEBSD */
#ifdef OS_NETBSD
snprintf (filename, length, "/dev/ttyC%d", com - 1);
#endif /* OS_NETBSD */
#ifdef OS_OPENBSD
/* support for for FreeBSD added by Martin Preuss<libchipcard@aquamaniac.de> */
snprintf (filename, length, "/dev/cua%02d", com - 1);
#endif /* OS_SOLARIS */
#endif /* IO_ENABLE_DEVPCSC */
}
static bool
IO_Serial_InitPnP (IO_Serial * io)
{
IO_Serial_Properties props;
int i = 0;
props.input_bitrate = 1200;
props.output_bitrate = 1200;
props.parity = IO_SERIAL_PARITY_NONE;
props.bits = 7;
props.stopbits = 1;
props.dtr = IO_SERIAL_HIGH;
props.rts = IO_SERIAL_HIGH;
if (!IO_Serial_SetProperties (io, &props))
return FALSE;
while ((i < IO_SERIAL_PNPID_SIZE) &&
IO_Serial_Read (io, 200, 1, &(io->PnP_id[i])))
i++;
io->PnP_id_size = i;
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1