/***************************************************************************
                          serial.c  -  description                              
                             -------------------                                         
    begin                : Thu Dec 9 1999                                           
    copyright            : (C) 1999 by Thomas Marquardt                         
    email                : th.marquardt@t-online.de                                     
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   * 
 *                                                                         *
 ***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <math.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <ctype.h>
#ifdef LINUX
 #include <linux/serial.h>
#endif
#ifndef LINUX
 /*...*/
#endif

#include "gesko.h"


/* --- Globale Variablen --- */
int PBXBaud;
unsigned char device [0x80];

extern int PBXHandle;
extern int timer;
extern int timeout;
extern int timeout_mode;
extern int logging;

/*****************************************************************************/
/* Funktion für die Zuweisung des Device-Strings - in Abhängigkeit vom OS    */
/* ----------------------------------------------------------------------    */
/* In:	com - Com-Port-Nummer                                                */
/* ---------------------------------------------------                       */
/* Out:	                                                                     */
/* ----------------------------------------------------                      */
/* Autor: Steffen Hildebrandt                                                */
/* letzte Aenderung: Steffen Hildebrandt, 14.05.2002                         */
/*****************************************************************************/
void SetDevice( int com , char *dev, int mode )
{

	/* falls COM-Schnittstelle NICHT als device-Name angegeben wurde -> nur Nummer... */
	if(mode == COM_NUMBER)
	{
 	 #ifdef LINUX
  	switch(com)
  	{
    	case 1  : sprintf(device, "%s", "/dev/ttyS0");
     	 break;
    	case 2  : sprintf(device, "%s", "/dev/ttyS1");
     	 break;
    	case 3  : sprintf(device, "%s", "/dev/ttyS2");
    	 break;
    	case 4  : sprintf(device, "%s", "/dev/ttyS3");
     	 break;
  	}
 	 #endif
 	 #ifdef FREEBSD
  	switch(com)
  	{
    	case 1  : sprintf(device, "%s", "/dev/cuaa0");
     	 break;
    	case 2  : sprintf(device, "%s", "/dev/cuaa1");
     	 break;
    	case 3  : sprintf(device, "%s", "/dev/cuaa2");
    	 break;
    	case 4  : sprintf(device, "%s", "/dev/cuaa3");
     	 break;
  	}
 	 #endif
 	 #ifdef SOLARIS
  	switch(com)
  	{
    	case 1  : sprintf(device, "%s", "/dev/cua0");
     	 break;
    	case 2  : sprintf(device, "%s", "/dev/cua1");
     	 break;
    	case 3  : sprintf(device, "%s", "/dev/cua2");
     	 break;
    	case 4  : sprintf(device, "%s", "/dev/cua3");
     	 break;
  	}
 	 #endif
	}

	/* falls COM-Schnittstelle device-Name angegeben wurde... */
	if(mode == COM_DEVICE)
		sprintf(device, "%s", dev);

  return;
}



/*****************************************************************************/
/* Funktion fuer die Initialisieren der seriellen Schnittstelle.             */
/* ----------------------------------------------------------------------    */
/* In:                                                                       */
/* ---------------------------------------------------                       */
/* Out:	NO_ERROR wenn die Operation geklappt hat, sonst ERROR                */
/* ----------------------------------------------------                      */
/* Autor: Bodo Rzany                                                         */
/* letzte Aenderung: Steffen Hildebrandt, 15.04.2003                         */
/*****************************************************************************/
int PbxOpen( void )
{
   struct termios line;
   unsigned char *buf = malloc(512);
   int ret = ERROR;


   if ( PBXHandle != 0 )
      close(PBXHandle);

   if ( ( PBXHandle = open (device, (O_RDWR|O_NOCTTY)) ) < 0 )	

   if (  PBXHandle < 0 )	
   {
     printf ("Error: %s  %s\n", strerror(errno), device);
     return (ret);
   }

   if ( tcgetattr(PBXHandle, &line) )		
   {
     strcpy (buf, "Error: illegal port or device");
     printf ("%s\n", buf); fflush(stdout);
     return (ret);
   }

   line.c_iflag &= ~(INPCK|IGNPAR|PARMRK|ISTRIP|IGNBRK|BRKINT|IGNCR|ICRNL|INLCR|IXOFF|IXON);
   line.c_oflag &= ~(OPOST);
   line.c_cflag &= ~(HUPCL|CSTOPB|PARENB|CSIZE);
//   line.c_cflag |= (CLOCAL|CREAD|CS8|CRTSCTS);
   line.c_cflag |= (CREAD|CS8|CRTSCTS);
   line.c_lflag &= ~(ICANON|ECHO|ISIG);
   line.c_cc[VMIN] = 0;
   line.c_cc[VTIME] = SERIAL_TIMEOUT * 10;
   cfsetispeed(&line, B19200);
   cfsetospeed(&line, B19200);
   tcsetattr(PBXHandle, TCSANOW, &line);
   ret = NO_ERROR;

  #ifdef DEBUG
   if ( ret == NO_ERROR )
     printf ("Open >%s< ok\n", device);
   else
     printf ("Open >%s< failed\n", device);
  #endif

	#ifdef DEBUG
	 printf("PBXHandle : %d\n", PBXHandle);
   printf("Die Standard-Verbindung erfolgt mit 19200 Baud!\n");
  #endif

   return (ret);
}


/*****************************************************************************/
/* Funktion zur Einstellung der Baudrate (Rechner, PBX)                      */
/* ----------------------------------------------------------------------    */
/* In:	baudrate : gewuenschte Baudrate                                      */
/* ---------------------------------------------------                       */
/* Out:	                                                                     */
/* ----------------------------------------------------                      */
/* Autor: Steffen Hildebrandt                                                */
/* letzte Aenderung: Steffen Hildebrandt, 15.04.2003                         */
/*****************************************************************************/
int SetBaudRate(int baudrate)
{
   int ret = NO_ERROR;
   int error = NO_ERROR;
   int baud = B19200;
   struct termios line;
   unsigned char *text = malloc(512);
   unsigned char *buf = malloc(512);
   cmdtype Cmd;


   /* PBX-Handle ermitteln */
   if ( PBXHandle != 0 )
      close(PBXHandle);

   if ( ( PBXHandle = open (device, (O_RDWR|O_NOCTTY)) ) < 0 )	
   if (  PBXHandle < 0 )	
   {
     printf ("FEHLER: %s  %s\n", strerror(errno), device);
     return (ret);
   }

   /* PBX-Baudrate einstellen */
   printf("Einstellen PBX-Baudrate...\n");

   printf("Die Verbindung erfolgt mit %d Baud!\n", baudrate);

   switch(baudrate)
   {
     case 9600   : sprintf (Cmd.befehl, "GTR4");
                   error = GTPbxPutAndGet (Cmd, text, 0);
                   baud = B9600;
      break;
     case 19200  : sprintf (Cmd.befehl, "GTR3");
                   error = GTPbxPutAndGet (Cmd, text, 0);
                   baud = B19200;
      break;
     case 38400  : sprintf (Cmd.befehl, "GTR2");
                   error = GTPbxPutAndGet (Cmd, text, 0);
                   baud = B38400;
      break;
     case 57600  : sprintf (Cmd.befehl, "GTR1");
                   error = GTPbxPutAndGet (Cmd, text, 0);
                   baud = B57600;
      break;
     case 115200 : sprintf (Cmd.befehl, "GTR0");
                   error = GTPbxPutAndGet (Cmd, text, 0);
                   baud = B115200;
      break;
     default     : printf("FEHLER : Falsche Baudrate gewaehlt! -> ABBRUCH!\n");
                   printf("         -> Moegliche Baudraten : 9600, 19200, 38400, 57600, 115200\n");
                   exit (0);
      break;
   }

   sleep(1);

   if(error == ERROR)
   {
     printf("\nFEHLER : Fehler bei Einstellen der PBX-Baudrate!\n");
     return ERROR;
   }
    else
     printf("PBX-Baudrate OK!\n");

   /* Rechner-Baudrate... */
   printf("Einstellen Rechner-Baudrate...\n");

   if ( tcgetattr(PBXHandle, &line) )		
   {
     strcpy (buf, "FEHLER: illegal port or device");
     printf ("%s\n", buf); fflush(stdout);
     return (ret);
   }

   line.c_iflag &= ~(INPCK|IGNPAR|PARMRK|ISTRIP|IGNBRK|BRKINT|IGNCR|ICRNL|INLCR|IXOFF|IXON);
   line.c_oflag &= ~(OPOST);
   line.c_cflag &= ~(HUPCL|CSTOPB|PARENB|CSIZE);
//   line.c_cflag |= (CLOCAL|CREAD|CS8|CRTSCTS);
   line.c_cflag |= (CREAD|CS8|CRTSCTS);
   line.c_lflag &= ~(ICANON|ECHO|ISIG);
   line.c_cc[VMIN] = 0;
   line.c_cc[VTIME] = SERIAL_TIMEOUT * 10;
   cfsetispeed(&line, baud);
   cfsetospeed(&line, baud);
   tcsetattr(PBXHandle, TCSANOW, &line);

   sleep(1);

   printf("Rechner-Baudrate OK!\n");

   return (ret);
}


/*****************************************************************************/
/* Funktion zur Einstellung der PC-Baudrate                                  */
/* ----------------------------------------------------------------------    */
/* In:	baudrate : gewuenschte Baudrate                                      */
/* ---------------------------------------------------                       */
/* Out:	                                                                     */
/* ----------------------------------------------------                      */
/* Autor: Steffen Hildebrandt                                                */
/* letzte Aenderung: Steffen Hildebrandt, 15.04.2003                         */
/*****************************************************************************/
int SetPcBaudRate(int baudrate)
{
   int ret = ERROR;
   int baud = B19200;
   struct termios line;
   unsigned char *buf = malloc(512);


   /* PBX-Handle ermitteln */
   if ( PBXHandle != 0 )
      close(PBXHandle);

   if ( ( PBXHandle = open (device, (O_RDWR|O_NOCTTY)) ) < 0 )	
   if (  PBXHandle < 0 )	
   {
     printf ("FEHLER: %s  %s\n", strerror(errno), device);
     return (ret);
   }

   switch(baudrate)
   {
     case 9600   : baud = B9600;
      break;
     case 19200  : baud = B19200;
      break;
     case 38400  : baud = B38400;
      break;
     case 57600  : baud = B57600;
      break;
     case 115200 : baud = B115200;
      break;
     default     : printf("FEHLER : Falsche Baudrate gewaehlt! -> ABBRUCH!\n");
                   printf("         -> Moegliche Baudraten : 9600, 19200, 38400, 57600, 115200\n");
                   exit (0);
      break;
   }

   /* Rechner-Baudrate... */
   printf("Einstellen Rechner-Baudrate (%d Baud)...", baudrate);

   if ( tcgetattr(PBXHandle, &line) )		
   {
     strcpy (buf, "FEHLER: illegal port or device");
     printf (" %s\n", buf);
     fflush(stdout);
     return (ret);
   }

   line.c_iflag &= ~(INPCK|IGNPAR|PARMRK|ISTRIP|IGNBRK|BRKINT|IGNCR|ICRNL|INLCR|IXOFF|IXON);
   line.c_oflag &= ~(OPOST);
   line.c_cflag &= ~(HUPCL|CSTOPB|PARENB|CSIZE);
//   line.c_cflag |= (CLOCAL|CREAD|CS8|CRTSCTS);
   line.c_cflag |= (CREAD|CS8|CRTSCTS);
   line.c_lflag &= ~(ICANON|ECHO|ISIG);
   line.c_cc[VMIN] = 0;
//   line.c_cc[VTIME] = 0;
   line.c_cc[VTIME] = SERIAL_TIMEOUT * 10;
   cfsetispeed(&line, baud);
   cfsetospeed(&line, baud);
   tcsetattr(PBXHandle, TCSANOW, &line);
   ret = NO_ERROR;

   printf("OK\n");

   return (ret);
}


/*****************************************************************************/
/* Funktion zur Ausgabe eines Buffers/Strings auf das gewaehlte device.      */
/* ----------------------------------------------------------------------    */
/* In:	OutString - auszugebende Zeichenkette                                */
/*      len       - Länge derselben                                          */
/* ---------------------------------------------------                       */
/* Out:	void                                                                 */
/* ----------------------------------------------------                      */
/* Autor: Th.Marquardt                                                       */
/* letzte Aenderung: Steffen Hildebrandt, 10.01.2003                         */
/*****************************************************************************/
int SerialOut (unsigned char *OutString, int Len)
{
   int j = 0;
   int msr, dsr;
//   int cts, dcts;
   short k;
  #ifdef DEBUG_SERIAL
   int i = 0;
  #endif


  tcflush(PBXHandle, TCIFLUSH);										/* Input-flush */

  #ifdef DEBUG_SERIAL
   printf ("\nLen (Out) = %d\n", Len);
   printf ("Seriell OUTPUT : ");
   do
   {
     if( OutString[i] == 0x0D )
        printf ("<CR>");
     else if( OutString[i] == 0x0A )
        printf ("<LF>");
     else
       printf("%.2X", OutString[i]);
   }
   while(++i != Len);
   printf("\n");
  #endif

  #ifdef TIMEOUT
   timeout_mode = SERIAL_OUT;
   StartTimer(SERIAL_TIMEOUT, 0);                 /* Timeout-Zeit setzen   */
  #endif

   j = 0;
   do
   {
     write( PBXHandle, &OutString[j], 1 );        /* write one Byte to serial port */
     tcdrain(PBXHandle);                          /* wait until written	*/
     k = MAX_TRYS;
     do
     {
       ioctl(PBXHandle, TIOCMGET, &msr);          /* get modem status register */
//       cts = msr & MSR_CTS;
//       dcts = msr & MSR_DCTS;
       dsr = msr & MSR_DSR;
      #ifdef DEBUG_SERIAL_MSR
//       printf("   Byte(j)=%3d   MSR=%d   MSR_CTS=%d   MSR_DCTS=%d   MSR_DSR=%d   k=%d\n", j + 1, msr, cts, dcts, dsr, k); 
       printf("   j=%3d   Byte=%.2X   MSR_DSR=%d   k=%d\n", j + 1, OutString[j], dsr, k); 
      #endif
			 if ( dsr == 0 && --k == 0)                 /* nach drei Versuchen mit Fehler zurueck */
				 return ERROR;
			 if ( dsr == 0 && j == Len - 1)			        /* beim letzten Zeichen die Schleife verlassen */
				 break;
     }
//     while(dsr != VALUE_MSR_DSR && --k != 0);
     while ( dsr != VALUE_MSR_DSR );
   }
   while(++j != Len);

  #ifdef TIMEOUT
   timeout_mode = NONE;
   StartTimer(0, 0);                              /* Timer zuruecksetzen    */
  #endif

   return NO_ERROR;
}


/*****************************************************************************/
/* Funktion zum Einlesen eines Strings vom gewaehlten device.                */
/* ----------------------------------------------------------------------    */
/* In: 	buf - Pointer auf die eingelesene Zeichenkette                       */
/* In:	EndeChar  - Endezeichen                                              */
/* ---------------------------------------------------                       */
/* Out:	NO_ERROR oder ERROR                                                  */
/* ----------------------------------------------------                      */
/* Autor: Th.Marquardt                                                       */
/* letzte Aenderung: Steffen Hildebrandt, 10.01.2003                         */
/*****************************************************************************/
int SerialIn (char *buf, char EndeChar)
{
  volatile int STOP = FALSE;
  int slen, j;
  char zeichen = 0;


	/* Output-Puffer loeschen */
	tcflush(PBXHandle, TCOFLUSH);	

  /* Timeout-Zeit setzen */
 #ifdef TIMEOUT
	if (logging == FALSE)
	{
		timeout_mode = SERIAL_IN;
  	StartTimer(SERIAL_TIMEOUT, 0);
	}
 #endif

 #ifndef DEBUG_SERIAL
  strcpy (buf, "");
  j = 0;
  while ( STOP == FALSE )
  {
    slen = read ( PBXHandle, &zeichen, 1 );
    if ( slen )
    {
      if ( zeichen == EndeChar )
      {
        buf[j] = '\0';
        STOP = TRUE;
      }
      else
        buf[j++] = zeichen;
    }
		 else
		{
			if (timer == TIMER_DOWN)
		 		return ERROR;
		}
	}

 #ifdef TIMEOUT
  timeout_mode = NONE;
  StartTimer(0, 0);
 #endif

  return NO_ERROR;
 #endif

/*****/

 #ifdef DEBUG_SERIAL
  printf ("Seriell INPUT  : ");
  strcpy (buf, "");
  j = 0;
  while ( STOP == FALSE )
  {
    slen = read ( PBXHandle, &zeichen, 1 );
    if ( slen )
    {
     	if ( zeichen == 0x0D )
        printf ("<CR>");
     	if ( zeichen == 0x0A )
        printf ("<LF>");

      if( zeichen == EndeChar )
			{
        buf[j] = '\0';
        STOP = TRUE;
			}
     	 else if (zeichen != 0x0D)
      {
         buf[j++] = zeichen;
         printf ("%.2X", zeichen);
      }
    }
		 else
		{
			if (timer == TIMER_DOWN)
		 		return ERROR;
		}
  }
  printf ("\n");

 #ifdef TIMEOUT
  timeout_mode = NONE;
  StartTimer(0, 0);
 #endif

  return NO_ERROR;
 #endif
}


/*****************************************************************************/
/* Funktion zum Einlesen eines Blockes vom gewaehlten device                 */
/* ----------------------------------------------------------------------    */
/* In: 	buf - Pointer auf die eingelesene Zeichenkette                       */
/* In:	len - Blocklaenge                                                    */
/* ---------------------------------------------------                       */
/* Out:	NO_ERROR oder ERROR                                                  */
/* ----------------------------------------------------                      */
/* Autor: Steffen Hildebrandt                                                */
/* letzte Aenderung: Steffen Hildebrandt, 10.01.2003                         */
/*****************************************************************************/
int SerialInBlock (char *buf, int len)
{
  int slen;
  int j = 0;
  char zeichen = 0;


  /* Timeout-Zeit setzen */
 #ifdef TIMEOUT
  timeout_mode = SERIAL_BLOCK_IN;
  StartTimer(SERIAL_TIMEOUT, 0);
 #endif

 #ifndef DEBUG_SERIAL
  strcpy (buf, "");
  do
  {
    slen = read ( PBXHandle, &zeichen, 1 );
    if(slen)
      buf[j] = zeichen;
     else
		{
      if ( timer == TIMER_DOWN )
		    return ERROR;
		}
  }
  while(++j != len);

 #ifdef TIMEOUT
  timeout_mode = NONE;
  StartTimer(0, 0);
 #endif

  return NO_ERROR;
 #endif

/*****/

 #ifdef DEBUG_SERIAL
  printf ("Seriell INPUT  : ");
  strcpy (buf, "");
  do
  {
    slen = read ( PBXHandle, &zeichen, 1 );
    if(slen)
    {
      buf[j] = zeichen;
      printf ("%.2X", zeichen);
      printf("\n... gelesene Zeichen : %d ...\n", j+1);
    }
     else
		{
      if ( timer == TIMER_DOWN )
		    return ERROR;
		}
  }
  while(++j != len);

 #ifdef TIMEOUT
  timeout_mode = NONE;
  StartTimer(0, 0);
 #endif

  printf("\nEs wurden %d Zeichen gelesen.\n", j + 1);

  return NO_ERROR;
 #endif
}

/*****************************************************************************/
/* Funktion zum Aendern der Timeoutzeit der seriellen Schnittstelle          */
/* ----------------------------------------------------------------------    */
/* In: 	timeout - Timout-Zeit                                                */
/* ---------------------------------------------------                       */
/* Out:	                                                                     */
/* ----------------------------------------------------                      */
/* Autor: Steffen Hildebrandt                                                */
/* letzte Aenderung: Steffen Hildebrandt, 23.05.2002                         */
/*****************************************************************************/
void SetComTimeout ( int timeout )
{
	struct termios terminfo;
	

	bzero(&terminfo, sizeof(terminfo));																								/* Struktur "leeren" fuer neue Daten */
	tcgetattr(PBXHandle, &terminfo);																									/* Modem-Flags auslesen und neu setzen (in Struktur termios)... */
	terminfo.c_cc[VMIN] = 0;
	terminfo.c_cc[VTIME] = timeout * 10;																		          /* Einstellen der Timeout für Einlesen */
	tcsetattr(PBXHandle, TCSANOW, &terminfo);																					/* Modem-Flags nach Definition neu setzen */			

 #ifdef DEBUG_TIMEOUT
	printf("Neue Timeout-Zeit (Parameter serielle Schnittstelle) : %d sec\n", timeout);
 #endif

	return;		
}

