📄 tty.c
字号:
/* This file contains the terminal driver, both for the IBM console and regular
* ASCII terminals. It handles only the device-independent part of a TTY, the
* device dependent parts are in console.c, rs232.c, etc. This file contains
* two main entry points, tty_task() and tty_wakeup(), and several minor entry
* points for use by the device-dependent code.
*
* The device-independent part accepts "keyboard" input from the device-
* dependent part, performs input processing (special key interpretation),
* and sends the input to a process reading from the TTY. Output to a TTY
* is sent to the device-dependent code for output processing and "screen"
* display. Input processing is done by the device by calling 'in_process'
* on the input characters, output processing may be done by the device itself
* or by calling 'out_process'. The TTY takes care of input queuing, the
* device does the output queuing. If a device receives an external signal,
* like an interrupt, then it causes tty_wakeup() to be run by the CLOCK task
* to, you guessed it, wake up the TTY to check if input or output can
* continue.
*
* The valid messages and their parameters are:
*
* HARD_INT: output has been completed or input has arrived
* DEV_READ: a process wants to read from a terminal
* DEV_WRITE: a process wants to write on a terminal
* DEV_IOCTL: a process wants to change a terminal's parameters
* DEV_OPEN: a tty line has been opened
* DEV_CLOSE: a tty line has been closed
* CANCEL: terminate a previous incomplete system call immediately
*
* m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS
* ---------------------------------------------------------------------------
* | HARD_INT | | | | | | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | DEV_READ |minor dev| proc nr | count | O_NONBLOCK| buf ptr |
* |-------------+---------+---------+---------+---------+---------+---------|
* | DEV_WRITE |minor dev| proc nr | count | | | buf ptr |
* |-------------+---------+---------+---------+---------+---------+---------|
* | DEV_IOCTL |minor dev| proc nr |func code|erase etc| flags | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | DEV_OPEN |minor dev| proc nr | O_NOCTTY| | | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | DEV_CLOSE |minor dev| proc nr | | | | |
* |-------------+---------+---------+---------+---------+---------+---------|
* | CANCEL |minor dev| proc nr | | | | |
* ---------------------------------------------------------------------------
*/
#include "kernel.h"
#include <termios.h>
#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT
#include <sgtty.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#include <minix/callnr.h>
#include <minix/com.h>
#if (CHIP == INTEL)
#include <minix/keymap.h>
#endif
#include "tty.h"
#include "proc.h"
/* Address of a tty structure. */
#define tty_addr(line) (&tty_table[line])
/* First minor numbers for the various classes of TTY devices. */
#define CONS_MINOR 0
#define LOG_MINOR 15
#define RS232_MINOR 16
#define TTYPX_MINOR 128
#define PTYPX_MINOR 192
/* Macros for magic tty types. */
#define isconsole(tp) ((tp) < tty_addr(NR_CONS))
/* 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)
/* RS232 lines or pseudo terminals can be completely configured out. */
#if NR_RS_LINES == 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
FORWARD _PROTOTYPE( void do_cancel, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void do_ioctl, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void do_open, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void do_close, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void do_read, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void do_write, (tty_t *tp, message *m_ptr) );
FORWARD _PROTOTYPE( void in_transfer, (tty_t *tp) );
FORWARD _PROTOTYPE( int echo, (tty_t *tp, int ch) );
FORWARD _PROTOTYPE( void rawecho, (tty_t *tp, int ch) );
FORWARD _PROTOTYPE( int back_over, (tty_t *tp) );
FORWARD _PROTOTYPE( void reprint, (tty_t *tp) );
FORWARD _PROTOTYPE( void dev_ioctl, (tty_t *tp) );
FORWARD _PROTOTYPE( void setattr, (tty_t *tp) );
FORWARD _PROTOTYPE( void tty_icancel, (tty_t *tp) );
FORWARD _PROTOTYPE( void tty_init, (tty_t *tp) );
FORWARD _PROTOTYPE( void settimer, (tty_t *tp, int on) );
#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT
FORWARD _PROTOTYPE( int compat_getp, (tty_t *tp, struct sgttyb *sg) );
FORWARD _PROTOTYPE( int compat_getc, (tty_t *tp, struct tchars *sg) );
FORWARD _PROTOTYPE( int compat_setp, (tty_t *tp, struct sgttyb *sg) );
FORWARD _PROTOTYPE( int compat_setc, (tty_t *tp, struct tchars *sg) );
FORWARD _PROTOTYPE( int tspd2sgspd, (speed_t tspd) );
FORWARD _PROTOTYPE( speed_t sgspd2tspd, (int sgspd) );
#if ENABLE_BINCOMPAT
FORWARD _PROTOTYPE( void do_ioctl_compat, (tty_t *tp, message *m_ptr) );
#endif
#endif
/* Default attributes. */
PRIVATE struct termios termios_defaults = {
TINPUT_DEF, TOUTPUT_DEF, TCTRL_DEF, TLOCAL_DEF, TSPEED_DEF, TSPEED_DEF,
{
TEOF_DEF, TEOL_DEF, TERASE_DEF, TINTR_DEF, TKILL_DEF, TMIN_DEF,
TQUIT_DEF, TTIME_DEF, TSUSP_DEF, TSTART_DEF, TSTOP_DEF,
TREPRINT_DEF, TLNEXT_DEF, TDISCARD_DEF,
},
};
PRIVATE struct winsize winsize_defaults; /* = all zeroes */
/*===========================================================================*
* tty_task *
*===========================================================================*/
PUBLIC void tty_task()
{
/* Main routine of the terminal task. */
message tty_mess; /* buffer for all incoming messages */
register tty_t *tp;
unsigned line;
/* Initialize the terminal lines. */
for (tp = FIRST_TTY; tp < END_TTY; tp++) tty_init(tp);
/* Display the Minix startup banner. */
printf("Minix %s.%s Copyright 1997 Prentice-Hall, Inc.\n\n",
OS_RELEASE, OS_VERSION);
#if (CHIP == INTEL)
/* Real mode, or 16/32-bit protected mode? */
#if _WORD_SIZE == 4
printf("Executing in 32-bit protected mode\n\n");
#else
printf("Executing in %s mode\n\n",
protected_mode ? "16-bit protected" : "real");
#endif
#endif
while (TRUE) {
/* Handle any events on any of the ttys. */
for (tp = FIRST_TTY; tp < END_TTY; tp++) {
if (tp->tty_events) handle_events(tp);
}
receive(ANY, &tty_mess);
/* A hardware interrupt is an invitation to check for events. */
if (tty_mess.m_type == HARD_INT) continue;
/* Check the minor device number. */
line = tty_mess.TTY_LINE;
if ((line - CONS_MINOR) < NR_CONS) {
tp = tty_addr(line - CONS_MINOR);
} else
if (line == LOG_MINOR) {
tp = tty_addr(0);
} 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);
do_pty(tp, &tty_mess);
continue; /* this is a pty, not a tty */
} else {
tp = NULL;
}
/* If the device doesn't exist or is not configured return ENXIO. */
if (tp == NULL || !tty_active(tp)) {
tty_reply(TASK_REPLY, tty_mess.m_source,
tty_mess.PROC_NR, ENXIO);
continue;
}
/* Execute the requested function. */
switch (tty_mess.m_type) {
case DEV_READ: do_read(tp, &tty_mess); break;
case DEV_WRITE: do_write(tp, &tty_mess); break;
case DEV_IOCTL: do_ioctl(tp, &tty_mess); break;
case DEV_OPEN: do_open(tp, &tty_mess); break;
case DEV_CLOSE: do_close(tp, &tty_mess); break;
case CANCEL: do_cancel(tp, &tty_mess); break;
default: tty_reply(TASK_REPLY, tty_mess.m_source,
tty_mess.PROC_NR, EINVAL);
}
}
}
/*===========================================================================*
* do_read *
*===========================================================================*/
PRIVATE void do_read(tp, m_ptr)
register tty_t *tp; /* pointer to tty struct */
message *m_ptr; /* pointer to message sent to the task */
{
/* A process wants to read from a terminal. */
int r;
/* Check if there is already a process hanging in a read, check if the
* parameters are correct, do I/O.
*/
if (tp->tty_inleft > 0) {
r = EIO;
} else
if (m_ptr->COUNT <= 0) {
r = EINVAL;
} else
if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) {
r = EFAULT;
} else {
/* Copy information from the message to the tty struct. */
tp->tty_inrepcode = TASK_REPLY;
tp->tty_incaller = m_ptr->m_source;
tp->tty_inproc = m_ptr->PROC_NR;
tp->tty_in_vir = (vir_bytes) m_ptr->ADDRESS;
tp->tty_inleft = m_ptr->COUNT;
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.
*/
lock();
settimer(tp, TRUE);
tp->tty_min = 1;
unlock();
} 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) {
lock();
settimer(tp, FALSE);
unlock();
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; /* already done */
/* There were no bytes in the input queue available, so either suspend
* the caller or break off the read if nonblocking.
*/
if (m_ptr->TTY_FLAGS & O_NONBLOCK) {
r = EAGAIN; /* cancel the read */
tp->tty_inleft = tp->tty_incum = 0;
} else {
r = SUSPEND; /* suspend the caller */
tp->tty_inrepcode = REVIVE;
}
}
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
}
/*===========================================================================*
* do_write *
*===========================================================================*/
PRIVATE void do_write(tp, m_ptr)
register tty_t *tp;
register message *m_ptr; /* pointer to message sent to the task */
{
/* A process wants to write on a terminal. */
int r;
/* Check if there is already a process hanging in a write, check if the
* parameters are correct, do I/O.
*/
if (tp->tty_outleft > 0) {
r = EIO;
} else
if (m_ptr->COUNT <= 0) {
r = EINVAL;
} else
if (numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) {
r = EFAULT;
} else {
/* Copy message parameters to the tty structure. */
tp->tty_outrepcode = TASK_REPLY;
tp->tty_outcaller = m_ptr->m_source;
tp->tty_outproc = m_ptr->PROC_NR;
tp->tty_out_vir = (vir_bytes) m_ptr->ADDRESS;
tp->tty_outleft = m_ptr->COUNT;
/* Try to write. */
handle_events(tp);
if (tp->tty_outleft == 0) return; /* already done */
/* None or not all the bytes could be written, so either suspend the
* caller or break off the write if nonblocking.
*/
if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* cancel the write */
r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
tp->tty_outleft = tp->tty_outcum = 0;
} else {
r = SUSPEND; /* suspend the caller */
tp->tty_outrepcode = REVIVE;
}
}
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r);
}
/*===========================================================================*
* do_ioctl *
*===========================================================================*/
PRIVATE void do_ioctl(tp, m_ptr)
register tty_t *tp;
message *m_ptr; /* pointer to message sent to task */
{
/* Perform an IOCTL on this terminal. Posix termios calls are handled
* by the IOCTL system call
*/
int r;
union {
int i;
#if ENABLE_SRCCOMPAT
struct sgttyb sg;
struct tchars tc;
#endif
} param;
phys_bytes user_phys;
size_t size;
/* Size of the ioctl parameter. */
switch (m_ptr->TTY_REQUEST) {
case TCGETS: /* Posix tcgetattr function */
case TCSETS: /* Posix tcsetattr function, TCSANOW option */
case TCSETSW: /* Posix tcsetattr function, TCSADRAIN option */
case TCSETSF: /* Posix tcsetattr function, TCSAFLUSH option */
size = sizeof(struct termios);
break;
case TCSBRK: /* Posix tcsendbreak function */
case TCFLOW: /* Posix tcflow function */
case TCFLSH: /* Posix tcflush function */
case TIOCGPGRP: /* Posix tcgetpgrp function */
case TIOCSPGRP: /* Posix tcsetpgrp function */
size = sizeof(int);
break;
case TIOCGWINSZ: /* get window size (not Posix) */
case TIOCSWINSZ: /* set window size (not Posix) */
size = sizeof(struct winsize);
break;
#if ENABLE_SRCCOMPAT
case TIOCGETP: /* BSD-style get terminal properties */
case TIOCSETP: /* BSD-style set terminal properties */
size = sizeof(struct sgttyb);
break;
case TIOCGETC: /* BSD-style get terminal special characters */
case TIOCSETC: /* BSD-style get terminal special characters */
size = sizeof(struct tchars);
break;
#endif
#if (MACHINE == IBM_PC)
case KIOCSMAP: /* load keymap (Minix extension) */
size = sizeof(keymap_t);
break;
case TIOCSFON: /* load font (Minix extension) */
size = sizeof(u8_t [8192]);
break;
#endif
case TCDRAIN: /* Posix tcdrain function -- no parameter */
default: size = 0;
}
if (size != 0) {
user_phys = numap(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, size);
if (user_phys == 0) {
tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EFAULT);
return;
}
}
r = OK;
switch (m_ptr->TTY_REQUEST) {
case TCGETS:
/* Get the termios attributes. */
phys_copy(vir2phys(&tp->tty_termios), user_phys, (phys_bytes) size);
break;
case TCSETSW:
case TCSETSF:
case TCDRAIN:
if (tp->tty_outleft > 0) {
/* Wait for all ongoing output processing to finish. */
tp->tty_iocaller = m_ptr->m_source;
tp->tty_ioproc = m_ptr->PROC_NR;
tp->tty_ioreq = m_ptr->REQUEST;
tp->tty_iovir = (vir_bytes) m_ptr->ADDRESS;
r = SUSPEND;
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -