ast@cs.vu.nl (Andy Tanenbaum) (07/14/88)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'tty.c.part1' sed 's/^X//' > 'tty.c.part1' << '+ END-OF-FILE ''tty.c.part1' X/* This file contains the terminal driver, both for the IBM console and regular X * ASCII terminals. It is split into two sections, a device-independent part X * and a device-dependent part. The device-independent part accepts X * characters to be printed from programs and queues them in a standard way X * for device-dependent output. It also accepts input and queues it for X * programs. This file contains 2 main entry points: tty_task() and keyboard(). X * When a key is struck on a terminal, an interrupt to an assembly language X * routine is generated. This routine saves the machine state and registers X * and calls keyboard(), which enters the character in an internal table, and X * then sends a message to the terminal task. The main program of the terminal X * task is tty_task(). It accepts not only messages about typed input, but X * also requests to read and write from terminals, etc. X * X * The device-dependent part interfaces with the IBM console and ASCII X * terminals. The IBM keyboard is unusual in that keystrokes yield key numbers X * rather than ASCII codes, and furthermore, an interrupt is generated when a X * key is depressed and again when it is released. The IBM display is memory X * mapped, so outputting characters such as line feed, backspace and bell are X * tricky. X * X * The valid messages and their parameters are: X * X * TTY_CHAR_INT: a character has been typed (character arrived interrupt) X * TTY_READ: a process wants to read from a terminal X * TTY_WRITE: a process wants to write on a terminal X * TTY_IOCTL: a process wants to change a terminal's parameters X * TTY_SETPGRP: indicate a change in a control terminal X * CANCEL: terminate a previous incomplete system call immediately X * X * m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS X * --------------------------------------------------------------------------- X * | TTY_CHAR_INT| | | | | |array ptr| X * |-------------+---------+---------+---------+---------+---------+---------| X * | TTY_O_DONE |minor dev| | | | |array ptr| X * |-------------+---------+---------+---------+---------+---------+---------| X * | TTY_READ |minor dev| proc nr | count | | | buf ptr | X * |-------------+---------+---------+---------+---------+---------+---------| X * | TTY_WRITE |minor dev| proc nr | count | | | buf ptr | X * |-------------+---------+---------+---------+---------+---------+---------| X * | TTY_IOCTL |minor dev| proc nr |func code|erase etc| flags | | X * |-------------+---------+---------+---------+---------+---------+---------| X * | TTY_SETPGRP |minor dev| proc nr | | | | | X * |-------------+---------+---------+---------+---------+---------+--------- X * | CANCEL |minor dev| proc nr | | | | | X * --------------------------------------------------------------------------- X */ X X#include "../h/const.h" X#include "../h/type.h" X#include "../h/callnr.h" X#include "../h/com.h" X#include "../h/error.h" X#include "../h/sgtty.h" X#include "../h/signal.h" X#include "const.h" X#include "type.h" X#include "glo.h" X#include "proc.h" X X#define NR_CONS 1 /* how many consoles can system handle */ X#define NR_RS_LINES 1 /* how many rs232 terminals can system handle*/ X#define TTY_IN_BYTES 200 /* input queue size */ X#define TTY_RAM_WORDS 320 /* ram buffer size */ X#define TTY_BUF_SIZE 256 /* unit for copying to/from queues */ X#define TAB_SIZE 8 /* distance between tabs */ X#define TAB_MASK 07 /* mask for tty_column when tabbing */ X#define MAX_OVERRUN 100 /* size of overrun input buffer */ X#define MAX_ESC_PARMS 2 /* number of escape sequence params allowed */ X X#define ERASE_CHAR '\b' /* default erase character */ X#define KILL_CHAR '@' /* default kill character */ X#define INTR_CHAR (char)0177 /* default interrupt character */ X#define QUIT_CHAR (char) 034 /* default quit character */ X#define XOFF_CHAR (char) 023 /* default x-off character (CTRL-S) */ X#define XON_CHAR (char) 021 /* default x-on character (CTRL-Q) */ X#define EOT_CHAR (char) 004 /* CTRL-D */ X#define MARKER (char) 000 /* non-escaped CTRL-D stored as MARKER */ X#define AT_SIGN 0220 /* code to yield for CTRL-@ */ X#define SCODE1 71 /* scan code for Home on numeric pad */ X#define SCODE2 81 /* scan code for PgDn on numeric pad */ X#define DEL_CODE (char) 83 /* DEL for use in CTRL-ALT-DEL reboot */ X#define ESC (char) 033 /* escape */ X#define BRACKET '[' /* Part of the ESC [ letter escape seq */ X X#define F1 59 /* scan code for function key F1 */ X#define F2 60 /* scan code for function key F2 */ X#define F3 61 /* scan code for function key F3 */ X#define F4 62 /* scan code for function key F4 */ X#define F5 63 /* scan code for function key F5 */ X#define F9 67 /* scan code for function key F9 */ X#define F10 68 /* scan code for function key F10 */ X#define TOP_ROW 14 /* codes below this are shifted if CTRL */ X XPRIVATE struct tty_struct { X /* Input queue. Typed characters are stored here until read by a program. */ X char tty_inqueue[TTY_IN_BYTES]; /* array used to store the characters */ X char *tty_inhead; /* pointer to place where next char goes */ X char *tty_intail; /* pointer to next char to be given to prog */ X int tty_incount; /* # chars in tty_inqueue */ X int tty_lfct; /* # line feeds in tty_inqueue */ X X /* Output section. */ X int tty_ramqueue[TTY_RAM_WORDS]; /* buffer for video RAM */ X int tty_rwords; /* number of WORDS (not bytes) in outqueue */ X int tty_org; /* location in RAM where 6845 base points */ X int tty_vid; /* current position of cursor in video RAM */ X char tty_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */ X char tty_esc_intro; /* Distinguishing character following ESC */ X int tty_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */ X int *tty_esc_parmp; /* pointer to current escape parameter */ X int tty_attribute; /* current attribute byte << 8 */ X int (*tty_devstart)(); /* routine to start actual device output */ X X /* Terminal parameters and status. */ X int tty_mode; /* terminal mode set by IOCTL */ X int tty_speed; /* low byte is ispeed; high byte is ospeed */ X int tty_column; /* current column number (0-origin) */ X int tty_row; /* current row (0 at top of screen) */ X char tty_busy; /* 1 when output in progress, else 0 */ X char tty_escaped; /* 1 when '\' just seen, else 0 */ X char tty_inhibited; /* 1 when CTRL-S just seen (stops output) */ X char tty_makebreak; /* 1 for terminals that interrupt twice/key */ X char tty_waiting; /* 1 when output process waiting for reply */ X X /* User settable characters: erase, kill, interrupt, quit, x-on; x-off. */ X char tty_erase; /* char used to erase 1 char (init ^H) */ X char tty_kill; /* char used to erase a line (init @) */ X char tty_intr; /* char used to send SIGINT (init DEL) */ X char tty_quit; /* char used for core dump (init CTRL-\) */ X char tty_xon; /* char used to start output (init CTRL-Q)*/ X char tty_xoff; /* char used to stop output (init CTRL-S) */ X char tty_eof; /* char used to stop output (init CTRL-D) */ X X /* Information about incomplete I/O requests is stored here. */ X char tty_incaller; /* process that made the call (usually FS) */ X char tty_inproc; /* process that wants to read from tty */ X char *tty_in_vir; /* virtual address where data is to go */ X int tty_inleft; /* how many chars are still needed */ X char tty_otcaller; /* process that made the call (usually FS) */ X char tty_outproc; /* process that wants to write to tty */ X char *tty_out_vir; /* virtual address where data comes from */ X phys_bytes tty_phys; /* physical address where data comes from */ X int tty_outleft; /* # chars yet to be copied to tty_outqueue */ X int tty_cum; /* # chars copied to tty_outqueue so far */ X int tty_pgrp; /* slot number of controlling process */ X X /* Miscellaneous. */ X int tty_ioport; /* I/O port number for this terminal */ X} tty_struct[NR_CONS+NR_RS_LINES]; X X/* Values for the fields. */ X#define NOT_ESCAPED 0 /* previous character on this line not '\' */ X#define ESCAPED 1 /* previous character on this line was '\' */ X#define RUNNING 0 /* no CRTL-S has been typed to stop the tty */ X#define STOPPED 1 /* CTRL-S has been typed to stop the tty */ X#define INACTIVE 0 /* the tty is not printing */ X#define BUSY 1 /* the tty is printing */ X#define ONE_INT 0 /* regular terminals interrupt once per char */ X#define TWO_INTS 1 /* IBM console interrupts two times per char */ X#define NOT_WAITING 0 /* no output process is hanging */ X#define WAITING 1 /* an output process is waiting for a reply */ X#define COMPLETED 2 /* output done; send a completion message */ X XPRIVATE char tty_driver_buf[2*MAX_OVERRUN+2]; /* driver collects chars here */ XPRIVATE char tty_copy_buf[2*MAX_OVERRUN]; /* copy buf used to avoid races */ XPRIVATE char tty_buf[TTY_BUF_SIZE]; /* scratch buffer to/from user space */ XPRIVATE int shift1, shift2, capslock, numlock; /* keep track of shift keys */ XPRIVATE int control, alt; /* keep track of key statii */ XPRIVATE int caps_off = 1; /* 1 = normal position, 0 = depressed */ XPRIVATE int num_off = 1; /* 1 = normal position, 0 = depressed */ XPRIVATE int softscroll = 0; /* 1 = software scrolling, 0 = hardware */ XPRIVATE int output_done; /* number of RS232 output messages to be sent*/ XPUBLIC int color; /* 1 if console is color, 0 if it is mono */ XPUBLIC int scan_code; /* scan code for '=' saved by bootstrap */ X X X/* Scan codes to ASCII for unshifted keys */ XPRIVATE char unsh[] = { X 0,033,'1','2','3','4','5','6', '7','8','9','0','-','=','\b','\t', X 'q','w','e','r','t','y','u','i', 'o','p','[',']',015,0202,'a','s', X 'd','f','g','h','j','k','l',';', 047,0140,0200,0134,'z','x','c','v', X 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,0205,0210,0267, 0270,0271,0211,0264,0265,0266,0214 X,0261,0262,0263,'0',0177 X}; X X/* Scan codes to ASCII for shifted keys */ XPRIVATE char sh[] = { X 0,033,'!','@','#','$','%','^', '&','*','(',')','_','+','\b','\t', X 'Q','W','E','R','T','Y','U','I', 'O','P','{','}',015,0202,'A','S', X 'D','F','G','H','J','K','L',':', 042,'~',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0204,0213,'7', '8','9',0211,'4','5','6',0214,'1', X '2','3','0','.' X}; X X X/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */ XPRIVATE char unm24[] = { X 0,033,'1','2','3','4','5','6', '7','8','9','0','-','^','\b','\t', X 'q','w','e','r','t','y','u','i', 'o','p','@','[','\r',0202,'a','s', X 'd','f','g','h','j','k','l',';', ':',']',0200,'\\','z','x','c','v', X 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261, X0262,0263,'0','.',' ',014,0212,'\r', 0264,0262,0266,0270,032,0213,' ','/', X 0253,0254,0255,0256,0257,0215,0216,0217 X}; X X/* Scan codes to ASCII for Olivetti M24 for shifted keys. */ XPRIVATE char m24[] = { X 0,033,'!','"','#','$','%','&', 047,'(',')','_','=','~','\b','\t', X 'Q','W','E','R' ,'T','Y','U','I', 'O','P',0140,'{','\r',0202,'A','S', X 'D','F','G','H','J','K','L','+', '*','}',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1', X '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/', X 0233,0234,0235,0236,0237,0275,0276,0277 X}; X Xchar scode_map[] = {'H', 'A', 'V', 'S', 'D', 'G', 'C', 'T', 'Y', 'B', 'U'}; X X/*===========================================================================* X * tty_task * X *===========================================================================*/ XPUBLIC tty_task() X{ X/* Main routine of the terminal task. */ X X message tty_mess; /* buffer for all incoming messages */ X register struct tty_struct *tp; X X tty_init(); /* initialize */ X init_rs232(); X X while (TRUE) { X receive(ANY, &tty_mess); X tp = &tty_struct[tty_mess.TTY_LINE]; X switch(tty_mess.m_type) { X case TTY_O_DONE: /* same action as TTY_CHAR_INT */ X case TTY_CHAR_INT: do_int(&tty_mess); break; X case TTY_READ: do_read(tp, &tty_mess); break; X case TTY_WRITE: do_write(tp, &tty_mess); break; X case TTY_IOCTL: do_ioctl(tp, &tty_mess); break; X case TTY_SETPGRP: do_setpgrp(tp, &tty_mess); break; X case CANCEL: do_cancel(tp, &tty_mess); break; X default: tty_reply(TASK_REPLY, tty_mess.m_source, X tty_mess.PROC_NR, EINVAL, 0L, 0L); X } X } X} X X X/*===========================================================================* X * do_int * X *===========================================================================*/ XPRIVATE do_int(m_ptr) Xmessage *m_ptr; X{ X/* The TTY task can generate two kinds of interrupts: X * - a character has been typed on the console or an RS232 line X * - an RS232 line has completed a write request (on behalf of a user) X * If either interrupt happens and the TTY task is idle, the task gets the X * interrupt message immediately and processes it. However, if the TTY X * task is busy, a bit is set in 'busy_map' and the message pointer stored. X * If multiple messages happen, the bit is only set once. No input data is X * lost even if this happens because all the input messages say is that there X * is some input. The actual input is in the tty_driver_buf array, so losing X * a message just means that when the one interrupt-generated message is given X * to the TTY task, it will find multiple characters in tty_driver_buf. X * X * The introduction of RS232 lines has complicated this situation somewhat. Now X * a message can mean that some RS232 line has finished transmitting all the X * output given to it. If a character is typed at the instant an RS232 line X * finishes, one of the two messages may be overwritten because MINIX only X * provides single buffering for interrupt messages (in proc.c).To avoid losing X * information, whenever an RS232 line finishes, the flag tty_waiting is set X * to COMPLETED and kept that way until its completion is processed and a X * message sent to FS saying that output is done. The number of RS232 lines in X * COMPLETED state is kept in output_done, which is checked on each interrupt, X * so that a lost TTY_O_DONE line completion interrupt will be quickly X * recovered. X * X * In short, when this procedure is called, it can check for RS232 line done X * by inspecting output_done and it can check for characters in the input X * buffer by inspecting tty_driver_buf[0]. Thus losing a message to the TTY X * task is not serious because the underlying conditions are explicitly checked X * for on each interrupt. X */ X X /* First check to see if any RS232 lines have completed. */ X if (output_done > 0) { X /* If a message is sent to FS for RS232 done, don't process any input X * characters now for fear of sending a second message to FS, which X * would be lost. X */ X if (tty_o_done()) { X return; X } X } X charint(m_ptr); /* check for input characters */ X} X X X/*===========================================================================* X * charint * X *===========================================================================*/ XPRIVATE charint(m_ptr) Xmessage *m_ptr; /* message containing pointer to char(s) */ X{ X/* A character has been typed. If a character is typed and the tty task is X * not able to service it immediately, the character is accumulated within X * the tty driver. Thus multiple chars may be accumulated. A single message X * to the tty task may have to process several characters. X */ X X int m, n, count, replyee, caller, old_state; X char *ptr, *copy_ptr, ch; X struct tty_struct *tp; X X old_state = lock(); X ptr = m_ptr->ADDRESS; /* pointer to accumulated char array */ X copy_ptr = tty_copy_buf; /* ptr to shadow array where chars copied */ X n = *ptr; /* how many chars have been accumulated */ X count = n; /* save the character count */ X n = n + n; /* each char occupies 2 bytes */ X ptr += 2; /* skip count field at start of array */ X while (n-- > 0) X *copy_ptr++ = *ptr++; /* copy the array to safety */ X ptr = m_ptr->ADDRESS; X *ptr = 0; /* accumulation count set to 0 */ X restore(old_state); X X /* Loop on the accumulated characters, processing each in turn. */ X if (count == 0) return; /* on TTY_O_DONE interrupt, count might be 0 */ X copy_ptr = tty_copy_buf; X while (count-- > 0) { X ch = *copy_ptr++; /* get the character typed */ X n = *copy_ptr++; /* get the line number it came in on */ X in_char(n, ch); /* queue the char and echo it */ X X /* See if a previously blocked reader can now be satisfied. */ X tp = &tty_struct[n]; /* pointer to struct for this character */ X if (tp->tty_inleft > 0 ) { /* does anybody want input? */ X m = tp->tty_mode & (CBREAK | RAW); X if (tp->tty_lfct > 0 || (m != 0 && tp->tty_incount > 0)) { X m = rd_chars(tp); X X /* Tell hanging reader that chars have arrived. */ X replyee = (int) tp->tty_incaller; X caller = (int) tp->tty_inproc; X tty_reply(REVIVE, replyee, caller, m, 0L, 0L); X } X } X } X} X X X/*===========================================================================* X * in_char * X *===========================================================================*/ XPRIVATE in_char(line, ch) Xint line; /* line number on which char arrived */ Xchar ch; /* scan code for character that arrived */ X{ X/* A character has just been typed in. Process, save, and echo it. */ X X register struct tty_struct *tp; X int mode, sig, scode; X char make_break(); X X scode = ch; /* save the scan code */ X tp = &tty_struct[line]; /* set 'tp' to point to proper struct */ X X /* Function keys are temporarily being used for debug dumps. */ X if (line == 0 && ch >= F1 && ch <= F10) { /* Check for function keys */ X func_key(ch); /* process function key */ X return; X } X if (tp->tty_incount >= TTY_IN_BYTES) return; /* no room, discard char */ X mode = tp->tty_mode & (RAW | CBREAK); X if (tp->tty_makebreak == TWO_INTS) X ch = make_break(ch); /* console give 2 ints/ch */ X else X if (mode != RAW) ch &= 0177; /* 7-bit chars except in raw mode */ X if (ch == 0) return; X X /* Processing for COOKED and CBREAK mode contains special checks. */ X if (mode == COOKED || mode == CBREAK) { X /* Handle erase, kill and escape processing. */ X if (mode == COOKED) { X /* First erase processing (rub out of last character). */ X if (ch == tp->tty_erase && tp->tty_escaped == NOT_ESCAPED) { X if (chuck(tp) != -1) { /* remove last char entered */ X echo(tp, '\b'); /* remove it from the screen */ X echo(tp, ' '); X echo(tp, '\b'); X } X return; X } X X /* Now do kill processing (remove current line). */ X if (ch == tp->tty_kill && tp->tty_escaped == NOT_ESCAPED) { X while( chuck(tp) == OK) /* keep looping */ ; X echo(tp, tp->tty_kill); X echo (tp, '\n'); X return; X } X X /* Handle EOT and the escape symbol (backslash). */ X if (tp->tty_escaped == NOT_ESCAPED) { X /* Normal case: previous char was not backslash. */ X if (ch == '\\') { X /* An escaped symbol has just been typed. */ X tp->tty_escaped = ESCAPED; X echo(tp, ch); X return; /* do not store the '\' */ X } X /* CTRL-D means end-of-file, unless it is escaped. It X * is stored in the text as MARKER, and counts as a X * line feed in terms of knowing whether a full line X * has been typed already. X */ X if (ch == tp->tty_eof) ch = MARKER; X } else { X /* Previous character was backslash. */ X tp->tty_escaped = NOT_ESCAPED; /* turn escaping off */ X if (ch != tp->tty_erase && ch != tp->tty_kill && X ch != tp->tty_eof) { X /* Store the escape previously skipped over */ X *tp->tty_inhead++ = '\\'; X tp->tty_incount++; X if (tp->tty_inhead == X &tp->tty_inqueue[TTY_IN_BYTES]) X tp->tty_inhead = tp->tty_inqueue; X } X } X } X /* Both COOKED and CBREAK modes come here; first map CR to LF. */ X if (ch == '\r' && (tp->tty_mode & CRMOD)) ch = '\n'; X X /* Check for interrupt and quit characters. */ X if (ch == tp->tty_intr || ch == tp->tty_quit) { X sig = (ch == tp->tty_intr ? SIGINT : SIGQUIT); X sigchar(tp, sig); X return; X } X X /* Check for and process CTRL-S (terminal stop). */ X if (ch == tp->tty_xoff) { X tp->tty_inhibited = STOPPED; X return; X } X X /* Check for and process CTRL-Q (terminal start). */ X if (tp->tty_inhibited == STOPPED) { X tp->tty_inhibited = RUNNING; X (*tp->tty_devstart)(tp); /* resume output */ X return; X } X } X X /* All 3 modes come here. */ X if (ch == '\n' || ch == MARKER) tp->tty_lfct++; /* count line feeds */ X X /* The numeric pad generates ASCII escape sequences: ESC [ letter */ X if (line == 0 && scode >= SCODE1 && scode <= SCODE2 && X shift1 == 0 && shift2 == 0 && numlock == 0) { X /* This key is to generate a three-character escape sequence. */ X *tp->tty_inhead++ = ESC; /* put ESC in the input queue */ X if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES]) X tp->tty_inhead = tp->tty_inqueue; /* handle wraparound */ X tp->tty_incount++; X echo(tp, 'E'); X *tp->tty_inhead++ = BRACKET; /* put ESC in the input queue */ X if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES]) X tp->tty_inhead = tp->tty_inqueue; /* handle wraparound */ X tp->tty_incount++; X echo(tp, BRACKET); X ch = scode_map[scode-SCODE1]; /* generate the letter */ X } X X *tp->tty_inhead++ = ch; /* save the character in the input queue */ X if (tp->tty_inhead == &tp->tty_inqueue[TTY_IN_BYTES]) X tp->tty_inhead = tp->tty_inqueue; /* handle wraparound */ X tp->tty_incount++; X echo(tp, ch); X} X X X#ifdef i8088 X/*===========================================================================* X * make_break * X *===========================================================================*/ XPRIVATE char make_break(ch) Xchar ch; /* scan code of key just struck or released */ X{ X/* This routine can handle keyboards that interrupt only on key depression, X * as well as keyboards that interrupt on key depression and key release. X * For efficiency, the interrupt routine filters out most key releases. X */ X X int c, make, code; X X X c = ch & 0177; /* high-order bit set on key release */ X make = (ch & 0200 ? 0 : 1); /* 1 when key depressed, 0 when key released */ X if (olivetti == FALSE) { X /* Standard IBM keyboard. */ X code = (shift1 || shift2 ? sh[c] : unsh[c]); X if (control && c < TOP_ROW) code = sh[c]; /* CTRL-(top row) */ X if (c > 70 && numlock) /* numlock depressed */ X code = (shift1 || shift2 ? unsh[c] : sh[c]); X } else { X /* (Olivetti M24 or AT&T 6300) with Olivetti-style keyboard. */ X code = (shift1 || shift2 ? m24[c] : unm24[c]); X if (control && c < TOP_ROW) code = sh[c]; /* CTRL-(top row) */ X if (c > 70 && numlock) /* numlock depressed */ X code = (shift1 || shift2 ? unm24[c] : m24[c]); X } X code &= BYTE; X if (code < 0200 || code >= 0206) { X /* Ordinary key, i.e. not shift, control, alt, etc. */ X if (capslock) X if (code >= 'A' && code <= 'Z') X code += 'a' - 'A'; X else if (code >= 'a' && code <= 'z') X code -= 'a' - 'A'; X if (alt) code |= 0200; /* alt key ORs 0200 into code */ X if (control) code &= 037; X if (code == 0) code = AT_SIGN; /* @ is 0100, so CTRL-@ = 0 */ X if (make == 0) code = 0; /* key release */ X return(code); X } X X /* Table entries 0200 - 0206 denote special actions. */ X switch(code - 0200) { X case 0: shift1 = make; break; /* shift key on left */ X case 1: shift2 = make; break; /* shift key on right */ X case 2: control = make; break; /* control */ X case 3: alt = make; break; /* alt key */ X case 4: if (make && caps_off) { X capslock = 1 - capslock; X set_leds(); X } X caps_off = 1 - make; X break; /* caps lock */ X case 5: if (make && num_off) { X numlock = 1 - numlock; X set_leds(); X } X num_off = 1 - make; X break; /* num lock */ X } X return(0); X} X#endif X X X/*===========================================================================* X * echo * X *===========================================================================*/ XPRIVATE echo(tp, c) Xregister struct tty_struct *tp; /* terminal on which to echo */ Xregister char c; /* character to echo */ X{ X/* Echo a character on the terminal. */ X X if ( (tp->tty_mode & ECHO) == 0) return; /* if no echoing, don't echo */ X if (c != MARKER) { X if (tp - tty_struct < NR_CONS) X out_char(tp, c); /* echo to console */ X else X rs_out_char(tp, c); /* echo to RS232 line */ X } X flush(tp); /* force character out onto the screen */ X} X X X/*===========================================================================* X * chuck * X *===========================================================================*/ XPRIVATE int chuck(tp) Xregister struct tty_struct *tp; /* from which tty should chars be removed */ X{ X/* Delete one character from the input queue. Used for erase and kill. */ X X char *prev; X X /* If input queue is empty, don't delete anything. */ X if (tp->tty_incount == 0) return(-1); X X /* Don't delete '\n' or '\r'. */ X prev = (tp->tty_inhead != tp->tty_inqueue ? tp->tty_inhead - 1 : X &tp->tty_inqueue[TTY_IN_BYTES-1]); X if (*prev == '\n' || *prev == '\r') return(-1); X tp->tty_inhead = prev; X tp->tty_incount--; X return(OK); /* char erasure was possible */ X} X X X/*===========================================================================* X * do_read * X *===========================================================================*/ XPRIVATE do_read(tp, m_ptr) Xregister struct tty_struct *tp; /* pointer to tty struct */ Xmessage *m_ptr; /* pointer to message sent to the task */ X{ X/* A process wants to read from a terminal. */ X X int code, caller; X X X if (tp->tty_inleft > 0) { /* if someone else is hanging, give up */ X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO, 0L, 0L); X return; X } X X /* Copy information from the message to the tty struct. */ X tp->tty_incaller = m_ptr->m_source; X tp->tty_inproc = m_ptr->PROC_NR; X tp->tty_in_vir = m_ptr->ADDRESS; X tp->tty_inleft = m_ptr->COUNT; X X /* Try to get chars. This call either gets enough, or gets nothing. */ X code = rd_chars(tp); X X caller = (int) tp->tty_inproc; X tty_reply(TASK_REPLY, m_ptr->m_source, caller, code, 0L, 0L); X} X X X/*===========================================================================* X * rd_chars * X *===========================================================================*/ XPRIVATE int rd_chars(tp) Xregister struct tty_struct *tp; /* pointer to terminal to read from */ X{ X/* A process wants to read from a terminal. First check if enough data is X * available. If so, pass it to the user. If not, send FS a message telling X * it to suspend the user. When enough data arrives later, the tty driver X * copies it to the user space directly and notifies FS with a message. X */ X X int cooked, ct, user_ct, buf_ct, cum, enough, eot_seen; X vir_bytes in_vir, left; X phys_bytes user_phys, tty_phys; X char ch, *tty_ptr; X struct proc *rp; X extern phys_bytes umap(); X X cooked = ( (tp->tty_mode & (RAW | CBREAK)) ? 0 : 1); /* 1 iff COOKED mode */ X if (tp->tty_incount == 0 || (cooked && tp->tty_lfct == 0)) return(SUSPEND); X rp = proc_addr(tp->tty_inproc); X in_vir = (vir_bytes) tp-> tty_in_vir; X left = (vir_bytes) tp->tty_inleft; X if ( (user_phys = umap(rp, D, in_vir, left)) == 0) return(E_BAD_ADDR); X tty_phys = umap(proc_addr(TTY), D, (vir_bytes) tty_buf, TTY_BUF_SIZE); X cum = 0; X enough = 0; X eot_seen = 0; X X /* The outer loop iterates on buffers, one buffer load per iteration. */ X while (tp->tty_inleft > 0) { X buf_ct = MIN(tp->tty_inleft, tp->tty_incount); X buf_ct = MIN(buf_ct, TTY_BUF_SIZE); X ct = 0; X tty_ptr = tty_buf; X X /* The inner loop fills one buffer. */ X while(buf_ct-- > 0) { X ch = *tp->tty_intail++; X if (tp->tty_intail == &tp->tty_inqueue[TTY_IN_BYTES]) X tp->tty_intail = tp->tty_inqueue; X *tty_ptr++ = ch; X ct++; X if (ch == '\n' || ch == MARKER) { X tp->tty_lfct--; X if (cooked && ch == MARKER) eot_seen++; X enough++; /* exit loop */ X if (cooked) break; /* only provide 1 line */ X } X } X X /* Copy one buffer to user space. Be careful about CTRL-D. In cooked X * mode it is not transmitted to user programs, and is not counted as X * a character as far as the count goes, but it does occupy space in X * the driver's tables and must be counted there. X */ X user_ct = (eot_seen ? ct - 1 : ct); /* bytes to copy to user */ X phys_copy(tty_phys, user_phys, (phys_bytes) user_ct); X user_phys += user_ct; X cum += user_ct; X tp->tty_inleft -= ct; X tp->tty_incount -= ct; X if (tp->tty_incount == 0 || enough) break; X } X X tp->tty_inleft = 0; X return(cum); X} X X X/*===========================================================================* X * finish * X *===========================================================================*/ XPRIVATE finish(tp, code) Xregister struct tty_struct *tp; /* pointer to tty struct */ Xint code; /* reply code */ X{ X/* A command has terminated (possibly due to DEL). Tell caller. */ X X int line, result, replyee, caller; X X tp->tty_rwords = 0; X tp->tty_outleft = 0; X if (tp->tty_waiting == NOT_WAITING) return; X line = tp - tty_struct; X result = (line < NR_CONS ? TASK_REPLY : REVIVE); X replyee = (int) tp->tty_otcaller; X caller = (int) tp->tty_outproc; X tty_reply(result, replyee, caller, code, 0L, 0L); X tp->tty_waiting = NOT_WAITING; X} X X X/*===========================================================================* X * do_write * X *===========================================================================*/ XPRIVATE do_write(tp, m_ptr) Xregister struct tty_struct *tp; /* pointer to tty struct */ Xmessage *m_ptr; /* pointer to message sent to the task */ X{ X/* A process wants to write on a terminal. */ X X vir_bytes out_vir, out_left; X struct proc *rp; X extern phys_bytes umap(); X int caller,replyee; X X /* If the slot is already in use, better return an error than mess it up. */ X if (tp->tty_outleft > 0) { /* if someone else is hanging, give up */ X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EIO, 0L, 0L); X return; X } X X /* Copy message parameters to the tty structure. */ X tp->tty_otcaller = m_ptr->m_source; X tp->tty_outproc = m_ptr->PROC_NR; X tp->tty_out_vir = m_ptr->ADDRESS; X tp->tty_outleft = m_ptr->COUNT; X tp->tty_waiting = WAITING; X tp->tty_cum = 0; X X /* Compute the physical address where the data is in user space. */ X rp = proc_addr(tp->tty_outproc); X out_vir = (vir_bytes) tp->tty_out_vir; X out_left = (vir_bytes) tp->tty_outleft; X if ( (tp->tty_phys = umap(rp, D, out_vir, out_left)) == 0) { X /* Buffer address provided by user is outside its address space. */ X tp->tty_cum = E_BAD_ADDR; X tp->tty_outleft = 0; X } X X /* Copy characters from the user process to the terminal. */ X (*tp->tty_devstart)(tp); /* copy data to queue and start I/O */ X X /* If output is for a bitmapped terminal as the IBM-PC console, the output- X * routine will return at once so there is no need to suspend the caller, X * on ascii terminals however, the call is suspended and later revived. X */ X if (m_ptr->TTY_LINE != 0) { X caller = (int) tp->tty_outproc; X replyee = (int) tp->tty_otcaller; X tty_reply(TASK_REPLY, replyee, caller, SUSPEND, 0L, 0L); X } X} X X X/*===========================================================================* X * do_ioctl * X *===========================================================================*/ XPRIVATE do_ioctl(tp, m_ptr) Xregister struct tty_struct *tp; /* pointer to tty_struct */ Xmessage *m_ptr; /* pointer to message sent to task */ X{ X/* Perform IOCTL on this terminal. */ X X long flags, erki, erase, kill, intr, quit, xon, xoff, eof; X int r; X X r = OK; X flags = 0; X erki = 0; X switch(m_ptr->TTY_REQUEST) { X case TIOCSETP: X /* Set erase, kill, and flags. */ X tp->tty_erase = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* erase */ X tp->tty_kill = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* kill */ X tp->tty_mode = (int) m_ptr->TTY_FLAGS; /* mode word */ X if (m_ptr->TTY_SPEED != 0) tp->tty_speed = m_ptr->TTY_SPEED; X if (tp-tty_struct >= NR_CONS) X set_uart(tp - tty_struct, tp->tty_mode, tp->tty_speed); X break; X X case TIOCSETC: X /* Set intr, quit, xon, xoff, eof (brk not used). */ X tp->tty_intr = (char) ((m_ptr->TTY_SPEK >> 24) & BYTE); /* interrupt */ X tp->tty_quit = (char) ((m_ptr->TTY_SPEK >> 16) & BYTE); /* quit */ X tp->tty_xon = (char) ((m_ptr->TTY_SPEK >> 8) & BYTE); /* CTRL-S */ X tp->tty_xoff = (char) ((m_ptr->TTY_SPEK >> 0) & BYTE); /* CTRL-Q */ X tp->tty_eof = (char) ((m_ptr->TTY_FLAGS >> 8) & BYTE); /* CTRL-D */ X break; X X case TIOCGETP: X /* Get erase, kill, and flags. */ X erase = ((long) tp->tty_erase) & BYTE; X kill = ((long) tp->tty_kill) & BYTE; X erki = (erase << 8) | kill; X flags = ( (long) tp->tty_speed << 16) | (long) tp->tty_mode; X break; X X case TIOCGETC: X /* Get intr, quit, xon, xoff, eof. */ X intr = ((long) tp->tty_intr) & BYTE; X quit = ((long) tp->tty_quit) & BYTE; X xon = ((long) tp->tty_xon) & BYTE; X xoff = ((long) tp->tty_xoff) & BYTE; X eof = ((long) tp->tty_eof) & BYTE; X erki = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0); X flags = (eof <<8); X break; X X default: X r = EINVAL; X } X X /* Send the reply. */ X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r, flags, erki); X} X X X/*===========================================================================* X * do_setpgrp * X *===========================================================================*/ XPRIVATE do_setpgrp(tp, m_ptr) Xregister struct tty_struct *tp; /* pointer to tty struct */ Xmessage *m_ptr; /* pointer to message sent to task */ X{ X/* A control process group has changed */ X X tp->tty_pgrp = m_ptr->TTY_PGRP; X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK, 0L, 0L); X} X X X/*===========================================================================* X * do_cancel * X *===========================================================================*/ XPRIVATE do_cancel(tp, m_ptr) Xregister struct tty_struct *tp; /* pointer to tty_struct */ Xmessage *m_ptr; /* pointer to message sent to task */ X{ X/* A signal has been sent to a process that is hanging trying to read or write. X * The pending read or write must be finished off immediately. X */ X X int mode; X X /* First check to see if the process is indeed hanging. If it is not, don't X * reply (to avoid race conditions). X */ X if (tp->tty_inleft == 0 && tp->tty_outleft == 0) return; X X /* Kill off input/output. */ X mode = m_ptr->COUNT; X if (mode & R_BIT) { X /* Process was reading when killed. Clean up input. */ X tp->tty_inhead = tp->tty_inqueue; /* discard all data */ X tp->tty_intail = tp->tty_inqueue; X tp->tty_incount = 0; X tp->tty_lfct = 0; X tp->tty_inleft = 0; X tp->tty_inhibited = RUNNING; X } X if (mode & W_BIT) { X /* Process was writing when killed. Clean up output. */ X tp->tty_outleft = 0; X tp->tty_waiting = NOT_WAITING; /* don't send reply */ X } X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR, 0L, 0L); X} X X X/*===========================================================================* X * tty_reply * X *===========================================================================*/ XPRIVATE tty_reply(code, replyee, proc_nr, status, extra, other) Xint code; /* TASK_REPLY or REVIVE */ Xint replyee; /* destination address for the reply */ Xint proc_nr; /* to whom should the reply go? */ Xint status; /* reply code */ Xlong extra; /* extra value */ Xlong other; /* used for IOCTL replies */ X{ X/* Send a reply to a process that wanted to read or write data. */ X X message tty_mess; X X tty_mess.m_type = code; X tty_mess.REP_PROC_NR = proc_nr; X tty_mess.REP_STATUS = status; X tty_mess.TTY_FLAGS = extra; /* used by IOCTL for flags (mode) */ X tty_mess.TTY_SPEK = other; /* used by IOCTL for erase and kill chars */ X send(replyee, &tty_mess); X} X X X/*===========================================================================* X * sigchar * X *===========================================================================*/ XPRIVATE sigchar(tp, sig) Xregister struct tty_struct *tp; /* pointer to tty_struct */ Xint sig; /* SIGINT, SIGQUIT, or SIGKILL */ X{ X/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard */ X X tp->tty_inhibited = RUNNING; /* do implied CRTL-Q */ X finish(tp, EINTR); /* send reply */ X tp->tty_inhead = tp->tty_inqueue; /* discard input */ X tp->tty_intail = tp->tty_inqueue; X tp->tty_incount = 0; X tp->tty_lfct = 0; X if (tp >= &tty_struct[NR_CONS]) rs_sig(tp); /* RS232 only */ X if (tp->tty_pgrp) cause_sig(tp->tty_pgrp, sig); X} X X X X X/*****************************************************************************/ X/*****************************************************************************/ X/*****************************************************************************/ X/*****************************************************************************/ X/*****************************************************************************/ X X#ifdef i8088 X/* Now begins the code and data for the device-dependent tty drivers. */ X X/* Definitions used by the console driver. */ X#define COLOR_BASE 0xB800 /* video ram paragraph for color display */ X#define MONO_BASE 0xB000 /* video ram address for mono display */ X#define C_VID_MASK 0x3FFF /* mask for 16K video RAM */ X#define M_VID_MASK 0x0FFF /* mask for 4K video RAM */ X#define C_RETRACE 0x0300 /* how many characters to display at once */ X#define M_RETRACE 0x7000 /* how many characters to display at once */ X#define WORD_MASK 0xFFFF /* mask for 16 bits */ X#define OFF_MASK 0x000F /* mask for 4 bits */ X#define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */ X#define B_TIME 0x2000 /* how long to sound the CTRL-G beep tone */ X#define BLANK 0x0700 /* determines cursor color on blank screen */ X#define LINE_WIDTH 80 /* # characters on a line */ X#define SCR_LINES 25 /* # lines on the screen */ X#define CTRL_S 31 /* scan code for letter S (for CRTL-S) */ X#define MONOCHROME 1 /* value for tty_ioport tells color vs. mono */ X#define CONSOLE 0 /* line number for console */ X#define GO_FORWARD 0 /* scroll forward */ X#define GO_BACKWARD 1 /* scroll backward */ X#define TIMER2 0x42 /* I/O port for timer channel 2 */ X#define TIMER3 0x43 /* I/O port for timer channel 3 */ X#define KEYBD 0x60 /* I/O port for keyboard data */ X#define PORT_B 0x61 /* I/O port for 8255 port B */ X#define KBIT 0x80 /* bit used to ack characters to keyboard */ X#define LED_CODE 0xED /* command to keyboard to set LEDs */ X#define LED_DELAY 0x80 /* device dependent delay needed */ X X/* Constants relating to the video RAM and 6845. */ X#define M_6845 0x3B0 /* port for 6845 mono */ X#define C_6845 0x3D0 /* port for 6845 color */ X#define EGA 0x3C0 /* port for EGA card */ X#define INDEX 4 /* 6845's index register */ X#define DATA 5 /* 6845's data register */ X#define CUR_SIZE 10 /* 6845's cursor size register */ X#define VID_ORG 12 /* 6845's origin register */ X#define CURSOR 14 /* 6845's cursor register */ X X/* Definitions used for determining if the keyboard is IBM or Olivetti type. */ X#define KB_STATUS 0x64 /* Olivetti keyboard status port */ X#define BYTE_AVAIL 0x01 /* there is something in KEYBD port */ X#define KB_BUSY 0x02 /* KEYBD port ready to accept a command */ X#define DELUXE 0x01 /* this bit is set up iff deluxe keyboard */ X#define GET_TYPE 5 /* command to get keyboard type */ X#define OLIVETTI_EQUAL 12 /* the '=' key is 12 on olivetti, 13 on IBM */ X X/* Global variables used by the console driver. */ XPUBLIC message keybd_mess; /* message used for console input chars */ XPRIVATE vid_retrace; /* how many characters to display per burst */ XPRIVATE unsigned vid_base; /* base of video ram (0xB000 or 0xB800) */ XPUBLIC int vid_mask; /* 037777 for color (16K) or 07777 for mono */ XPRIVATE int vid_port; /* I/O port for accessing 6845 */ X X X/*===========================================================================* X * keyboard * X *===========================================================================*/ XPUBLIC keyboard() X{ X/* A keyboard interrupt has occurred. Process it. */ X X int val, code, k, raw_bit; X char stopc; X X /* Fetch the character from the keyboard hardware and acknowledge it. */ X port_in(KEYBD, &code); /* get the scan code for the key struck */ X port_in(PORT_B, &val); /* strobe the keyboard to ack the char */ X port_out(PORT_B, val | KBIT); /* strobe the bit high */ X port_out(PORT_B, val); /* now strobe it low */ X X /* The IBM keyboard interrupts twice per key, once when depressed, once when X * released. Filter out the latter, ignoring all but the shift-type keys. X * The shift-type keys 29, 42, 54, 56, 58, and 69 must be processed normally. X */ X k = code - 0200; /* codes > 0200 mean key release */ X if (k > 0) { X /* A key has been released. */ X if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69) { X port_out(INT_CTL, ENABLE); /* re-enable interrupts */ X return; /* don't call tty_task() */ X } X } else { X /* Check to see if character is CTRL-S, to stop output. Setting xoff X * to anything other than CTRL-S will not be detected here, but will X * be detected later, in the driver. A general routine to detect any X * xoff character here would be complicated since we only have the X * scan code here, not the ASCII character. X */ X raw_bit = tty_struct[CONSOLE].tty_mode & RAW; X stopc = tty_struct[CONSOLE].tty_xoff; X if (raw_bit == 0 && control && code == CTRL_S && stopc == XOFF_CHAR) { X tty_struct[CONSOLE].tty_inhibited = STOPPED; X port_out(INT_CTL, ENABLE); X return; X } X } X X /* Check for CTRL-ALT-DEL, and if found, reboot the computer. */ X if (control && alt && code == DEL_CODE) reboot(); /* CTRL-ALT-DEL */ X X /* Store the character in memory so the task can get at it later. X * tty_driver_buf[0] is the current count, and tty_driver_buf[1] is the X * maximum allowed to be stored. X */ X if ( (k = tty_driver_buf[0]) < tty_driver_buf[1]) { X /* There is room to store this character; do it. */ X k = k + k; /* each entry contains two bytes */ X tty_driver_buf[k+2] = code; /* store the scan code */ X tty_driver_buf[k+3] = CONSOLE; /* tell which line it came from */ X tty_driver_buf[0]++; /* increment counter */ X X /* Build and send the interrupt message. */ X keybd_mess.m_type = TTY_CHAR_INT; X keybd_mess.ADDRESS = tty_driver_buf; X interrupt(TTY, &keybd_mess); /* send a message to the tty task */ X } else { X /* Too many characters have been buffered. Discard excess. */ X port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ X } X} X X X/*===========================================================================* X * console * X *===========================================================================*/ XPRIVATE console(tp) Xregister struct tty_struct *tp; /* tells which terminal is to be used */ X{ X/* Copy as much data as possible to the output queue, then start I/O. On X * memory-mapped terminals, such as the IBM console, the I/O will also be X * finished, and the counts updated. Keep repeating until all I/O done. X */ X X extern char get_byte(); X int count; X char c; X unsigned segment, offset, offset1; X X /* Loop over the user bytes one at a time, outputting each one. */ X segment = (tp->tty_phys >> 4) & WORD_MASK; X offset = tp->tty_phys & OFF_MASK; X offset1 = offset; X count = 0; X X while (tp->tty_outleft > 0 && tp->tty_inhibited == RUNNING) { X c = get_byte(segment, offset); /* fetch 1 byte from user space */ X out_char(tp, c); /* write 1 byte to terminal */ X offset++; /* advance one character in user buffer */ X tp->tty_outleft--; /* decrement count */ X } X flush(tp); /* clear out the pending characters */ X + END-OF-FILE tty.c.part1 chmod 'u=rw,g=r,o=r' 'tty.c.part1' set `wc -c 'tty.c.part1'` count=$1 case $count in 44390) :;; *) echo 'Bad character count in ''tty.c.part1' >&2 echo 'Count should be 44390' >&2 esac exit 0