diff options
Diffstat (limited to 'otherlibs/unix/termios.c')
-rw-r--r-- | otherlibs/unix/termios.c | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/otherlibs/unix/termios.c b/otherlibs/unix/termios.c new file mode 100644 index 000000000..fdb0fb95d --- /dev/null +++ b/otherlibs/unix/termios.c @@ -0,0 +1,303 @@ +#include <mlvalues.h> +#include <alloc.h> +#include "unix.h" + +#ifdef HAS_TERMIOS + +#include <termios.h> +#include <errno.h> + +static struct termios terminal_status; + +enum { Bool, Enum, Speed, Char, End }; + +enum { Input, Output }; + +#define iflags ((long)(&terminal_status.c_iflag)) +#define oflags ((long)(&terminal_status.c_oflag)) +#define cflags ((long)(&terminal_status.c_cflag)) +#define lflags ((long)(&terminal_status.c_lflag)) +#define cc(n) ((long)(&terminal_status.c_cc[n])) + +/* Number of fields in the terminal_io record field. Cf. unix.mli */ + +#define NFIELDS 51 + +/* Structure of the terminal_io record. Cf. unix.mli */ + +static long terminal_io_descr[] = { + /* Input modes */ + Bool, iflags, IGNBRK, + Bool, iflags, BRKINT, + Bool, iflags, IGNPAR, + Bool, iflags, PARMRK, + Bool, iflags, INPCK, + Bool, iflags, ISTRIP, + Bool, iflags, INLCR, + Bool, iflags, IGNCR, + Bool, iflags, ICRNL, + Bool, iflags, IXON, + Bool, iflags, IXOFF, + /* Output modes */ + Bool, oflags, OPOST, + Bool, oflags, OLCUC, + Bool, oflags, ONLCR, + Bool, oflags, OCRNL, + Bool, oflags, ONOCR, + Bool, oflags, ONLRET, + Bool, oflags, OFILL, + Bool, oflags, OFDEL, + Enum, oflags, 0, 2, NLDLY, NL0, NL1, + Enum, oflags, 0, 2, CRDLY, CR0, CR1, + Enum, oflags, 0, 4, TABDLY, TAB0, TAB1, TAB2, TAB3, + Enum, oflags, 0, 2, BSDLY, BS0, BS1, + Enum, oflags, 0, 2, VTDLY, VT0, VT1, + Enum, oflags, 0, 2, FFDLY, FF0, FF1, + /* Control modes */ + Speed, Output, + Speed, Input, + Enum, cflags, 5, 4, CSIZE, CS5, CS6, CS7, CS8, + Enum, cflags, 1, 2, CSTOPB, 0, CSTOPB, + Bool, cflags, CREAD, + Bool, cflags, PARENB, + Bool, cflags, PARODD, + Bool, cflags, HUPCL, + Bool, cflags, CLOCAL, + /* Local modes */ + Bool, lflags, ISIG, + Bool, lflags, ICANON, + Bool, lflags, NOFLSH, + Bool, lflags, ECHO, + Bool, lflags, ECHOE, + Bool, lflags, ECHOK, + Bool, lflags, ECHONL, + /* Control characters */ + Char, cc(VINTR), + Char, cc(VQUIT), + Char, cc(VERASE), + Char, cc(VKILL), + Char, cc(VEOF), + Char, cc(VEOL), + Char, cc(VMIN), + Char, cc(VTIME), + Char, cc(VSTART), + Char, cc(VSTOP), + End +}; + +#undef iflags +#undef oflags +#undef cflags +#undef lflags +#undef cc + +struct speedtable_entry ; + +static struct { + speed_t speed; + int baud; +} speedtable[] = { + B0, 0, + B50, 50, + B75, 75, + B110, 110, + B134, 134, + B150, 150, + B300, 300, + B600, 600, + B1200, 1200, + B1800, 1800, + B2400, 2400, + B4800, 4800, + B9600, 9600, + B19200, 19200, + B38400, 38400 +}; + +#define NSPEEDS (sizeof(speedtable) / sizeof(speedtable[0])) + +static void encode_terminal_status(dst) + value * dst; +{ + long * pc; + int i; + + for(pc = terminal_io_descr; *pc != End; dst++) { + switch(*pc++) { + case Bool: + { int * src = (int *) (*pc++); + int msk = *pc++; + *dst = Val_bool(*src & msk); + break; } + case Enum: + { int * src = (int *) (*pc++); + int ofs = *pc++; + int num = *pc++; + int msk = *pc++; + for (i = 0; i < num; i++) { + if ((*src & msk) == pc[i]) { + *dst = Val_int(i + ofs); + break; + } + } + pc += num; + break; } + case Speed: + { int which = *pc++; + speed_t speed; + switch (which) { + case Output: + speed = cfgetospeed(&terminal_status); break; + case Input: + speed = cfgetispeed(&terminal_status); break; + } + for (i = 0; i < NSPEEDS; i++) { + if (speed == speedtable[i].speed) { + *dst = Val_int(speedtable[i].baud); + break; + } + } + break; } + case Char: + { unsigned char * src = (unsigned char *) (*pc++); + *dst = Val_int(*src); + break; } + } + } +} + +static void decode_terminal_status(src) + value * src; +{ + long * pc; + int i; + + for (pc = terminal_io_descr; *pc != End; src++) { + switch(*pc++) { + case Bool: + { int * dst = (int *) (*pc++); + int msk = *pc++; + if (Tag_val(*src) != 0) + *dst |= msk; + else + *dst &= ~msk; + break; } + case Enum: + { int * dst = (int *) (*pc++); + int ofs = *pc++; + int num = *pc++; + int msk = *pc++; + i = Int_val(*src) - ofs; + if (i >= 0 && i < num) { + *dst = (*dst & ~msk) | pc[i]; + } else { + unix_error(EINVAL, "tcsetattr", Nothing); + } + pc += num; + break; } + case Speed: + { int which = *pc++; + int baud = Int_val(*src); + int res; + for (i = 0; i < NSPEEDS; i++) { + if (baud == speedtable[i].baud) { + switch (which) { + case Output: + res = cfsetospeed(&terminal_status, speedtable[i].speed); break; + case Input: + res = cfsetispeed(&terminal_status, speedtable[i].speed); break; + } + if (res == -1) uerror("tcsetattr", Nothing); + goto ok; + } + } + unix_error(EINVAL, "tcsetattr", Nothing); + ok: + break; } + case Char: + { unsigned char * dst = (unsigned char *) (*pc++); + *dst = Int_val(*src); + break; } + } + } +} + +value unix_tcgetattr(fd) + value fd; +{ + value res; + + if (tcgetattr(Int_val(fd), &terminal_status) == -1) + uerror("tcgetattr", Nothing); + res = alloc_tuple(NFIELDS); + encode_terminal_status(&Field(res, 0)); + return res; +} + +static int when_flag_table[] = { + TCSANOW, TCSADRAIN, TCSAFLUSH +}; + +value unix_tcsetattr(fd, when, arg) + value fd, when, arg; +{ + if (tcgetattr(Int_val(fd), &terminal_status) == -1) + uerror("tcsetattr", Nothing); + decode_terminal_status(&Field(arg, 0)); + if (tcsetattr(Int_val(fd), + when_flag_table[Tag_val(when)], + &terminal_status) == -1) + uerror("tcsetattr", Nothing); + return Val_unit; +} + +value unix_tcsendbreak(fd, delay) + value fd, delay; +{ + if (tcsendbreak(Int_val(fd), Int_val(delay)) == -1) + uerror("tcsendbreak", Nothing); + return Val_unit; +} + +value unix_tcdrain(fd) + value fd; +{ + if (tcdrain(Int_val(fd)) == -1) uerror("tcdrain", Nothing); + return Val_unit; +} + +static int queue_flag_table[] = { + TCIFLUSH, TCOFLUSH, TCIOFLUSH +}; + +value unix_tcflush(fd, queue) + value fd, queue; +{ + if (tcflush(Int_val(fd), queue_flag_table[Tag_val(queue)]) == -1) + uerror("tcflush", Nothing); + return Val_unit; +} + +static int action_flag_table[] = { + TCOOFF, TCOON, TCIOFF, TCION +}; + +value unix_tcflow(fd, action) + value fd, action; +{ + if (tcflow(Int_val(fd), action_flag_table[Tag_val(action)]) == -1) + uerror("tcflow", Nothing); + return Val_unit; +} + +#else + +value unix_tcgetattr() { invalid_argument("tcgetattr not implemented"); } +value unix_tcsetattr() { invalid_argument("tcsetattr not implemented"); } +value unix_tcsendbreak() { invalid_argument("tcsendbreak not implemented"); } +value unix_tcdrain() { invalid_argument("tcdrain not implemented"); } +value unix_tcflush() { invalid_argument("tcflush not implemented"); } +value unix_tcflow() { invalid_argument("tcflow not implemented"); } + +#endif + |