diff --git a/commands/DESCRIBE/DESCRIBE.sh b/commands/DESCRIBE/DESCRIBE.sh index 5ad21eb0c..7f44975fe 100644 --- a/commands/DESCRIBE/DESCRIBE.sh +++ b/commands/DESCRIBE/DESCRIBE.sh @@ -126,18 +126,6 @@ do ;; 4,125) des="video output" dev=video ;; - 4,12[89]|4,1[3-8]?|4,19[01]) - p=`expr \\( $minor - 128 \\) / 16 | tr '0123' 'pqrs'` - n=`expr $minor % 16` - test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` - des="pseudo tty `expr $minor - 128`" dev=tty$p$n - ;; - 4,???) - p=`expr \\( $minor - 192 \\) / 16 | tr '0123' 'pqrs'` - n=`expr $minor % 16` - test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` - des="controller of tty$p$n" dev=pty$p$n - ;; 5,0) des="anonymous tty" dev=tty ;; 6,0) des="line printer, parallel port" dev=lp @@ -171,6 +159,18 @@ do fi esac ;; + 9,12[89]|9,1[3-8]?|9,19[01]) + p=`expr \\( $minor - 128 \\) / 16 | tr '0123' 'pqrs'` + n=`expr $minor % 16` + test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` + des="pseudo tty `expr $minor - 128`" dev=tty$p$n + ;; + 9,???) + p=`expr \\( $minor - 192 \\) / 16 | tr '0123' 'pqrs'` + n=`expr $minor % 16` + test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` + des="controller of tty$p$n" dev=pty$p$n + ;; 11,0) des="block filter" dev=filter ;; diff --git a/commands/MAKEDEV/MAKEDEV.sh b/commands/MAKEDEV/MAKEDEV.sh index 30b8ae76a..08d4f8bc4 100755 --- a/commands/MAKEDEV/MAKEDEV.sh +++ b/commands/MAKEDEV/MAKEDEV.sh @@ -418,8 +418,8 @@ do minor_tty=`expr ${group} '*' 16 + ${pty} + 128` minor_pty=`expr ${group} '*' 16 + ${pty} + 192` - makedev tty${dev} c 4 ${minor_tty} ${uname} tty 666 - makedev pty${dev} c 4 ${minor_pty} ${uname} tty 666 + makedev tty${dev} c 9 ${minor_tty} ${uname} tty 666 + makedev pty${dev} c 9 ${minor_pty} ${uname} tty 666 ;; ttyc[1-7]) # Virtual consoles. diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 979aaa360..634bf0d2c 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -4892,6 +4892,7 @@ ./usr/sbin/pfs minix-sys ./usr/sbin/pm minix-sys ./usr/sbin/postinstall minix-sys +./usr/sbin/pty minix-sys ./usr/sbin/pwd_mkdb minix-sys ./usr/sbin/rdate minix-sys ./usr/sbin/rs minix-sys diff --git a/drivers/Makefile b/drivers/Makefile index 176a2d956..0554d9a22 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,7 +31,7 @@ SUBDIR= bmp085 cat24c256 fb gpio i2c lan8710a \ .endif # Drivers available on all platforms -SUBDIR+= log mmc random tty uds vnd readclock +SUBDIR+= log mmc pty random tty uds vnd readclock .endif # ${MKIMAGEONLY} != "yes" diff --git a/drivers/pty/Makefile b/drivers/pty/Makefile new file mode 100644 index 000000000..007a152a8 --- /dev/null +++ b/drivers/pty/Makefile @@ -0,0 +1,13 @@ +# Makefile for pseudo terminal driver (PTY) + +PROG= pty +SRCS= tty.c pty.c + +DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} +LDADD+= -lchardriver -lsys -ltimers + +MAN= + +BINDIR?= /usr/sbin + +.include diff --git a/drivers/tty/pty.c b/drivers/pty/pty.c similarity index 99% rename from drivers/tty/pty.c rename to drivers/pty/pty.c index 3e46d7548..541739624 100644 --- a/drivers/tty/pty.c +++ b/drivers/pty/pty.c @@ -23,8 +23,6 @@ #include #include "tty.h" -#if NR_PTYS > 0 - /* PTY bookkeeping structure, one per pty/tty pair. */ typedef struct pty { tty_t *tty; /* associated TTY structure */ @@ -618,7 +616,7 @@ void pty_init(tty_t *tp) int line; /* Associate PTY and TTY structures. */ - line = tp - &tty_table[NR_CONS + NR_RS_LINES]; + line = tp - tty_table; pp = tp->tty_priv = &pty_table[line]; pp->tty = tp; pp->select_ops = 0; @@ -638,5 +636,3 @@ void pty_init(tty_t *tp) tp->tty_close = pty_slave_close; tp->tty_select_ops = 0; } - -#endif /* NR_PTYS > 0 */ diff --git a/drivers/pty/tty.c b/drivers/pty/tty.c new file mode 100644 index 000000000..32094602f --- /dev/null +++ b/drivers/pty/tty.c @@ -0,0 +1,1281 @@ +/* TTY part of PTY. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "tty.h" + +#include +#include + +/* Address of a tty structure. */ +#define tty_addr(line) (&tty_table[line]) + +/* Macros for magic tty structure pointers. */ +#define FIRST_TTY tty_addr(0) +#define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0])) + +/* A device exists if at least its 'devread' function is defined. */ +#define tty_active(tp) ((tp)->tty_devread != NULL) + +struct kmessages kmess; + +static void tty_timed_out(minix_timer_t *tp); +static void settimer(tty_t *tty_ptr, int enable); +static void in_transfer(tty_t *tp); +static int tty_echo(tty_t *tp, int ch); +static void rawecho(tty_t *tp, int ch); +static int back_over(tty_t *tp); +static void reprint(tty_t *tp); +static void dev_ioctl(tty_t *tp); +static void setattr(tty_t *tp); +static void tty_icancel(tty_t *tp); + +static int do_open(devminor_t minor, int access, endpoint_t user_endpt); +static int do_close(devminor_t minor); +static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt, + cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); +static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt, + cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); +static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, + cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id); +static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); +static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt); + +static struct chardriver tty_tab = { + .cdr_open = do_open, + .cdr_close = do_close, + .cdr_read = do_read, + .cdr_write = do_write, + .cdr_ioctl = do_ioctl, + .cdr_cancel = do_cancel, + .cdr_select = do_select +}; + +/* Default attributes. */ +static struct termios termios_defaults = { + .c_iflag = TTYDEF_IFLAG, + .c_oflag = TTYDEF_OFLAG, + .c_cflag = TTYDEF_CFLAG, + .c_lflag = TTYDEF_LFLAG, + .c_ispeed = TTYDEF_SPEED, + .c_ospeed = TTYDEF_SPEED, + .c_cc = { + [VEOF] = CEOF, + [VEOL] = CEOL, + [VERASE] = CERASE, + [VINTR] = CINTR, + [VKILL] = CKILL, + [VMIN] = CMIN, + [VQUIT] = CQUIT, + [VTIME] = CTIME, + [VSUSP] = CSUSP, + [VSTART] = CSTART, + [VSTOP] = CSTOP, + [VREPRINT] = CREPRINT, + [VLNEXT] = CLNEXT, + [VDISCARD] = CDISCARD, + [VSTATUS] = CSTATUS + } +}; +static struct winsize winsize_defaults; /* = all zeroes */ + +/* Global variables for the TTY task (declared extern in tty.h). */ +tty_t tty_table[NR_PTYS]; +u32_t system_hz; + +static void tty_startup(void); +static int tty_init(int, sef_init_info_t *); + +/*===========================================================================* + * tty_task * + *===========================================================================*/ +int main(void) +{ +/* Main routine of the terminal task. */ + + message tty_mess; /* buffer for all incoming messages */ + int ipc_status; + unsigned line; + int r; + register tty_t *tp; + + tty_startup(); + + while (TRUE) { + /* Check for and handle any events on any of the ttys. */ + for (tp = FIRST_TTY; tp < END_TTY; tp++) { + if (tp->tty_events) handle_events(tp); + } + + /* Get a request message. */ + r= driver_receive(ANY, &tty_mess, &ipc_status); + if (r != 0) + panic("driver_receive failed with: %d", r); + + if (is_ipc_notify(ipc_status)) { + switch (_ENDPOINT_P(tty_mess.m_source)) { + case CLOCK: + /* run watchdogs of expired timers */ + expire_timers(tty_mess.m_notify.timestamp); + break; + default: + /* do nothing */ + break; + } + + /* done, get new message */ + continue; + } + + if (!IS_CDEV_RQ(tty_mess.m_type)) { + chardriver_process(&tty_tab, &tty_mess, ipc_status); + continue; + } + + /* Only device requests should get to this point. + * All requests have a minor device number. + */ + line = tty_mess.CDEV_MINOR; + if (line - PTYPX_MINOR < NR_PTYS && + tty_mess.m_type != CDEV_IOCTL) { + /* Terminals and pseudo terminals belong together. We can only + * make a distinction between the two based on position in the + * tty_table and not on minor number. Hence this special case. + */ + do_pty(&tty_mess, ipc_status); + continue; + } + + /* Execute the requested device driver function. */ + chardriver_process(&tty_tab, &tty_mess, ipc_status); + } + + return 0; +} + +tty_t * +line2tty(devminor_t line) +{ +/* Convert a terminal line to tty_table pointer */ + + tty_t* tp; + + if ((line - TTYPX_MINOR) < NR_PTYS) { + tp = tty_addr(line - TTYPX_MINOR); + } else if ((line - PTYPX_MINOR) < NR_PTYS) { + tp = tty_addr(line - PTYPX_MINOR); + } else { + tp = NULL; + } + + return(tp); +} + +/*===========================================================================* + * tty_startup * + *===========================================================================*/ +static void tty_startup(void) +{ + /* Register init callbacks. */ + sef_setcb_init_fresh(tty_init); + sef_setcb_init_restart(tty_init); + + /* No live update support for now. */ + + /* No signal support for now. */ + + /* Let SEF perform startup. */ + sef_startup(); +} + +/*===========================================================================* + * do_read * + *===========================================================================*/ +static ssize_t do_read(devminor_t minor, u64_t UNUSED(position), + endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, + cdev_id_t id) +{ +/* A process wants to read from a terminal. */ + tty_t *tp; + int r; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + /* Check if there is already a process hanging in a read, check if the + * parameters are correct, do I/O. + */ + if (tp->tty_incaller != NONE || tp->tty_inleft > 0) + return EIO; + if (size <= 0) + return EINVAL; + + /* Copy information from the message to the tty struct. */ + tp->tty_incaller = endpt; + tp->tty_inid = id; + tp->tty_ingrant = grant; + assert(tp->tty_incum == 0); + tp->tty_inleft = size; + + if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) { + if (tp->tty_termios.c_cc[VMIN] == 0) { + /* MIN & TIME specify a read timer that finishes the + * read in TIME/10 seconds if no bytes are available. + */ + settimer(tp, TRUE); + tp->tty_min = 1; + } else { + /* MIN & TIME specify an inter-byte timer that may + * have to be cancelled if there are no bytes yet. + */ + if (tp->tty_eotct == 0) { + settimer(tp, FALSE); + tp->tty_min = tp->tty_termios.c_cc[VMIN]; + } + } + } + + /* Anything waiting in the input buffer? Clear it out... */ + in_transfer(tp); + /* ...then go back for more. */ + handle_events(tp); + if (tp->tty_inleft == 0) + return EDONTREPLY; /* already done */ + + /* There were no bytes in the input queue available. */ + if (flags & CDEV_NONBLOCK) { + tty_icancel(tp); + r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; + tp->tty_inleft = tp->tty_incum = 0; + tp->tty_incaller = NONE; + return r; + } + + if (tp->tty_select_ops) + select_retry(tp); + + return EDONTREPLY; /* suspend the caller */ +} + +/*===========================================================================* + * do_write * + *===========================================================================*/ +static ssize_t do_write(devminor_t minor, u64_t UNUSED(position), + endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, + cdev_id_t id) +{ +/* A process wants to write on a terminal. */ + tty_t *tp; + int r; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + /* Check if there is already a process hanging in a write, check if the + * parameters are correct, do I/O. + */ + if (tp->tty_outcaller != NONE || tp->tty_outleft > 0) + return EIO; + if (size <= 0) + return EINVAL; + + /* Copy message parameters to the tty structure. */ + tp->tty_outcaller = endpt; + tp->tty_outid = id; + tp->tty_outgrant = grant; + assert(tp->tty_outcum == 0); + tp->tty_outleft = size; + + /* Try to write. */ + handle_events(tp); + if (tp->tty_outleft == 0) + return EDONTREPLY; /* already done */ + + /* None or not all the bytes could be written. */ + if (flags & CDEV_NONBLOCK) { + r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; + tp->tty_outleft = tp->tty_outcum = 0; + tp->tty_outcaller = NONE; + return r; + } + + if (tp->tty_select_ops) + select_retry(tp); + + return EDONTREPLY; /* suspend the caller */ +} + +/*===========================================================================* + * do_ioctl * + *===========================================================================*/ +static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, + cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id) +{ +/* Perform an IOCTL on this terminal. POSIX termios calls are handled + * by the IOCTL system call. + */ + tty_t *tp; + int i, r; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + r = OK; + switch (request) { + case TIOCGETA: + /* Get the termios attributes. */ + r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios, + sizeof(struct termios)); + break; + + case TIOCSETAW: + case TIOCSETAF: + case TIOCDRAIN: + if (tp->tty_outleft > 0) { + if (flags & CDEV_NONBLOCK) + return EAGAIN; + /* Wait for all ongoing output processing to finish. */ + tp->tty_iocaller = endpt; + tp->tty_ioid = id; + tp->tty_ioreq = request; + tp->tty_iogrant = grant; + return EDONTREPLY; /* suspend the caller */ + } + if (request == TIOCDRAIN) break; + if (request == TIOCSETAF) tty_icancel(tp); + /*FALL THROUGH*/ + case TIOCSETA: + /* Set the termios attributes. */ + r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios, + sizeof(struct termios)); + if (r != OK) break; + setattr(tp); + break; + + case TIOCFLUSH: + r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, sizeof(i)); + if (r != OK) break; + if(i & FREAD) { tty_icancel(tp); } + if(i & FWRITE) { (*tp->tty_ocancel)(tp, 0); } + break; + case TIOCSTART: + tp->tty_inhibited = 0; + tp->tty_events = 1; + break; + case TIOCSTOP: + tp->tty_inhibited = 1; + tp->tty_events = 1; + break; + case TIOCSBRK: /* tcsendbreak - turn break on */ + if (tp->tty_break_on != NULL) (*tp->tty_break_on)(tp,0); + break; + case TIOCCBRK: /* tcsendbreak - turn break off */ + if (tp->tty_break_off != NULL) (*tp->tty_break_off)(tp,0); + break; + + case TIOCGWINSZ: + r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, + sizeof(struct winsize)); + break; + + case TIOCSWINSZ: + r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize, + sizeof(struct winsize)); + sigchar(tp, SIGWINCH, 0); + break; + case TIOCGETD: /* get line discipline */ + { + int disc = TTYDISC; + r = sys_safecopyto(endpt, grant, 0, + (vir_bytes) &disc, (vir_bytes) sizeof(disc)); + break; + } + case TIOCSETD: /* set line discipline */ + printf("TTY: TIOCSETD: can't set any other line discipline.\n"); + r = ENOTTY; + break; + + case TIOCSCTTY: + /* Process sets this tty as its controlling tty */ + tp->tty_pgrp = user_endpt; + break; + +/* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is + * not defined. + */ + case TIOCGPGRP: + case TIOCSPGRP: + default: + r = ENOTTY; + } + + return r; +} + +/*===========================================================================* + * do_open * + *===========================================================================*/ +static int do_open(devminor_t minor, int access, endpoint_t user_endpt) +{ +/* A tty line has been opened. Make it the callers controlling tty if + * CDEV_NOCTTY is *not* set and it is not the log device. CDEV_CTTY is returned + * if the tty is made the controlling tty, otherwise OK or an error code. + */ + tty_t *tp; + int r = OK; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + if (!(access & CDEV_NOCTTY)) { + tp->tty_pgrp = user_endpt; + r = CDEV_CTTY; + } + tp->tty_openct++; + if (tp->tty_openct == 1) { + /* Tell the device that the tty is opened */ + (*tp->tty_open)(tp, 0); + } + + return r; +} + +/*===========================================================================* + * do_close * + *===========================================================================*/ +static int do_close(devminor_t minor) +{ +/* A tty line has been closed. Clean up the line if it is the last close. */ + tty_t *tp; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + if (--tp->tty_openct == 0) { + tp->tty_pgrp = 0; + tty_icancel(tp); + (*tp->tty_ocancel)(tp, 0); + (*tp->tty_close)(tp, 0); + tp->tty_termios = termios_defaults; + tp->tty_winsize = winsize_defaults; + setattr(tp); + } + + return OK; +} + +/*===========================================================================* + * do_cancel * + *===========================================================================*/ +static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id) +{ +/* A signal has been sent to a process that is hanging trying to read or write. + * The pending read or write must be finished off immediately. + */ + tty_t *tp; + int r; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + /* Check the parameters carefully, to avoid cancelling twice. */ + r = EDONTREPLY; + if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) { + /* Process was reading when killed. Clean up input. */ + tty_icancel(tp); + r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN; + tp->tty_inleft = tp->tty_incum = 0; + tp->tty_incaller = NONE; + } else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller && + id == tp->tty_outid) { + /* Process was writing when killed. Clean up output. */ + r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; + tp->tty_outleft = tp->tty_outcum = 0; + tp->tty_outcaller = NONE; + } else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller && + id == tp->tty_ioid) { + /* Process was waiting for output to drain. */ + r = EINTR; + tp->tty_ioreq = 0; + tp->tty_iocaller = NONE; + } + if (r != EDONTREPLY) + tp->tty_events = 1; + /* Only reply if we found a matching request. */ + return r; +} + +int select_try(struct tty *tp, int ops) +{ + int ready_ops = 0; + + /* Special case. If line is hung up, no operations will block. + * (and it can be seen as an exceptional condition.) + */ + if (tp->tty_termios.c_ospeed == B0) { + ready_ops |= ops; + } + + if (ops & CDEV_OP_RD) { + /* will i/o not block on read? */ + if (tp->tty_inleft > 0) { + ready_ops |= CDEV_OP_RD; /* EIO - no blocking */ + } else if (tp->tty_incount > 0) { + /* Is a regular read possible? tty_incount + * says there is data. But a read will only succeed + * in canonical mode if a newline has been seen. + */ + if (!(tp->tty_termios.c_lflag & ICANON) || + tp->tty_eotct > 0) { + ready_ops |= CDEV_OP_RD; + } + } + } + + if (ops & CDEV_OP_WR) { + if (tp->tty_outleft > 0) ready_ops |= CDEV_OP_WR; + else if ((*tp->tty_devwrite)(tp, 1)) ready_ops |= CDEV_OP_WR; + } + return ready_ops; +} + +int select_retry(struct tty *tp) +{ + int ops; + + if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) { + chardriver_reply_select(tp->tty_select_proc, + tp->tty_select_minor, ops); + tp->tty_select_ops &= ~ops; + } + return OK; +} + +/*===========================================================================* + * do_select * + *===========================================================================*/ +static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt) +{ + tty_t *tp; + int ready_ops, watch; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + + watch = (ops & CDEV_NOTIFY); + ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR); + + ready_ops = select_try(tp, ops); + + ops &= ~ready_ops; + if (ops && watch) { + /* Translated minor numbers are a problem with late select replies. We + * have to save the minor number used to do the select, since otherwise + * VFS won't be able to make sense of those late replies. We do not + * support selecting on two different minors for the same object. + */ + if (tp->tty_select_ops != 0 && tp->tty_select_minor != minor) { + printf("TTY: select on one object with two minors (%d, %d)\n", + tp->tty_select_minor, minor); + return EBADF; + } + tp->tty_select_ops |= ops; + tp->tty_select_proc = endpt; + tp->tty_select_minor = minor; + } + + return ready_ops; +} + +/*===========================================================================* + * handle_events * + *===========================================================================*/ +void handle_events(tp) +tty_t *tp; /* TTY to check for events. */ +{ +/* Handle any events pending on a TTY. */ + + do { + tp->tty_events = 0; + + /* Read input and perform input processing. */ + (*tp->tty_devread)(tp, 0); + + /* Perform output processing and write output. */ + (*tp->tty_devwrite)(tp, 0); + + /* Ioctl waiting for some event? */ + if (tp->tty_ioreq != 0) dev_ioctl(tp); + } while (tp->tty_events); + + /* Transfer characters from the input queue to a waiting process. */ + in_transfer(tp); + + /* Reply if enough bytes are available. */ + if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) { + chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); + tp->tty_inleft = tp->tty_incum = 0; + tp->tty_incaller = NONE; + } + if (tp->tty_select_ops) + { + select_retry(tp); + } + select_retry_pty(tp); +} + +/*===========================================================================* + * in_transfer * + *===========================================================================*/ +static void in_transfer(tp) +register tty_t *tp; /* pointer to terminal to read from */ +{ +/* Transfer bytes from the input queue to a process reading from a terminal. */ + + int ch; + int count; + char buf[64], *bp; + + /* Force read to succeed if the line is hung up, looks like EOF to reader. */ + if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0; + + /* Anything to do? */ + if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return; + + bp = buf; + while (tp->tty_inleft > 0 && tp->tty_eotct > 0) { + ch = *tp->tty_intail; + + if (!(ch & IN_EOF)) { + /* One character to be delivered to the user. */ + *bp = ch & IN_CHAR; + tp->tty_inleft--; + if (++bp == bufend(buf)) { + /* Temp buffer full, copy to user space. */ + sys_safecopyto(tp->tty_incaller, + tp->tty_ingrant, tp->tty_incum, + (vir_bytes) buf, (vir_bytes) buflen(buf)); + tp->tty_incum += buflen(buf); + bp = buf; + } + } + + /* Remove the character from the input queue. */ + if (++tp->tty_intail == bufend(tp->tty_inbuf)) + tp->tty_intail = tp->tty_inbuf; + tp->tty_incount--; + if (ch & IN_EOT) { + tp->tty_eotct--; + /* Don't read past a line break in canonical mode. */ + if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0; + } + } + + if (bp > buf) { + /* Leftover characters in the buffer. */ + count = bp - buf; + sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum, + (vir_bytes) buf, (vir_bytes) count); + tp->tty_incum += count; + } + + /* Usually reply to the reader, possibly even if incum == 0 (EOF). */ + if (tp->tty_inleft == 0) { + chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum); + tp->tty_inleft = tp->tty_incum = 0; + tp->tty_incaller = NONE; + } +} + +/*===========================================================================* + * in_process * + *===========================================================================*/ +int in_process(tp, buf, count) +register tty_t *tp; /* terminal on which character has arrived */ +char *buf; /* buffer with input characters */ +int count; /* number of input characters */ +{ +/* Characters have just been typed in. Process, save, and echo them. Return + * the number of characters processed. + */ + + int ch, sig, ct; + int timeset = FALSE; + + for (ct = 0; ct < count; ct++) { + /* Take one character. */ + ch = *buf++ & BYTE; + + /* Strip to seven bits? */ + if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F; + + /* Input extensions? */ + if (tp->tty_termios.c_lflag & IEXTEN) { + + /* Previous character was a character escape? */ + if (tp->tty_escaped) { + tp->tty_escaped = NOT_ESCAPED; + ch |= IN_ESC; /* protect character */ + } + + /* LNEXT (^V) to escape the next character? */ + if (ch == tp->tty_termios.c_cc[VLNEXT]) { + tp->tty_escaped = ESCAPED; + rawecho(tp, '^'); + rawecho(tp, '\b'); + continue; /* do not store the escape */ + } + + /* REPRINT (^R) to reprint echoed characters? */ + if (ch == tp->tty_termios.c_cc[VREPRINT]) { + reprint(tp); + continue; + } + } + + /* _POSIX_VDISABLE is a normal character value, so better escape it. */ + if (ch == _POSIX_VDISABLE) ch |= IN_ESC; + + /* Map CR to LF, ignore CR, or map LF to CR. */ + if (ch == '\r') { + if (tp->tty_termios.c_iflag & IGNCR) continue; + if (tp->tty_termios.c_iflag & ICRNL) ch = '\n'; + } else + if (ch == '\n') { + if (tp->tty_termios.c_iflag & INLCR) ch = '\r'; + } + + /* Canonical mode? */ + if (tp->tty_termios.c_lflag & ICANON) { + + /* Erase processing (rub out of last character). */ + if (ch == tp->tty_termios.c_cc[VERASE]) { + (void) back_over(tp); + if (!(tp->tty_termios.c_lflag & ECHOE)) { + (void) tty_echo(tp, ch); + } + continue; + } + + /* Kill processing (remove current line). */ + if (ch == tp->tty_termios.c_cc[VKILL]) { + while (back_over(tp)) {} + if (!(tp->tty_termios.c_lflag & ECHOE)) { + (void) tty_echo(tp, ch); + if (tp->tty_termios.c_lflag & ECHOK) + rawecho(tp, '\n'); + } + continue; + } + + /* EOF (^D) means end-of-file, an invisible "line break". */ + if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF; + + /* The line may be returned to the user after an LF. */ + if (ch == '\n') ch |= IN_EOT; + + /* Same thing with EOL, whatever it may be. */ + if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT; + } + + /* Start/stop input control? */ + if (tp->tty_termios.c_iflag & IXON) { + + /* Output stops on STOP (^S). */ + if (ch == tp->tty_termios.c_cc[VSTOP]) { + tp->tty_inhibited = STOPPED; + tp->tty_events = 1; + continue; + } + + /* Output restarts on START (^Q) or any character if IXANY. */ + if (tp->tty_inhibited) { + if (ch == tp->tty_termios.c_cc[VSTART] + || (tp->tty_termios.c_iflag & IXANY)) { + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + if (ch == tp->tty_termios.c_cc[VSTART]) + continue; + } + } + } + + if (tp->tty_termios.c_lflag & ISIG) { + /* Check for INTR, QUIT and STATUS characters. */ + int sig = -1; + if (ch == tp->tty_termios.c_cc[VINTR]) + sig = SIGINT; + else if(ch == tp->tty_termios.c_cc[VQUIT]) + sig = SIGQUIT; + else if(ch == tp->tty_termios.c_cc[VSTATUS]) + sig = SIGINFO; + + if(sig >= 0) { + sigchar(tp, sig, 1); + (void) tty_echo(tp, ch); + continue; + } + } + + /* Is there space in the input buffer? */ + if (tp->tty_incount == buflen(tp->tty_inbuf)) { + /* No space; discard in canonical mode, keep in raw mode. */ + if (tp->tty_termios.c_lflag & ICANON) continue; + break; + } + + if (!(tp->tty_termios.c_lflag & ICANON)) { + /* In raw mode all characters are "line breaks". */ + ch |= IN_EOT; + + /* Start an inter-byte timer? */ + if (!timeset && tp->tty_termios.c_cc[VMIN] > 0 + && tp->tty_termios.c_cc[VTIME] > 0) { + settimer(tp, TRUE); + timeset = TRUE; + } + } + + /* Perform the intricate function of echoing. */ + if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = tty_echo(tp, ch); + + /* Save the character in the input queue. */ + *tp->tty_inhead++ = ch; + if (tp->tty_inhead == bufend(tp->tty_inbuf)) + tp->tty_inhead = tp->tty_inbuf; + tp->tty_incount++; + if (ch & IN_EOT) tp->tty_eotct++; + + /* Try to finish input if the queue threatens to overflow. */ + if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp); + } + return ct; +} + +/*===========================================================================* + * echo * + *===========================================================================*/ +static int tty_echo(tp, ch) +register tty_t *tp; /* terminal on which to echo */ +register int ch; /* pointer to character to echo */ +{ +/* Echo the character if echoing is on. Some control characters are echoed + * with their normal effect, other control characters are echoed as "^X", + * normal characters are echoed normally. EOF (^D) is echoed, but immediately + * backspaced over. Return the character with the echoed length added to its + * attributes. + */ + int len, rp; + + ch &= ~IN_LEN; + if (!(tp->tty_termios.c_lflag & ECHO)) { + if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag + & (ICANON|ECHONL)) == (ICANON|ECHONL)) + (*tp->tty_echo)(tp, '\n'); + return(ch); + } + + /* "Reprint" tells if the echo output has been messed up by other output. */ + rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint; + + if ((ch & IN_CHAR) < ' ') { + switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) { + case '\t': + len = 0; + do { + (*tp->tty_echo)(tp, ' '); + len++; + } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0); + break; + case '\r' | IN_EOT: + case '\n' | IN_EOT: + (*tp->tty_echo)(tp, ch & IN_CHAR); + len = 0; + break; + default: + (*tp->tty_echo)(tp, '^'); + (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR)); + len = 2; + } + } else + if ((ch & IN_CHAR) == '\177') { + /* A DEL prints as "^?". */ + (*tp->tty_echo)(tp, '^'); + (*tp->tty_echo)(tp, '?'); + len = 2; + } else { + (*tp->tty_echo)(tp, ch & IN_CHAR); + len = 1; + } + if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; } + + tp->tty_reprint = rp; + return(ch | (len << IN_LSHIFT)); +} + +/*===========================================================================* + * rawecho * + *===========================================================================*/ +static void rawecho(tp, ch) +register tty_t *tp; +int ch; +{ +/* Echo without interpretation if ECHO is set. */ + int rp = tp->tty_reprint; + if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch); + tp->tty_reprint = rp; +} + +/*===========================================================================* + * back_over * + *===========================================================================*/ +static int back_over(tp) +register tty_t *tp; +{ +/* Backspace to previous character on screen and erase it. */ + u16_t *head; + int len; + + if (tp->tty_incount == 0) return(0); /* queue empty */ + head = tp->tty_inhead; + if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); + if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */ + if (tp->tty_reprint) reprint(tp); /* reprint if messed up */ + tp->tty_inhead = head; + tp->tty_incount--; + if (tp->tty_termios.c_lflag & ECHOE) { + len = (*head & IN_LEN) >> IN_LSHIFT; + while (len > 0) { + rawecho(tp, '\b'); + rawecho(tp, ' '); + rawecho(tp, '\b'); + len--; + } + } + return(1); /* one character erased */ +} + +/*===========================================================================* + * reprint * + *===========================================================================*/ +static void reprint(tp) +register tty_t *tp; /* pointer to tty struct */ +{ +/* Restore what has been echoed to screen before if the user input has been + * messed up by output, or if REPRINT (^R) is typed. + */ + int count; + u16_t *head; + + tp->tty_reprint = FALSE; + + /* Find the last line break in the input. */ + head = tp->tty_inhead; + count = tp->tty_incount; + while (count > 0) { + if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); + if (head[-1] & IN_EOT) break; + head--; + count--; + } + if (count == tp->tty_incount) return; /* no reason to reprint */ + + /* Show REPRINT (^R) and move to a new line. */ + (void) tty_echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC); + rawecho(tp, '\r'); + rawecho(tp, '\n'); + + /* Reprint from the last break onwards. */ + do { + if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf; + *head = tty_echo(tp, *head); + head++; + count++; + } while (count < tp->tty_incount); +} + +/*===========================================================================* + * out_process * + *===========================================================================*/ +void out_process(tp, bstart, bpos, bend, icount, ocount) +tty_t *tp; +char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */ +int *icount; /* # input chars / input chars used */ +int *ocount; /* max output chars / output chars used */ +{ +/* Perform output processing on a circular buffer. *icount is the number of + * bytes to process, and the number of bytes actually processed on return. + * *ocount is the space available on input and the space used on output. + * (Naturally *icount < *ocount.) The column position is updated modulo + * the TAB size, because we really only need it for tabs. + */ + + int tablen; + int ict = *icount; + int oct = *ocount; + int pos = tp->tty_position; + + while (ict > 0) { + switch (*bpos) { + case '\7': + break; + case '\b': + pos--; + break; + case '\r': + pos = 0; + break; + case '\n': + if ((tp->tty_termios.c_oflag & (OPOST|ONLCR)) + == (OPOST|ONLCR)) { + /* Map LF to CR+LF if there is space. Note that the + * next character in the buffer is overwritten, so + * we stop at this point. + */ + if (oct >= 2) { + *bpos = '\r'; + if (++bpos == bend) bpos = bstart; + *bpos = '\n'; + pos = 0; + ict--; + oct -= 2; + } + goto out_done; /* no space or buffer got changed */ + } + break; + case '\t': + /* Best guess for the tab length. */ + tablen = TAB_SIZE - (pos & TAB_MASK); + + if ((tp->tty_termios.c_oflag & (OPOST|OXTABS)) + == (OPOST|OXTABS)) { + /* Tabs must be expanded. */ + if (oct >= tablen) { + pos += tablen; + ict--; + oct -= tablen; + do { + *bpos = ' '; + if (++bpos == bend) bpos = bstart; + } while (--tablen != 0); + } + goto out_done; + } + /* Tabs are output directly. */ + pos += tablen; + break; + default: + /* Assume any other character prints as one character. */ + pos++; + } + if (++bpos == bend) bpos = bstart; + ict--; + oct--; + } +out_done: + tp->tty_position = pos & TAB_MASK; + + *icount -= ict; /* [io]ct are the number of chars not used */ + *ocount -= oct; /* *[io]count are the number of chars that are used */ +} + +/*===========================================================================* + * dev_ioctl * + *===========================================================================*/ +static void dev_ioctl(tp) +tty_t *tp; +{ +/* The ioctl's TCSETSW, TCSETSF and TIOCDRAIN wait for output to finish to make + * sure that an attribute change doesn't affect the processing of current + * output. Once output finishes the ioctl is executed as in do_ioctl(). + */ + int result = EINVAL; + + if (tp->tty_outleft > 0) return; /* output not finished */ + + if (tp->tty_ioreq != TIOCDRAIN) { + if (tp->tty_ioreq == TIOCSETAF) tty_icancel(tp); + result = sys_safecopyfrom(tp->tty_iocaller, tp->tty_iogrant, 0, + (vir_bytes) &tp->tty_termios, + (vir_bytes) sizeof(tp->tty_termios)); + if (result == OK) setattr(tp); + } + tp->tty_ioreq = 0; + chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result); + tp->tty_iocaller = NONE; +} + +/*===========================================================================* + * setattr * + *===========================================================================*/ +static void setattr(tp) +tty_t *tp; +{ +/* Apply the new line attributes (raw/canonical, line speed, etc.) */ + u16_t *inp; + int count; + + if (!(tp->tty_termios.c_lflag & ICANON)) { + /* Raw mode; put a "line break" on all characters in the input queue. + * It is undefined what happens to the input queue when ICANON is + * switched off, a process should use TCSAFLUSH to flush the queue. + * Keeping the queue to preserve typeahead is the Right Thing, however + * when a process does use TCSANOW to switch to raw mode. + */ + count = tp->tty_eotct = tp->tty_incount; + inp = tp->tty_intail; + while (count > 0) { + *inp |= IN_EOT; + if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf; + --count; + } + } + + /* Inspect MIN and TIME. */ + settimer(tp, FALSE); + if (tp->tty_termios.c_lflag & ICANON) { + /* No MIN & TIME in canonical mode. */ + tp->tty_min = 1; + } else { + /* In raw mode MIN is the number of chars wanted, and TIME how long + * to wait for them. With interesting exceptions if either is zero. + */ + tp->tty_min = tp->tty_termios.c_cc[VMIN]; + if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0) + tp->tty_min = 1; + } + + if (!(tp->tty_termios.c_iflag & IXON)) { + /* No start/stop output control, so don't leave output inhibited. */ + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + } + + /* Setting the output speed to zero hangs up the phone. */ + if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1); + + /* Set new line speed, character size, etc at the device level. */ + (*tp->tty_ioctl)(tp, 0); +} + +/*===========================================================================* + * sigchar * + *===========================================================================*/ +void sigchar(tp, sig, mayflush) +register tty_t *tp; +int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */ +int mayflush; +{ +/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from + * a tty close, "stty 0", or a real RS-232 hangup. PM will send the signal to + * the process group (INT, QUIT), all processes (KILL), or the session leader + * (HUP). + */ + int status; + + if (tp->tty_pgrp != 0) { + if (OK != (status = sys_kill(tp->tty_pgrp, sig))) { + panic("Error; call to sys_kill failed: %d", status); + } + } + + if (mayflush && !(tp->tty_termios.c_lflag & NOFLSH)) { + tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */ + tp->tty_intail = tp->tty_inhead; + (*tp->tty_ocancel)(tp, 0); /* kill all output */ + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + } +} + +/*===========================================================================* + * tty_icancel * + *===========================================================================*/ +static void tty_icancel(tp) +register tty_t *tp; +{ +/* Discard all pending input, tty buffer or device. */ + + tp->tty_incount = tp->tty_eotct = 0; + tp->tty_intail = tp->tty_inhead; + (*tp->tty_icancel)(tp, 0); +} + +/*===========================================================================* + * tty_devnop * + *===========================================================================*/ +static int tty_devnop(tty_t *UNUSED(tp), int UNUSED(try)) +{ + /* Some functions need not be implemented at the device level. */ + return 0; +} + +/*===========================================================================* + * tty_init * + *===========================================================================*/ +static int tty_init(int UNUSED(type), sef_init_info_t *UNUSED(info)) +{ +/* Initialize tty structure and call device initialization routines. */ + + register tty_t *tp; + int s; + + system_hz = sys_hz(); + + /* Initialize the terminal lines. */ + memset(tty_table, '\0' , sizeof(tty_table)); + + for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) { + + tp->tty_index = s; + init_timer(&tp->tty_tmr); + + tp->tty_intail = tp->tty_inhead = tp->tty_inbuf; + tp->tty_min = 1; + tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE; + tp->tty_termios = termios_defaults; + tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close = + tp->tty_open = tty_devnop; + pty_init(tp); + tp->tty_minor = s + TTYPX_MINOR; + } + + return OK; +} + +/*===========================================================================* + * tty_timed_out * + *===========================================================================*/ +static void tty_timed_out(minix_timer_t *tp) +{ +/* This timer has expired. Set the events flag, to force processing. */ + tty_t *tty_ptr; + tty_ptr = &tty_table[tmr_arg(tp)->ta_int]; + tty_ptr->tty_min = 0; /* force read to succeed */ + tty_ptr->tty_events = 1; +} + +/*===========================================================================* + * settimer * + *===========================================================================*/ +static void settimer(tty_ptr, enable) +tty_t *tty_ptr; /* line to set or unset a timer on */ +int enable; /* set timer if true, otherwise unset */ +{ + clock_t ticks; + + if (enable) { + ticks = tty_ptr->tty_termios.c_cc[VTIME] * (system_hz/10); + + /* Set a new timer for enabling the TTY events flags. */ + set_timer(&tty_ptr->tty_tmr, ticks, tty_timed_out, tty_ptr->tty_index); + } else { + /* Remove the timer from the active and expired lists. */ + cancel_timer(&tty_ptr->tty_tmr); + } +} diff --git a/drivers/pty/tty.h b/drivers/pty/tty.h new file mode 100644 index 000000000..7fae103ee --- /dev/null +++ b/drivers/pty/tty.h @@ -0,0 +1,121 @@ +/* tty.h - Terminals */ + +#include +#include + +/* First minor numbers for the various classes of TTY devices. */ +#define TTYPX_MINOR 128 +#define PTYPX_MINOR 192 + +#define TTY_IN_BYTES 256 /* tty input queue size */ +#define TAB_SIZE 8 /* distance between tab stops */ +#define TAB_MASK 7 /* mask to compute a tab stop position */ + +#define ESC '\33' /* escape */ + +struct tty; +typedef int(*devfun_t) (struct tty *tp, int try_only); +typedef void(*devfunarg_t) (struct tty *tp, int c); + +typedef struct tty { + int tty_events; /* set when TTY should inspect this line */ + int tty_index; /* index into TTY table */ + devminor_t tty_minor; /* device minor number */ + + /* Input queue. Typed characters are stored here until read by a program. */ + u16_t *tty_inhead; /* pointer to place where next char goes */ + u16_t *tty_intail; /* pointer to next char to be given to prog */ + int tty_incount; /* # chars in the input queue */ + int tty_eotct; /* number of "line breaks" in input queue */ + devfun_t tty_devread; /* routine to read from low level buffers */ + devfun_t tty_icancel; /* cancel any device input */ + int tty_min; /* minimum requested #chars in input queue */ + minix_timer_t tty_tmr; /* the timer for this tty */ + + /* Output section. */ + devfun_t tty_devwrite; /* routine to start actual device output */ + devfunarg_t tty_echo; /* routine to echo characters input */ + devfun_t tty_ocancel; /* cancel any ongoing device output */ + devfun_t tty_break_on; /* let the device assert a break */ + devfun_t tty_break_off; /* let the device de-assert a break */ + + /* Terminal parameters and status. */ + int tty_position; /* current position on the screen for echoing */ + char tty_reprint; /* 1 when echoed input messed up, else 0 */ + char tty_escaped; /* 1 when LNEXT (^V) just seen, else 0 */ + char tty_inhibited; /* 1 when STOP (^S) just seen (stops output) */ + endpoint_t tty_pgrp; /* endpoint of controlling process */ + char tty_openct; /* count of number of opens of this tty */ + + /* Information about incomplete I/O requests is stored here. */ + endpoint_t tty_incaller; /* process that made the call, or NONE */ + cdev_id_t tty_inid; /* ID of suspended read request */ + cp_grant_id_t tty_ingrant; /* grant where data is to go */ + size_t tty_inleft; /* how many chars are still needed */ + size_t tty_incum; /* # chars input so far */ + endpoint_t tty_outcaller; /* process that made the call, or NONE */ + cdev_id_t tty_outid; /* ID of suspended write request */ + cp_grant_id_t tty_outgrant; /* grant where data comes from */ + size_t tty_outleft; /* # chars yet to be output */ + size_t tty_outcum; /* # chars output so far */ + endpoint_t tty_iocaller; /* process that made the call, or NONE */ + cdev_id_t tty_ioid; /* ID of suspended ioctl request */ + unsigned int tty_ioreq; /* ioctl request code */ + cp_grant_id_t tty_iogrant; /* virtual address of ioctl buffer or grant */ + + /* select() data */ + unsigned int tty_select_ops; /* which operations are interesting */ + endpoint_t tty_select_proc; /* which process wants notification */ + devminor_t tty_select_minor; /* minor used to start select query */ + + /* Miscellaneous. */ + devfun_t tty_ioctl; /* set line speed, etc. at the device level */ + devfun_t tty_open; /* tell the device that the tty is opened */ + devfun_t tty_close; /* tell the device that the tty is closed */ + void *tty_priv; /* pointer to per device private data */ + struct termios tty_termios; /* terminal attributes */ + struct winsize tty_winsize; /* window size (#lines and #columns) */ + + u16_t tty_inbuf[TTY_IN_BYTES];/* tty input buffer */ + +} tty_t; + +/* Memory allocated in tty.c, so extern here. */ +extern tty_t tty_table[NR_PTYS]; +extern u32_t system_hz; /* system clock frequency */ + +/* Values for the fields. */ +#define NOT_ESCAPED 0 /* previous character is not LNEXT (^V) */ +#define ESCAPED 1 /* previous character was LNEXT (^V) */ +#define RUNNING 0 /* no STOP (^S) has been typed to stop output */ +#define STOPPED 1 /* STOP (^S) has been typed to stop output */ + +/* Fields and flags on characters in the input queue. */ +#define IN_CHAR 0x00FF /* low 8 bits are the character itself */ +#define IN_LEN 0x0F00 /* length of char if it has been echoed */ +#define IN_LSHIFT 8 /* length = (c & IN_LEN) >> IN_LSHIFT */ +#define IN_EOT 0x1000 /* char is a line break (^D, LF) */ +#define IN_EOF 0x2000 /* char is EOF (^D), do not return to user */ +#define IN_ESC 0x4000 /* escaped by LNEXT (^V), no interpretation */ + +/* Number of elements and limit of a buffer. */ +#define buflen(buf) (sizeof(buf) / sizeof((buf)[0])) +#define bufend(buf) ((buf) + buflen(buf)) + +/* Function prototypes for TTY driver. */ +/* tty.c */ +void handle_events(struct tty *tp); +void sigchar(struct tty *tp, int sig, int mayflush); +void tty_task(void); +tty_t *line2tty(devminor_t minor); +int in_process(struct tty *tp, char *buf, int count); +void out_process(struct tty *tp, char *bstart, char *bpos, char *bend, + int *icount, int *ocount); +void tty_wakeup(clock_t now); +int select_try(struct tty *tp, int ops); +int select_retry(struct tty *tp); + +/* pty.c */ +void do_pty(message *m_ptr, int ipc_status); +void pty_init(struct tty *tp); +void select_retry_pty(struct tty *tp); diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile index 5575a4ccf..16dbd7abe 100644 --- a/drivers/tty/Makefile +++ b/drivers/tty/Makefile @@ -4,7 +4,7 @@ PROG= tty .include "arch/${MACHINE_ARCH}/Makefile.inc" -SRCS += tty.c pty.c +SRCS += tty.c DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} LDADD+= -lchardriver -lsys -ltimers diff --git a/drivers/tty/tty.c b/drivers/tty/tty.c index 6bc715743..5470f6d72 100644 --- a/drivers/tty/tty.c +++ b/drivers/tty/tty.c @@ -44,7 +44,6 @@ unsigned long rs_irq_set = 0; /* Macros for magic tty types. */ #define isconsole(tp) ((tp) < tty_addr(NR_CONS)) -#define ispty(tp) ((tp) != NULL && (tp)->tty_minor >= TTYPX_MINOR) /* Macros for magic tty structure pointers. */ #define FIRST_TTY tty_addr(0) @@ -58,11 +57,6 @@ unsigned long rs_irq_set = 0; #define rs_init(tp) ((void) 0) #endif -#if NR_PTYS == 0 -#define pty_init(tp) ((void) 0) -#define do_pty(tp, mp) ((void) 0) -#endif - struct kmessages kmess; static void tty_timed_out(minix_timer_t *tp); @@ -132,7 +126,7 @@ static struct termios termios_defaults = { static struct winsize winsize_defaults; /* = all zeroes */ /* Global variables for the TTY task (declared extern in tty.h). */ -tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS]; +tty_t tty_table[NR_CONS+NR_RS_LINES]; int ccurrent; /* currently active console */ struct machine machine; /* kernel environment variables */ u32_t system_hz; @@ -232,15 +226,6 @@ int main(void) if (line == VIDEO_MINOR) { do_video(&tty_mess, ipc_status); continue; - } else if (line - PTYPX_MINOR < NR_PTYS && - tty_mess.m_type != CDEV_IOCTL) { - /* Terminals and pseudo terminals belong together. We can only - * make a distinction between the two based on position in the - * tty_table and not on minor number (i.e., use ispty macro). - * Hence this special case. - */ - do_pty(&tty_mess, ipc_status); - continue; } /* Execute the requested device driver function. */ @@ -290,10 +275,6 @@ line2tty(devminor_t line) tp = tty_addr(line - CONS_MINOR); } else if ((line - RS232_MINOR) < NR_RS_LINES) { tp = tty_addr(line - RS232_MINOR + NR_CONS); - } else if ((line - TTYPX_MINOR) < NR_PTYS) { - tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES); - } else if ((line - PTYPX_MINOR) < NR_PTYS) { - tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES); } else { tp = NULL; } @@ -957,10 +938,6 @@ tty_t *tp; /* TTY to check for events. */ { select_retry(tp); } -#if NR_PTYS > 0 - if (ispty(tp)) - select_retry_pty(tp); -#endif } /*===========================================================================* @@ -1582,13 +1559,9 @@ static void tty_init() kb_init(tp); tp->tty_minor = CONS_MINOR + s; - } else - if (tp < tty_addr(NR_CONS+NR_RS_LINES)) { + } else { rs_init(tp); tp->tty_minor = RS232_MINOR + s-NR_CONS; - } else { - pty_init(tp); - tp->tty_minor = s - (NR_CONS+NR_RS_LINES) + TTYPX_MINOR; } } diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h index ffde3006c..12c42526a 100644 --- a/drivers/tty/tty.h +++ b/drivers/tty/tty.h @@ -8,8 +8,6 @@ #define LOG_MINOR 15 #define RS232_MINOR 16 #define VIDEO_MINOR 125 -#define TTYPX_MINOR 128 -#define PTYPX_MINOR 192 #define CONS_ARG 30 /* console= boot param length (incl. nul) */ #define LINEWRAP 1 /* console.c - wrap lines at column 80 */ @@ -88,7 +86,7 @@ typedef struct tty { } tty_t; /* Memory allocated in tty.c, so extern here. */ -extern tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS]; +extern tty_t tty_table[NR_CONS+NR_RS_LINES]; extern int ccurrent; /* currently visible console */ extern u32_t system_hz; /* system clock frequency */ @@ -155,8 +153,3 @@ void kb_init_once(void); int kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant); void do_fkey_ctl(message *m); void do_input(message *m); - -/* pty.c */ -void do_pty(message *m_ptr, int ipc_status); -void pty_init(struct tty *tp); -void select_retry_pty(struct tty *tp); diff --git a/etc/system.conf b/etc/system.conf index 8e0cb3455..d47fd9a59 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -707,3 +707,13 @@ service uds ; uid 0; # only for checkperms(2) and copyfd(2) }; + +service pty +{ + system + KILL # 06 + ; + ipc + SYSTEM vfs rs vm + ; +}; diff --git a/etc/usr/rc b/etc/usr/rc index 2090c0eba..225bb6c0e 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -196,6 +196,8 @@ start|autoboot) up inet -script /etc/rs.inet -dev /dev/ip fi + up pty -dev /dev/ptyp0 + up uds -dev /dev/uds up -n ipc diff --git a/include/minix/dmap.h b/include/minix/dmap.h index 32bff92be..f754bcae9 100644 --- a/include/minix/dmap.h +++ b/include/minix/dmap.h @@ -18,7 +18,7 @@ #define PRINTER_MAJOR 6 /* 6 = /dev/lp (printer driver) */ #define INET_MAJOR 7 /* 7 = /dev/ip (inet) */ /* 8 = /dev/c1 */ - /* 9 = not used */ +#define PTY_MAJOR 9 /* 9 = /dev/ptyp0 (pty driver) */ /* 10 = /dev/c2 */ #define FILTER_MAJOR 11 /* 11 = /dev/filter (filter driver) */ /* 12 = /dev/c3 */