304 lines
6.9 KiB
C
304 lines
6.9 KiB
C
#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
|
|
|