#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

/*
   Test of serial comms under Tru64
   Should be POSIX compliant
*/

#define TRUE  1
#define FALSE 0

int OpenSerial(char *);
int SetupSerial(int,int,int,int,int);

int main(int argc,char **argv)
{
   int fdes;
   int i,n,c,nterm=0,ready2send=FALSE;
   char buffer[64];
   struct termios newsettings,oldsettings;

   /* Standard ports on Dec Alpha Xp1000 are tty00 and tty01 */
   if ((fdes = OpenSerial("/dev/tty00")) < 0) {
      fprintf(stderr,"Port open failed\n");
      exit(-1);
   }

   /* Set port characteristics */
   if (!SetupSerial(fdes,4800,8,1,'n')) {
      fprintf(stderr,"Port setup failed\n");
      close(fdes);
      exit(-1);
   }

   /* Set up non blocking IO on the terminal */
   tcgetattr(0,&oldsettings);
   newsettings = oldsettings;
   newsettings.c_lflag &= (~ICANON); 
   newsettings.c_cc[VTIME] = 1;
   newsettings.c_cc[VMIN] = 0;
   tcsetattr(0,TCSANOW,&newsettings); 
   
   /*
   if (setvbuf(stdin,NULL,_IONBF,1) != 0) 
      fprintf(stderr,"setvbuf failed\n");
   */

   /* Read and Write */
   for (;;) {

      /* Anything on the port */
      while ((n = read(fdes,buffer,60)) > 0) {
         fprintf(stderr,"<<== %03d \"",n);
         for (i=0;i<n;i++) {
            if (buffer[i] < ' ' || buffer[i] > 126)
               fprintf(stderr,"<%d>",buffer[i]);
            else
               fprintf(stderr,"%c",buffer[i]);
         }
         fprintf(stderr,"\"\n");
      }

      /* Anything from the keyboard */
      if ((c = getchar()) > 0) {
         if (c == '\n' || c == '\r' || nterm > 60)
            ready2send = TRUE;
         else
            buffer[nterm++] = c;
         if (nterm > 1 && buffer[0] == 'Z' && buffer[1] == 'Z') {
            fprintf(stderr,"\n");
            break;
         }
      }

      /* Send on a CR or LF */
      if (ready2send) {
         buffer[nterm++] = '\r';
         fprintf(stderr,"==>> %03d \"",nterm);
         for (i=0;i<nterm;i++) {
            if (buffer[i] < ' ' || buffer[i] > 126)
               fprintf(stderr,"<%d>",buffer[i]);
            else
               fprintf(stderr,"%c",buffer[i]);
         }
         fprintf(stderr,"\"\n");
         if (write(fdes,buffer,nterm) != nterm) 
            fprintf(stderr,"Write failed\n");
         nterm = 0;
         ready2send = FALSE;
         /* sleep(1); */
      }

   }

   /* Close the serial port (Should restore settings) */
   close(fdes);
   
   /* Restore terminal settings */
   tcsetattr(0,TCSANOW,&oldsettings);
}

/*
   Attempt to open the serial port
   Return the file descriptor
*/
int OpenSerial(char *port)
{
   int fdes;

   if ((fdes = open(port,O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
      perror("OpenSerial");
   } else {
      fcntl(fdes,F_SETFL,FNDELAY);
   }

   return(fdes);
}

/*
   Setup the communication options
   Hopefully POSIX compliant
*/
int SetupSerial(int fdes,int baud,int databits,int stopbits,int parity)
{
   int n;
   struct termios options;

   /* Get the current options */
   if (tcgetattr(fdes,&options) != 0) {
      perror("SetupSerial 1");
      return(FALSE);
   }

   /* Set the baud rate */
   switch (baud) {
   case 2400:
      n =  cfsetispeed(&options,B2400);
      n += cfsetospeed(&options,B2400);
      break;
   case 4800:
      n =  cfsetispeed(&options,B4800);
      n += cfsetospeed(&options,B4800);
      break;
   case 9600:
      n =  cfsetispeed(&options,B9600);
      n += cfsetospeed(&options,B9600);
      break;
   case 19200:
      n =  cfsetispeed(&options,B19200);
      n += cfsetospeed(&options,B19200);
      break;
   default:
      fprintf(stderr,"Unsupported baud rate\n");
      return(FALSE);
   }
   if (n != 0) {
      perror("SetupSerial 2");
      return(FALSE);
   }

   /* To check the speed, try 
   input_speed = cfgetispeed(&options);
   output_speed = cfgetospeed(&options);
   */

   /* Set the data size */
   options.c_cflag &= ~CSIZE;
   switch (databits) {
   case 7:
      options.c_cflag |= CS7;
      break;
   case 8:
      options.c_cflag |= CS8;
      break;
   default:
      fprintf(stderr,"Unsupported data size\n");
      return(FALSE);
   }

   /* Set up parity */
   switch (parity) {
   case 'n':
   case 'N':
      options.c_cflag &= ~PARENB; /* Clear parity enable */
      options.c_iflag &= ~INPCK; /* Enable parity checking */
      break;
   case 'o':
   case 'O':
      options.c_cflag |= (PARODD | PARENB); /* Enable odd parity */
      options.c_iflag |= INPCK;  /* Disnable parity checking */ 
      break;
   case 'e':
   case 'E':
      options.c_cflag |= PARENB; /* Enable parity */
      options.c_cflag &= ~PARODD; /* Turn odd off => even */
      options.c_iflag |= INPCK; /* Disnable parity checking */
      break;
   default:
      fprintf(stderr,"Unsupported parity\n");
      return(FALSE);
   }

   /* Set up stop bits */
   switch (stopbits) {
   case 1:
      options.c_cflag &= ~CSTOPB;
      break;
   case 2:
      options.c_cflag |= CSTOPB;
      break;
   default:
      fprintf(stderr,"Unsupported stop bits\n");
      return(FALSE);
   }

   /* Set input parity option */
   if (parity != 'n')
      options.c_iflag |= INPCK;

   /* Strip high order bit 
   options.c_iflag |= ISTRIP;
   */

   /* Deal with hardware or software flow control */
   options.c_cflag &= ~CRTSCTS; /* Disable RTS/CTS */
   options.c_iflag |= (IXON | IXOFF | IXANY); /* xon/xoff flow control */

   /* Output processing */
   options.c_oflag &= ~OPOST; /* No output processing */
   options.c_oflag &= ~ONLCR; /* Don't convert linefeeds */ 

   /* Input processing */
   options.c_iflag |= IGNBRK; /* Ignore break conditions */
   options.c_iflag &= ~IUCLC; /* Don't map upper to lower case */
   options.c_iflag &= ~BRKINT; /* Ignore break signals */
   /* options.c_iflag &= ~INLCR; Map NL to CR */
   /* options.c_iflag &= ~ICRNL; Map CR to NL */

   /* Miscellaneous stuff */
   options.c_cflag |= (CLOCAL | CREAD); /* Enable receiver, set local */
	/* Linux seems to have problem with the following ??!! */
   options.c_cflag |= (IXON | IXOFF); /* Software flow control */
   options.c_lflag = 0; /* no local flags */ 
   options.c_cflag |= HUPCL; /* Drop DTR on close */
   
   /* Setup non blocking, return on 1 character */
   options.c_cc[VMIN] = 0;
   options.c_cc[VTIME] = 1;

   /* Clear the line */
   tcflush(fdes,TCIFLUSH);

   /* Update the options and do it NOW */
   if (tcsetattr(fdes,TCSANOW,&options) != 0) {
      perror("SetupSerial 3");
      return(FALSE);
   }

   return(TRUE);
}



