#!/usr/bin/perl -w

package Tty;
use Common;

use POSIX qw(:termios_h :unistd_h);
use Fcntl;

# /usr/include/asm-generic/termbits.h
sub VREPRINT { 12 }
sub VDISCARD { 13 }
sub VWERASE  { 14 }
sub VLNEXT   { 15 }
sub VEOL2    { 16 }
sub B57600   { 0010001 }
sub B115200  { 0010002 }
sub B230400  { 0010003 }
sub B460800  { 0010004 }
sub CRTSCTS  { 020000000000 }

# /usr/include/bits/ioctl-types.h
sub TIOCM_DTR { 0x0002 }
# /usr/include/asm-generic/ioctls.h
sub TIOCMBIS  { 0x5416 }
sub TIOCMBIC  { 0x5417 }

my $save_fh;
my $save_termios;
my $save_dev;
my $baudrate;

sub termios_rawline($) {
    my $ts = shift;

    $ts->setiflag(IGNPAR|IGNCR);
    $ts->setoflag(0);
    $ts->setcflag(CS8|CREAD|CLOCAL|HUPCL);
    $ts->setlflag(ICANON);

    $ts->setispeed(B115200);
    $ts->setospeed(B115200);
    $baudrate = 115200;

    $ts->setcc(&POSIX::VINTR,    _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VQUIT,    _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VERASE,   _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VKILL,    _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VEOF,     _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VEOL,     ord('\n'));
    $ts->setcc(&POSIX::VSTART,   _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VSTOP,    _POSIX_VDISABLE);
    $ts->setcc(&POSIX::VSUSP,    _POSIX_VDISABLE);

    $ts->setcc(&POSIX::VMIN,  1);
    $ts->setcc(&POSIX::VTIME, 0);

    $ts->setcc(VEOL2,    _POSIX_VDISABLE);
    $ts->setcc(VREPRINT, _POSIX_VDISABLE);
    $ts->setcc(VWERASE,  _POSIX_VDISABLE);
    $ts->setcc(VLNEXT,   _POSIX_VDISABLE);
    $ts->setcc(VDISCARD, _POSIX_VDISABLE);

    return $ts;
}

sub tty_open($) {
    my $dev = shift;
    my $fh;
    my $fd;

    $save_dev = $dev;

    if (!sysopen($fh, $dev, O_RDWR|O_NOCTTY|O_NONBLOCK)) {
	die("cannot open $dev");
    }

    if (!isatty($fh)) {
	die("$dev is not a tty");
    }

    $fd = fileno($fh);
    $save_termios = POSIX::Termios->new();
    $save_termios->getattr($fd) || die("$dev termios->getattr() failure");

    my $new_termios = POSIX::Termios->new();
    $new_termios->getattr($fd) || die("$dev termios->getattr() failure");
    #my $new_termios = $save_termios;
    termios_rawline($new_termios);
    $new_termios->setattr($fd) || die("$dev termios->setattr() failure");

    $fh->autoflush(1);

    my $flags = fcntl($fh, F_GETFL, 0) ||
	die "$dev: fcntl getfl failure\n";

    fcntl($fh, F_SETFL, $flags & ~O_NONBLOCK) ||
	 die "$dev: fcntl setfl failure\n";

    ( $fh, $save_termios );
}

sub tty_close(;$$$) {
    my $fh = shift // $save_fh;
    my $termios = shift // $save_termios;
    my $dev = shift // $save_dev;

    if ($fh) {
	my $fd = fileno($fh);
	$termios->setattr($fd) || warn("$dev termios setattr() failure");
	close($fh);
    }
}

sub tty_modem_DTR($$) {
    my $fd = shift;
    my $dtr = shift;

    my $bits = TIOCM_DTR;

    if ($dtr) {
	ioctl($fd, TIOCMBIS, \$bits);
    } else {
	ioctl($fd, TIOCMBIC, \$bits);
    }
}

END {
    tty_close();
}


1;

