*** /dev/null	Thu Mar 19 11:10:38 1992
--- include/asm/bitops.h	Mon Oct  5 01:24:18 1992
***************
*** 0 ****
--- 1,96 ----
+ #ifndef _ASM_BITOPS_H
+ /*
+  * Copyright 1992, Linus Torvalds.
+  */
+ 
+ #ifdef i386
+ /*
+  * These have to be done with inline assembly: that way the bit-setting
+  * is guaranteed to be atomic. Both set_bit and clear_bit return 0
+  * if the bit-setting went ok, != 0 if the bit already was set/cleared.
+  *
+  * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+  */
+ extern inline int set_bit(int nr,int * addr)
+ {
+ 	char ok;
+ 
+ 	__asm__ __volatile__("btsl %1,%2\n\tsetb %0":
+ 		"=q" (ok):"r" (nr),"m" (*(addr)));
+ 	return ok;
+ }
+ 
+ extern inline int clear_bit(int nr, int * addr)
+ {
+ 	char ok;
+ 
+ 	__asm__ __volatile__("btrl %1,%2\n\tsetnb %0":
+ 		"=q" (ok):"r" (nr),"m" (*(addr)));
+ 	return ok;
+ }
+ 
+ /*
+  * This routine doesn't need to be atomic, but it's faster to code it
+  * this way.
+  */
+ extern inline int test_bit(int nr, int * addr)
+ {
+ 	char ok;
+ 
+ 	__asm__ __volatile__("btl %1,%2\n\tsetb %0":
+ 		"=q" (ok):"r" (nr),"m" (*(addr)));
+ 	return ok;
+ }
+ 
+ #else
+ /*
+  * For the benefit of those who are trying to port Linux to another
+  * architecture, here are some C-language equivalents.  You should
+  * recode these in the native assmebly language, if at all possible.
+  * To guarantee atomicity, these routines call cli() and sti() to
+  * disable interrupts while they operate.  (You have to provide inline
+  * routines to cli() and sti().)
+  *
+  * Also note, these routines assume that you have 32 bit integers.
+  * You will have to change this if you are trying to port Linux to the
+  * Alpha architecture or to a Cray.  :-)
+  * 
+  * C language equivalents written by Theodore Ts'o, 9/26/92
+  */
+ 
+ extern inline int set_bit(int nr,int * addr)
+ {
+ 	int	mask, retval;
+ 
+ 	addr += nr >> 5;
+ 	mask = 1 << (nr & 0x1f);
+ 	cli();
+ 	retval = (mask & *addr) != 0;
+ 	*addr |= mask;
+ 	sti();
+ 	return retval;
+ }
+ 
+ extern inline int clear_bit(int nr, int * addr)
+ {
+ 	int	mask, retval;
+ 
+ 	addr += nr >> 5;
+ 	mask = 1 << (nr & 0x1f);
+ 	cli();
+ 	retval = (mask & *addr) == 0;
+ 	*addr &= ~mask;
+ 	sti();
+ 	return retval;
+ }
+ 
+ extern inline int test_bit(int nr, int * addr)
+ {
+ 	int	mask;
+ 
+ 	addr += nr >> 5;
+ 	mask = 1 << (nr & 0x1f);
+ 	return ((mask & *addr) != 0);
+ }
+ #endif	/* i386 */
+ #endif /* _ASM_BITOPS_H */
===================================================================
RCS file: include/linux/RCS/termios.h,v
retrieving revision 1.1
diff -c -r1.1 include/linux/termios.h
*** 1.1	1992/10/05 01:25:34
--- include/linux/termios.h	1992/10/05 01:25:40
***************
*** 174,180 ****
  #define HUPCL	0002000
  #define CLOCAL	0004000
  #define CIBAUD	03600000		/* input baud rate (not used) */
! #define CRTSCTS	020000000000		/* flow control */
  
  /* c_lflag bits */
  #define ISIG	0000001
--- 174,181 ----
  #define HUPCL	0002000
  #define CLOCAL	0004000
  #define CIBAUD	03600000		/* input baud rate (not used) */
! #define CNORTSCTS 010000000000		/* no flow control */
! #define CRTSCTS	  020000000000		/* flow control */
  
  /* c_lflag bits */
  #define ISIG	0000001
===================================================================
RCS file: include/linux/RCS/tty.h,v
retrieving revision 1.1
diff -c -r1.1 include/linux/tty.h
*** 1.1	1992/10/05 01:23:18
--- include/linux/tty.h	1992/10/05 01:23:30
***************
*** 93,98 ****
--- 93,99 ----
   */
  #define ASYNC_NOSCRATCH	0x0001	/* 16XXX UART with no scratch register */
  #define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+ #define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
  
  #define ASYNC_SPD_MASK	0x0030
  #define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
***************
*** 148,153 ****
--- 149,155 ----
  #define I_CRNL(tty)	_I_FLAG((tty),ICRNL)
  #define I_NOCR(tty)	_I_FLAG((tty),IGNCR)
  #define I_IXON(tty)	_I_FLAG((tty),IXON)
+ #define I_IXANY(tty)	_I_FLAG((tty),IXANY)
  #define I_STRP(tty)	_I_FLAG((tty),ISTRIP)
  
  #define O_POST(tty)	_O_FLAG((tty),OPOST)
***************
*** 221,239 ****
   * Again, the low-level driver is free to ignore any of these, and has
   * to implement RQ_THREHOLD_LW for itself if it wants it.
   */
! #define SQ_THRESHOLD_LW	0
  #define SQ_THRESHOLD_HW 768
! #define RQ_THRESHOLD_LW 64
  #define RQ_THRESHOLD_HW 768
  
  /*
-  * so that interrupts won't be able to mess up the
-  * queues, copy_to_cooked must be atomic with repect
-  * to itself, as must tty->write. These are the flag
-  * bit-numbers. Use the set_bit() and clear_bit()
-  * macros to make it all atomic.
-  * 
   * These bits are used in the flags field of the tty structure.
   */
  #define TTY_WRITE_BUSY 0
  #define TTY_READ_BUSY 1
--- 223,240 ----
   * Again, the low-level driver is free to ignore any of these, and has
   * to implement RQ_THREHOLD_LW for itself if it wants it.
   */
! #define SQ_THRESHOLD_LW	16
  #define SQ_THRESHOLD_HW 768
! #define RQ_THRESHOLD_LW 16
  #define RQ_THRESHOLD_HW 768
  
  /*
   * These bits are used in the flags field of the tty structure.
+  * 
+  * So that interrupts won't be able to mess up the queues,
+  * copy_to_cooked must be atomic with repect to itself, as must
+  * tty->write.  Thus, you must use the inline functions set_bit() and
+  * clear_bit() to make things atomic.
   */
  #define TTY_WRITE_BUSY 0
  #define TTY_READ_BUSY 1
***************
*** 241,269 ****
  #define TTY_SQ_THROTTLED 3
  #define TTY_RQ_THROTTLED 4
  
- /*
-  * These have to be done with inline assembly: that way the bit-setting
-  * is guaranteed to be atomic. Both set_bit and clear_bit return 0
-  * if the bit-setting went ok, != 0 if the bit already was set/cleared.
-  */
- extern inline int set_bit(int nr,int * addr)
- {
- 	char ok;
- 
- 	__asm__ __volatile__("btsl %1,%2\n\tsetb %0":
- 		"=q" (ok):"r" (nr),"m" (*(addr)));
- 	return ok;
- }
- 
- extern inline int clear_bit(int nr, int * addr)
- {
- 	char ok;
- 
- 	__asm__ __volatile__("btrl %1,%2\n\tsetnb %0":
- 		"=q" (ok):"r" (nr),"m" (*(addr)));
- 	return ok;
- }
- 
  #define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
  #define TTY_READ_FLUSH(tty) tty_read_flush((tty))
  
--- 242,247 ----
***************
*** 303,308 ****
--- 281,287 ----
  extern int is_ignored(int sig);
  extern int tty_signal(int sig, struct tty_struct *tty);
  extern int kill_pg(int pgrp, int sig, int priv);
+ extern void do_SAK(struct tty_struct *tty);
  
  /* tty write functions */
  
===================================================================
RCS file: include/linux/RCS/serial.h,v
retrieving revision 1.1
diff -c -r1.1 include/linux/serial.h
*** 1.1	1992/10/05 01:23:56
--- include/linux/serial.h	1992/10/05 01:24:03
***************
*** 45,54 ****
--- 45,66 ----
  	int			timeout;
  	int			xmit_fifo_size;
  	int			custom_divisor;
+ 	int			x_char;	/* xon/xoff characater */
+ 	int			event;
  	int			line;
  };
  
  /*
+  * Events are used to schedule things to happen at timer-interrupt
+  * time, instead of at rs interrupt time.
+  */
+ #define RS_EVENT_READ_PROCESS	0
+ #define RS_EVENT_WRITE_WAKEUP	1
+ #define RS_EVENT_HUP_PGRP	2
+ #define RS_EVENT_BREAK_INT	3
+ #define RS_EVENT_DO_SAK		4
+ 
+ /*
   * These are the UART port assignments, expressed as offsets from the base
   * register.  These assignments should hold for any serial port based on
   * a 8250, 16450, or 16550(A).
***************
*** 113,119 ****
  /*
   * These are the definitions for the Interrupt Indentification Register
   */
! #define UART_IIR_PEND	0x01	/* Interrupt pending */
  #define UART_IIR_ID	0x06	/* Mask for the interrupt ID */
  
  #define UART_IIR_MSI	0x00	/* Modem status interrupt */
--- 125,131 ----
  /*
   * These are the definitions for the Interrupt Indentification Register
   */
! #define UART_IIR_NO_INT	0x01	/* No interrupts pending */
  #define UART_IIR_ID	0x06	/* Mask for the interrupt ID */
  
  #define UART_IIR_MSI	0x00	/* Modem status interrupt */
***************
*** 149,151 ****
--- 161,164 ----
  #define UART_MSR_TERI	0x04	/* Trailing edge ring indicator */
  #define UART_MSR_DDSR	0x02	/* Delta DSR */
  #define UART_MSR_DCTS	0x01	/* Delta CTS */
+ #define UART_MSR_ANY_DELTA 0x0F	/* Any of the delta bits! */
===================================================================
RCS file: kernel/chr_drv/RCS/serial.c,v
retrieving revision 1.1
diff -c -r1.1 kernel/chr_drv/serial.c
*** 1.1	1992/10/05 01:28:27
--- kernel/chr_drv/serial.c	1992/10/06 11:29:27
***************
*** 27,45 ****
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/segment.h>
  
  #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
  
  /*
!  * rs_read_process	- Bitfield of serial lines that have I/O processing
!  *				to be done at the next clock tick.
!  * rs_write_timeout	- Bitfield of serial lines that have a possible 
!  *				write timeout pending.  (In case the THRE
!  *				interrupt gets lost.)
   */
! static unsigned long rs_read_process = 0; 
! static unsigned long rs_write_timeout = 0;
  
  static void UART_ISR_proc(async_ISR ISR, int line);
  static void FourPort_ISR_proc(async_ISR ISR, int line);
  
--- 27,52 ----
  #include <asm/system.h>
  #include <asm/io.h>
  #include <asm/segment.h>
+ #include <asm/bitops.h>
  
  #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
+ #define AUTO_IRQ
  
  /*
!  * rs_event		- Bitfield of serial lines that events pending
!  * 				to be processed at the next clock tick.
!  * rs_write_active	- Bitfield of serial lines that are actively
!  * 				transmitting (and therefore have a
!  * 				write timeout pending, in case the
!  * 				THRE interrupt gets lost.)
!  * IRQ_ISR[]		- Array to store the head of the ISR linked list
!  * 				for each IRQ.
   */
! static unsigned long rs_event = 0;
! static unsigned long rs_write_active = 0;
  
+ static async_ISR IRQ_ISR[16];
+ 
  static void UART_ISR_proc(async_ISR ISR, int line);
  static void FourPort_ISR_proc(async_ISR ISR, int line);
  
***************
*** 85,93 ****
  
  #define NR_PORTS	(sizeof(rs_table)/sizeof(struct async_struct))
  
- static async_ISR IRQ_ISR[16];	/* Used to store the head of the */
- 				/* ISR linked list chain for each IRQ */
- 
  /*
   * This is used to figure out the divsor speeds and the timeouts
   */
--- 92,97 ----
***************
*** 95,100 ****
--- 99,108 ----
  	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
  	9600, 19200, 38400, 56000, 115200, 0 };
  
+ static void startup(struct async_struct * info);
+ static void shutdown(struct async_struct * info);
+ static void rs_throttle(struct tty_struct * tty, int status);
+ 
  static void send_break(	struct async_struct * info)
  {
  	unsigned short port;
***************
*** 106,250 ****
  	current->timeout = jiffies + 25;
  	outb_p(inb_p(port) | UART_LCR_SBC, port);
  	schedule();
! 	outb_p(inb_p(port) & ~UART_LCR_SBC, port);
! }
! 
! /*
!  * There are several races here: we avoid most of them by disabling
!  * timer_active for the crucial part of the process.. That's a good
!  * idea anyway. 
!  *
!  * The problem is that we have to output characters /both/ from interrupts
!  * and from the normal write: the latter to be sure the interrupts start up
!  * again. With serial lines, the interrupts can happen so often that the
!  * races actually are noticeable.
!  */
! static void send_intr(struct async_struct * info)
! {
! 	unsigned short port = info->port;
! 	int line = info->line;
! 	struct tty_queue * queue = &info->tty->write_q;
! 	int c, count = 0;
! 
! 	if (info->tty->stopped)
! 		return;
! 
! 	if (info->type == PORT_16550A)
! 		count = 16;	
! 	else
! 		count = 1;
! 	
! 	rs_write_timeout &= ~(1 << line);
! 
! 	if (inb_p(UART_LSR + info->port) & UART_LSR_THRE) {
! 		while (count-- && !info->tty->stopped) {
! 			if (queue->tail == queue->head)
! 				goto end_send;
! 			c = queue->buf[queue->tail];
! 			queue->tail++;
! 			queue->tail &= TTY_BUF_SIZE-1;
! 			outb(c, UART_TX + port);
! 		}
! 	}
! 	info->timer = jiffies + info->timeout;
! 	if (info->timer < timer_table[RS_TIMER].expires)
! 		timer_table[RS_TIMER].expires = info->timer;
! 	rs_write_timeout |= 1 << line;
! 	timer_active |= 1 << RS_TIMER;
! end_send:
! 	if (LEFT(queue) > WAKEUP_CHARS)
! 		wake_up(&queue->proc_list);
  }
  
! static void receive_intr(struct async_struct * info)
  {
! 	unsigned short port = info->port;
! 	struct tty_queue * queue = &info->tty->read_q;
! 	int head = queue->head;
! 	int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1);
! 	int count = 0;
! 
! 	rs_read_process &= ~(1 << info->line);
! 	do {
! 		count++;
! 		queue->buf[head] = inb(UART_TX + port);
! 		if (head != maxhead) {
! 			head++;
! 			head &= TTY_BUF_SIZE-1;
! 		}
! 	} while (inb(UART_LSR + port) & UART_LSR_DR);
! 	queue->head = head;
! 	rs_read_process |= 1 << info->line;
  	timer_table[RS_TIMER].expires = 0;
! 	timer_active |= 1<<RS_TIMER;
! }
! 
! static void line_status_intr(struct async_struct * info)
! {
! 	unsigned char status = inb(UART_LSR + info->port);
! 
! /*	printk("line status: %02x\n",status);  */
! }
! 
! static void modem_status_intr(struct async_struct * info)
! {
! 	unsigned char status = inb(UART_MSR + info->port);
! 
! 	if (!(info->tty->termios->c_cflag & CLOCAL)) {
! 		if (((status & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD)
! 		    && info->tty->pgrp > 0)
! 			kill_pg(info->tty->pgrp,SIGHUP,1);
! 
! 		if (info->tty->termios->c_cflag & CRTSCTS)
! 			info->tty->stopped = !(status & UART_MSR_CTS);
! 
! 		if (!info->tty->stopped)
! 			send_intr(info);
! 	}
  }
  
- static void (*jmp_table[4])(struct async_struct *) = {
- 	modem_status_intr,
- 	send_intr,
- 	receive_intr,
- 	line_status_intr
- };
- 
  /*
   * This ISR handles the COM1-4 8250, 16450, and 16550A UART's.  It is
   * also called by the FourPort ISR, since the FourPort also uses the
   * same National Semiconduct UART's, with some interrupt multiplexing
   * thrown in.
   */
  static void UART_ISR_proc(async_ISR ISR, int line)
  {
! 	unsigned char ident;
  	struct async_struct * info = rs_table + line;
  
  	if (!info || !info->tty || !info->port)
- 		  return;
- 	while (1) {
- 		ident = inb(UART_IIR + info->port) & 7;
- 		if (ident & 1)
- 			return;
- 		ident = ident >> 1;
- 		jmp_table[ident](info);
- 	}
- }
- 
- /*
-  * Again, we disable interrupts to be sure there aren't any races:
-  * see send_intr for details.
-  */
- static inline void do_rs_write(struct async_struct * info)
- {
- 	if (!info->tty || !info->port)
  		return;
! 	if (EMPTY(&info->tty->write_q))
! 		return;
! 	cli();
! 	send_intr(info);
! 	sti();
  }
  
  /*
--- 114,267 ----
  	current->timeout = jiffies + 25;
  	outb_p(inb_p(port) | UART_LCR_SBC, port);
  	schedule();
! 	outb(inb_p(port) & ~UART_LCR_SBC, port);
  }
  
! static inline void rs_sched_event(int line,
! 				  struct async_struct *info,
! 				  int event)
  {
! 	info->event |= 1 << event;
! 	rs_event |= 1 << line;
  	timer_table[RS_TIMER].expires = 0;
! 	timer_active |= 1 << RS_TIMER;
  }
  
  /*
   * This ISR handles the COM1-4 8250, 16450, and 16550A UART's.  It is
   * also called by the FourPort ISR, since the FourPort also uses the
   * same National Semiconduct UART's, with some interrupt multiplexing
   * thrown in.
+  * 
+  * This routine assumes nobody else will be mucking with the tty
+  * queues its working on.  It should be called with the interrupts
+  * disabled, since it is not reentrant, and it assumes it doesn't need
+  * to worry about other routines mucking about its data structures
+  * while it keeps copies of critical pointers in registers.
   */
  static void UART_ISR_proc(async_ISR ISR, int line)
  {
! 	unsigned char status;
  	struct async_struct * info = rs_table + line;
+ 	struct tty_queue * queue;
+ 	int head, tail, count, ch;
+ 	int cflag, iflag;
+ 	
+ 	/*
+ 	 * Just like the LEFT(x) macro, except it uses the loal tail
+ 	 * and head variables.
+ 	 */
+ #define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))
  
  	if (!info || !info->tty || !info->port)
  		return;
! 	cflag = info->tty->termios->c_cflag;
! 	iflag = info->tty->termios->c_iflag;
! 	
! 	do {
! 	restart:
! 		status = inb(UART_LSR + info->port);
! 		if (status & UART_LSR_DR) {
! 			queue = &info->tty->read_q;
! 			head = queue->head;
! 			tail = queue->tail;
! 			do {
! 				ch = inb(UART_RX + info->port);
! 				/*
! 				 * There must be at least 3 characters
! 				 * free in the queue; otherwise we punt.
! 				 */
! 				if (VLEFT < 3)
! 					continue;
! 				if (status & (UART_LSR_BI |
! 					      UART_LSR_FE |
! 					      UART_LSR_PE)) {
! 					if (status & (UART_LSR_BI)) {
! 						if (info->flags & ASYNC_SAK)
! 			rs_sched_event(line, info, RS_EVENT_DO_SAK);
! 						else if (iflag & IGNBRK)
! 							continue;
! 						else if (iflag & BRKINT) 
! 			rs_sched_event(line, info, RS_EVENT_BREAK_INT);
! 						else
! 							ch = 0;
! 					} else if (iflag & IGNPAR)
! 						continue;
! 					if (iflag & PARMRK) {
! 						queue->buf[head++] = 0xff;
! 						head &= TTY_BUF_SIZE-1;
! 						queue->buf[head++] = 0;
! 						head &= TTY_BUF_SIZE-1;
! 					} else
! 						ch = 0;
! 				} else if ((iflag & PARMRK) && (ch == 0xff)) {
! 					queue->buf[head++] = 0xff;
! 					head &= TTY_BUF_SIZE-1;
! 				}
! 				queue->buf[head++] = ch;
! 				head &= TTY_BUF_SIZE-1;
! 			} while ((status = inb(UART_LSR + info->port)) &
! 				 UART_LSR_DR);
! 			queue->head = head;
! 			if ((VLEFT < RQ_THRESHOLD_LW)
! 			    && !set_bit(TTY_RQ_THROTTLED, &info->tty->flags)) 
! 				rs_throttle(info->tty, TTY_THROTTLE_RQ_FULL);
! 			rs_sched_event(line, info, RS_EVENT_READ_PROCESS);
! 		}
! 		if ((status & UART_LSR_THRE) &&
! 		    !info->tty->stopped) {
! 			queue = &info->tty->write_q;
! 			head = queue->head;
! 			tail = queue->tail;
! 			if (head==tail && !info->x_char)
! 				goto no_xmit;
! 			if (info->x_char) {
! 				outb_p(info->x_char, UART_TX + info->port);
! 				info->x_char = 0;
! 			} else {
! 				count = info->xmit_fifo_size;
! 				while (count--) {
! 					if (tail == head)
! 						break;
! 					outb_p(queue->buf[tail++],
! 					       UART_TX + info->port);
! 					tail &= TTY_BUF_SIZE-1;
! 				}
! 			}
! 			queue->tail = tail;
! 			if (VLEFT > WAKEUP_CHARS)
! 				rs_sched_event(line, info,
! 					       RS_EVENT_WRITE_WAKEUP);
! 			info->timer = jiffies + info->timeout;
! 			if (info->timer < timer_table[RS_TIMER].expires)
! 				timer_table[RS_TIMER].expires = info->timer;
! 			rs_write_active |= 1 << line;
! 			timer_active |= 1 << RS_TIMER;
! 		}
! 	no_xmit:
! 		status = inb(UART_MSR + info->port);
! 		
! 		if (!(cflag & CLOCAL) && (status & UART_MSR_DDCD)) {
! 			if (!(status & UART_MSR_DCD))
! 				rs_sched_event(line, info, RS_EVENT_HUP_PGRP);
! 		}
! 		/*
! 		 * Because of the goto statement, this block must be
! 		 * last.  We have to skip the do/while test condition
! 		 * because the THRE interrupt has probably been lost.
! 		 */
! 		if ((cflag & CRTSCTS) ||
! 		    ((status & UART_MSR_DSR) &&
! 		     !(cflag & CNORTSCTS))) {
! 			if (info->tty->stopped) {
! 				if (status & UART_MSR_CTS) {
! 					info->tty->stopped = 0;
! 					goto restart;
! 				}
! 			} else 
! 				info->tty->stopped = !(status & UART_MSR_CTS);
! 		}
! 	} while (!(inb(UART_IIR + info->port) & UART_IIR_NO_INT));
  }
  
  /*
***************
*** 256,266 ****
  	unsigned char ivec;
  
  	ivec = ~inb(ISR->port) & 0x0F;
! 	for (i = line; ivec; i++) {
! 		if (ivec & 1)
! 			UART_ISR_proc(ISR, i);
! 		ivec = ivec >> 1;
! 	}
  }
  
  /*
--- 273,286 ----
  	unsigned char ivec;
  
  	ivec = ~inb(ISR->port) & 0x0F;
! 	do {
! 		for (i = line; ivec; i++) {
! 			if (ivec & 1)
! 				UART_ISR_proc(ISR, i);
! 			ivec = ivec >> 1;
! 		}
! 		ivec = ~inb(ISR->port) & 0x0F;
! 	} while (ivec);
  }
  
  /*
***************
*** 276,281 ****
--- 296,315 ----
  	}
  }
  
+ #ifdef AUTO_IRQ
+ /*
+  * This is the serial driver's interrupt routine while we are probing
+  * for submarines.
+  */
+ static volatile int rs_irq_triggered;
+ 
+ static void rs_probe(int irq)
+ {
+ 	rs_irq_triggered = irq;
+ 	return;
+ }
+ #endif
+ 
  /*
   * This subroutine handles all of the timer functionality required for
   * the serial ports.
***************
*** 291,309 ****
  	info = rs_table;
  	next_timeout = END_OF_TIME;
  	for (mask = 1 ; mask ; info++, mask <<= 1) {
! 		if ((mask > rs_read_process) &&
! 		    (mask > rs_write_timeout))
  			break;
! 		if (mask & rs_read_process) {
! 			TTY_READ_FLUSH(info->tty);
! 			rs_read_process &= ~mask;
! 		}
! 		if (mask & rs_write_timeout) {
! 			if (info->timer > jiffies) {
! 				rs_write_timeout &= ~mask;
! 				do_rs_write(info);
  			}
! 			if ((mask & rs_write_timeout) &&
  			    (info->timer < next_timeout))
  				next_timeout = info->timer;
  		}
--- 325,366 ----
  	info = rs_table;
  	next_timeout = END_OF_TIME;
  	for (mask = 1 ; mask ; info++, mask <<= 1) {
! 		if ((mask > rs_event) &&
! 		    (mask > rs_write_active))
  			break;
! 		if (mask & rs_event) {
! 			if (!clear_bit(RS_EVENT_READ_PROCESS, &info->event)) {
! 				TTY_READ_FLUSH(info->tty);
! 			}
! 			if (!clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
! 				wake_up(&info->tty->write_q.proc_list);
! 			}
! 			if (!clear_bit(RS_EVENT_HUP_PGRP, &info->event)) {
! 				if (info->tty->pgrp > 0)
! 					kill_pg(info->tty->pgrp,SIGHUP,1);
! 			}
! 			if (!clear_bit(RS_EVENT_BREAK_INT, &info->event)) {
! 				flush_input(info->tty);
! 				flush_output(info->tty);
! 				if (info->tty->pgrp > 0)
! 					kill_pg(info->tty->pgrp,SIGINT,1);
! 			}
! 			if (!clear_bit(RS_EVENT_DO_SAK, &info->event)) {
! 				do_SAK(info->tty);
! 			}
! 			cli();
! 			if (info->event) 
! 				next_timeout = 0;
! 			else
! 				rs_event &= ~mask;
! 			sti();
! 		}
! 		if (mask & rs_write_active) {
! 			if (info->timer <= jiffies) {
! 				rs_write_active &= ~mask;
! 				rs_write(info->tty);
  			}
! 			if ((mask & rs_write_active) &&
  			    (info->timer < next_timeout))
  				next_timeout = info->timer;
  		}
***************
*** 310,385 ****
  	}
  	if (next_timeout != END_OF_TIME) {
  		timer_table[RS_TIMER].expires = next_timeout;
  		timer_active |= 1 << RS_TIMER;
  	}
  }
  
! static void init(struct async_struct * info)
  {
! 	unsigned char status1, status2, scratch, scratch2;
! 	unsigned short port = info->port;
  
! 	/* 
! 	 * Check to see if a UART is really there.  
! 	 */
! 	scratch = inb_p(UART_MCR + port);
! 	outb_p(UART_MCR_LOOP | scratch, UART_MCR + port);
! 	scratch2 = inb_p(UART_MSR + port);
! 	outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
! 	status1 = inb_p(UART_MSR + port) & 0xF0;
! 	outb_p(scratch, UART_MCR + port);
! 	outb_p(scratch2, UART_MSR + port);
! 	if (status1 != 0x90) {
! 		info->type = PORT_UNKNOWN;
  		return;
  	}
  	
- 	if (!(info->flags & ASYNC_NOSCRATCH)) {
- 		scratch = inb(UART_SCR + port);
- 		outb_p(0xa5, UART_SCR + port);
- 		status1 = inb(UART_SCR + port);
- 		outb_p(0x5a, UART_SCR + port);
- 		status2 = inb(UART_SCR + port);
- 		outb_p(scratch, UART_SCR + port);
-         } else {
- 	  	status1 = 0xa5;
- 		status2 = 0x5a;
- 	}
- 	if (status1 == 0xa5 && status2 == 0x5a) {
- 		outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
- 		scratch = inb(UART_IIR + port) >> 6;
- 		info->xmit_fifo_size = 1;
- 		switch (scratch) {
- 			case 0:
- 				info->type = PORT_16450;
- 				break;
- 			case 1:
- 				info->type = PORT_UNKNOWN;
- 				break;
- 			case 2:
- 				info->type = PORT_16550;
- 				break;
- 			case 3:
- 				info->type = PORT_16550A;
- 				info->xmit_fifo_size = 16;
- 				break;
- 		}
- 	} else
- 		info->type = PORT_8250;
- 	change_speed(info->line);
- 	outb_p(0x00,		UART_IER + port);	/* disable all intrs */
- 	outb_p(0x00,		UART_MCR + port);  /* reset DTR,RTS,OUT_2 */
- 	(void)inb(UART_RX + port);     /* read data port to reset things (?) */
  }
  
! /*
!  * This routine gets called when tty_write has put something into
!  * the write_queue. It must check wheter the queue is empty, and
!  * set the interrupt register accordingly
!  */
! void rs_write(struct tty_struct * tty)
  {
! 	do_rs_write(rs_table+DEV_TO_SL(tty->line));
  }
  
  /*
--- 367,443 ----
  	}
  	if (next_timeout != END_OF_TIME) {
  		timer_table[RS_TIMER].expires = next_timeout;
+ #ifdef i386
+ 		/*
+ 		 * This must compile to a single, atomic instruction.
+ 		 * It does using 386 with GCC; if you're not sure, use
+ 		 * the set_bit function, which is supposed to be atomic.
+ 		 */
  		timer_active |= 1 << RS_TIMER;
+ #else
+ 		set_bit(RS_TIMER, &timer_active);
+ #endif
  	}
  }
  
! /*
!  * This routine gets called when tty_write has put something into
!  * the write_queue. It calls UART_ISR_proc to simulate an interrupt,
!  * which gets things going.
!  */
! void rs_write(struct tty_struct * tty)
  {
! 	struct async_struct *info;
  
! 	if (!tty || tty->stopped || EMPTY(&tty->write_q))
  		return;
+ 	info = rs_table + DEV_TO_SL(tty->line);
+ 	if (!test_bit(info->line, &rs_write_active)) {
+ 		cli();
+ 		UART_ISR_proc(info->ISR, info->line);
+ 		sti();
  	}
  	
  }
  
! static void rs_throttle(struct tty_struct * tty, int status)
  {
! 	struct async_struct *info;
! 	unsigned char mcr;
! 
! 	printk("throttle tty%d: %d (%d, %d)....\n", DEV_TO_SL(tty->line),
! 	       status, LEFT(&tty->read_q), LEFT(&tty->secondary));
! 	switch (status) {
! 	case TTY_THROTTLE_RQ_FULL:
! 		info = rs_table + DEV_TO_SL(tty->line);
! 		if (tty->termios->c_iflag & IXOFF) {
! 			info->x_char = STOP_CHAR(tty);
! 		} else if ((tty->termios->c_cflag & CRTSCTS) ||
! 			   ((inb(UART_MSR + info->port) & UART_MSR_DSR) &&
! 			    !(tty->termios->c_cflag & CNORTSCTS))) {
! 			mcr = inb(UART_MCR + info->port);
! 			mcr &= ~UART_MCR_RTS;
! 			outb_p(mcr, UART_MCR + info->port);
! 		}
! 		break;
! 	case TTY_THROTTLE_RQ_AVAIL:
! 		info = rs_table + DEV_TO_SL(tty->line);
! 		if (tty->termios->c_iflag & IXOFF) {
! 			cli();
! 			if (info->x_char)
! 				info->x_char = 0;
! 			else
! 				info->x_char = START_CHAR(tty);
! 			sti();
! 		} else if ((tty->termios->c_cflag & CRTSCTS) ||
! 			   ((inb(UART_MSR + info->port) & UART_MSR_DSR) &&
! 			    !(tty->termios->c_cflag & CNORTSCTS))) {
! 			mcr = inb(UART_MCR + info->port);
! 			mcr |= UART_MCR_RTS;
! 			outb_p(mcr, UART_MCR + info->port);
! 		}
! 		break;
! 	}
  }
  
  /*
***************
*** 401,410 ****
  	info = rs_table + line;
  	if (!info->port)
  		return;
  	info->tty = 0;
- 	outb_p(0x00, UART_IER + info->port);	/* disable all intrs */
- 	outb_p(0x00, UART_MCR + info->port);	/* reset DTR, RTS, */
- 	outb(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */
  	ISR = info->ISR;
  	irq = ISR->irq;
  	if (irq == 2)
--- 459,466 ----
  	info = rs_table + line;
  	if (!info->port)
  		return;
+ 	shutdown(info);
  	info->tty = 0;
  	ISR = info->ISR;
  	irq = ISR->irq;
  	if (irq == 2)
***************
*** 473,478 ****
--- 529,548 ----
  	}
  }
  
+ static void shutdown(struct async_struct * info)
+ {
+ 	unsigned short port = info->port;
+ 	
+ 	outb_p(0x00,		UART_IER + port);	/* disable all intrs */
+ 	if (info->tty && !(info->tty->termios->c_cflag & HUPCL))
+ 		outb_p(UART_MCR_DTR, UART_MCR + port);
+ 	else
+ 		/* reset DTR,RTS,OUT_2 */		
+ 		outb_p(0x00,		UART_MCR + port);
+ 	outb_p(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */
+ 	(void)inb(UART_RX + port);     /* read data port to reset things */
+ }
+ 
  void change_speed(unsigned int line)
  {
  	struct async_struct * info;
***************
*** 509,519 ****
  		quot = 0;
  		info->timeout = 0;
  	}
! 	if (!quot)
! 		outb(0x00,UART_MCR + port);
! 	else if (!inb(UART_MCR + port))
! 		startup(info);
! /* byte size and parity */
  	cval = cflag & (CSIZE | CSTOPB);
  	cval >>= 4;
  	if (cflag & PARENB)
--- 579,589 ----
  		quot = 0;
  		info->timeout = 0;
  	}
! 	if (!quot) {
! 		shutdown(info);
! 		return;
! 	}
! 	/* byte size and parity */
  	cval = cflag & (CSIZE | CSTOPB);
  	cval >>= 4;
  	if (cflag & PARENB)
***************
*** 569,578 ****
  	irq = ISR->irq;
  	if (irq == 2)
  		irq = 9;
- 	if (!new_irq)
- 		new_irq = irq;
- 	if (!new_port)
- 		new_port = info->port;
  	if (irq != new_irq) {
  		/*
  		 * We need to change the IRQ for this board.  OK, if
--- 639,644 ----
***************
*** 614,624 ****
  	}
  	cli();
  	if (new_port != info->port) {
! 		outb_p(0x00, UART_IER + info->port);	/* disable all intrs */
! 		outb(0x00, UART_MCR + info->port);	/* reset DTR, RTS, */
  		info->port = new_port;
- 		init(info);
  		startup(info);
  	}
  	sti();
  	return 0;
--- 680,689 ----
  	}
  	cli();
  	if (new_port != info->port) {
! 		shutdown(info);
  		info->port = new_port;
  		startup(info);
+ 		change_speed(info->line);
  	}
  	sti();
  	return 0;
***************
*** 737,748 ****
  	if ((line < 0) || (line >= NR_PORTS))
  		return -ENODEV;
  	info = rs_table + line;
! 	if (!info->port)
  		return -ENODEV;
  	info->tty = tty;
  	tty->write = rs_write;
  	tty->close = rs_close;
  	tty->ioctl = rs_ioctl;
  	ISR = info->ISR;
  	irq = ISR->irq;
  	if (irq == 2)
--- 802,814 ----
  	if ((line < 0) || (line >= NR_PORTS))
  		return -ENODEV;
  	info = rs_table + line;
! 	if (!info->port || !info->ISR->irq)
  		return -ENODEV;
  	info->tty = tty;
  	tty->write = rs_write;
  	tty->close = rs_close;
  	tty->ioctl = rs_ioctl;
+ 	tty->throttle = rs_throttle;
  	ISR = info->ISR;
  	irq = ISR->irq;
  	if (irq == 2)
***************
*** 771,785 ****
  	return 0;
  }
  
  long rs_init(long kmem_start)
  {
  	int i;
  	struct async_struct * info;
! 
  	timer_table[RS_TIMER].fn = rs_timer;
  	timer_table[RS_TIMER].expires = 0;
  	for (i = 0; i < 16; i++) {
  		IRQ_ISR[i] = 0;
  	}
  	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
  		info->line = i;
--- 837,969 ----
  	return 0;
  }
  
+ static void init(struct async_struct * info)
+ {
+ #ifdef AUTO_IRQ
+ 	unsigned char status1, status2, scratch, save_ICP=0;
+ 	unsigned short ICP=0, port = info->port;
+ 	unsigned long timeout;
+ 
+ 	/*
+ 	 * Enable interrupts and see who answers
+ 	 */
+ 	rs_irq_triggered = 0;
+ 	scratch = inb_p(UART_IER + port);
+ 	status1 = inb_p(UART_MCR + port);
+ 	if (info->flags & ASYNC_FOURPORT)  {
+ 		outb_p(UART_MCR_DTR | UART_MCR_RTS, UART_MCR + port);
+ 		outb_p(0x0f,UART_IER + port);	/* enable all intrs */
+ 		ICP = (port & 0xFE0) | 0x01F;
+ 		save_ICP = inb_p(ICP);
+ 		outb_p(0x80, ICP);
+ 		(void) inb(ICP);
+ 	} else {
+ 		outb_p(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, 
+ 		       UART_MCR + port);
+ 		outb_p(0x0f,UART_IER + port);	/* enable all intrs */
+ 	}
+ 	timeout = jiffies+2;
+ 	while (timeout >= jiffies) {
+ 		if (rs_irq_triggered)
+ 			break;
+ 	}
+ 	/*
+ 	 * Now check to see if we got any business, and clean up.
+ 	 */
+ 	if (rs_irq_triggered) {
+ 		outb_p(0, UART_IER + port);
+ 		info->ISR->irq = rs_irq_triggered;
+ 	} else {
+ 		outb_p(scratch, UART_IER + port);
+ 		outb_p(status1, UART_MCR + port);
+ 		if (info->flags & ASYNC_FOURPORT)
+ 			outb_p(save_ICP, ICP);
+ 		info->type = PORT_UNKNOWN;
+ 		return;
+ 	}
+ #else /* AUTO_IRQ */
+ 	unsigned char status1, status2, scratch, scratch2;
+ 	unsigned short port = info->port;
+ 
+ 	/* 
+ 	 * Check to see if a UART is really there.  
+ 	 */
+ 	scratch = inb_p(UART_MCR + port);
+ 	outb_p(UART_MCR_LOOP | scratch, UART_MCR + port);
+ 	scratch2 = inb_p(UART_MSR + port);
+ 	outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
+ 	status1 = inb_p(UART_MSR + port) & 0xF0;
+ 	outb_p(scratch, UART_MCR + port);
+ 	outb_p(scratch2, UART_MSR + port);
+ 	if (status1 != 0x90) {
+ 		info->type = PORT_UNKNOWN;
+ 		return;
+ 	}
+ #endif /* AUTO_IRQ */
+ 	
+ 	if (!(info->flags & ASYNC_NOSCRATCH)) {
+ 		scratch = inb(UART_SCR + port);
+ 		outb_p(0xa5, UART_SCR + port);
+ 		status1 = inb(UART_SCR + port);
+ 		outb_p(0x5a, UART_SCR + port);
+ 		status2 = inb(UART_SCR + port);
+ 		outb_p(scratch, UART_SCR + port);
+         } else {
+ 	  	status1 = 0xa5;
+ 		status2 = 0x5a;
+ 	}
+ 	if (status1 == 0xa5 && status2 == 0x5a) {
+ 		outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
+ 		scratch = inb(UART_IIR + port) >> 6;
+ 		info->xmit_fifo_size = 1;
+ 		switch (scratch) {
+ 			case 0:
+ 				info->type = PORT_16450;
+ 				break;
+ 			case 1:
+ 				info->type = PORT_UNKNOWN;
+ 				break;
+ 			case 2:
+ 				info->type = PORT_16550;
+ 				break;
+ 			case 3:
+ 				info->type = PORT_16550A;
+ 				info->xmit_fifo_size = 16;
+ 				break;
+ 		}
+ 	} else
+ 		info->type = PORT_8250;
+ 	startup(info);
+ 	change_speed(info->line);
+ 	shutdown(info);
+ }
+ 
  long rs_init(long kmem_start)
  {
  	int i;
  	struct async_struct * info;
! #ifdef AUTO_IRQ
! 	int irq_lines = 0;
! 	struct sigaction sa;
! 	/*
! 	 *  We will be auto probing for irq's, so turn on interrupts now!
! 	 */
! 	sti();
! 	
! 	sa.sa_handler = rs_probe;
! 	sa.sa_flags = (SA_INTERRUPT);
! 	sa.sa_mask = 0;
! 	sa.sa_restorer = NULL;
! #endif	
  	timer_table[RS_TIMER].fn = rs_timer;
  	timer_table[RS_TIMER].expires = 0;
+ 	
  	for (i = 0; i < 16; i++) {
  		IRQ_ISR[i] = 0;
+ #ifdef AUTO_IRQ
+ 		if (!irqaction(i, &sa))
+ 			irq_lines |= 1 << i;
+ #endif
  	}
  	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
  		info->line = i;
***************
*** 787,792 ****
--- 971,978 ----
  		info->type = PORT_UNKNOWN;
  		info->timer = 0;
  		info->custom_divisor = 0;
+ 		info->x_char = 0;
+ 		info->event = 0;
  		if (!info->ISR->line) {
  			info->ISR->line = i;
  			info->ISR->refcnt = 0;
***************
*** 817,822 ****
--- 1003,1015 ----
  				break;
  		}
  	}
+ #ifdef AUTO_IRQ
+ 	cli();
+ 	for (i = 0; i < 16; i++) {
+ 		if (irq_lines & (1 << i))
+ 			free_irq(i);
+ 	}
+ #endif
  	return kmem_start;
  }
  
===================================================================
RCS file: kernel/chr_drv/RCS/tty_io.c,v
retrieving revision 1.1
diff -c -r1.1 kernel/chr_drv/tty_io.c
*** 1.1	1992/10/05 01:27:12
--- kernel/chr_drv/tty_io.c	1992/10/06 11:27:34
***************
*** 33,41 ****
  #include <linux/mm.h>
  #include <linux/string.h>
  
- #include <asm/io.h>
  #include <asm/segment.h>
  #include <asm/system.h>
  
  #include "vt_kern.h"
  
--- 33,41 ----
  #include <linux/mm.h>
  #include <linux/string.h>
  
  #include <asm/segment.h>
  #include <asm/system.h>
+ #include <asm/bitops.h>
  
  #include "vt_kern.h"
  
***************
*** 207,214 ****
  				tty->stopped=1;
  				continue;
  			}
! 			if ((START_CHAR(tty) != __DISABLED_CHAR) &&
! 			    (c==START_CHAR(tty))) {
  			        tty->status_changed = 1;
  				tty->ctrl_status |= TIOCPKT_START;
  				tty->stopped=0;
--- 207,215 ----
  				tty->stopped=1;
  				continue;
  			}
! 			if (((I_IXANY(tty)) && tty->stopped) ||
! 			    ((START_CHAR(tty) != __DISABLED_CHAR) &&
! 			     (c==START_CHAR(tty)))) {
  			        tty->status_changed = 1;
  				tty->ctrl_status |= TIOCPKT_START;
  				tty->stopped=0;
***************
*** 258,264 ****
  	if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW)
  	    && !clear_bit(TTY_RQ_THROTTLED, &tty->flags))
  		tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL);
! 	
  }
  
  int is_ignored(int sig)
--- 259,267 ----
  	if (tty->throttle && (LEFT(&tty->read_q) >= RQ_THRESHOLD_HW)
  	    && !clear_bit(TTY_RQ_THROTTLED, &tty->flags))
  		tty->throttle(tty, TTY_THROTTLE_RQ_AVAIL);
! 	if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
! 	    && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
! 		tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
  }
  
  int is_ignored(int sig)
***************
*** 373,378 ****
--- 376,388 ----
  				break;
  		};
  		wake_up(&tty->read_q.proc_list);
+ 		/*
+ 		 * If there is enough space in the secondary queue
+ 		 * now, let the low-level driver know.
+ 		 */
+ 		if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
+ 		    && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
+ 			tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
  		if (b-buf >= minimum || !current->timeout)
  			break;
  		if (current->signal & ~current->blocked) 
***************
*** 382,394 ****
  		TTY_READ_FLUSH(tty);
  		if (tty->link)
  			TTY_WRITE_FLUSH(tty->link);
- 		/*
- 		 * If there is enough space in the secondary queue
- 		 * now, let the low-level driver know.
- 		 */
- 		if (tty->throttle && (LEFT(&tty->secondary) >= SQ_THRESHOLD_HW)
- 		    && !clear_bit(TTY_SQ_THROTTLED, &tty->flags))
- 			tty->throttle(tty, TTY_THROTTLE_SQ_AVAIL);
  		cli();
  		if (EMPTY(&tty->secondary))
  			interruptible_sleep_on(&tty->secondary.proc_list);
--- 392,397 ----
***************
*** 558,565 ****
  			return retval;
  		}
  		if (IS_A_PTY(dev) && !tty_table[PTY_OTHER(dev)]) {
! 			o_tty = tty_table[PTY_OTHER(dev)] =
! 				(struct tty_struct *) get_free_page(GFP_USER);
  			if (!o_tty) {
  				free_page((unsigned long)tty);
  				return -ENOMEM;
--- 561,575 ----
  			return retval;
  		}
  		if (IS_A_PTY(dev) && !tty_table[PTY_OTHER(dev)]) {
! 			o_tty = (struct tty_struct *) get_free_page(GFP_USER);
! 			/*
! 			 * Check for race condition, since get_free_page may sleep.
! 			 */
! 			if (tty_table[PTY_OTHER(dev)]) {
! 				free_page((unsigned long) o_tty);
! 				goto other_done;
! 			}
! 			tty_table[PTY_OTHER(dev)] = o_tty;
  			if (!o_tty) {
  				free_page((unsigned long)tty);
  				return -ENOMEM;
***************
*** 573,578 ****
--- 583,589 ----
  			tty->link = o_tty;
  			o_tty->link = tty;
  		}
+ 	other_done:
  	}
  	if (IS_A_PTY_MASTER(dev)) {
  		if (tty->count)
***************
*** 650,661 ****
  		return;
  	if (tty->close)
  		tty->close(tty, filp);
! 	if (!tty->count && (tty == redirect))
  		redirect = NULL;
! 	if (tty = tty->link)
! 		if (!tty->count && (tty == redirect))
! 			redirect = NULL;
! 	if (!tty->count && !(tty->link && tty->link->count)) {
  		if (tty->link) {
  			free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev)));
  			TTY_TABLE(PTY_OTHER(dev)) = 0;
--- 661,671 ----
  		return;
  	if (tty->close)
  		tty->close(tty, filp);
! 	if (tty == redirect)
  		redirect = NULL;
! 	if (tty->link && !tty->link->count && (tty->link == redirect))
! 		redirect = NULL;
! 	if (!tty->link || !tty->link->count) {
  		if (tty->link) {
  			free_page((unsigned long) TTY_TABLE(PTY_OTHER(dev)));
  			TTY_TABLE(PTY_OTHER(dev)) = 0;
***************
*** 720,725 ****
--- 730,776 ----
  };
  
  /*
+  * This implements the "Secure Attention Key" ---  the idea is to
+  * prevent trojan horses by killing all processes associated with this
+  * tty when the user hits the "Secure Attention Key".  Required for
+  * super-paranoid applications --- see the Orange Book for more details.
+  * 
+  * This code could be nicer; ideally it should send a HUP, wait a few
+  * seconds, then send a INT, and then a KILL signal.  But you then
+  * have to coordinate with the init process, since all processes associated
+  * with the current tty must be dead before the new getty is allowed
+  * to spawn.
+  */
+ void do_SAK( struct tty_struct *tty)
+ {
+ 	struct task_struct **p;
+ 	int line = tty->line;
+ 	int session = tty->session;
+ 	int		i;
+ 	struct file	*filp;
+ 	
+ 	flush_input(tty);
+ 	flush_output(tty);
+  	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
+ 		if (!(*p))
+ 			continue;
+ 		if (((*p)->tty == line) ||
+ 		    ((session > 0) && ((*p)->session == session)))
+ 			send_sig(SIGKILL, *p, 1);
+ 		else {
+ 			for (i=0; i < NR_FILE; i++) {
+ 				filp = (*p)->filp[i];
+ 				if (filp && (filp->f_op == &tty_fops) &&
+ 				    (MINOR(filp->f_rdev) == line)) {
+ 					send_sig(SIGKILL, *p, 1);
+ 					break;
+ 				}
+ 			}
+ 		}
+ 	}
+ }
+ 
+ /*
   * This subroutine initializes a tty structure.  We have to set up
   * things correctly for each different type of tty.
   */
***************
*** 739,756 ****
  		memset(tp, 0, sizeof(struct termios));
  		memcpy(tp->c_cc, INIT_C_CC, NCCS);
  		if (IS_A_CONSOLE(line)) {
! 			tp->c_iflag = ICRNL;
  			tp->c_oflag = OPOST | ONLCR;
! 			tp->c_cflag = B38400 | CS8;
! 			tp->c_lflag = IXON | ISIG | ICANON | ECHO |
! 				ECHOCTL | ECHOKE;
  		} else if IS_A_SERIAL(line) {
! 			tp->c_cflag = B2400 | CS8;
  		} else if IS_A_PTY_MASTER(line)	{
! 			tp->c_cflag = B9600 | CS8;
  		} else if IS_A_PTY_SLAVE(line) {
! 			tp->c_cflag = B9600 | CS8;
! 			tp->c_lflag = IXON | ISIG | ICANON;
  		}
  	}
  	tty->termios = tty_termios[line];
--- 790,806 ----
  		memset(tp, 0, sizeof(struct termios));
  		memcpy(tp->c_cc, INIT_C_CC, NCCS);
  		if (IS_A_CONSOLE(line)) {
! 			tp->c_iflag = ICRNL | IXON;
  			tp->c_oflag = OPOST | ONLCR;
! 			tp->c_cflag = B38400 | CS8 | CREAD;
! 			tp->c_lflag = ISIG | ICANON | ECHO | ECHOCTL | ECHOKE;
  		} else if IS_A_SERIAL(line) {
! 			tp->c_cflag = B2400 | CS8 | CREAD | HUPCL;
  		} else if IS_A_PTY_MASTER(line)	{
! 			tp->c_cflag = B9600 | CS8 | CREAD;
  		} else if IS_A_PTY_SLAVE(line) {
! 			tp->c_cflag = B9600 | CS8 | CREAD;
! 			tp->c_lflag = ISIG | ICANON;
  		}
  	}
  	tty->termios = tty_termios[line];
