Path: utzoo!mnetor!uunet!husc6!linus!encore!paradis From: paradis@encore.UUCP (Jim Paradis) Newsgroups: comp.os.minix Subject: Serial TTY driver (Part 2 of 2) Message-ID: <2461@encore.UUCP> Date: 7 Jan 88 15:11:04 GMT Organization: The Whizzo Chocolate Company Lines: 2107 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 X#include X#include 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 /