csrng@daisy.warwick.ac.uk (C S PCB Group) (12/14/87)
We use MINIX permanently through a serial tty driver, and we too had the problem of hanging due to missed interrupts. The problems are to do with the way MINIX deals with interrupts that cannot be handled immediately. Basically when an interrupt goes off , if it cannot be accepted by the relevant task then the message is remembered and sent when that task becomes free. The first problem is that there is only one pending message so further missed interupts overwrite previous ones, this is usually the problem with the tty. If you type fast enough you will overwrite an output interupt with an input one. Usually this will cause the serial driver to hang up. If you cure this by having one interupt slot for each task things improve, but if you wish for a task to handle more than one interrupt, such as the tty driver with input and output, perhaps to multiple lines, then you have to change the way messages are queued. We use one slot for each task * maximum number of interrups each task can accept, which is kind of wasteful, but I can't be bothered to rewrite it since it works. Even with this done we have GREAT difficulty handling any signifant load on the serial lines. We have found 2400 baud to the highest we can cope with. I guess a PC AT may do a bit better, but don't expect wonders John. --------------------------------- John Vaudin: arthur@uk.ac.warwick
paradis@encore.UUCP (Jim Paradis) (01/07/88)
Here's part 2 of my serial TTY driver for MINIX: Shar and enjoy! :-) --------cut here--------valuable coupon--------unpack with /bin/sh------ echo x - mpx88.newstf gres '^X' '' > mpx88.newstf << '/' X.globl _ser1_int, _ser2_int X X|*===========================================================================* X|* ser1_int * X|*===========================================================================* X_ser1_int: | Interrupt routine for primary serial port X call save | save the machine state X mov ax,#0 X push ax X call _ser_int | process a serial interrupt X jmp _restart | continue execution X X|*===========================================================================* X|* ser2_int * X|*===========================================================================* X_ser2_int: | Interrupt routine for secondary serial port X call save | save the machine state X mov ax,#1 X push ax X call _ser_int | process a keyboard interrupt X jmp _restart | continue execution X / echo x - rs232.c gres '^X' '' > rs232.c << '/' X/************************************************************************** X * File: rs232.c X * Creation date: 11/09/87 X * X * All original code is Copyright 1988, James R. Paradis. Permission X * granted to copy and redistribute for educational and non-commercial X * use. Commercial use requires permission of copyright holder. X * X * This file contains the low-level serial driver support routines X * for the MINIX tty driver. These routines interface directly to X * the IBM-PC hardware. X * X * Entry points: X * X * ser_init(minor, dev_num) X * ser_putc(minor, char) X * ser_ioctl(minor, func, addr) X * ser_fc(minor) X * ser_nofc(minor) X * ser_oflush(minor) X * ser_bufin(minor) X * X * Internal routines: X * ser_int(line) X * X **************************************************************************/ X X#include "../h/type.h" X#include "../h/const.h" X#include "../h/com.h" X#include "tty.h" X#include "const.h" X X X#define RS232_OBUFSZ 0x100 X#define RS_OBUFMASK 0x0ff X#define CIBUFSZ 0x80 X#define CIBUFSZMASK 0x7f X#define MAX_CHARS_PER_INT 3 X X/* Minor-device table, to translate TTY minor devices to serial line X * numbers. X */ XPRIVATE int ser_minor[NUM_SERIAL_DEV]; X X/* Inverse mapping table, to translate minor device numbers to X * serial line numbers. Note that this table assumes that we'll X * NEVER have a minor device number greater than NUM_CONSOLES + X * NUM_SERIAL_DEVS! X */ XPRIVATE int ser_lns[NUM_CONSOLES + NUM_SERIAL_DEV]; X#define find_rsstruct(minor) (&(ser_lines[ser_lns[minor]])) X X/* RS232 device structure, one per device. */ Xtypedef struct { X char rs_obuf[RS232_OBUFSZ]; /* TTY output buffer */ X int rs_first; /* Circular queue, first element */ X int rs_last; /* Last element */ X int rs_outrdy; /* Ready for immediate output */ X int rs_qsize; /* Size of queue */ X int rs_line; /* Line number */ X int rs_fc; /* 1 if flow ctl on, 0 if off */ X message rs_ttymsg; /* Message buffer */ X int rs_ibuf[CIBUFSZ]; /* Input buffer */ X int rs_ifirst; /* Queue pointers */ X int rs_ilast; X int rs_baud; /* Baud rate divisor */ X int rs_cint_sent; X} RS232_STRUCT; X XRS232_STRUCT ser_lines[NUM_SERIAL_DEV]; X X/* Port defines. Note that these are macros, with the argument being X * the line number. Currently these macros work for lines 0 and 1. X * Other lines will require different macros. X */ X X/* Table of 8250 base addresses. */ XPRIVATE int addr_8250[] = { X 0x3f8, /* COM1: (line 0) */ X 0x2f8, /* COM2: (line 1) */ X 0x3e8, /* COM3: (line 2) */ X 0x2e8 }; /* COM4: (line 3) */ X X#define XMIT_REG(line) addr_8250[line] X#define RECV_REG(line) addr_8250[line] X#define DIV_LOW_REG(line) addr_8250[line] X#define INT_ENA_REG(line) (addr_8250[line] + 1) X#define DIV_HI_REG(line) (addr_8250[line] + 1) X#define INT_ID_REG(line) (addr_8250[line] + 2) X#define LINE_CTL_REG(line) (addr_8250[line] + 3) X#define MOD_CTL_REG(line) (addr_8250[line] + 4) X#define LINE_STATUS_REG(line) (addr_8250[line] + 5) X#define MOD_STATUS_REG(line) (addr_8250[line] + 6) X X/* Value definitions for the II (interrtupt ID) register */ X#define II_MASK 7 X#define II_LINE 6 /* Receiver line status */ X#define II_DATA_READY 4 /* Received data available */ X#define II_OUTPUT_READY 2 /* Transmit holding register empty */ X#define II_MODEM 0 /* MODEM status available */ X#define II_INT_PENDING 1 /* This bit is ZERO if interrupt pending! */ X X/* Bit defines for the IE (interrupt enable) register */ X#define IE_ERBFI 1 /* Rec'd data available interrupt */ X#define IE_ETBEI 2 /* Trans hold reg empty interrupt */ X#define IE_ELSI 4 /* Line status interrupt */ X#define IE_EDSSI 8 /* Modem status interrupt */ X X/* Bit defines for the LC (line control) register */ X#define LC_5BITS 0 /* Word length = 5 bits */ X#define LC_6BITS 1 /* Word length = 6 bits */ X#define LC_7BITS 2 /* Word length = 7 bits */ X#define LC_8BITS 3 /* Word length = 8 bits */ X#define LC_1STOP 0 /* One stop bit */ X#define LC_2STOP 4 /* Two stop bits */ X#define LC_PE 8 /* Parity enable */ X#define LC_EVENP 0x10 /* Even parity select */ X#define LC_STICKP 0x20 /* Stick parity */ X#define LC_SETBRK 0x40 /* Set (send) break */ X#define LC_DLAB 0x80 /* Divisor latch address bit */ X X/* Bit defines for the MC (modem control) register */ X#define MC_DTR 1 X#define MC_RTS 2 X#define MC_OUT1 4 X#define MC_OUT2 8 X#define MC_LOOP 0x10 X X/* Bit defines for the LS (line status) register */ X#define LS_DATA_READY 1 X#define LS_OVERRUN 2 X#define LS_PERROR 4 /* Parity error */ X#define LS_FERROR 8 /* Framing error */ X#define LS_BREAK 0x10 /* Break interrupt */ X#define LS_THRE 0x20 /* Transmit holding register empty */ X#define LS_TSRE 0x40 /* Transmit shift register empty */ X X/* Bit defines for MS (modem status) register */ X#define MS_DCTS 1 X#define MS_DDSR 2 X#define MS_TERI 4 X#define MS_DSLSD 8 X#define MS_CTS 0x10 X#define MS_DSR 0x20 X#define MS_RING 0x40 X#define MS_RLSD 0x80 X X/* Divisor defines for various baud rates */ X#define DIV_110 1047 /* 110 baud */ X#define DIV_150 768 /* 150 baud */ X#define DIV_300 384 /* 300 baud */ X#define DIV_600 192 /* 600 baud */ X#define DIV_1200 96 /* 1200 baud */ X#define DIV_2400 48 /* 2400 baud */ X#define DIV_4800 24 /* 4800 baud */ X#define DIV_9600 12 /* 9600 baud */ X X/************************************************************************ X * Function: X * ser_init(minor, devnum) X * X * Description: X * Initializes the serial layer X * X * Arguments: X * minor - Minor device number for this device in the tty X * driver. X * X * devnum - Hardware device number to associate with this X * minor device number. X * X * Return Value: X * None. X * X *************************************************************************/ XPUBLIC ser_init(minor, devnum) Xint minor; Xint devnum; X{ X X /* Record the minor device number in the table */ X ser_minor[devnum] = minor; X ser_lns[minor] = devnum; X X /* Set up the serial line output queue */ X ser_lines[devnum].rs_first = 0; X ser_lines[devnum].rs_last = 0; X ser_lines[devnum].rs_outrdy = 1; X ser_lines[devnum].rs_qsize = 0; X ser_lines[devnum].rs_line = devnum; X ser_lines[devnum].rs_fc = 0; X ser_lines[devnum].rs_ifirst = 0; X ser_lines[devnum].rs_ilast = 0; X ser_lines[devnum].rs_baud = DIV_2400; X ser_lines[devnum].rs_cint_sent = 0; X X /* Set us up initially for 2400 baud, no parity, 8 data bits, X * 1 stop bit X */ X port_out(LINE_CTL_REG(devnum), LC_8BITS | LC_1STOP); X port_out(INT_ENA_REG(devnum), 0); X port_out(MOD_CTL_REG(devnum), MC_OUT2 | MC_RTS | MC_DTR); X port_out(LINE_CTL_REG(devnum), LC_DLAB); X port_out(DIV_HI_REG(devnum), DIV_2400 >> 8); X port_out(DIV_LOW_REG(devnum), DIV_2400 & 0xff); X port_out(LINE_CTL_REG(devnum), LC_8BITS | LC_1STOP ); X X /* Enable interrupts for data ready and transmit holding X * register empty X */ X port_out(INT_ENA_REG(devnum), IE_ERBFI | IE_ETBEI); X X} X X X/************************************************************************ X * Function: X * ser_putc(minor, ch) X * X * Description: X * Writes a character out to the serial line. X * X * Arguments: X * minor - Minor device number. X * X * ch - character to write out. X * X * Return Value: X * None X * X *************************************************************************/ XPUBLIC ser_putc(minor, ch) Xint minor; Xchar ch; X{ X RS232_STRUCT * rs; X X rs = find_rsstruct(minor); X X /* If the device is ready NOW, then set the device as X * not-ready and send the byte out immediately. X */ X if(rs->rs_outrdy) { X rs->rs_outrdy = 0; X port_out(XMIT_REG(rs->rs_line), ch); X } X else if(rs->rs_qsize < RS232_OBUFSZ) { X /* If there's room, then drop the character on the X * output queue. Otherwise, we drop it on the floor. X */ X rs->rs_obuf[rs->rs_last] = ch; X rs->rs_last = (rs->rs_last + 1) % RS232_OBUFSZ; X rs->rs_qsize++; X } X X} X X/************************************************************************ X * Function: X * ser_ioctl(minor, func, addr) X * X * Description: X * Performs ioctl functions. X * X * Arguments: X * minor - Minor device number. X * X * func - ioctl function to perform. X * X * addr - Address of ioctl buffer. X * X * Return Value: X * None. X *************************************************************************/ XPUBLIC ser_ioctl(minor, func, addr) Xint minor; Xint func; Xchar * addr; X{ X /* Device IOCTLs not yet supported... */ X X} X X X/************************************************************************ X * Function: X * ser_fc(minor) X * X * Description: X * Turns on flow control for the specified serial line. X * X * Arguments: X * minor - Minor device number. X * X * Return Value: X * None. X *************************************************************************/ XPUBLIC ser_fc(minor) Xint minor; X{ X RS232_STRUCT * rs; X X rs = find_rsstruct(minor); X rs->rs_fc = 1; X X} X X X/************************************************************************ X * Function: X * ser_nofc(minor) X * X * Description: X * Turns off flow control for the specified serial line. X * X * Arguments: X * minor - minor device number. X * X * Return Value: X * None. X *************************************************************************/ XPUBLIC ser_nofc(minor) Xint minor; X{ X RS232_STRUCT * rs; X X rs = find_rsstruct(minor); X rs->rs_fc = 0; X X} X X X/************************************************************************ X * Function: X * ser_oflush(minor) X * X * Description: X * Flushes any pending output on the specified serial line. X * X * Arguments: X * minor - Minor device number. X * X * Return Value: X * None. X *************************************************************************/ XPUBLIC ser_oflush(minor) Xint minor; X{ X RS232_STRUCT * rs; X X rs = find_rsstruct(minor); X X /* Wait for interrupt service to take all the characters... */ X while(rs->rs_qsize) ; X return; X X rs->rs_outrdy = 1; X X} X X/************************************************************************ X * Function: X * ser_int(irq) X * X * Description: X * Called whenever an interrupt on a serial device occurs X * X * Arguments: X * irq - indicates which interrupt happened. 0 = IRQ4, 1 = X * IRQ3. X * X * Return Value: X * None. X *************************************************************************/ Xser_int(irq) Xint irq; X{ X int istat; X int line; X int int_processed = 0; X X switch(irq) { X case 0: X /* IRQ4 can be either COM1 or COM3. Try COM1 first */ X#if (NUM_SERIAL_DEV > 0) X port_in(INT_ID_REG(0), &istat); X if((istat & II_INT_PENDING) == 0) { X ser_doint(0, istat); X int_processed = 1; X } X#endif X X#if (NUM_SERIAL_DEV > 2) X port_in(INT_ID_REG(2), &istat); X if((istat & II_INT_PENDING) == 0) { X ser_doint(2, istat); X int_processed = 1; X } X#endif X break; X X case 1: X X /* IRQ3 can be either COM2 or COM4. Try COM2 first */ X#if (NUM_SERIAL_DEV > 1) X port_in(INT_ID_REG(1), &istat); X if((istat & II_INT_PENDING) == 0) { X ser_doint(1, istat); X int_processed = 1; X } X#endif X X#if (NUM_SERIAL_DEV > 3) X port_in(INT_ID_REG(3), &istat); X if((istat & II_INT_PENDING) == 0) { X ser_doint(3, istat); X int_processed = 3; X } X#endif X break; X } X X if(!int_processed) { X /* Something wrong... */ X printf("TTY: Int %d received but no interrupt pending!\n", irq); X port_out(INT_CTL, ENABLE); X } X} X X/************************************************************************ X * Function: X * ser_doint(line, istat) X * X * Description: X * Called whenever an interrupt on a serial device occurs X * X * Arguments: X * line - LINE number of the line that the interrupt occurred on. X * istat - interrupt status register for the line that the X * interrupt occurred on. X * X * Return Value: X * None. X *************************************************************************/ XPRIVATE ser_doint(line, istat) Xint line; Xint istat; X{ X RS232_STRUCT *rs; X int int_id; X int pstat; X int idata; X int i; X int send_interrupt = 0; X int another; X int latency; X int nchars_recd = 0; X X rs = &(ser_lines[line]); X X if(istat == II_DATA_READY) { X /* A byte of data just came in. Grab it, then see if X * there are any other bytes immediately following. X * Grab as many as we can, then send them all up to the X * TTY layer. X */ X X port_in(RECV_REG(line), &idata); X /* Drop the char on the floor if the buffer is full... */ X for(;;) { X if((((rs->rs_ilast + 1) & CIBUFSZMASK) == rs->rs_ifirst) || X (++nchars_recd > MAX_CHARS_PER_INT)) { break; } X rs->rs_ibuf[rs->rs_ilast] = (idata & 0xff); X rs->rs_ilast = (rs->rs_ilast + 1) & CIBUFSZMASK; X X /* Hang around a bit and see if there's another X * character coming... X */ X another = 0; X for(latency = 0; latency < (rs->rs_baud << 1); latency++) { X int pstatus; X port_in(LINE_STATUS_REG(line), &pstatus); X if(pstatus & LS_DATA_READY) { X another = 1; X break; X } X } X if(another) { X port_in(RECV_REG(line), &idata); X } X else { X break; X } X } X X /* Prepare a message to send to the TTY layer */ X rs->rs_ttymsg.m_type = TTY_CHAR_INT; X rs->rs_ttymsg.TTY_LINE = ser_minor[line]; X send_interrupt = 1; X } X X else if(istat == II_OUTPUT_READY) { X /* Ready to output a character. X * Determine if there's anything to send. If there X * is, then send it. Otherwise, mark this tty as X * being ready for immediate output. X */ X X if(rs->rs_qsize > 0) { X X port_out(XMIT_REG(line), rs->rs_obuf[rs->rs_first]); X rs->rs_first = (rs->rs_first + 1) % RS232_OBUFSZ; X rs->rs_qsize--; X X } X else { X rs->rs_outrdy = 1; X } X X } X X /* EITHER enable interrupts OR send interrupt to TTY (since X * the interrupt() routine enables interrupts also... X */ X if(send_interrupt && !(rs->rs_cint_sent)) { X rs->rs_cint_sent = 1; X interrupt(TTY, &(rs->rs_ttymsg)); X } X else { X port_out(INT_CTL, ENABLE); X } X} X X/************************************************************************ X * Function: X * ser_bufin(minor, bufptr, maxchars) X * X * Description: X * Called by the TTY layer to get whatever buffered input X * the device layer may have. X * X * Arguments: X * minor Minor device. X * bufptr Address of buffer to put characters into. X * maxchars Maximum number of characters to get. X * X * Return value: X * Returns number of characters actually gotten. X * X *************************************************************************/ XPUBLIC int ser_bufin(minor, bufptr, maxchars) Xint minor; Xchar * bufptr; Xint maxchars; X{ X RS232_STRUCT * rs; X int nchars; X int i; X X rs = find_rsstruct(minor); X X X /* nchars = min(maxchars, con_size) */ X nchars = rs->rs_ilast - rs->rs_ifirst; X if(nchars < 0) nchars += CIBUFSZ; X if(maxchars < nchars) nchars = maxchars; X X for(i = 0; i < nchars; i++) { X bufptr[i] = rs->rs_ibuf[rs->rs_ifirst]; X rs->rs_ifirst = (rs->rs_ifirst + 1) & CIBUFSZMASK; X } X X rs->rs_cint_sent = 0; X return(nchars); X} X X / echo x - t.c gres '^X' '' > t.c << '/' X#include <signal.h> X#include <sgtty.h> X#include <stdio.h> X Xmain() X{ X int pid; X X printf("Poor Man's Terminal Program, V0.0.\n"); X printf("Hit F10 to return to MINIX\n\n"); X X pid = fork(); X if(pid < 0) { X perror("Cannot fork"); X exit(0); X } X else if(pid == 0) { X /* Child path. */ X indata(); X } X else { X /* Parent path */ X outdata(); X /* Upon return, kill child proc */ X kill(pid, 9); X } X} X Xindata() X{ X int fd; X char buffer[128]; X int nread; X struct sgttyb st; X X /* Open the terminal */ X fd = open("/dev/tty1", 2); X if(fd < 0) { X perror("Cannot open /dev/tty1"); X exit(0); X } X X /* Set raw mode */ X ioctl(fd, TIOCGETP, &st); X st.sg_flags &= ~(ECHO | RAW | CBREAK); X st.sg_flags |= RAW; X ioctl(fd, TIOCSETP, &st); X X /* Loop forever, getting whatever we can from the X * tty and writing it out to our terminal. X */ X for(;;) { X X nread = read(fd, buffer, 128); X X if(nread < 0) { X perror("Read error on /dev/tty1"); X } X else { X X write(1, buffer, nread); X X } X } X} X Xoutdata() X{ X int fd; X char buffer[128]; X int nread; X struct sgttyb st; X struct sgttyb old_tty_st; X X /* Open the terminal */ X fd = open("/dev/tty1", 2); X if(fd < 0) { X perror("Cannot open /dev/tty1"); X exit(0); X } X X /* Set raw mode */ X ioctl(fd, TIOCGETP, &st); X st.sg_flags &= ~(ECHO | RAW | CBREAK); X st.sg_flags |= RAW; X ioctl(fd, TIOCSETP, &st); X X /* Set our own terminal for raw mode... */ X ioctl(0, TIOCGETP, &st); X ioctl(0, TIOCGETP, &old_tty_st); X st.sg_flags &= ~(ECHO | RAW | CBREAK); X st.sg_flags |= RAW; X ioctl(0, TIOCSETP, &st); X X /* Loop forever, getting whatever we can from our X * console and writing it out to the tty. X */ X for(;;) { X X nread = read(0, buffer, 128); X X if(nread < 0) { X perror("Read error on console"); X } X else if(buffer[0] == 27) { X if(buffer[1] == 'O' && buffer[2] == 'Y') { X /* F10 hit */ X ioctl(0, TIOCSETP, &old_tty_st); X return; X } X else { X write(fd, buffer, nread); X } X } X else { X if(write(fd, buffer, nread) < 0) { X perror("Write error on /dev/tty1"); X } X } X } X} X X X X X X X X X X / echo x - tty.c gres '^X' '' > tty.c << '/' X/* All original code is Copyright 1988, James R. Paradis. Permission X * granted to copy and redistribute for educational and non-commercial X * use. Commercial use requires permission of copyright holder. X * X * This file contains the device-independent portion of the MINIX terminal X * driver. It has one entry point: tty_task(). X * X * The valid messages and their parameters are: X * X * TTY_CHAR_INT: a character has been typed on a terminal (input 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 * 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|minor dev| | | | | | 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 * | 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#include "tty.h" X XPRIVATE TTY_STRUCT tty_list[NUM_TTYS]; X X/* Staging buffer for data copies */ XPRIVATE char tty_sbuf[TTY_IBUFSZ]; X X X/* External function declarations */ Xextern void con_init(); Xextern void con_putc(); Xextern void con_ioctl(); Xextern void con_fc(); Xextern void con_nofc(); Xextern void con_oflush(); Xextern int con_bufin(); X Xextern void ser_init(); Xextern void ser_putc(); Xextern void ser_ioctl(); Xextern void ser_fc(); Xextern void ser_nofc(); Xextern void ser_oflush(); Xextern int ser_bufin(); X X XPRIVATE char dq_fifo_char(); XPRIVATE char dq_lifo_char(); X Xextern phys_bytes umap(); X XPRIVATE message recv_message; XPRIVATE message reply_message; XPRIVATE message *recv_ptr; XPRIVATE message *reply_ptr; X X/*********************************************************************** X * Function: X * tty_task() X * X * Description: X * Main entry point for the TTY driver. Initializes the tty X * driver and the low-level devices, then enters an endless X * loop waiting for messages and processing them. X * X * Arguments: X * None X * X * Return Value: X * Never returns! X * X *************************************************************************/ X XPUBLIC tty_task() X{ X/* Main routine of the terminal task. */ X X register TTY_STRUCT *tp; X int i, ttnum; X X recv_ptr = &recv_message; X reply_ptr = &reply_message; X X tty_init(); /* initialize */ X X while (TRUE) { X receive(ANY, recv_ptr); X tp = &tty_list[recv_ptr->TTY_LINE]; X X switch(recv_ptr->m_type) { X case TTY_CHAR_INT: X ttnum = recv_ptr->TTY_LINE; X for(i = 0; i < NUM_TTYS; i++) { X do_charint(&tty_list[ttnum]); X ttnum = (ttnum + 1) % NUM_TTYS; X } X break; X case TTY_READ: do_read(tp, recv_ptr); break; X case TTY_WRITE: do_write(tp, recv_ptr); break; X case TTY_IOCTL: do_ioctl(tp, recv_ptr); break; X case CANCEL : do_cancel(tp, recv_ptr); break; X default: X tty_reply(TASK_REPLY, recv_ptr->m_source, X recv_ptr->PROC_NR, EINVAL, 0L, 0L); X break; X } X } X} X X X/************************************************************************ X * Function: X * tty_init() X * X * Description: X * Performs initialization functions for the TTY driver. X * Sets up the tty_list, then calls lower-level functions X * to initialize the physical devices. X * X * Return Value: X * None X *************************************************************************/ Xtty_init() X{ X int i; X TTY_STRUCT *tp; X X /* First, set up those fields common to all TTYs */ X for(i = 0; i < NUM_TTYS; i++) { X tp = &tty_list[i]; X X tp->tt_minor = i; X tp->tt_first = 0; X tp->tt_last = 0; X tp->tt_bufsz = 0; X tp->tt_last_nl = -1; X tp->tt_readsz = 0; X tp->tt_proc = 0; X tp->tt_usrbuf = (char *)0; X tp->tt_fc_on = TTY_FC_ON; X tp->tt_fc_off = TTY_FC_OFF; X tp->tt_erase = ERASE_CHAR; X tp->tt_kill = KILL_CHAR; X tp->tt_wera = WERA_CHAR; X tp->tt_intr = INTR_CHAR; X tp->tt_quit = QUIT_CHAR; X tp->tt_stop = STOP_CHAR; X tp->tt_susp = XOFF_CHAR; X tp->tt_resume = XON_CHAR; X tp->tt_rprnt = RDSP_CHAR; X tp->tt_lnext = LNXT_CHAR; X tp->tt_eot = EOT_CHAR; X tp->tt_uflags = CRMOD | ECHO; X tp->tt_iflags = 0; X } X X /* Set up the function fields for the console */ X tp = tty_list; X tp->tt_dev_init = con_init; X tp->tt_dev_putc = con_putc; X tp->tt_dev_ioctl = con_ioctl; X tp->tt_dev_fcon = con_fc; X tp->tt_dev_fcoff = con_nofc; X tp->tt_dev_oflush = con_oflush; X tp->tt_dev_bufin = con_bufin; X X /* Set up the function fields for the serial device(s) */ X X for(i = 1; i <= NUM_SERIAL_DEV; i++) { X tp = &tty_list[i]; X tp->tt_dev_init = ser_init; X tp->tt_dev_putc = ser_putc; X tp->tt_dev_ioctl = ser_ioctl; X tp->tt_dev_fcon = ser_fc; X tp->tt_dev_fcoff = ser_nofc; X tp->tt_dev_oflush = ser_oflush; X tp->tt_dev_bufin = ser_bufin; X } X X X /* Call lower-level functions to initialize physical devices */ X (tty_list[0].tt_dev_init)(0, 0); X X for(i = 1; i <= NUM_SERIAL_DEV; i++) { X (tty_list[1].tt_dev_init)(i, i - 1); X } X X} X X X/************************************************************************ X * Function: X * do_charint(tp, m_ptr) X * X * Description: X * Processes an incoming character from the specified serial X * device X * X * Arguments: X * tp Pointer to the TTY structure for the device X * X * Return Value: X * None X *************************************************************************/ X XPRIVATE do_charint(tp) Xregister TTY_STRUCT * tp; X{ X char cbuf[132]; X char nchars; X int i; X char ch; X char tmp_ch; X X /* Get the incoming character(s) */ X nchars = (tp->tt_dev_bufin)(tp->tt_minor, cbuf, 132); X for(i = 0; i < nchars; i++) { X ch = cbuf[i]; X X /* If we're suspended, only accept resume char */ X if(tp->tt_iflags & TT_SUSPENDED) { X if(ch == tp->tt_resume) { X tp->tt_iflags &= ~TT_SUSPENDED; X X /* Do any blocked write */ X if(tp->tt_iflags & TT_WR_SUSP) { X int result; X X tp->tt_iflags &= ~TT_WR_SUSP; X result = tty_write(tp, tp->tt_wproc, tp->tt_waddr, X tp->tt_wcount); X (tp->tt_dev_oflush)(tp->tt_minor); X X tty_areply(REVIVE, (int)tp->tt_wcaller, X (int)tp->tt_wproc, (int)tp->tt_wrcount, 0L, 0L); X } X } X /* Pass signal-generating characters through */ X#ifdef JOB_CONTROL X else if((ch != tp->tt_intr) && (ch != tp->tt_quit) && X (ch != tp->tt_stop)) { X#else X else if((ch != tp->tt_intr) && (ch != tp->tt_quit)) { X#endif JOB_CONTROL X return; X } X } X X /* If we're in RAW mode or LITNEXT mode, then just take the X * character as input. X */ X if((tp->tt_uflags & RAW) || (tp->tt_iflags & TT_LITNEXT)) { X enque_char(tp, ch); X if(tp->tt_uflags & ECHO) { X output_char(tp, ch); X (tp->tt_dev_oflush)(tp->tt_minor); X } X if(tp->tt_iflags & TT_LITNEXT) { X tp->tt_iflags &= ~TT_LITNEXT; X } X } X X /* Otherwise, see if the input character is a special char. X * If so, process it accordingly. If not, enque it. Note X * that if we're in CBREAK mode then we'll only process X * certain special chars: \r, intr, quit, (stop), and xoff. X */ X else { X if((ch == tp->tt_erase) && !(tp->tt_uflags & CBREAK)) { X /* Erase the last character typed (if any) */ X if(tp->tt_bufsz > 0) { X /* But not if it's a newline */ X if((tmp_ch = dq_lifo_char(tp)) == '\n') { X enque_char(tp, '\n'); X } X else { X tty_do_bs(tp, tmp_ch); X (tp->tt_dev_oflush)(tp->tt_minor); X } X } X } X else if((ch == tp->tt_kill) && !(tp->tt_uflags & CBREAK)) { X /* Erase everything up to the last newline OR to the X * beginning of the buffer if none... X */ X while(tp->tt_bufsz) { X if((tmp_ch = dq_lifo_char(tp)) == '\n') { X enque_char(tp, '\n'); X break; X } X else { X tty_do_bs(tp, tmp_ch); X } X } X (tp->tt_dev_oflush)(tp->tt_minor); X } X else if((ch == tp->tt_wera) && !(tp->tt_uflags & CBREAK)) { X /* Erase everything up to the last newline OR whitespace OR X * beginning of buffer. X */ X while(tp->tt_bufsz) { X X tmp_ch = dq_lifo_char(tp); X if (tmp_ch == '\n' || tmp_ch == ' ' || tmp_ch == '\t') { X enque_char(tp, tmp_ch); X break; X } X X tty_do_bs(tp, tmp_ch); X X } X X /* Now chomp down any leading whitespace */ X X while(tp->tt_bufsz) { X X tmp_ch = dq_lifo_char(tp); X if(tmp_ch == ' ' || tmp_ch == '\t') { X tty_do_bs(tp, tmp_ch); X } X else { X enque_char(tp, tmp_ch); X break; X } X } X (tp->tt_dev_oflush)(tp->tt_minor); X } X else if(ch == tp->tt_intr) { X /* Send SIGINT to controlling process */ X tty_sig(tp, SIGINT); X } X else if(ch == tp->tt_quit) { X /* Send SIGQUIT to controlling process */ X tty_sig(tp, SIGQUIT); X } X#ifdef JOB_CONTROL X else if(ch == tp->tt_stop) { X tty_sig(tp, SIGTSTP); X } X#endif JOB_CONTROL X else if(ch == tp->tt_susp) { X /* Suspend I/O on this device */ X tp->tt_iflags |= TT_SUSPENDED; X } X else if(ch == tp->tt_resume) { X /* Do nothing with resume char */ X } X else if(ch == tp->tt_rprnt && (tp->tt_uflags & ECHO) X && !(tp->tt_uflags & CBREAK)) { X /* Re-print the last line entered. We'll do our own X * queue traversal here, since it's rather weird and it's X * a read-only operation on the queue. X */ X int i; X X /* Figure out where to start. If there are newlines in the X * buffer, then start after the last one. Otherwise, start X * at the beginning of the buffer. X */ X if(tp->tt_last_nl < 0) { X i = tp->tt_first; X } X else { X i = (tp->tt_last_nl + 1) % TTY_IBUFSZ; X } X X /* Put out a newline first */ X (tp->tt_dev_putc)(tp->tt_minor, '\r'); X (tp->tt_dev_putc)(tp->tt_minor, '\n'); X X /* Now put out each char in turn */ X while(i != tp->tt_last) { X if(tp->tt_inbuf[i] == '\n') { X (tp->tt_dev_putc)(tp->tt_minor, '\r'); X (tp->tt_dev_putc)(tp->tt_minor, '\n'); X } X else { X output_char(tp, tp->tt_inbuf[i]); X } X i = (i + 1) % TTY_IBUFSZ; X } X (tp->tt_dev_oflush)(tp->tt_minor); X } X else if (ch == tp->tt_rprnt && !(tp->tt_uflags & CBREAK)) { X /* Do nothing if rprnt typed but echo off */ X } X else if (ch == tp->tt_lnext && !(tp->tt_uflags & CBREAK)) { X /* Turn on 'literal-next' mode */ X tp->tt_iflags |= TT_LITNEXT; X } X else if ((ch == '\r') && (tp->tt_uflags & CRMOD)) { X /* Map CR to newline */ X enque_char(tp, '\n'); X if(tp->tt_uflags & ECHO) { X (tp->tt_dev_putc)(tp->tt_minor, '\r'); X (tp->tt_dev_putc)(tp->tt_minor, '\n'); X (tp->tt_dev_oflush)(tp->tt_minor); X } X } X else if(ch == '\n') { X enque_char(tp, ch); X if(tp->tt_uflags & ECHO) { X (tp->tt_dev_putc)(tp->tt_minor, ch); X (tp->tt_dev_oflush)(tp->tt_minor); X } X } X else if(ch == tp->tt_eot) { X /* if it's EOT, echo it only if it's LITNEXT, RAW, or CBREAK */ X enque_char(tp, ch); X if((tp->tt_uflags & ECHO) && X ((tp->tt_uflags & (RAW | CBREAK)) || X (tp->tt_iflags & TT_LITNEXT))) { X output_char(tp, ch); X (tp->tt_dev_oflush)(tp->tt_minor); X } X } X else { X /* It's a real character. Enqueue it and echo if desired */ X enque_char(tp, ch); X if(tp->tt_uflags & ECHO) { X output_char(tp, ch); X (tp->tt_dev_oflush)(tp->tt_minor); X } X } X } X } X X /* Determine if we can now satisfy any outstanding read requests. X * If we're in RAW or CBREAK mode, we can do so if there is at least X * one character in the buffer. Otherwise, we can do so if there's X * a newline in the buffer OR if the buffer size is greater than the X * outstanding read size. X */ X if((tp->tt_readsz > 0) && (tp->tt_bufsz > 0)) { X int read_amt = 0; X if(tp->tt_uflags & (RAW | CBREAK)) { X read_amt = MIN(tp->tt_bufsz, tp->tt_readsz); X } X else if (tp->tt_last_nl > 0) { X int i; X X /* Calculate number of characters from beginning of X * buffer to the first newline. Include the newline X * in the count! (EOT counts as newline since we're not X * in RAW or CBREAK mode) X */ X i = tp->tt_first; X while((tp->tt_inbuf[i] != '\n') && X (tp->tt_inbuf[i] != MARKER)){ X read_amt++; X i = (i + 1) % TTY_IBUFSZ; X } X /* Add one for the newline... */ X read_amt++; X X /* But never more than we asked for... */ X if(read_amt > tp->tt_readsz) { X read_amt = tp->tt_readsz; X } X } X X /* If there's anything to copy out, do so here. X * Notify the calling process that a read request X * is being honored, then set the outstanding read X * request amount to zero. X */ X if(read_amt > 0) { X /* copyout returns either the number of bytes read or X * E_BAD_ADDR X */ X X read_amt = copyout(tp, (char)tp->tt_caller, tp->tt_usrbuf, X read_amt); X X tp->tt_readsz = 0; X tty_areply(REVIVE, tp->tt_proc, tp->tt_caller, read_amt, X 0L, 0L); X } X } X} X Xtty_do_bs(tp, ch) Xregister TTY_STRUCT *tp; Xchar ch; /* char we're backing up over */ X{ X int i, nbs; X X nbs = (ch <= 27 && ch != 9) ? 2 : 1; X X /* Put out ^H ^H (May want to make provision for X * non-crt terminals later) X */ X if(tp->tt_uflags & ECHO) { X for(i = 0; i < nbs; i++) { X (tp->tt_dev_putc)(tp->tt_minor, 8); X (tp->tt_dev_putc)(tp->tt_minor, ' '); X (tp->tt_dev_putc)(tp->tt_minor, 8); X } X } X} X X/************************************************************************ X * Function: X * do_read(tp, m_ptr) X * X * Description: X * Handle a read request. X * X * Arguments: X * tp Pointer to the TTY structure on which the read is X * requested. X * m_ptr Pointer to the original message. X * X *************************************************************************/ XPRIVATE do_read(tp, m_ptr) Xregister TTY_STRUCT *tp; /* pointer to tty struct */ Xmessage *m_ptr; /* pointer to message sent to the task */ X{ X int read_amt = 0; X int i; X X X /* Never read anything bigger than your buffer... */ X if(m_ptr->COUNT > TTY_IBUFSZ) { X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, E2BIG, X 0L, 0L); X return; X } X X /* If someone else is hanging on this tty, give up */ X if(tp->tt_readsz > 0) { X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, X E_TRY_AGAIN, 0L, 0L); X return; X } X X /* Determine if we can satisfy the request immediately. */ X X if(tp->tt_uflags & (RAW | CBREAK)) { X X /* RAW and CBREAK mode will be satisfied with as little X * as one character... X */ X read_amt = MIN(tp->tt_bufsz, m_ptr->COUNT); X } X else if (tp->tt_last_nl > 0) { X X /* If we're in cooked mode, then a newline will X * terminate the read. Feed back whatever characters X * we have up to the newline. EOT counts as newline, X * since we're not in RAW mode. X */ X i = tp->tt_first; X while((tp->tt_inbuf[i] != '\n') && X (tp->tt_inbuf[i] != MARKER)){ X read_amt++; X i = (i + 1) % TTY_IBUFSZ; X } X /* Add one for the newline... */ X read_amt++; X X /* But don't EVER give back more than we asked for! */ X if(read_amt > m_ptr->COUNT) { X read_amt = m_ptr->COUNT; X } X } X else if (tp->tt_bufsz >= m_ptr->COUNT) { X /* No newlines in the buffer, but we have enough characters X * to satisfy the read request, so let's do it! X */ X read_amt = m_ptr->COUNT; X } X X /* If there's anything to copy out, do so here. X * Notify the calling process that a read request X * is being honored, then set the outstanding read X * request amount to zero. X */ X if(read_amt > 0) { X /* copyout returns either the number of bytes read or E_BAD_ADDR */ X X read_amt = copyout(tp, (char)m_ptr->PROC_NR, X (char *)(m_ptr->ADDRESS), read_amt); X X tp->tt_readsz = 0; X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, read_amt, X 0L, 0L); X } X else { X /* We can't satisfy the read request right away, so we X * record the parameters of this read request and tell X * the caller to suspend itself... X */ X tp->tt_readsz = m_ptr->COUNT; X tp->tt_proc = m_ptr->m_source; X tp->tt_caller = m_ptr->PROC_NR; X tp->tt_usrbuf = (char *)(m_ptr->ADDRESS); X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, X SUSPEND, 0L, 0L); X } X X} X X X/************************************************************************ X * Function: X * copyout(tp, proc_nr, vaddr, nbytes) X * X * Description: X * Copy the specified number of bytes from the input queue X * to the specified user buffer. X * X * Arguments: X * tp Pointer to the TTY struct X * proc_nr Proc number of the user process to copy to X * vaddr Process virtual address of the destination X * nbytes Number of bytes to copy X * X * Return Value: X * Number of bytes copied if successful X * 0 if unsuccessful. X *************************************************************************/ XPRIVATE copyout(tp, proc_nr, vaddr, nbytes) XTTY_STRUCT * tp; Xchar proc_nr; Xchar * vaddr; Xint nbytes; X{ X phys_bytes user_phys, tty_phys; X int i; X int num_copied = 0; X char ch; X X user_phys = umap(proc_addr(proc_nr), D, (vir_bytes)vaddr, X (vir_bytes)nbytes); X if(user_phys == 0L) { X return (E_BAD_ADDR); X } X X tty_phys = umap(proc_addr(TTY), D, (vir_bytes)tty_sbuf, X (vir_bytes)nbytes); X X /* Take the bytes off the queue and stuff them into the staging X * buffer. X */ X for(i = 0; i < nbytes; i++) { X ch = dq_fifo_char(tp); X if(ch != MARKER || (tp->tt_uflags & (RAW | CBREAK))) { X tty_sbuf[num_copied++] = ch; X } X } X X phys_copy((long)tty_phys, (long)user_phys, (long)num_copied); X return(num_copied); X} X X/************************************************************************ X * Function: X * do_write(tp, m_ptr) X * X * Description: X * Handle a write request. X * X * Arguments: X * tp Pointer to the TTY structure on which the write is X * requested. X * m_ptr Pointer to the original message. X * X *************************************************************************/ XPRIVATE do_write(tp, m_ptr) Xregister TTY_STRUCT *tp; /* pointer to tty struct */ Xmessage *m_ptr; /* pointer to message sent to the task */ X{ X X int result; X int address; X int count; X int num_written; X int num_to_write; X X /* If we're suspended, then suspend this write */ X if(tp->tt_iflags & TT_SUSPENDED) { X tp->tt_iflags |= TT_WR_SUSP; X tp->tt_wproc = m_ptr->PROC_NR; X tp->tt_wcaller = m_ptr->m_source; X tp->tt_waddr = m_ptr->ADDRESS; X tp->tt_wcount = m_ptr->COUNT; X tp->tt_wrcount = m_ptr->COUNT; X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND, X 0L, 0L); X return; X } X X /* Otherwise, do the write */ X address = m_ptr->ADDRESS; X count = m_ptr->COUNT; X num_written = 0; X while(count > 0) { X num_to_write = (count > WRITE_CHUNK_SIZE) ? WRITE_CHUNK_SIZE : count; X result = tty_write(tp, (char)m_ptr->PROC_NR, X (vir_bytes)m_ptr->ADDRESS, (int)m_ptr->COUNT); X num_written += result; X count -= result; X address += result; X X /* If we're suspended, then suspend this write */ X if(tp->tt_iflags & TT_SUSPENDED) { X tp->tt_iflags |= TT_WR_SUSP; X tp->tt_wproc = m_ptr->PROC_NR; X tp->tt_wcaller = m_ptr->m_source; X tp->tt_waddr = address; X tp->tt_wcount = count; X tp->tt_wrcount = m_ptr->COUNT; X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, SUSPEND, X 0L, 0L); X return; X } X } X X X /* Signal the calling process that we're done, THEN flush X * the data in the device layer. This way, the caller doesn't X * get hung up waiting for the flush to complete. X */ X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, result, X 0L, 0L); X X (tp->tt_dev_oflush)(tp->tt_minor); X X} X X/************************************************************************ X * Function: X * tty_write(tp, uproc, uaddr, ucount) X * X * Description: X * Actually do the writing. This form of the function can be X * called from anywhere within the tty driver. X * X * Arguments: X * tp Pointer to the TTY structure on which the write is X * requested. X * uproc Process number of the user process doing the write X * uaddr Virtual address (in user space) of the data to write X * ucount Number of bytes to write. X * X *************************************************************************/ XPRIVATE tty_write(tp, uproc, uaddr, ucount) Xregister TTY_STRUCT *tp; /* pointer to tty struct */ Xchar uproc; Xvir_bytes uaddr; Xint ucount; X{ X phys_bytes user_phys, tty_phys; X int i, bytes_to_copy, bytes_written; X X bytes_written = 0; X X while(ucount > 0) { X X bytes_to_copy = MIN(ucount, TTY_IBUFSZ); X X /* Copy the data to be written into the TTY staging buffer */ X user_phys = umap(proc_addr(uproc), D, X (vir_bytes)uaddr, (vir_bytes)bytes_to_copy); X X if(user_phys == 0) { X return(E_BAD_ADDR); X } X X tty_phys = umap(proc_addr(TTY), D, (vir_bytes)tty_sbuf, X (vir_bytes)bytes_to_copy); X X /* Do the copy */ X X phys_copy((long)user_phys, (long)tty_phys, (long)bytes_to_copy); X X for(i = 0; i < bytes_to_copy; i++) { X /* Handle newlines if CRMOD set for this tty */ X if((tp->tt_uflags & CRMOD) && (tty_sbuf[i] == '\n')) { X (tp->tt_dev_putc)(tp->tt_minor, '\r'); X } X (tp->tt_dev_putc)(tp->tt_minor, tty_sbuf[i]); X } X X ucount -= bytes_to_copy; X uaddr += bytes_to_copy; X bytes_written += bytes_to_copy; X } X X return(bytes_written); X} X X X/************************************************************************ X * Function: X * do_ioctl(tp, m_ptr) X * X * Description: X * Perform IOCTL function. X * NOTE: This version of do_ioctl is severly limited, for X * compatibility with the existing filesystem interface. X * The filesystem interface should be redesigned so as X * to allow access to the full array of IOCTL functions. X * X * Arguments: X * tp Pointer to TTY struct to do ioctl on X * m_ptr Pointer to original message. X * X *************************************************************************/ XPRIVATE do_ioctl(tp, m_ptr) Xregister TTY_STRUCT *tp; /* pointer to TTY struct */ Xmessage *m_ptr; /* pointer to message sent to task */ 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 X X switch(m_ptr->TTY_REQUEST) { X case TIOCSETP: X /* Set erase, kill, and flags */ X tp->tt_erase = (char)((m_ptr->TTY_SPEK >> 8) & BYTE); X tp->tt_kill = (char)((m_ptr->TTY_SPEK >> 0) & BYTE); X tp->tt_uflags = (int)m_ptr->TTY_FLAGS; X X /* GROSS HACK for plug-compatibility: If we enter X * CBREAK mode on the console (device 0) then turn OFF X * line wrapping on the console. Otherwise, turn it on. X * We only do this for compatibility with the 1.1 version X * of MINED. If we fix MINED so it doesn't assume that X * writing past the end of the screen won't wrap, then we X * can pull this hack. X */ X if(tp->tt_minor == 0) { X if(tp->tt_uflags & CBREAK) { X connowrap(); X } X else { X conwrap(); X } X } X /* END GROSS HACK */ X break; X X case TIOCSETC: X /* set, intr, quit, xon, xoff, eof */ X tp->tt_intr = (char)((m_ptr->TTY_SPEK >> 24) & BYTE); X tp->tt_quit = (char)((m_ptr->TTY_SPEK >> 16) & BYTE); X tp->tt_resume = (char)((m_ptr->TTY_SPEK >> 8) & BYTE); X tp->tt_susp = (char)((m_ptr->TTY_SPEK >> 0) & BYTE); X /* tp->tt_eof not used */ X break; X X case TIOCGETP: X /* Get erase, kill, and flags */ X erase = ((long) tp->tt_erase) & BYTE; X kill = ((long) tp->tt_kill) & BYTE; X erki = (erase << 8) | kill; X flags = (long)tp->tt_uflags; X break; X X case TIOCGETC: X /* Get intr, quit, xon, xoff, eof. */ X intr = ((long) tp->tt_intr) & BYTE; X quit = ((long) tp->tt_quit) & BYTE; X xon = ((long) tp->tt_resume) & BYTE; X xoff = ((long) tp->tt_susp) & BYTE; X eof = 4; X erki = (intr << 24) | (quit << 16) | (xon << 8) | (xoff << 0); X flags = (eof << 8); X break; X X default: X r = EINVAL; X break; 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/************************************************************************ X * Function: X * do_cancel(tp, m_ptr) X * X * Description: X * Cancel any pending I/O on the terminal immediately. X * This is called when a process catches a signal. X * Causes the pending I/O to return with EINTR. X * X * Arguments: X * tp Poiner to the TTY structure involved. X * m_ptr Pointer to original message. X * X *************************************************************************/ X XPRIVATE do_cancel(tp, m_ptr) Xregister TTY_STRUCT *tp; /* pointer to TTY struct */ Xmessage *m_ptr; /* pointer to message sent to task */ X{ X /* Reply only if this process indeed has I/O pending */ X if(tp->tt_readsz == 0 && !(tp->tt_iflags & (TT_SUSPENDED | TT_WR_SUSP))) { X return; X } X X /* Kill any pending input */ X tp->tt_first = 0; X tp->tt_last = 0; X tp->tt_bufsz = 0; X X /* And any pending output */ X tp->tt_wcount = 0; X tp->tt_wrcount = 0; X X /* Cancel any outstanding read request */ X tp->tt_readsz = 0; X X /* Turn off XOFF */ X tp->tt_iflags &= ~(TT_SUSPENDED | TT_WR_SUSP); X X /* Reply */ X tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR, 0L, 0L); X} X X X X/************************************************************************ X * Function: X * tty_reply(code, replyee, proc_nr, status, extra, other) X * X * Description: X * Send a message in reply to the message we just processed. X * X * Arguments: X * code What kind of message to send, either TASK_REPLY or X * REVIVE X * replyee Destination process for the reply (Usually FS) X * proc_nr Originator process. X * status Reply code, either number of bytes processed or X * some error code. X * extra extra value (for IOCTL) X * other Another extra value (for IOCTL) X * 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 reply_ptr->m_type = code; X reply_ptr->REP_PROC_NR = proc_nr; X reply_ptr->REP_STATUS = status; X reply_ptr->TTY_FLAGS = extra; /* used by IOCTL for flags (mode) */ X reply_ptr->TTY_SPEK = other; /* used by IOCTL for erase and kill chars */ X X send(replyee, reply_ptr); X X} X X XPRIVATE tty_areply(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 an asynchronous reply to a process that wanted to read or write X * data. An asynchronous reply is one that is not directly in response X * to a request from the filesystem (e.g. reviving a process waiting for X * an I/O to complete). Such replies are sent to another task to be X * forwarded to their destination. This is to prevent deadlock conditions X * where the filesystem is sending us a message at the same time that X * we're sending a message for an asynchronous reply. X */ X X X reply_ptr->m_type = code; X reply_ptr->REP_PROC_NR = proc_nr; X reply_ptr->REP_STATUS = status; X reply_ptr->TTY_FLAGS = extra; /* used by IOCTL for flags (mode) */ X reply_ptr->TTY_SPEK = other; /* used by IOCTL for erase and kill chars */ X reply_ptr->m2_i3 = replyee; X X send(TTY_ASYNC, reply_ptr); X X} X Xtty_async_task() X{ X message async_message; X X /* This task is a simple message forwarder. It waits for a message, X * which should contain the REAL destination for the message in the X * m2_i3 field. It then sends the message to that destination. X */ X X while(TRUE) { X receive(TTY, &async_message); X X send(async_message.m2_i3, &async_message); X } X} X X/************************************************************************ X * Function: X * enque_char(tp, ch) X * X * Description: X * Enqueues a character on the specified device's input queue. X * If the queue becomes too full, turn flow control on. X * X * Arguments: X * tp Pointer to TTY struct for this device X * ch character to enqueue. X * X * Return Value: X * None X *************************************************************************/ XPRIVATE enque_char(tp, ch) XTTY_STRUCT * tp; Xchar ch; X{ X /* If the queue is full, drop the character on the floor */ X if(tp->tt_bufsz == TTY_IBUFSZ) { return; } X X if(!((tp->tt_uflags & (RAW | CBREAK)) || X (tp->tt_iflags & TT_LITNEXT)) && (ch == tp->tt_eot)) { X tp->tt_inbuf[tp->tt_last] = MARKER; X /* EOT counts as a newline for purposes of denoting end-of-line */ X tp->tt_last_nl = tp->tt_last; X } X else { X tp->tt_inbuf[tp->tt_last] = ch; X } X X if (ch == '\n') { X tp->tt_last_nl = tp->tt_last; X } X X tp->tt_last = (tp->tt_last + 1) % TTY_IBUFSZ; X tp->tt_bufsz++; X X /* If the queue got too full, turn on flow control */ X if(tp->tt_bufsz >= tp->tt_fc_on) { X if(!(tp->tt_iflags & TT_FC_ON)) { X tp->tt_iflags |= TT_FC_ON; X tp->tt_dev_fcon(tp->tt_minor); X } X } X} X X X/************************************************************************ X * Function: X * char dq_fifo_char(tp) X * X * Description: X * Dequeues a character from the device's character queue in a X * fifo fashion. This is normally done when presenting characters X * to a read command. X * X * Arguments: X * tp Pointer to this terminal device X * X * Return Value: X * The character taken off the queue. X * X * Note: X * If the queue is empty, this routine will return immediately with X * an indeterminate value. It is the responsibility of the CALLER X * to determine if there is something in the queue BEFORE calling X * this routine. X * X * If flow control is on and this routine causes us to fall below X * the flow control threshold, then flow control is turned off. X *************************************************************************/ XPRIVATE char dq_fifo_char(tp) XTTY_STRUCT * tp; X{ X char ch; X X /* If the queue is empty, don't bother. */ X if(tp->tt_bufsz == 0) { return; } X X ch = tp->tt_inbuf[tp->tt_first]; X X /* If we ate the last newline in the buffer, then note that X * we have none. X */ X if(tp->tt_first == tp->tt_last_nl) { tp->tt_last_nl = -1; } X tp->tt_first = (tp->tt_first + 1) % TTY_IBUFSZ; X tp->tt_bufsz--; X X /* See if we should turn off flow control */ X if((tp->tt_iflags & TT_FC_ON) && (tp->tt_bufsz <= tp->tt_fc_off)) { X tp->tt_iflags &= ~TT_FC_ON; X tp->tt_dev_fcoff(tp->tt_minor); X } X X return(ch); X} X X X/************************************************************************ X * Function: X * char dq_lifo_char(tp) X * X * Description: X * Dequeues a character from the device's character queue in a X * lifo fashion. This is normally done when editing the input line. X * X * Arguments: X * tp Pointer to this terminal device X * X * Return Value: X * The character taken off the queue. X * X * Note: X * If the queue is empty, this routine will return immediately with X * an indeterminate value. It is the responsibility of the CALLER X * to determine if there is something in the queue BEFORE calling X * this routine. X * X * If flow control is on and this routine causes us to fall below X * the flow control threshold, then flow control is turned off. X * X * Note that this routine DOES NOT BOTHER to check and see if we X * should reset tt_last_nl. This is because as of this writing X * ALL callers of this routine IMMEDIATELY put back any newlines X * that they remove; enque_char will thus handle this. X *************************************************************************/ XPRIVATE char dq_lifo_char(tp) XTTY_STRUCT * tp; X{ X char ch; X X /* If the queue is empty, don't bother. */ X if(tp->tt_bufsz == 0) { return; } X X if(--tp->tt_last < 0) { tp->tt_last = TTY_IBUFSZ - 1; } X ch = tp->tt_inbuf[tp->tt_last]; X tp->tt_bufsz--; X X /* See if we should turn off flow control */ X if((tp->tt_iflags & TT_FC_ON) && (tp->tt_bufsz <= tp->tt_fc_off)) { X tp->tt_iflags &= ~TT_FC_ON; X tp->tt_dev_fcoff(tp->tt_minor); X } X X return(ch); X} X X X/************************************************************************ X * Function: X * output_char(tp, ch) X * X * Description: X * Outputs the specified character to the specified device. X * Performs certain mappings for non-printing characters. X * X * Arguments: X * tp Pointer to TTY struct X * ch character to print out. X * X *************************************************************************/ XPRIVATE output_char(tp, ch) XTTY_STRUCT * tp; Xchar ch; X{ X if((ch > 27) || (ch == 9)) { X (tp->tt_dev_putc)(tp->tt_minor, ch); X } X else { X (tp->tt_dev_putc)(tp->tt_minor, '^'); X (tp->tt_dev_putc)(tp->tt_minor, ch + '@'); X } X} X X/************************************************************************ X * Function: X * putc(c) X * X * Description: X * Kernel-internal version of 'putc'. Writes the specified X * character to the system console. X * X * Arguments: X * c Character to write. X *************************************************************************/ XPUBLIC putc(c) Xchar c; X{ X if(c == '\n') { con_putc(0, '\r'); } X con_putc(0, c); X con_oflush(0); X} X Xtty_sig(tp, sig) XTTY_STRUCT * tp; Xint sig; X{ X /* THIS IS A HACK for now... the PROPER thing to do is to go X * through the filesystem to find the process for which this X * is the control terminal. NOTE: If/when we ever do job control, X * we'll have to do some special-case stuff because we may not X * want to cancel pending I/O... X */ X /* Kill any pending input */ X tp->tt_first = 0; X tp->tt_last = 0; X tp->tt_bufsz = 0; X X /* And any pending output */ X tp->tt_wcount = 0; X tp->tt_wrcount = 0; X X /* Send an EINTR message to any procs with I/O pending... */ X if(tp->tt_readsz != 0) { X tty_areply(REVIVE, tp->tt_proc, tp->tt_caller, EINTR, 0L, 0L); X tp->tt_readsz = 0; X } X if(tp->tt_iflags & TT_WR_SUSP) { X tty_areply(REVIVE, tp->tt_wcaller, tp->tt_wproc, EINTR, 0L, 0L); X } X X /* Turn off XOFF */ X tp->tt_iflags &= ~(TT_SUSPENDED | TT_WR_SUSP); X X cause_sig(LOW_USER + 1 + tp->tt_minor, sig); X} X / echo x - tty.h gres '^X' '' > tty.h << '/' 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 /* Marker for EOT */ X#define WERA_CHAR (char) 027 /* default word-erase char (CTRL-W) */ X#define STOP_CHAR (char) 032 /* default stop char (CTRL-Z) */ X#define RDSP_CHAR (char) 022 /* default redisplay char (CTRL-R) */ X#define LNXT_CHAR '\\' /* default literal-next char */ X X#define WRITE_CHUNK_SIZE 16 X X/* The TTY structure, one per active TTY */ X X#define TTY_IBUFSZ 1536 X X/* Turn flow control on when buffer is 3/4 full */ X#define TTY_FC_ON ((TTY_IBUFSZ >> 1) + (TTY_IBUFSZ >> 2)) X X/* Turn it back off when buffer is 5/8 full */ X#define TTY_FC_OFF ((TTY_IBUFSZ >> 1) + (TTY_IBUFSZ >> 3)) X Xtypedef struct { X int tt_minor; /* Minor device number */ X char tt_inbuf[TTY_IBUFSZ]; /* Input buffer */ X int tt_first; /* Index of first char in buf */ X int tt_last; /* Index of next avail slot */ X int tt_bufsz; /* Number of chars in buffer */ X int tt_last_nl; /* Last newline in buffer. */ X int tt_readsz; /* Size of outstanding read */ X char tt_proc; /* Process waiting on read */ X char tt_caller; /* Caller of blocking read (FS) */ X char *tt_usrbuf; /* virtual addr of user buffer */ X char tt_wproc; /* Proc waiting on write */ X char tt_wcaller; /* Caller of blocking write (FS) */ X char *tt_waddr; /* Address of waited write */ X int tt_wcount; /* Count of waited write */ X int tt_wrcount; /* Total count of waited write */ X int tt_fc_on; /* Num chars to turn on flow ctl */ X int tt_fc_off; /* Num chars to turn OFF flow ctl */ X char tt_erase; /* Erase char */ X char tt_kill; /* Kill char */ X char tt_wera; /* Word-erase char */ X char tt_intr; /* Interrupt char */ X char tt_quit; /* Quit char */ X char tt_stop; /* Stop char */ X char tt_susp; /* XOFF char */ X char tt_resume; /* XON char */ X char tt_rprnt; /* Redisplay char */ X char tt_lnext; /* Lit next char */ X char tt_eot; /* EOT char */ X char tt_uflags; /* User-visible flags */ X char tt_iflags; /* Internal flags */ X X /* The following are the routine entry points into the X * device layer for the device associated with this TTY. X */ X X void (*tt_dev_init)(); /* Device initialization */ X void (*tt_dev_putc)(); /* Write a char to device */ X void (*tt_dev_ioctl)(); /* perform device ioctl */ X void (*tt_dev_fcon)(); /* Turn flow control on */ X void (*tt_dev_fcoff)(); /* Turn flow control off */ X void (*tt_dev_oflush)(); /* Flush any pending output */ X int (*tt_dev_bufin)(); /* Get buffered input */ X} TTY_STRUCT; X X/* Number of console devices */ X#define NUM_CONSOLES 1 X X/* Number of serial devices supported. This parameter is used both by X * tty.c and rs232.c. The current tty driver supports up to FOUR such X * devices. (COM1: through COM4: in IBM-PC parlance) X */ X#define NUM_SERIAL_DEV 1 X X/* Total number of TTYs */ X#define NUM_TTYS (NUM_CONSOLES + NUM_SERIAL_DEV) X X/* Internal flags definitions */ X#define TT_LITNEXT 1 X#define TT_FC_ON 2 X#define TT_SUSPENDED 4 X#define TT_WR_SUSP 8 X X X X /