/* Copyright (C) 1992, 1993, 1996, 1997, 1998 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
 

   About the only thing remaining here fromthe original Linux-8086 C library
   version by Robert de Bath <robert@mayday.compulink.co.uk>, is the general
   layout.  All else has been recently stolen from GNU libc, since that was
   much more current.
 */

#include <errno.h>
#include <stddef.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <termios.h>

#ifdef L_isatty
/* Return 1 if FD is a terminal, 0 if not.  */

int isatty(int fd)
{
    struct termios term;
    return (tcgetattr (fd, &term) == 0);
}
#endif

#ifdef L_tcdrain
/* Wait for pending output to be written on FD.  */
int __libc_tcdrain (int fd)
{
      /* With an argument of 1, TCSBRK waits for the output to drain.  */
      return ioctl(fd, TCSBRK, 1);
}
weak_alias(__libc_tcdrain, tcdrain)
#endif

#ifdef L_tcflow
/* Suspend or restart transmission on FD.  */
int tcflow ( int fd, int action)
{
      return ioctl(fd, TCXONC, action);
}
#endif

#ifdef L_tcflush
/* Flush pending data on FD.  */
int tcflush ( int fd, int queue_selector)
{
      return ioctl(fd, TCFLSH, queue_selector);
}
#endif

#ifdef L_tcsendbreak
/* Send zero bits on FD.  */
int tcsendbreak( int fd, int duration)
{
	/* The break lasts 0.25 to 0.5 seconds if DURATION is zero,
	   and an implementation-defined period if DURATION is nonzero.
	   We define a positive DURATION to be number of milliseconds to break.  */
	if (duration <= 0)
		return ioctl(fd, TCSBRK, 0);

#ifdef TCSBRKP
	/* Probably Linux-specific: a positive third TCSBRKP ioctl argument is
	   defined to be the number of 100ms units to break.  */
	return ioctl(fd, TCSBRKP, (duration + 99) / 100);
#else
	/* ioctl can't send a break of any other duration for us.
	   This could be changed to use trickery (e.g. lower speed and
	   send a '\0') to send the break, but for now just return an error.  */
	__set_errno (EINVAL);
	return -1;
#endif
}
#endif

#ifdef L_tcsetpgrp
/* Set the foreground process group ID of FD set PGRP_ID.  */
int tcsetpgrp ( int fd, pid_t pgrp_id)
{
      return ioctl (fd, TIOCSPGRP, &pgrp_id);
}
#endif

#ifdef L_tcgetpgrp
/* Return the foreground process group ID of FD.  */
pid_t tcgetpgrp ( int fd)
{
    int pgrp;

    if (ioctl (fd, TIOCGPGRP, &pgrp) < 0)
	return (pid_t) -1;
    return (pid_t) pgrp;
}
#endif

/* This is a gross hack around a kernel bug.  If the cfsetispeed functions is
 * called with the SPEED argument set to zero this means use the same speed as
 * for output.  But we don't have independent input and output speeds and
 * therefore cannot record this.
 *
 * We use an unused bit in the `c_iflag' field to keep track of this use of
 * `cfsetispeed'.  The value here must correspond to the one used in
 * `tcsetattr.c'.  */
#define IBAUD0  020000000000

#ifdef L_cfgetospeed
/* Return the output baud rate stored in *TERMIOS_P.  */
speed_t cfgetospeed ( const struct termios *termios_p)
{
      return termios_p->c_cflag & (CBAUD | CBAUDEX);
}
#endif

#ifdef L_cfgetispeed

/* Return the input baud rate stored in *TERMIOS_P.
 * Although for Linux there is no difference between input and output
 * speed, the numerical 0 is a special case for the input baud rate. It
 * should set the input baud rate to the output baud rate. */
speed_t cfgetispeed (const struct termios *termios_p)
{
    return ((termios_p->c_iflag & IBAUD0)
	    ? 0 : termios_p->c_cflag & (CBAUD | CBAUDEX));
}
#endif

#ifdef L_cfsetospeed
/* Set the output baud rate stored in *TERMIOS_P to SPEED.  */
int cfsetospeed  (struct termios *termios_p, speed_t speed)
{
    if ((speed & ~CBAUD) != 0
	    && (speed < B57600 || speed > B460800))
    {
	__set_errno(EINVAL);
	return -1;
    }

    termios_p->c_cflag &= ~(CBAUD | CBAUDEX);
    termios_p->c_cflag |= speed;

    return 0;
}
#endif

#ifdef L_cfsetispeed
/* Set the input baud rate stored in *TERMIOS_P to SPEED.
 *    Although for Linux there is no difference between input and output
 *       speed, the numerical 0 is a special case for the input baud rate.  It
 *          should set the input baud rate to the output baud rate.  */
int cfsetispeed ( struct termios *termios_p, speed_t speed)
{
    if ((speed & ~CBAUD) != 0
	    && (speed < B57600 || speed > B460800))
    {
	__set_errno(EINVAL);
	return -1;
    }

    if (speed == 0)
	termios_p->c_iflag |= IBAUD0;
    else
    {
	termios_p->c_iflag &= ~IBAUD0;
	termios_p->c_cflag &= ~(CBAUD | CBAUDEX);
	termios_p->c_cflag |= speed;
    }

    return 0;
}
#endif

#ifdef L_cfsetspeed
struct speed_struct
{
  speed_t value;
  speed_t internal;
};

static const struct speed_struct speeds[] =
  {
#ifdef B0
    { 0, B0 },
#endif
#ifdef B50
    { 50, B50 },
#endif
#ifdef B75
    { 75, B75 },
#endif
#ifdef B110
    { 110, B110 },
#endif
#ifdef B134
    { 134, B134 },
#endif
#ifdef B150
    { 150, B150 },
#endif
#ifdef B200
    { 200, B200 },
#endif
#ifdef B300
    { 300, B300 },
#endif
#ifdef B600
    { 600, B600 },
#endif
#ifdef B1200
    { 1200, B1200 },
#endif
#ifdef B1200
    { 1200, B1200 },
#endif
#ifdef B1800
    { 1800, B1800 },
#endif
#ifdef B2400
    { 2400, B2400 },
#endif
#ifdef B4800
    { 4800, B4800 },
#endif
#ifdef B9600
    { 9600, B9600 },
#endif
#ifdef B19200
    { 19200, B19200 },
#endif
#ifdef B38400
    { 38400, B38400 },
#endif
#ifdef B57600
    { 57600, B57600 },
#endif
#ifdef B76800
    { 76800, B76800 },
#endif
#ifdef B115200
    { 115200, B115200 },
#endif
#ifdef B153600
    { 153600, B153600 },
#endif
#ifdef B230400
    { 230400, B230400 },
#endif
#ifdef B307200
    { 307200, B307200 },
#endif
#ifdef B460800
    { 460800, B460800 },
#endif
  };


/* Set both the input and output baud rates stored in *TERMIOS_P to SPEED.  */
int cfsetspeed (struct termios *termios_p, speed_t speed)
{
  size_t cnt;

  for (cnt = 0; cnt < sizeof (speeds) / sizeof (speeds[0]); ++cnt)
    if (speed == speeds[cnt].internal)
      {
	cfsetispeed (termios_p, speed);
	cfsetospeed (termios_p, speed);
	return 0;
      }
    else if (speed == speeds[cnt].value)
      {
	cfsetispeed (termios_p, speeds[cnt].internal);
	cfsetospeed (termios_p, speeds[cnt].internal);
	return 0;
      }

  __set_errno (EINVAL);

  return -1;
}
#endif

#ifdef L_cfmakeraw
/* Copyright (C) 1992, 1996, 1997 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
*/
#include <termios.h>

/* Set *T to indicate raw mode.  */
void
cfmakeraw (struct termios *t)
{
  t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
  t->c_oflag &= ~OPOST;
  t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
  t->c_cflag &= ~(CSIZE|PARENB);
  t->c_cflag |= CS8;
  t->c_cc[VMIN] = 1;		/* read returns when one char is available.  */
  t->c_cc[VTIME] = 0;
}
#endif