📄 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 * SYS_SIG: e.g., MINIX wants to shutdown; run code to cleanly stop * 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 * DEV_SELECT: start select notification request * DEV_STATUS: FS wants to know status for SELECT or REVIVE * CANCEL: terminate a previous incomplete system call immediately * * m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS * --------------------------------------------------------------------------- * | HARD_INT | | | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | SYS_SIG | sig set | | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | 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 | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | DEV_SELECT | | | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | DEV_STATUS | | | | | | | * |-------------+---------+---------+---------+---------+---------+---------| * | CANCEL |minor dev| proc nr | | | | | * --------------------------------------------------------------------------- * * Changes: * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder) * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder) * Jul 13, 2004 support for function key observers (Jorrit N. Herder) */#include "../drivers.h"#include <termios.h>#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT#include <sgtty.h>#endif#include <sys/ioc_tty.h>#include <signal.h>#include <minix/callnr.h>#if (CHIP == INTEL)#include <minix/keymap.h>#endif#include "tty.h"#include <sys/time.h>#include <sys/select.h>extern int irq_hook_id;unsigned long kbd_irq_set = 0;unsigned long rs_irq_set = 0;/* Address of a tty structure. */#define tty_addr(line) (&tty_table[line])/* Macros for magic tty types. */#define isconsole(tp) ((tp) < tty_addr(NR_CONS))#define ispty(tp) ((tp) >= tty_addr(NR_CONS+NR_RS_LINES))/* 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)#endifstruct kmessages kmess;FORWARD _PROTOTYPE( void tty_timed_out, (timer_t *tp) );FORWARD _PROTOTYPE( void expire_timers, (void) );FORWARD _PROTOTYPE( void settimer, (tty_t *tty_ptr, int enable) );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 do_select, (tty_t *tp, message *m_ptr) );FORWARD _PROTOTYPE( void do_status, (message *m_ptr) );FORWARD _PROTOTYPE( void in_transfer, (tty_t *tp) );FORWARD _PROTOTYPE( int tty_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, (void) );#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPATFORWARD _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_BINCOMPATFORWARD _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 *//* Global variables for the TTY task (declared extern in tty.h). */PUBLIC tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS];PUBLIC int ccurrent; /* currently active console */PUBLIC timer_t *tty_timers; /* queue of TTY timers */PUBLIC clock_t tty_next_timeout; /* time that the next alarm is due */PUBLIC struct machine machine; /* kernel environment variables *//*===========================================================================* * tty_task * *===========================================================================*/PUBLIC void main(void){/* Main routine of the terminal task. */ message tty_mess; /* buffer for all incoming messages */ unsigned line; int r, s; register struct proc *rp; register tty_t *tp; /* Initialize the TTY driver. */ tty_init(); /* Get kernel environment (protected_mode, pc_at and ega are needed). */ if (OK != (s=sys_getmachine(&machine))) { panic("TTY","Couldn't obtain kernel environment.", s); } /* Final one-time keyboard initialization. */ kb_init_once(); printf("\n"); 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= receive(ANY, &tty_mess); if (r != 0) panic("TTY", "receive failed with %d", r); /* First handle all kernel notification types that the TTY supports. * - An alarm went off, expire all timers and handle the events. * - A hardware interrupt also is an invitation to check for events. * - A new kernel message is available for printing. * - Reset the console on system shutdown. * Then see if this message is different from a normal device driver * request and should be handled separately. These extra functions * do not operate on a device, in constrast to the driver requests. */ switch (tty_mess.m_type) { case SYN_ALARM: /* fall through */ expire_timers(); /* run watchdogs of expired timers */ continue; /* contine to check for events */ case DEV_PING: notify(tty_mess.m_source); continue; case HARD_INT: { /* hardware interrupt notification */ if (tty_mess.NOTIFY_ARG & kbd_irq_set) kbd_interrupt(&tty_mess);/* fetch chars from keyboard */#if NR_RS_LINES > 0 if (tty_mess.NOTIFY_ARG & rs_irq_set) rs_interrupt(&tty_mess);/* serial I/O */#endif expire_timers(); /* run watchdogs of expired timers */ continue; /* contine to check for events */ } case SYS_SIG: { /* system signal */ sigset_t sigset = (sigset_t) tty_mess.NOTIFY_ARG; if (sigismember(&sigset, SIGKSTOP)) { cons_stop(); /* switch to primary console */ if (irq_hook_id != -1) { sys_irqdisable(&irq_hook_id); sys_irqrmpolicy(KEYBOARD_IRQ, &irq_hook_id); } } if (sigismember(&sigset, SIGTERM)) cons_stop(); if (sigismember(&sigset, SIGKMESS)) do_new_kmess(&tty_mess); continue; } case PANIC_DUMPS: /* allow panic dumps */ cons_stop(); /* switch to primary console */ do_panic_dumps(&tty_mess); continue; case DIAGNOSTICS: /* a server wants to print some */ do_diagnostics(&tty_mess); continue; case GET_KMESS: do_get_kmess(&tty_mess); continue; case FKEY_CONTROL: /* (un)register a fkey observer */ do_fkey_ctl(&tty_mess); continue; default: /* should be a driver request */ ; /* do nothing; end switch */ } /* Only device requests should get to this point. All requests, * except DEV_STATUS, have a minor device number. Check this * exception and get the minor device number otherwise. */ if (tty_mess.m_type == DEV_STATUS) { do_status(&tty_mess); continue; } 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); if (tty_mess.m_type != DEV_IOCTL) { do_pty(tp, &tty_mess); continue; } } else { tp = NULL; } /* If the device doesn't exist or is not configured return ENXIO. */ if (tp == NULL || ! tty_active(tp)) { printf("Warning, TTY got illegal request %d from %d\n", tty_mess.m_type, tty_mess.m_source); if (tty_mess.m_source != LOG_PROC_NR) { tty_reply(TASK_REPLY, tty_mess.m_source, tty_mess.PROC_NR, ENXIO); } continue; } /* Execute the requested device driver 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 DEV_SELECT: do_select(tp, &tty_mess); break; case CANCEL: do_cancel(tp, &tty_mess); break; default: printf("Warning, TTY got unexpected request %d from %d\n", tty_mess.m_type, tty_mess.m_source); tty_reply(TASK_REPLY, tty_mess.m_source, tty_mess.PROC_NR, EINVAL); } }}/*===========================================================================* * do_status * *===========================================================================*/PRIVATE void do_status(m_ptr)message *m_ptr;{ register struct tty *tp; int event_found; int status; int ops; /* Check for select or revive events on any of the ttys. If we found an, * event return a single status message for it. The FS will make another * call to see if there is more. */ event_found = 0; for (tp = FIRST_TTY; tp < END_TTY; tp++) { if ((ops = select_try(tp, tp->tty_select_ops)) && tp->tty_select_proc == m_ptr->m_source) { /* I/O for a selected minor device is ready. */ m_ptr->m_type = DEV_IO_READY; m_ptr->DEV_MINOR = tp->tty_minor; m_ptr->DEV_SEL_OPS = ops; tp->tty_select_ops &= ~ops; /* unmark select event */ event_found = 1; break; } else if (tp->tty_inrevived && tp->tty_incaller == m_ptr->m_source) { /* Suspended request finished. Send a REVIVE. */ m_ptr->m_type = DEV_REVIVE; m_ptr->REP_PROC_NR = tp->tty_inproc; m_ptr->REP_STATUS = tp->tty_incum; tp->tty_inleft = tp->tty_incum = 0; tp->tty_inrevived = 0; /* unmark revive event */ event_found = 1; break; } else if (tp->tty_outrevived && tp->tty_outcaller == m_ptr->m_source) { /* Suspended request finished. Send a REVIVE. */ m_ptr->m_type = DEV_REVIVE; m_ptr->REP_PROC_NR = tp->tty_outproc; m_ptr->REP_STATUS = tp->tty_outcum; tp->tty_outcum = 0; tp->tty_outrevived = 0; /* unmark revive event */ event_found = 1; break; } }#if NR_PTYS > 0 if (!event_found) event_found = pty_status(m_ptr);#endif if (! event_found) { /* No events of interest were found. Return an empty message. */ m_ptr->m_type = DEV_NO_STATUS; } /* Almost done. Send back the reply message to the caller. */ if ((status = send(m_ptr->m_source, m_ptr)) != OK) { panic("TTY","send in do_status failed, status\n", status); }}/*===========================================================================* * do_read * *===========================================================================*/PRIVATE void do_read(tp, m_ptr)register tty_t *tp; /* pointer to tty struct */register message *m_ptr; /* pointer to message sent to the task */{/* A process wants to read from a terminal. */ int r, status; phys_bytes phys_addr; /* 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 (sys_umap(m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT, &phys_addr) != OK) { 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. */ 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) { if (tp->tty_select_ops) select_retry(tp); 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); if (tp->tty_select_ops) select_retry(tp);}/*===========================================================================* * 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; phys_bytes phys_addr; /* 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 (sys_umap(m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT, &phys_addr) != OK) { 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -