#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>	/* for usleep() */
#include <string.h>	/* for bzero() */
#include <termios.h>
#if defined(linux) || defined(_IBMR2)
# include <sys/ioctl.h>	/* for ioctl() */
#endif
#include <sys/errno.h>

extern int debug, dowakeup, errno;

#if defined(sun) || defined(linux) || defined(_IBMR2)
# define mflag int
# define getline(fd, z) if(ioctl(fd,TIOCMGET,&z)<0){perror("TCGET");return 0;}
# define PSION_ALIVE (z & (TIOCM_DSR | TIOCM_CD | TIOCM_CTS))
# define dtr(fd, set) { int d = TIOCM_DTR;\
                        if(ioctl(fd, set ? TIOCMBIS : TIOCMBIC, &d) < 0) {\
			perror("TIOCMBIC/S"); return 0; } }
#endif /* sun || linux */

/*
#if !defined(CRTSCTS) && defined(_IBMR2)
#define CRTSCTS 0x80000000
#endif
*/

#ifdef hpux
# include <sys/termiox.h>
# include <sys/modem.h>

# define getline(fd, z) if(ioctl(fd,MCGETA,&z)<0) {perror("MCGETA");return 0;}
# define PSION_ALIVE (z & (MDSR | MDCD | MCTS))
# define dtr(fd, set) { if (set) z |= MDTR; else z &= ~MDTR;\
      			if(ioctl(fd, MCSETA, &z) < 0) {\
			perror("MCSETA"); return 0; } }
#endif

/* returns 1 if OK. */
int 
fd_is_still_alive(fd, wake)
int fd, wake;
{
  int w;
  mflag z;

  if(debug > 2) { printf("fd_is_alive: check lines "); fflush(stdout);}
  getline(fd, z)

  if(dowakeup)
    {
      if(debug > 2) printf("fd_is_alive: %s dtr\n", wake ? "set" : "clear");
      dtr(fd, wake);
    }

  if(dowakeup)
    {
      if(! PSION_ALIVE && wake) {
	if(debug > 1) printf("Trying to wake psion\n");
	/* wake up psion by raising DTR */
	dtr(fd, 1);

	w = 10;
	do {
	  if(w < 10)
	    usleep(100000);
	  getline(fd, z)
	} while(! PSION_ALIVE && --w);
	
	if(debug > 1)
	  printf("Is %swake (%d tries left)\n",PSION_ALIVE?"":"not",w);
      }
    }

  if(debug > 2) printf("fd_is_alive: %d\n", PSION_ALIVE);
  return PSION_ALIVE;
}

int
init_serial(dev, speed, flags)
  char *dev;
  int speed, flags;
{
  int fd;
  struct termios ti;
#ifdef hpux
  struct termiox tx;
#endif
  static struct baud { int speed, baud; } btable[] =
    {
      9600,	B9600,
#ifdef B19200
      19200,	B19200,
#else
# ifdef EXTA
      19200,	EXTA,
# endif
#endif
#ifdef B38400
      38400,	B38400,
#else
# ifdef EXTB
      38400,	EXTB,
# endif
#endif
      4800,	B4800,
      2400,	B2400,
      1200,	B1200,
      300, 	B300,
      75,	B75,
      50,	B50,
      0,	B0
    }, *bptr;
  
  for (bptr = btable; bptr->speed; bptr++)
    if (bptr->speed == speed)
      break;
  if (!bptr->baud)
    {
      fprintf(stderr, "Cannot match selected speed %d\n", speed);
      exit(1);
    }

  if(debug) printf("using %s...\n", dev);
  if(!(fd = open(dev, O_RDWR | O_NDELAY | O_NOCTTY , 0)))
    {
      perror(dev); exit(1);
    }
  if(debug) printf("open done\n");

  bzero(&ti, sizeof(struct termios));
#if defined(hpux) || defined(_IBMR2) 
  ti.c_cflag = bptr->baud | CS8 | HUPCL | CLOCAL | CREAD;
#endif
#if defined(sun)
  ti.c_cflag = bptr->baud | CS8 | HUPCL | CLOCAL | CRTSCTS | CREAD;
#endif
#ifdef linux
  ti.c_cflag = bptr->baud | CS8 | HUPCL | CLOCAL | CRTSCTS | CREAD;
  ti.c_iflag = IGNBRK | IGNPAR;
  ti.c_oflag = 0;
  ti.c_lflag = 0;
  ti.c_cc[VMIN] = 1;
  ti.c_cc[VTIME] = 0;
  ti.c_line = N_TTY;
#endif

  if(tcsetattr(fd, TCSANOW, &ti) < 0)
    perror("tcsetattr");

#ifdef hpux
  bzero(&tx, sizeof(struct termiox));
  tx.x_hflag = RTSXOFF | CTSXON;
  if (ioctl(fd, TCSETXW, &tx) < 0)
    perror("TCSETXW");
#endif

#if defined(_IBMR2)
  ioctl(fd, TXDELCD, "dtr");
  ioctl(fd, TXDELCD, "xon");
  ioctl(fd, TXADDCD, "rts");  /* That's how AIX does CRTSCTS */
#endif

  return fd;
}

#ifdef sun
/*
 * Before exiting we should close the serial line file descriptor
 * as we are probably on a "wicked sun", we have to reset the CRTSCTS
 * flag first, otherwise we will hang in close(2).
 * (suns that are not "wicked" are "broken" , i.e.: they do not have
 * patch 100513-04. There are also suns that are "wicked" and "broken".
 * These run Solaris >= 2.0 !! * YESSSSSS!!! gec
 */

void
ser_exit(status,fd)
int status,fd;
{
  struct termios ti;
  if(ioctl(fd, TCGETS, &ti) < 0) {
    perror("TCGETSW");
  }
  ti.c_cflag &= ~CRTSCTS;
  if(ioctl(fd, TCSETS, &ti) < 0) {
    perror("TCSETSW");
  }
  (void) close(fd);
}
#endif /* sun */
