/* 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 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 #include #include #include #include #include #include #ifdef HAVE_POLL #include #else #include #include #include #endif #include #include #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; ifd, 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 */ 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 */ 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; }