/****************************************************************************/
/* Copyright (c) 2000 MBARI                                                 */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Summary  :                                                               */
/* Filename : SerialDevice.cc                                               */
/* Author   :                                                               */
/* Project  :                                                               */
/* Version  : 1.0                                                           */
/* Created  : 02/07/2000                                                    */
/* Modified :                                                               */
/* Archived :                                                               */
/****************************************************************************/
/* Modification History:                                                    */
/****************************************************************************/
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ioctl.h>
#include <sys/dev.h>
#include <sys/select.h>

#include "Syslog.h"
#include "SerialDevice.h"
#include "TimeP.h"



SerialDevice::SerialDevice(const char *ttyName)
  : DeviceInterface(ttyName)
{
  initialize(ttyName);
}



SerialDevice::SerialDevice(SerialParameters *params)
  : DeviceInterface(params->portName)
{
  initialize(params->portName);
  setLineFormat(params);

}


int connectAtPort(const char *prot, const char *machine, int port)
{
int sd;
struct hostent *hp;
struct sockaddr_in server;
  
  memset((char *)&server, 0, sizeof(struct sockaddr_in));
  
  server.sin_family = AF_INET;
  server.sin_port = htons((u_short)port);

  if (isdigit(machine[0])) {
    server.sin_addr.s_addr = inet_addr(machine);
  } else {
    if ((hp = gethostbyname((char *)machine)) == NULL) {
      return ERROR;
    }
    memcpy((char *)&server.sin_addr, (char *)hp->h_addr,  hp->h_length);
  }
  
  if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
    return ERROR;
  }
  
  /*
  if ((setsockopt(sd, SOL_SOCKET, SO_REUSEADDR,
		  (char *)&item, sizeof(int))) < 0) {
    close(sd);
    return ERROR;
  }


  if ((setsockopt(socket, IPPROTO_TCP, TCP_NODELAY,
		  (char *)&value, sizeof(int))) < 0) {
    close(*readSd);
    if (*readSd != *writeSd) {
      close(*writeSd);
    }
    *readSd = NO_FD;
    *writeSd = NO_FD;
    return ERROR;
    }
  */
    
  if (connect(sd, (struct sockaddr *)&server, sizeof(server)) < 0) {
    return ERROR;
  }

  printf("sd is %d\n", sd);
  return sd;
}


void SerialDevice::initialize(const char *ttyName)
{
  char errorBuf[256];
  char *protocol, *machine;
  int port;

  _isSocket = False;
  _commsDebugMode = DebugOff;		// Debugging is off by default
  
  if (!strncmp(ttyName, "tcp", 3)) {
    _isSocket = True;
    _raw = False;
    char *serverString = strdup(ttyName);
    printf("server string is %s\n", serverString);
    protocol = strtok(serverString, ":\0");
    machine = strtok(NULL, ":\0");
    port = atoi(strtok(NULL,":\0"));
		
    _fd = connectAtPort(protocol, machine, port);
    if (_fd == ERROR) {
      sprintf(errorBuf,"SerialDevice::SerialDevice() - "
	      "open of protocol %s server %s port %d failed",
	      protocol, machine, port);
      free(serverString);
      throw BadSystemCall(errorBuf);
    }
    free(serverString);
  } else {
    _fd = open(ttyName, O_RDWR);	// Open device

    if (_fd == ERROR) {
      sprintf(errorBuf, "SerialDevice::SerialDevice() - "
	      "open of \"%s\" failed: %s",
	      ttyName, strerror(errno));

      throw BadSystemCall(errorBuf);
    }

    if (isatty(_fd)) {
      // Store orig termios structure 
      if (tcgetattr( _fd, &orig_termios_p) == ERROR) {
	sprintf(errorBuf, "SerialDevice::SerialDevice() - F_GETFT failed: %s",
		strerror(errno));
	throw BadSystemCall(errorBuf);
      }

      if (!(orig_termios_p.c_lflag & ICANON))
	// In "raw" mode
	_raw = True;
      else
	_raw = False;

      tcflush(_fd, TCIOFLUSH);		// Flush input and output queues
    }
  }

}



SerialDevice::~SerialDevice()
{
  // Reset original parameters
  if (_fd != ERROR)
    {
      tcsetattr( _fd, TCSADRAIN, &orig_termios_p);
      close (_fd);	    		// Close device

      if (_commsDebugMode == DebugOn)
	Syslog::write ("SerialDevice::~SerialDevice() - closed\n");
    }
}


void SerialDevice::reset()
{
}

int SerialDevice::nonBlocking()
{
  int flags = 0;
  int status;

  if (_fd == ERROR)
    return EBADF;

  if (_isSocket) {
    return OK;
  }

  if ( (flags = fcntl(_fd, F_GETFL )) == ERROR)
    {
      Syslog::write ("F_GETFL failed\n");
      return ERROR;
    }

  flags |= O_NONBLOCK;	
  status = fcntl(_fd, F_SETFL, &flags);

  if (status == ERROR)
    Syslog::write ("F_SETFL failed\n");

  else if (_commsDebugMode == DebugOn)
    Syslog::write ("SerialDevice::nonBlocking() - "
		   "set to non-blocking mode\n");

  return status;	
} 

int SerialDevice::blocking()
{
  int flags = 0;
  int status;

  if (_fd == ERROR)
    return EBADF;

  if (_isSocket) {
    return OK;
  }

  if ( (flags = fcntl( _fd, F_GETFL)) == ERROR)
    {
      Syslog::write ("_F_GETFL failed\n");
      return ERROR;
    }
    
  flags &= ~O_NONBLOCK;	
  status = fcntl(_fd, F_SETFL, &flags);
    
  if (status == ERROR)
    Syslog::write ("_F_SETFL failed\n");

  else if (_commsDebugMode == DebugOn)
    {
      Syslog::write ("SerialDevice::blocking() - set to blocking mode\n");
    }
  return status;	
} 


int SerialDevice::raw()
{
  int status;
  struct termios termios_p;

  if (_isSocket) {
    return OK;
  }

  if (tcgetattr( _fd, &termios_p) == ERROR)
    {
      Syslog::write ("F_GETFL failed\n");
      return ERROR;
    }

  termios_p.c_cc[VMIN]  =  1;
  termios_p.c_cc[VTIME] =  0;

  termios_p.c_iflag = 0;
  termios_p.c_oflag &= ~OPOST;
  termios_p.c_lflag &= ~(ISIG | ICANON | ECHO );
  termios_p.c_oflag &= ~( CSIZE | PARENB );
  termios_p.c_oflag |= CS8;

  if ( (status = tcsetattr( _fd, TCSANOW, &termios_p)) == ERROR)
    Syslog::write ("F_GETFL failed\n");

  if (!_isSocket) _raw = True;

  return status;
}

int SerialDevice::raw(int vmin, int vtime)
{
  int status;
  struct termios termios_p;

  if (tcgetattr( _fd, &termios_p) == ERROR)
    {
      Syslog::write ("F_GETFL failed\n");
      return ERROR;
    }

  termios_p.c_cc[VMIN]  =  vmin;
  termios_p.c_cc[VTIME] =  vtime;

  termios_p.c_iflag = 0;
  termios_p.c_oflag &= ~OPOST;
  termios_p.c_lflag &= ~(ISIG | ICANON | ECHO );
  termios_p.c_oflag &= ~( CSIZE | PARENB );
  termios_p.c_oflag |= CS8;

  if ( (status = tcsetattr( _fd, TCSANOW, &termios_p)) == ERROR)
    Syslog::write ("F_GETFL failed\n");

  _raw = True;

  return status;
}

int SerialDevice::nonRaw()
{
  int status;
  struct termios termios_p;

  if (_isSocket) {
    return OK;
  }

  if (tcgetattr( _fd, &termios_p) == ERROR)
    {
      Syslog::write ("F_GETFL failed\n");
      return ERROR;
    }

  termios_p.c_lflag |= (ICANON|ECHO|ISIG|ECHOE|ECHOK|ECHONL);

  if ( (status = tcsetattr( _fd, TCSADRAIN, &termios_p)) == ERROR)
    Syslog::write ("F_GETFL failed\n");

  _raw = False;

  return status;
}


int SerialDevice::setLineFormat(SerialParameters *params)
{
  const char *parity;

  if (_isSocket) {
    return OK;
  }

  switch (params->parity) {

  case SerialParameters::NoParity:
    parity = "NONE";
    break;

  case SerialParameters::EvenParity:
    parity = "EVEN";
    break;

  case SerialParameters::OddParity:
    parity = "ODD";
    break;

  default:
    parity = "NONE";
  }

  return setLineFormat(params->baud, params->dataBits, 
		       params->stopBits, parity);
}


int SerialDevice::setLineFormat(speed_t baud, int data, int stop, const char *parity)
{
  //  Syslog::write("speed: %d\n",baud);
  int status;

  if (_fd == ERROR)
    return (EBADF);

  if (_isSocket) {
    return OK;
  }
 
  struct termios termios_p;
 
  if (tcgetattr( _fd, &termios_p) == ERROR)
    {
      Syslog::write ("F_GETFL failed\n");
      return ERROR;
    }
  
  cfsetispeed(   &termios_p, baud );
  
  cfsetospeed(   &termios_p, baud );
  
  csetStopBits(  &termios_p, stop);
  
  csetDataBits(  &termios_p, data);
  
  csetParityBits(&termios_p, parity);
    

                                        // Leave end-of-line unprocessed 
  termios_p.c_iflag |= IGNCR;		// Ignore CR on input
  termios_p.c_iflag &= ~ICRNL;	// Don't map CR to NL
  termios_p.c_iflag &= ~INLCR;	// Don't map NL to CR

  termios_p.c_lflag &= ~ICANON;	// Disable Line Oriented Mode 
  termios_p.c_lflag &= ~ECHO;		// Disable input to output echo

  termios_p.c_lflag |= IEXTEN;	// Enable QNX Extensions to POSIX
  termios_p.c_cflag |= CLOCAL;	// Ignore modem status lines
  termios_p.c_cflag |= CREAD;		// Enable Receiver

  termios_p.c_cflag &= ~IHFLOW;	// Ignore modem status lines
  termios_p.c_cflag &= ~OHFLOW;	// Ignore modem status lines

  if ( (status = tcsetattr( _fd, TCSANOW, &termios_p)) == ERROR)
    Syslog::write ("F_GETFL failed\n");
    
  Syslog::write("SerialDevice: done...\n");
  return status;
}


int SerialDevice::getFd()
{
  return (_fd);
}


const char *SerialDevice::devName()
{
  return name();
}


int SerialDevice::nRecvdBytes()
{ 
  if (_fd == ERROR)
    return ERROR;
 
  if (_isSocket) {
	int val = 0;
	ioctl(_fd, FIONREAD, &val);
	return val;
  } else {
 
     // Return number of bytes available to be read
     return dev_ischars(_fd);
  }
} 


int SerialDevice::sendBreak()
{   
  if (_commsDebugMode == DebugOn)
    Syslog::write("SerialDevice::sendBreak()\n");

  if (_isSocket) {
    return OK;
  }

  // Send a 300 millisecond break 
  return tcsendbreak(_fd, 300);
}


int SerialDevice::sendBreak(int milliseconds)
{   
  if (_commsDebugMode == DebugOn)
    Syslog::write("SerialDevice::sendBreak(millisec)\n");

  if (_isSocket) {
    return OK;
  }

  // Send break with a user specified duration 
  return tcsendbreak(_fd, milliseconds);
}


void SerialDevice::commsDebugMode(CommsDebugMode mode)
{
  // Toggle debug mode on/off
  _commsDebugMode = mode;
}


int SerialDevice::csetStopBits(struct termios *termios_p, int stopBits )
{

  // old version had structure backwards
  // replaced == 1 with ==2
  // JAR and AM 12/09/99
  
  if (stopBits == 2)
    termios_p->c_cflag |= CSTOPB;
  else
    termios_p->c_cflag &= ~CSTOPB;

  return OK;
} 


int SerialDevice::csetDataBits(struct termios *termios_p, int dataBits )
{
  
  int cflag = termios_p->c_cflag;
  
  // old version was not inverting CSIZE and consequently
  // not setting databits correctly!
  // JAR and AM 12/09/99

  if (dataBits == 5)	
    cflag = (cflag & ~CSIZE) | CS5;
  
  else if (dataBits == 6)	
    cflag = (cflag & ~CSIZE) | CS6;

  else if (dataBits == 7)	
    cflag = (cflag & ~CSIZE) | CS7;

  else if (dataBits == 8)	
    cflag = (cflag & ~CSIZE) | CS8;

  else
    return ERROR;

  termios_p->c_cflag = cflag;
  
  return OK;
} 


int SerialDevice::csetParityBits(struct termios *termios_p, const char* parity)
{
  int cflag = termios_p->c_cflag;

  if (stricmp(parity, "NONE") == 0)
    cflag &= ~PARENB;
	
  else if (stricmp(parity, "EVEN") == 0) 
    cflag |= PARENB, cflag &= ~PARODD; 

  else if (stricmp(parity, "ODD") == 0)
    cflag |= PARENB, cflag |= PARODD; 

  else
    return ERROR;

  termios_p->c_cflag = cflag;
  return OK;
} 


int SerialDevice::read(char *buf, int maxBytes, int timeout)
{
  int nBytes;

  if (_commsDebugMode == DebugOn)
    Syslog::write ("SerialDevice::read()\n");
  
  if (timeout > 0) {

    // Throw exception on timeout
    selectTimeout(timeout);
  } 

  if ( (nBytes = ::read(_fd, buf, maxBytes)) == ERROR) {
    if (_commsDebugMode == DebugOn)
      Syslog::write ("SerialDevice::read() - %s", strerror(errno));

    throw BadSystemCall("select()");
  }

  buf[nBytes] = '\0';		// Null terminate string
  
  if ( _commsDebugMode == DebugOn )
    Syslog::write("%s: read %d chars %s", ttyname(_fd), nBytes, buf);

  return nBytes;
}

/*-----------------------------------------------------------------------*/
int SerialDevice::readNChars(char *buf, int count, int timeout)
{
  int nBytes = 0;

  int flags;

  if (debugging())
    Syslog::write ("SerialDevice::readNChars() - timeout=%d\n", timeout);

  //  if (nRecvdBytes() <= count) {
    if (timeout < 100) {
    /* If timeout is less than 100, then timeout args sent to dev_read()
       will be equal to zero, which causes infinite blocking. */
      timeout = 100;
    }

    if (!isRaw()) {

      // If not in "raw" mode, dev_read() ignores the timeout parameters,
      // so we use select() to test for timeout instead
      if (debugging()) 
        Syslog::write("SerialDevice::readNChars() - using selectTimeout()");

      // Throw exception on timeout
      selectTimeout(timeout);
    }
  

    //  }

  if(_isSocket) {
    if ( (nBytes = ::read(_fd, (void *)buf, count)) < 0) {
      if (_commsDebugMode == DebugOn) 
	Syslog::write("SerialDevice::readNChars() - %s", strerror(errno));
      return ERROR;
    }
  } else if (!isatty(_fd)) {
    if ( (nBytes = ::read(_fd, buf,count)) == ERROR) {
      if (_commsDebugMode == DebugOn) 
	Syslog::write("SerialDevice::readNChars() - %s", strerror(errno));
      
      return ERROR;
    }
  } else {
    if( (nBytes = ::dev_read( _fd, buf, count,
			      count, timeout/100, timeout/100, 0, 0 )) 
	== ERROR ) {
      if (_commsDebugMode == DebugOn) 
	Syslog::write("SerialDevice::readNChars() - %s", strerror(errno));
      
      return ERROR;
    }
  }

  buf[nBytes] = '\0';           // Null terminate string
  
  if ( _commsDebugMode == DebugOn )
    Syslog::write("read %d chars from dev %s\n", nBytes, ttyname(_fd));
  
  return nBytes;
}


int SerialDevice::write(const char *string, int nBytes)
{
  char errorBuf[100];

  if (::write( _fd, string, nBytes) == ERROR)
    {
      if (_commsDebugMode == DebugOn) {
	Syslog::write ("SerialDevice::write() - %s", strerror(errno));
      }
      return ERROR;
    }

  if ( _commsDebugMode == DebugOn ) 
    Syslog::write("%s: wrote %d chars %s", ttyname(_fd), nBytes, string);

  return nBytes;
}

int SerialDevice::writeRepeat( const char *string, int nBytes, int reps )
{
  int count = 0;
  for( int i=0; i<reps; i++ )
    count += write( string, nBytes );
  return count;
}

void SerialDevice::clearPort(void)
{
  char buf[256];

  tcflush(_fd, TCIFLUSH);
//  while (dev_ischars(_fd) > 0 )
//    ::read(_fd, buf, 256);
  // tcflush(_fd, TCIFLUSH);		// Flush input queues
}


int SerialDevice::flushReceiveBuf()
{
  clearPort();
  return 0;
}


int SerialDevice::readUntil(char *buf, int maxBytes, char *term, int timeout) 
{
  // Using readNchars() instead of blocking read()
  unsigned long t0, readTimeout;

  t0 = Time::milliseconds();

  char c;
  int i = 0;
  int nBytes = 0;

  while (term[i] != '\0')
    {
      unsigned int elapsed = Time::milliseconds() - t0;

      if (elapsed > timeout)
	throw TimedOut(nBytes);

      if (nBytes == maxBytes) {
	if( _commsDebugMode == DebugOn )
	  Syslog::write("readUntil: Buffer full! - %d chars: %s",
			nBytes, buf );

	throw BufferFull();
      }
	  
      // Time remaining for read() 
      readTimeout = max(timeout - elapsed, 1);

      // Read a single character from tty port
      //      Syslog::write("ReadUntil - waiting %d", readTimeout );
      //      if (SerialDevice::read( buf + nBytes, 1, readTimeout) != ERROR)
      if( SerialDevice::readNChars( buf+nBytes, 1, readTimeout) != ERROR )
	{
	  c = *(buf + nBytes);
	  nBytes++;

	  // Is character read part of term? */
	  if ( c != term[i++] )
	    i = 0;

	  if ( _commsDebugMode == DebugOn )
	    Syslog::write("readUntil: read %c", c);
	} /* if */
    } /* while */

  if (i != strlen(term))
    {
      // Must have timed out
      if ( _commsDebugMode == DebugOn )
	Syslog::write("readUntil: expired in %d having read %d chars)\n", 
		      Time::milliseconds() - t0, nBytes);

      throw TimedOut(nBytes); 

    } /* if */

  if ( _commsDebugMode == DebugOn )
    Syslog::write("readUntil: succeeded in %d msec\n",
		  Time::milliseconds() - t0);

  return nBytes;
} /* readUntil() */


int SerialDevice::readUntil(char *term, int timeout)
{
  unsigned long t0, readTimeout;

  t0 = Time::milliseconds();

  if ( _commsDebugMode == DebugOn )
    Syslog::write("readUntil: term = %s\n", term );

  char c;
  int i = 0;
  int nBytes = 0;

  while (term[i] != '\0')
    {
      int elapsed = Time::milliseconds() - t0;
      if (elapsed > timeout)
	throw TimedOut(nBytes);

      readTimeout = max(timeout - elapsed, 1);
      // Read a single character from tty port
      //if (SerialDevice::read( &c, 1, readTimeout) != ERROR)
      if( SerialDevice::readNChars( &c, 1, readTimeout) != ERROR )
	{
	  nBytes++;

	  // Is character read part of term? */
	  if ( c != term[i++] )
	    i = 0;

	  if ( _commsDebugMode == DebugOn )
	    Syslog::write("readUntil: read %c", c);
	} /* if */

    } /* while */

  if ( _commsDebugMode == DebugOn )
    Syslog::write("readUntil: succeeded in %d msec\n",
		  Time::milliseconds() - t0);

  return OK;
}



int SerialDevice::confirm(char *term, int timeout)
{
  int status = ERROR;

  try
    {
      status = readUntil(term, timeout);
    }
  catch (TimedOut e)
    {
      Syslog::write("SerialDevice::confirm() - %s", e.msg);
    }
  catch (BadSystemCall e)
    {
      Syslog::write("SerialDevice::confirm() - %s", e.msg);
    }
  return status;
}


Boolean SerialDevice::isRaw()
{
  return _raw;
}


void SerialDevice::selectTimeout(int timeoutMillisec)
{
  if (timeoutMillisec > 0) {

    fd_set readFds;
    struct timeval timeout_tv;

    // Convert from milliseconds to timeval format
    timeout_tv.tv_sec  = timeoutMillisec / 1000;
    timeout_tv.tv_usec = (timeoutMillisec % 1000) * 1000;

    FD_ZERO(&readFds);
    FD_SET(_fd, &readFds);

    if (_commsDebugMode == DebugOn)
      Syslog::write ("SerialDevice::selectTimeout() - entering select\n");

    switch (select(_fd + 1, &readFds, 0, 0, &timeout_tv)) {
    case -1:
      if (_commsDebugMode == DebugOn)
	Syslog::write ("SerialDevice::selectTimeout() - select() failed\n");
      throw BadSystemCall("select");

    case 0:
      if (_commsDebugMode == DebugOn)
	Syslog::write ("SerialDevice::selectTimeout() - timed out\n");
      throw TimedOut(0);

    default:
      if (_commsDebugMode == DebugOn)
	Syslog::write ("SerialDevice::selectTimeout() - data available\n");

    } /* switch */
  } /* if */
}


Boolean SerialDevice::debugging()
{
  if (_commsDebugMode == DebugOn)
    return True;
  else
    return False;
}


void SerialDevice::printTermios()
{
  struct termios termios_p;

  if (_isSocket) {
    return;
  }

  tcgetattr( _fd, &termios_p);

  printf ("Signal interrupt on break %s\n", 
	  ((termios_p.c_iflag & BRKINT) ? "Enabled" : "Disabled"));

  if (termios_p.c_iflag & ICRNL)
    printf ("CR mapped to NL on input\n");

  if (termios_p.c_iflag & IGNBRK)
    printf ("Break ignored\n");

  if (termios_p.c_iflag & IGNCR)
    printf ("CR ignored\n");

  if (termios_p.c_iflag & IGNPAR)
    printf ("Ignore Parity Errors\n");

  if (termios_p.c_iflag & INLCR)
    printf ("NL mapped to CR on input");

  if (termios_p.c_iflag & INPCK)
    printf ("Parity checking enabled on input");

  if (termios_p.c_iflag & ISTRIP)
    printf ("Top bit stripping Enabled\n");

  if (termios_p.c_iflag & IXOFF)
    printf ("Input Software Flow Control Xon/Xoff Enabled\n");

  if (termios_p.c_iflag & IXON)
    printf ("Output Software Flow Control Xon/Xoff Enabled\n");

  if (termios_p.c_iflag & PARMRK)
    printf ("Mark parity errors on input data\n");


  if (termios_p.c_oflag & OPOST)
    printf ("Enable output processing\n");


  printf ("Modem status lines %s \n",
	  ((termios_p.c_cflag & CLOCAL) ? "ignored" : "used"));

  printf ("Receiver %s \n",
	  ((termios_p.c_cflag & CREAD) ? "enabled" : "disabled"));


  printf ("Input baud rate: %d\n",  termios_p.c_ispeed);
  printf ("Output baud rate: %d\n", termios_p.c_ospeed);

  switch (termios_p.c_cflag & CSIZE)
    {
    case CS5:
      printf ("Data bits: 5\n");
      break;

    case CS6:
      printf ("Data bits: 6\n");
      break;
	
    case CS7:
      printf ("Data bits: 7\n");
      break;

    case CS8:
      printf ("Data bits: 8\n");
      break;
    } /* switch */

  printf ("Stop bits: %d\n", (termios_p.c_cflag & CSTOPB ? 2 : 1));

  if (termios_p.c_cflag & PARENB)
    printf ("Parity: %s\n", (termios_p.c_cflag & PARODD ? "Odd" : "Even"));
  else
    printf ("Parity: Disabled\n");

  if (termios_p.c_cflag & HUPCL)
    printf ("Hangup on last close enabled\n");


  if (termios_p.c_lflag & IEXTEN)
    {	
      if (termios_p.c_cflag & PARSTK)
	printf ("Stick parity\n");

      if (termios_p.c_cflag & IHFLOW)
	printf ("Hardware input flow control enabled\n");

      if (termios_p.c_cflag & OHFLOW)
	printf ("Hardware output flow control enabled\n");
    } /* if */

  if (termios_p.c_lflag & ECHO)
    printf ("Echo enabled\n");

  if (termios_p.c_lflag & ICANON)
    printf ("Canonical input mode enabled\n");

  if (termios_p.c_lflag & ISIG)
    printf ("Signals Enabled\n");

  if (termios_p.c_lflag & NOFLSH)
    printf ("Disabled flush after interrupt, quit or suspend\n");

  if (termios_p.c_lflag & TOSTOP)
    printf ("Send SIGTTOU for background output\n");
   
} 

SerialDevice::TimedOut::TimedOut(int nBytesRead)
  : Exception("Timed Out")
{
  _nBytes = nBytesRead;
}

int SerialDevice::TimedOut::nBytesRead()
{
  return _nBytes;
}

SerialDevice::BufferFull::BufferFull()
  : Exception("Buffer Full")
{
}

SerialDevice::BadSystemCall::BadSystemCall(char *systemCall)
  : Exception(systemCall)
{
}


