root/src/system/libroot/posix/termios.c
/*
 * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
 * Copyright 2003, Daniel Reinhold, danielre@users.sf.net. All rights reserved.
 *
 * Distributed under the terms of the MIT License.
 */


#include <termios.h>
#include <unistd.h>
#include <errno.h>

#include <errno_private.h>


/*! get the attributes of the TTY device at fd */
int
tcgetattr(int fd, struct termios *termios)
{
        return ioctl(fd, TCGETA, termios);
}


/*! set the attributes for the TTY device at fd */
int
tcsetattr(int fd, int opt, const struct termios *termios)
{
        int method;

        switch (opt) {
                case TCSANOW:
                        // set the attributes immediately
                        method = TCSETA;
                        break;
                case TCSADRAIN:
                        // wait for ouput to finish before setting the attributes
                        method = TCSETAW;
                        break;
                case TCSAFLUSH:
                        method = TCSETAF;
                        break;

                default:
                        // no other valid options
                        __set_errno(EINVAL);
                        return -1;
        }

        return ioctl(fd, method, termios);
}


/*! wait for all output to be transmitted */
int
tcdrain(int fd)
{
        /* Some termios implementations have a TIOCDRAIN command
         * expressly for this purpose (e.g. ioctl(fd, TIOCDRAIN, 0).
         * However, the BeOS implementation adheres to another
         * interface which uses a non-zero last parameter to the
         * TCSBRK ioctl to signify this functionality.
         */
        return ioctl(fd, TCSBRK, 1);
}


/*! suspend or restart transmission */
int
tcflow(int fd, int action)
{
        switch (action) {
                case TCIOFF:
                case TCION:
                case TCOOFF:
                case TCOON:
                        break;

                default:
                        __set_errno(EINVAL);
                        return -1;
        }

        return ioctl(fd, TCXONC, action);
}


/*! flush all pending data (input or output) */
int
tcflush(int fd, int queueSelector)
{
        return ioctl(fd, TCFLSH, queueSelector);
}


/*! send zero bits for the specified duration */
int
tcsendbreak(int fd, int duration)
{
        // Posix spec says this should take ~ 0.25 to 0.5 seconds.
        // As the interpretation of the duration is undefined, we'll just ignore it
        return ioctl(fd, TCSBRK, 0);
}


speed_t
cfgetispeed(const struct termios *termios)
{
        if ((termios->c_cflag & CBAUD) == CBAUD)
                return termios->c_ispeed + ((uint32_t)termios->c_ispeed_high << 16);

        return termios->c_cflag & CBAUD;
}


int
cfsetispeed(struct termios *termios, speed_t speed)
{
        /*      Check for custom baudrates, which must be stored in the c_ispeed
        field instead of inlined in the flags.
        Note that errors from hardware device (unsupported baudrates, etc) are
        detected only when the tcsetattr() function is called */
        if (speed > B31250) {
                termios->c_cflag |= CBAUD;
                termios->c_ispeed = speed & 0xFFFF;
                termios->c_ispeed_high = speed >> 16;
                return 0;
        }

        termios->c_cflag &= ~CBAUD;
        termios->c_cflag |= speed;
        return 0;
}


speed_t
cfgetospeed(const struct termios *termios)
{
        if ((termios->c_cflag & CBAUD) == CBAUD)
                return termios->c_ospeed + ((uint32_t)termios->c_ospeed_high << 16);

        return termios->c_cflag & CBAUD;
}


int
cfsetospeed(struct termios *termios, speed_t speed)
{
        /* Check for custom speed values (see above) */
        if (speed > B31250) {
                termios->c_cflag |= CBAUD;
                termios->c_ospeed = speed & 0xFFFF;
                termios->c_ospeed_high = speed >> 16;
                return 0;
        }

        termios->c_cflag &= ~CBAUD;
        termios->c_cflag |= speed;
        return 0;
}


void
cfmakeraw(struct termios *termios)
{
        termios->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR
                | ICRNL | IXON);
        termios->c_oflag &= ~OPOST;
        termios->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
        termios->c_cflag &= ~(CSIZE | PARENB);
        termios->c_cflag |= CS8;
        termios->c_cc[VMIN] = 1;        // input is available character by character
        termios->c_cc[VTIME] = 0;
}


pid_t
tcgetsid(int fd)
{
        int sid;

        if (ioctl(fd, TIOCGSID, &sid) == 0)
                return sid;

        return -1;
}


int
tcsetsid(int fd, pid_t pid)
{
        if (pid != getsid(0)) {
                errno = EINVAL;
                return -1;
        }

        return ioctl(fd, TIOCSCTTY, NULL);
}


int
tcgetwinsize(int fd, struct winsize* winsize)
{
        return ioctl(fd, TIOCGWINSZ, winsize, sizeof(*winsize));
}


int
tcsetwinsize(int fd, const struct winsize* winsize)
{
        return ioctl(fd, TIOCSWINSZ, winsize, sizeof(*winsize));
}