1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/mi.termios.c,v 1.5 2006/03/02 18:46:44 christos Exp $ */
259243Sobrien/* termios.c - fake termios interface using sgtty interface
359243Sobrien * 	       by Magnus Doell and Bruce Evans.
459243Sobrien *
559243Sobrien */
659243Sobrien#include "sh.h"
7167465SmpRCSID("$tcsh: mi.termios.c,v 1.5 2006/03/02 18:46:44 christos Exp $")
859243Sobrien
969408Sache#if defined(_MINIX) && !defined(_MINIX_VMD)
1059243Sobrien
1159243Sobrien
1259243Sobrien/* Undefine everything that clashes with sgtty.h. */
1359243Sobrien#undef B0
1459243Sobrien#undef B50
1559243Sobrien#undef B75
1659243Sobrien#undef B110
1759243Sobrien#undef B134
1859243Sobrien#undef B150
1959243Sobrien#undef B200
2059243Sobrien#undef B300
2159243Sobrien#undef B600
2259243Sobrien#undef B1200
2359243Sobrien#undef B1800
2459243Sobrien#undef B2400
2559243Sobrien#undef B4800
2659243Sobrien#undef B9600
2759243Sobrien#undef B19200
2859243Sobrien#undef B28800
2959243Sobrien#undef B38400
3059243Sobrien#undef B57600
3159243Sobrien#undef B115200
3259243Sobrien/* Do not #undef CRMOD. We want a warning when they differ! */
3359243Sobrien#undef ECHO
3459243Sobrien/* Do not #undef XTABS. We want a warning when they differ! */
3559243Sobrien
3659243Sobrien/* Redefine some of the termios.h names just undefined with 'T_' prefixed
3759243Sobrien * to the name.  Don't bother with the low speeds - Minix does not support
3859243Sobrien * them.  Add support for higher speeds (speeds are now easy and don't need
3959243Sobrien * defines because they are not encoded).
4059243Sobrien */
4159243Sobrien#define T_ECHO		000001
4259243Sobrien
4359243Sobrien#include <errno.h>
4459243Sobrien#include <sgtty.h>
4559243Sobrien
4659243Sobrienstatic _PROTOTYPE( int tc_to_sg_speed, (speed_t speed) );
4759243Sobrienstatic _PROTOTYPE( speed_t sg_to_tc_speed, (int speed) );
4859243Sobrien#define B19200   192
4959243Sobrien
5059243Sobrien/* The speed get/set functions could be macros in the Minix implementation
5159243Sobrien * because there are speed fields in the structure with no fancy packing
5259243Sobrien * and it is not practical to check the values outside the driver.
5359243Sobrien * Where tests are necessary because the driver acts different from what
5459243Sobrien * POSIX requires, they are done in tcsetattr.
5559243Sobrien */
5659243Sobrien
5759243Sobrienspeed_t cfgetispeed(termios_p)
5859243Sobrienstruct termios *termios_p;
5959243Sobrien{
6059243Sobrien    return termios_p->c_ispeed;
6159243Sobrien}
6259243Sobrien
6359243Sobrienspeed_t cfgetospeed(termios_p)
6459243Sobrienstruct termios *termios_p;
6559243Sobrien{
6659243Sobrien    return termios_p->c_ospeed;
6759243Sobrien}
6859243Sobrien
6959243Sobrienspeed_t cfsetispeed(termios_p, speed)
7059243Sobrienstruct termios *termios_p;
7159243Sobrienspeed_t speed;
7259243Sobrien{
7359243Sobrien    termios_p->c_ispeed = speed;
7459243Sobrien    return 0;
7559243Sobrien}
7659243Sobrien
7759243Sobrienspeed_t cfsetospeed(termios_p, speed)
7859243Sobrienstruct termios *termios_p;
7959243Sobrienspeed_t speed;
8059243Sobrien{
8159243Sobrien    termios_p->c_ospeed = speed;
8259243Sobrien    return 0;
8359243Sobrien}
8459243Sobrien
8559243Sobrienstatic speed_t sg_to_tc_speed(speed)
8659243Sobrienint speed;
8759243Sobrien{
8859243Sobrien    /* The speed encodings in sgtty.h and termios.h are different.  Both are
8959243Sobrien     * inflexible.  Minix doesn't really support B0 but we map it through
9059243Sobrien     * anyway.  It doesn't support B50, B75 or B134.
9159243Sobrien     */
9259243Sobrien    switch (speed) {
9359243Sobrien	case B0: return 0;
9459243Sobrien	case B110: return 110;
9559243Sobrien	case B200: return 200;
9659243Sobrien	case B300: return 300;
9759243Sobrien	case B600: return 600;
9859243Sobrien	case B1200: return 1200;
9959243Sobrien	case B1800: return 1800;
10059243Sobrien	case B2400: return 2400;
10159243Sobrien	case B4800: return 4800;
10259243Sobrien	case B9600: return 9600;
10359243Sobrien	case B19200: return 19200;
10459243Sobrien#ifdef B28800
10559243Sobrien	case B28800: return 28800;
10659243Sobrien#endif
10759243Sobrien#ifdef B38400
10859243Sobrien	case B38400: return 38400;
10959243Sobrien#endif
11059243Sobrien#ifdef B57600
11159243Sobrien	case B57600: return 57600;
11259243Sobrien#endif
11359243Sobrien#ifdef B115200
11459243Sobrien	case B115200: return 115200;
11559243Sobrien#endif
11659243Sobrien	default: return (speed_t)-1;
11759243Sobrien    }
11859243Sobrien}
11959243Sobrien
12059243Sobrienstatic int tc_to_sg_speed(speed)
12159243Sobrienspeed_t speed;
12259243Sobrien{
12359243Sobrien    /* Don't use a switch here in case the compiler is 16-bit and doesn't
12459243Sobrien     * properly support longs (speed_t's) in switches.  It turns out the
12559243Sobrien     * switch is larger and slower for most compilers anyway!
12659243Sobrien     */
12759243Sobrien    if (speed == 0) return 0;
12859243Sobrien    if (speed == 110) return B110;
12959243Sobrien    if (speed == 200) return B200;
13059243Sobrien    if (speed == 300) return B300;
13159243Sobrien    if (speed == 600) return B600;
13259243Sobrien    if (speed == 1200) return B1200;
13359243Sobrien    if (speed == 1800) return B1800;
13459243Sobrien    if (speed == 2400) return B2400;
13559243Sobrien    if (speed == 4800) return B4800;
13659243Sobrien    if (speed == 9600) return B9600;
13759243Sobrien    if (speed == 19200) return B19200;
13859243Sobrien#ifdef B28800
13959243Sobrien    if (speed == 28800) return B28800;
14059243Sobrien#endif
14159243Sobrien#ifdef B38400
14259243Sobrien    if (speed == 38400) return B38400;
14359243Sobrien#endif
14459243Sobrien#ifdef B57600
14559243Sobrien    if (speed == 57600) return B57600;
14659243Sobrien#endif
14759243Sobrien#ifdef B115200
14859243Sobrien    if (speed == 115200) return B115200;
14959243Sobrien#endif
15059243Sobrien    return -1;
15159243Sobrien}
15259243Sobrien
15359243Sobrienint tcgetattr(filedes, termios_p)
15459243Sobrienint filedes;
15559243Sobrienstruct termios *termios_p;
15659243Sobrien{
15759243Sobrien    struct sgttyb sgbuf;
15859243Sobrien    struct tchars tcbuf;
15959243Sobrien
16059243Sobrien    if (ioctl(filedes, TIOCGETP, &sgbuf) < 0
16159243Sobrien	|| ioctl(filedes, TIOCGETC, (struct sgttyb *) &tcbuf) < 0)
16259243Sobrien    {
16359243Sobrien	return -1;
16459243Sobrien    }
16559243Sobrien
16659243Sobrien    /* Minix input flags:
16759243Sobrien     *   BRKINT:  forced off (break is not recognized)
16859243Sobrien     *   IGNBRK:  forced on (break is not recognized)
16959243Sobrien     *   ICRNL:   set if CRMOD is set and not RAW (CRMOD also controls output)
17059243Sobrien     *   IGNCR:   forced off (ignoring cr's is not supported)
17159243Sobrien     *   INLCR:   forced off (mapping nl's to cr's is not supported)
17259243Sobrien     *   ISTRIP:  forced off (should be off for consoles, on for rs232 no RAW)
17359243Sobrien     *   IXOFF:   forced off (rs232 uses CTS instead of XON/XOFF)
17459243Sobrien     *   IXON:    forced on if not RAW
17559243Sobrien     *   PARMRK:  forced off (no '\377', '\0', X sequence on errors)
17659243Sobrien     * ? IGNPAR:  forced off (input with parity/framing errors is kept)
17759243Sobrien     * ? INPCK:   forced off (input parity checking is not supported)
17859243Sobrien     */
17959243Sobrien    termios_p->c_iflag = IGNBRK;
18059243Sobrien    if (!(sgbuf.sg_flags & RAW))
18159243Sobrien    {
18259243Sobrien	termios_p->c_iflag |= IXON;
18359243Sobrien	if (sgbuf.sg_flags & CRMOD)
18459243Sobrien	{
18559243Sobrien	    termios_p->c_iflag |= ICRNL;
18659243Sobrien	}
18759243Sobrien    }
18859243Sobrien
18959243Sobrien    /* Minix output flags:
19059243Sobrien     *   OPOST:   set if CRMOD or XTABS is set
19159243Sobrien     *   XTABS:   copied from sg_flags
19259243Sobrien     *   CRMOD:	  copied from sg_flags
19359243Sobrien     */
19459243Sobrien    termios_p->c_oflag = sgbuf.sg_flags & (CRMOD | XTABS);
19559243Sobrien    if (termios_p->c_oflag)
19659243Sobrien    {
19759243Sobrien	termios_p->c_oflag |= OPOST;
19859243Sobrien    }
19959243Sobrien
20059243Sobrien    /* Minix local flags:
20159243Sobrien     *   ECHO:    set if ECHO is set
20259243Sobrien     *   ECHOE:   set if ECHO is set (ERASE echoed as error-corecting backspace)
20359243Sobrien     *   ECHOK:   set if ECHO is set ('\n' echoed after KILL char)
20459243Sobrien     *   ECHONL:  forced off ('\n' not echoed when ECHO isn't set)
20559243Sobrien     *   ICANON:  set if neither CBREAK nor RAW
20659243Sobrien     *   IEXTEN:  forced off
20759243Sobrien     *   ISIG:    set if not RAW
20859243Sobrien     *   NOFLSH:  forced off (input/output queues are always flushed)
20959243Sobrien     *   TOSTOP:  forced off (no job control)
21059243Sobrien     */
21159243Sobrien    termios_p->c_lflag = 0;
21259243Sobrien    if (sgbuf.sg_flags & ECHO)
21359243Sobrien    {
21459243Sobrien	termios_p->c_lflag |= T_ECHO | ECHOE | ECHOK;
21559243Sobrien    }
21659243Sobrien    if (!(sgbuf.sg_flags & RAW))
21759243Sobrien    {
21859243Sobrien	termios_p->c_lflag |= ISIG;
21959243Sobrien	if (!(sgbuf.sg_flags & CBREAK))
22059243Sobrien	{
22159243Sobrien	    termios_p->c_lflag |= ICANON;
22259243Sobrien	}
22359243Sobrien    }
22459243Sobrien
22559243Sobrien    /* Minix control flags:
22659243Sobrien     *   CLOCAL:  forced on (ignore modem status lines - not quite right)
22759243Sobrien     *   CREAD:   forced on (receiver is always enabled)
22859243Sobrien     *   CSIZE:   CS5-CS8 correspond directly to BITS5-BITS8
22959243Sobrien     *   CSTOPB:  set for B110 (driver will generate 2 stop-bits than)
23059243Sobrien     *   HUPCL:   forced off
23159243Sobrien     *   PARENB:  set if EVENP or ODDP is set
23259243Sobrien     *   PARODD:  set if ODDP is set
23359243Sobrien     */
23459243Sobrien    termios_p->c_cflag = CLOCAL | CREAD;
23559243Sobrien    switch (sgbuf.sg_flags & BITS8)
23659243Sobrien    {
23759243Sobrien	case BITS5: termios_p->c_cflag |= CS5; break;
23859243Sobrien	case BITS6: termios_p->c_cflag |= CS6; break;
23959243Sobrien	case BITS7: termios_p->c_cflag |= CS7; break;
24059243Sobrien	case BITS8: termios_p->c_cflag |= CS8; break;
24159243Sobrien    }
24259243Sobrien    if (sgbuf.sg_flags & ODDP)
24359243Sobrien    {
24459243Sobrien	termios_p->c_cflag |= PARENB | PARODD;
24559243Sobrien    }
24659243Sobrien    if (sgbuf.sg_flags & EVENP)
24759243Sobrien    {
24859243Sobrien	termios_p->c_cflag |= PARENB;
24959243Sobrien    }
25059243Sobrien    if (sgbuf.sg_ispeed == B110)
25159243Sobrien    {
25259243Sobrien	termios_p->c_cflag |= CSTOPB;
25359243Sobrien    }
25459243Sobrien
25559243Sobrien    /* Minix may give back different input and output baudrates,
25659243Sobrien     * but only the input baudrate is valid for both.
25759243Sobrien     * As our termios emulation will fail, if input baudrate differs
25859243Sobrien     * from output baudrate, force them to be equal.
25959243Sobrien     * Otherwise it would be very suprisingly not to be able to set
26059243Sobrien     * the terminal back to the state returned by tcgetattr :).
26159243Sobrien     */
26259243Sobrien    termios_p->c_ospeed =
26359243Sobrien    termios_p->c_ispeed =
26459243Sobrien		sg_to_tc_speed((unsigned char) sgbuf.sg_ispeed);
26559243Sobrien
26659243Sobrien    /* Minix control characters correspond directly except VSUSP and the
26759243Sobrien     * important VMIN and VTIME are not really supported.
26859243Sobrien     */
26959243Sobrien    termios_p->c_cc[VEOF] = tcbuf.t_eofc;
27059243Sobrien    termios_p->c_cc[VEOL] = tcbuf.t_brkc;
27159243Sobrien    termios_p->c_cc[VERASE] = sgbuf.sg_erase;
27259243Sobrien    termios_p->c_cc[VINTR] = tcbuf.t_intrc;
27359243Sobrien    termios_p->c_cc[VKILL] = sgbuf.sg_kill;
27459243Sobrien    termios_p->c_cc[VQUIT] = tcbuf.t_quitc;
27559243Sobrien    termios_p->c_cc[VSTART] = tcbuf.t_startc;
27659243Sobrien    termios_p->c_cc[VSTOP] = tcbuf.t_stopc;
27759243Sobrien    termios_p->c_cc[VMIN] = 1;
27859243Sobrien    termios_p->c_cc[VTIME] = 0;
27959243Sobrien    termios_p->c_cc[VSUSP] = 0;
28059243Sobrien
28159243Sobrien    return 0;
28259243Sobrien}
28359243Sobrien
28459243Sobrienint tcsetattr(filedes, opt_actions, termios_p)
28559243Sobrienint filedes;
28659243Sobrienint opt_actions;
28759243Sobrienstruct termios *termios_p;
28859243Sobrien{
28959243Sobrien    struct sgttyb sgbuf;
29059243Sobrien    struct tchars tcbuf;
29159243Sobrien    int sgspeed;
29259243Sobrien
29359243Sobrien    /* Posix 1003.1-1988 page 135 says:
29459243Sobrien     * Attempts to set unsupported baud rates shall be ignored, and it is
29559243Sobrien     * implementation-defined whether an error is returned by any or all of
29659243Sobrien     * cfsetispeed(), cfsetospeed(), or tcsetattr(). This refers both to
29759243Sobrien     * changes to baud rates not supported by the hardware, and to changes
29859243Sobrien     * setting the input and output baud rates to different values if the
29959243Sobrien     * hardware does not support it.
30059243Sobrien     * Ignoring means not to change the existing settings, doesn't it?
30159243Sobrien     */
30259243Sobrien    if ((termios_p->c_ispeed != 0 && termios_p->c_ispeed != termios_p->c_ospeed)
30359243Sobrien	|| (sgspeed = tc_to_sg_speed(termios_p->c_ospeed)) < 0)
30459243Sobrien    {
30559243Sobrien	errno = EINVAL;
30659243Sobrien	return -1;
30759243Sobrien    }
30859243Sobrien
30959243Sobrien    sgbuf.sg_ispeed = sgbuf.sg_ospeed = sgspeed;
31059243Sobrien    sgbuf.sg_flags = 0;
31159243Sobrien
31259243Sobrien    /* I don't know what should happen with requests that are not supported by
31359243Sobrien     * old Minix drivers and therefore cannot be emulated.
31459243Sobrien     * Returning an error may confuse the application (the values aren't really
31559243Sobrien     * invalid or unsupported by the hardware, they just couldn't be satisfied
31659243Sobrien     * by the driver). Not returning an error might be even worse because the
31759243Sobrien     * driver will act different to what the application requires it to act
31859243Sobrien     * after sucessfully setting the attributes as specified.
31959243Sobrien     * Settings that cannot be emulated fully include:
32059243Sobrien     *   c_ospeed != 110 && c_cflag & CSTOPB
32159243Sobrien     *   c_ospeed == 110 && ! c_cflag & CSTOPB
32259243Sobrien     *   (c_cc[VMIN] != 1 || c_cc[VTIME] != 0) && ! c_lflag & ICANON
32359243Sobrien     *   c_lflag & ICANON && ! c_lflag & ISIG
32459243Sobrien     * For the moment I just ignore these conflicts.
32559243Sobrien     */
32659243Sobrien
32759243Sobrien    if (termios_p->c_oflag & OPOST)
32859243Sobrien    {
32959243Sobrien	/* CRMOD isn't Posix and may conflict with ICRNL, which is Posix,
33059243Sobrien	 * so we just ignore it.
33159243Sobrien	 */
33259243Sobrien	if (termios_p->c_oflag & XTABS)
33359243Sobrien	{
33459243Sobrien		sgbuf.sg_flags |= XTABS;
33559243Sobrien	}
33659243Sobrien    }
33759243Sobrien
33859243Sobrien    if (termios_p->c_iflag & ICRNL)
33959243Sobrien    {
34059243Sobrien	/* We couldn't do it better :-(. */
34159243Sobrien	sgbuf.sg_flags |= CRMOD;
34259243Sobrien    }
34359243Sobrien
34459243Sobrien    if (termios_p->c_lflag & T_ECHO)
34559243Sobrien    {
34659243Sobrien	sgbuf.sg_flags |= ECHO;
34759243Sobrien    }
34859243Sobrien    if (!(termios_p->c_lflag & ICANON))
34959243Sobrien    {
35059243Sobrien	if (termios_p->c_lflag & ISIG)
35159243Sobrien	{
35259243Sobrien	     sgbuf.sg_flags |= CBREAK;
35359243Sobrien	}
35459243Sobrien	else
35559243Sobrien	{
35659243Sobrien	     sgbuf.sg_flags |= RAW;
35759243Sobrien	}
35859243Sobrien    }
35959243Sobrien
36059243Sobrien    switch (termios_p->c_cflag & CSIZE)
36159243Sobrien    {
36259243Sobrien	case CS5: sgbuf.sg_flags |= BITS5; break;
36359243Sobrien	case CS6: sgbuf.sg_flags |= BITS6; break;
36459243Sobrien	case CS7: sgbuf.sg_flags |= BITS7; break;
36559243Sobrien	case CS8: sgbuf.sg_flags |= BITS8; break;
36659243Sobrien    }
36759243Sobrien    if (termios_p->c_cflag & PARENB)
36859243Sobrien    {
36959243Sobrien	if (termios_p->c_cflag & PARODD)
37059243Sobrien	{
37159243Sobrien	    sgbuf.sg_flags |= ODDP;
37259243Sobrien	}
37359243Sobrien	else
37459243Sobrien	{
37559243Sobrien	    sgbuf.sg_flags |= EVENP;
37659243Sobrien	}
37759243Sobrien    }
37859243Sobrien
37959243Sobrien    sgbuf.sg_erase = termios_p->c_cc[VERASE];
38059243Sobrien    sgbuf.sg_kill = termios_p->c_cc[VKILL];
38159243Sobrien
38259243Sobrien    tcbuf.t_intrc = termios_p->c_cc[VINTR];
38359243Sobrien    tcbuf.t_quitc = termios_p->c_cc[VQUIT];
38459243Sobrien    tcbuf.t_startc = termios_p->c_cc[VSTART];
38559243Sobrien    tcbuf.t_stopc = termios_p->c_cc[VSTOP];
38659243Sobrien    tcbuf.t_eofc = termios_p->c_cc[VEOF];
38759243Sobrien    tcbuf.t_brkc = termios_p->c_cc[VEOL];
38859243Sobrien
38959243Sobrien    return ioctl(filedes, TIOCSETP, &sgbuf) < 0 &&
39059243Sobrien	   ioctl(filedes, TIOCSETC, (struct sgttyb *) &tcbuf) < 0 ?
39159243Sobrien		-1 : 0;
39259243Sobrien}
39369408Sache#endif /* _MINIX && !_MINIX_VMD */
394